/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 *           (C) 2000 Dirk Mueller (mueller@kde.org)
 *           (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com)
 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 Apple Inc.
 *               All rights reserved.
 * Copyright (C) 2009 Google Inc. All rights reserved.
 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved.
 *               (http://www.torchmobile.com/)
 *
 * 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_object.h"

#include <algorithm>
#include <memory>
#include <type_traits>
#include <utility>

#include "base/auto_reset.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "partition_alloc/partition_alloc.h"
#include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
#include "third_party/blink/renderer/core/animation/element_animations.h"
#include "third_party/blink/renderer/core/css/counter_style.h"
#include "third_party/blink/renderer/core/css/properties/longhands.h"
#include "third_party/blink/renderer/core/css/resolver/style_adjuster.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/css/style_containment_scope.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/display_lock/display_lock_utilities.h"
#include "third_party/blink/renderer/core/dom/column_pseudo_element.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/dom/first_letter_pseudo_element.h"
#include "third_party/blink/renderer/core/dom/pseudo_element.h"
#include "third_party/blink/renderer/core/dom/scroll_marker_pseudo_element.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
#include "third_party/blink/renderer/core/editing/layout_selection.h"
#include "third_party/blink/renderer/core/editing/position_with_affinity.h"
#include "third_party/blink/renderer/core/editing/text_affinity.h"
#include "third_party/blink/renderer/core/editing/visible_units.h"
#include "third_party/blink/renderer/core/frame/event_handler_registry.h"
#include "third_party/blink/renderer/core/frame/local_frame.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_select_element.h"
#include "third_party/blink/renderer/core/html/html_body_element.h"
#include "third_party/blink/renderer/core/html/html_element.h"
#include "third_party/blink/renderer/core/html/html_hr_element.h"
#include "third_party/blink/renderer/core/html/html_html_element.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
#include "third_party/blink/renderer/core/html/html_summary_element.h"
#include "third_party/blink/renderer/core/html/html_table_cell_element.h"
#include "third_party/blink/renderer/core/html/html_table_element.h"
#include "third_party/blink/renderer/core/html/image_document.h"
#include "third_party/blink/renderer/core/html/media/html_video_element.h"
#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
#include "third_party/blink/renderer/core/intersection_observer/element_intersection_observer_data.h"
#include "third_party/blink/renderer/core/layout/block_node.h"
#include "third_party/blink/renderer/core/layout/custom/layout_custom.h"
#include "third_party/blink/renderer/core/layout/flex/layout_flexible_box.h"
#include "third_party/blink/renderer/core/layout/forms/layout_fieldset.h"
#include "third_party/blink/renderer/core/layout/geometry/axis.h"
#include "third_party/blink/renderer/core/layout/geometry/transform_state.h"
#include "third_party/blink/renderer/core/layout/grid/layout_grid.h"
#include "third_party/blink/renderer/core/layout/grid_lanes/layout_grid_lanes.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/layout_counter.h"
#include "third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/layout/layout_image.h"
#include "third_party/blink/renderer/core/layout/layout_image_resource_style_image.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_object_inl.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_ruby_as_block.h"
#include "third_party/blink/renderer/core/layout/layout_text_combine.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/list/layout_inline_list_item.h"
#include "third_party/blink/renderer/core/layout/list/layout_inside_list_marker.h"
#include "third_party/blink/renderer/core/layout/list/layout_list_item.h"
#include "third_party/blink/renderer/core/layout/list/layout_outside_list_marker.h"
#include "third_party/blink/renderer/core/layout/mathml/layout_mathml_block.h"
#include "third_party/blink/renderer/core/layout/physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_info.h"
#include "third_party/blink/renderer/core/layout/table/layout_table.h"
#include "third_party/blink/renderer/core/layout/table/layout_table_caption.h"
#include "third_party/blink/renderer/core/layout/table/layout_table_cell.h"
#include "third_party/blink/renderer/core/layout/table/layout_table_column.h"
#include "third_party/blink/renderer/core/layout/table/layout_table_row.h"
#include "third_party/blink/renderer/core/layout/table/layout_table_section.h"
#include "third_party/blink/renderer/core/layout/unpositioned_float.h"
#include "third_party/blink/renderer/core/overscroll/overscroll_area_tracker.h"
#include "third_party/blink/renderer/core/page/autoscroll_controller.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/paint/clip_path_clipper.h"
#include "third_party/blink/renderer/core/paint/fragment_data_iterator.h"
#include "third_party/blink/renderer/core/paint/object_paint_invalidator.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/paint/paint_property_tree_builder.h"
#include "third_party/blink/renderer/core/paint/timing/paint_timing_detector.h"
#include "third_party/blink/renderer/core/scroll/scroll_into_view_util.h"
#include "third_party/blink/renderer/core/style/computed_style_base_constants.h"
#include "third_party/blink/renderer/core/style/computed_style_constants.h"
#include "third_party/blink/renderer/core/style/content_data.h"
#include "third_party/blink/renderer/core/style/cursor_data.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
#include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h"
#include "third_party/blink/renderer/platform/graphics/touch_action.h"
#include "third_party/blink/renderer/platform/heap/disallow_new_wrapper.h"
#include "third_party/blink/renderer/platform/heap/thread_state.h"
#include "third_party/blink/renderer/platform/heap/thread_state_storage.h"
#include "third_party/blink/renderer/platform/instrumentation/instance_counters.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/format.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
#include "third_party/skia/include/docs/SkPDFDocument.h"
#include "ui/gfx/geometry/rect_conversions.h"

namespace blink {

namespace {

LayoutObject* FindColumnSpannerContainer(
    const LayoutObject* spanner,
    LayoutObject::AncestorSkipInfo* skip_info) {
  DCHECK(spanner->IsColumnSpanAll());
  for (LayoutObject* walker = spanner->Parent(); walker;
       walker = walker->ContainingBlock(skip_info)) {
    if (walker->IsMulticolContainer()) {
      return walker;
    }
    if (skip_info) {
      skip_info->Update(*walker);
    }
  }
  // It's possible to end up here, because this function is also called after
  // the object has been taken out of the tree, during destruction.
  return nullptr;
}

template <typename Predicate>
LayoutObject* FindAncestorByPredicate(const LayoutObject* descendant,
                                      LayoutObject::AncestorSkipInfo* skip_info,
                                      Predicate predicate) {
  for (auto* object = descendant->Parent(); object;) {
    if (predicate(object))
      return object;
    if (skip_info)
      skip_info->Update(*object);

    if (object->IsColumnSpanAll()) [[unlikely]] {
      object = FindColumnSpannerContainer(object, skip_info);
      continue;
    }
    object = object->Parent();
  }
  return nullptr;
}

inline bool CalculateCanTraversePhysicalFragments(const LayoutObject& obj) {
  // The NG paint system currently doesn't support replaced content.
  if (obj.IsLayoutReplaced())
    return false;
  // Text controls have some logic in the layout objects that will be missed if
  // we traverse the fragment tree when hit-testing.
  if (obj.IsTextControl()) {
    return false;
  }
  // Table columns don't generate fragments.
  if (obj.IsLayoutTableCol()) {
    return false;
  }
  // SVG objects do their own things. The exception here is SVG block containers
  // (such as foreignObject or text), which are fully handled by the regular
  // layout engine.
  if (obj.IsSVG() && !obj.IsLayoutBlock()) {
    return false;
  }

  return true;
}

bool HasNativeBackgroundPainter(Node* node) {
  if (!RuntimeEnabledFeatures::CompositeBGColorAnimationEnabled())
    return false;

  Element* element = DynamicTo<Element>(node);
  if (!element)
    return false;

  ElementAnimations* element_animations = element->GetElementAnimations();
  if (!element_animations)
    return false;

  return element_animations->CompositedBackgroundColorStatus() ==
         ElementAnimations::CompositedPaintStatus::kComposited;
}

bool NeedsForcedUpdateForBackgroundPainter(Node* node) {
  Element* element = To<Element>(node);
  ElementAnimations* element_animations = element->GetElementAnimations();
  CHECK(element_animations);
  NativePaintWorkletData* npw_data =
      element_animations->GetBackgroundColorNpwData();
  CHECK(npw_data);
  if (npw_data->NeedsKeyframeSnapshotUpdate()) {
    return true;
  }

  return false;
}

bool HasClipPathPaintWorklet(Node* node) {
  if (!RuntimeEnabledFeatures::CompositeClipPathAnimationEnabled())
    return false;

  Element* element = DynamicTo<Element>(node);
  if (!element)
    return false;

  ElementAnimations* element_animations = element->GetElementAnimations();
  if (!element_animations)
    return false;

  return element_animations->CompositedClipPathStatus() ==
         ElementAnimations::CompositedPaintStatus::kComposited;
}

StyleDifference AdjustForCompositableAnimationPaint(
    const ComputedStyle* old_style,
    const ComputedStyle* new_style,
    Node* node,
    StyleDifference diff) {
  DCHECK(new_style);

  bool skip_background_color_paint_invalidation =
      HasNativeBackgroundPainter(node)
          ? !NeedsForcedUpdateForBackgroundPainter(node)
          : !diff.background_color_changed;
  if (!skip_background_color_paint_invalidation)
    diff.SetNeedsNormalPaintInvalidation();

  bool skip_clip_path_paint_invalidation =
      !diff.clip_path_changed || HasClipPathPaintWorklet(node);
  if (!skip_clip_path_paint_invalidation)
    diff.SetNeedsNormalPaintInvalidation();

  return diff;
}

}  // namespace

static int g_allow_destroying_layout_object_in_finalizer = 0;

void ApplyVisibleOverflowToClipRect(OverflowClipAxes overflow_clip,
                                    PhysicalRect& clip_rect) {
  DCHECK_NE(overflow_clip, kOverflowClipBothAxis);
  const gfx::Rect infinite_rect(InfiniteIntRect());
  if ((overflow_clip & kOverflowClipX) == kNoOverflowClip) {
    clip_rect.offset.left = LayoutUnit(infinite_rect.x());
    clip_rect.size.width = LayoutUnit(infinite_rect.width());
  }
  if ((overflow_clip & kOverflowClipY) == kNoOverflowClip) {
    clip_rect.offset.top = LayoutUnit(infinite_rect.y());
    clip_rect.size.height = LayoutUnit(infinite_rect.height());
  }
}

PhysicalRect LocalRectForBoxQuad(const PhysicalBoxFragment& fragment,
                                 BoxQuadType box_type) {
  PhysicalRect rect({}, fragment.Size());
  switch (box_type) {
    case BoxQuadType::kMargin:
      rect.Expand(fragment.Margins());
      break;
    case BoxQuadType::kBorder:
      break;
    case BoxQuadType::kPadding:
      rect.Contract(fragment.Borders() + fragment.Scrollbar());
      break;
    case BoxQuadType::kContent:
      rect.Contract(fragment.Borders() + fragment.Scrollbar() +
                    fragment.Padding());
      break;
  }
  rect.size.width = rect.size.width.ClampNegativeToZero();
  rect.size.height = rect.size.height.ClampNegativeToZero();
  return rect;
}

AllowDestroyingLayoutObjectInFinalizerScope::
    AllowDestroyingLayoutObjectInFinalizerScope() {
  ++g_allow_destroying_layout_object_in_finalizer;
}
AllowDestroyingLayoutObjectInFinalizerScope::
    ~AllowDestroyingLayoutObjectInFinalizerScope() {
  CHECK_GT(g_allow_destroying_layout_object_in_finalizer, 0);
  --g_allow_destroying_layout_object_in_finalizer;
}

#if DCHECK_IS_ON()

LayoutObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope(
    LayoutObject& layout_object)
    : layout_object_(layout_object),
      preexisting_forbidden_(layout_object_.IsSetNeedsLayoutForbidden()) {
  layout_object_.SetNeedsLayoutIsForbidden(true);
}

LayoutObject::SetLayoutNeededForbiddenScope::~SetLayoutNeededForbiddenScope() {
  layout_object_.SetNeedsLayoutIsForbidden(preexisting_forbidden_);
}
#endif

struct SameSizeAsLayoutObject : public GarbageCollected<SameSizeAsLayoutObject>,
                                ImageResourceObserver,
                                DisplayItemClient {
  // Normally these additional bitfields can use the gap between
  // DisplayItemClient and bitfields_.
  uint8_t additional_bitfields_;
  uint16_t additional_bitfields2_;
#if DCHECK_IS_ON()
  unsigned debug_bitfields_;
#endif
  unsigned bitfields_;
  unsigned bitfields2_;
  unsigned bitfields3_;
  Member<void*> members[6];
#if DCHECK_IS_ON()
  bool is_destroyed_;
#endif
};

ASSERT_SIZE(LayoutObject, SameSizeAsLayoutObject);

LayoutObject* LayoutObject::CreateObject(Element* element,
                                         const ComputedStyle& style) {
  DCHECK(IsAllowedToModifyLayoutTreeStructure(element->GetDocument()));

  // Minimal support for content properties replacing an entire element.
  // Works only if we have exactly one piece of content and it's a URL, with
  // some optional alternative text. Otherwise acts as if we didn't support this
  // feature.
  const ContentData* content_data = style.GetContentData();
  if (!element->IsPseudoElement() &&
      ShouldUseContentDataForElement(content_data)) {
    LayoutImage* image = MakeGarbageCollected<LayoutImage>(element);
    // LayoutImageResourceStyleImage requires a style being present on the
    // image but we don't want to trigger a style change now as the node is
    // not fully attached. Moving this code to style change doesn't make sense
    // as it should be run once at layoutObject creation.
    image->SetStyleInternal(const_cast<ComputedStyle*>(&style));
    if (const StyleImage* style_image =
            To<ImageContentData>(content_data)->GetImage()) {
      image->SetImageResource(
          MakeGarbageCollected<LayoutImageResourceStyleImage>(
              const_cast<StyleImage*>(style_image)));
      image->SetIsGeneratedContent();
    } else {
      image->SetImageResource(MakeGarbageCollected<LayoutImageResource>());
    }
    image->ResetStyle();
    return image;
  } else if (element->GetPseudoId() == kPseudoIdMarker) {
    const Element* parent = element->parentElement();
    if (parent->GetComputedStyle()->MarkerShouldBeInside(
            *parent, style.GetDisplayStyle())) {
      return MakeGarbageCollected<LayoutInsideListMarker>(element);
    }
    return MakeGarbageCollected<LayoutOutsideListMarker>(element);
  }

  switch (style.Display()) {
    case EDisplay::kNone:
    case EDisplay::kContents:
      return nullptr;
    case EDisplay::kInline:
      return MakeGarbageCollected<LayoutInline>(element);
    case EDisplay::kInlineListItem:
      return MakeGarbageCollected<LayoutInlineListItem>(element);
    case EDisplay::kFlowRootListItem:
    case EDisplay::kInlineFlowRootListItem:
      [[fallthrough]];
    case EDisplay::kBlock:
    case EDisplay::kFlowRoot:
    case EDisplay::kInlineBlock:
    case EDisplay::kListItem:
      return CreateBlockFlowOrListItem(element, style);
    case EDisplay::kTable:
    case EDisplay::kInlineTable:
      return MakeGarbageCollected<LayoutTable>(element);
    case EDisplay::kTableRowGroup:
    case EDisplay::kTableHeaderGroup:
    case EDisplay::kTableFooterGroup:
      return MakeGarbageCollected<LayoutTableSection>(element);
    case EDisplay::kTableRow:
      return MakeGarbageCollected<LayoutTableRow>(element);
    case EDisplay::kTableColumnGroup:
    case EDisplay::kTableColumn:
      return MakeGarbageCollected<LayoutTableColumn>(element);
    case EDisplay::kTableCell:
      return MakeGarbageCollected<LayoutTableCell>(element);
    case EDisplay::kTableCaption:
      return MakeGarbageCollected<LayoutTableCaption>(element);
    case EDisplay::kWebkitBox:
    case EDisplay::kWebkitInlineBox:
      UseCounter::Count(element->GetDocument(),
                        WebFeature::kWebkitBoxWithoutWebkitLineClamp);
      return MakeGarbageCollected<LayoutFlexibleBox>(element);
    case EDisplay::kFlex:
    case EDisplay::kInlineFlex:
      UseCounter::Count(element->GetDocument(), WebFeature::kCSSFlexibleBox);
      return MakeGarbageCollected<LayoutFlexibleBox>(element);
    case EDisplay::kGrid:
    case EDisplay::kInlineGrid:
      UseCounter::Count(element->GetDocument(), WebFeature::kCSSGridLayout);
      return MakeGarbageCollected<LayoutGrid>(element);
    case EDisplay::kGridLanes:
    case EDisplay::kInlineGridLanes:
      // TODO(almaher): Add use counter for CSS Grid Lanes.
      return MakeGarbageCollected<LayoutGridLanes>(element);
    case EDisplay::kMath:
    case EDisplay::kBlockMath:
      return MakeGarbageCollected<LayoutMathMLBlock>(element);
    case EDisplay::kRuby:
      return MakeGarbageCollected<LayoutInline>(element);
    case EDisplay::kBlockRuby:
      return MakeGarbageCollected<LayoutRubyAsBlock>(element);
    case EDisplay::kRubyText:
      return MakeGarbageCollected<LayoutInline>(element);
    case EDisplay::kLayoutCustom:
    case EDisplay::kInlineLayoutCustom:
      return MakeGarbageCollected<LayoutCustom>(element);
  }

  NOTREACHED();
}

// static
LayoutBlockFlow* LayoutObject::CreateBlockFlowOrListItem(
    Element* element,
    const ComputedStyle& style) {
  if (style.IsDisplayListItem() && element &&
      element->GetPseudoId() != kPseudoIdBackdrop) {
    // Create a LayoutBlockFlow with a ListItemOrdinal and maybe a ::marker.
    // ::backdrop is excluded since it's not tree-abiding, and ListItemOrdinal
    // needs to traverse the tree.
    return MakeGarbageCollected<LayoutListItem>(element);
  }

  // Create a plain LayoutBlockFlow
  return MakeGarbageCollected<LayoutBlockFlow>(element);
}

LayoutObject::LayoutObject(Node* node)
    : is_anonymous_(!node),
      node_(node),
      fragment_(MakeGarbageCollected<FragmentDataList>()) {
#if DCHECK_IS_ON()
  fragment_->SetIsFirst();
#endif

  InstanceCounters::IncrementCounter(InstanceCounters::kLayoutObjectCounter);
  if (node_)
    GetFrameView()->IncrementLayoutObjectCount();
}

LayoutObject::~LayoutObject() {
  DCHECK(being_destroyed_);
#if DCHECK_IS_ON()
  DCHECK(is_destroyed_);
#endif
  InstanceCounters::DecrementCounter(InstanceCounters::kLayoutObjectCounter);
}

bool LayoutObject::IsDescendantOf(const LayoutObject* obj) const {
  NOT_DESTROYED();
  for (const LayoutObject* r = this; r; r = r->parent_) {
    if (r == obj)
      return true;
  }
  return false;
}

bool LayoutObject::IsContainedBy(const LayoutObject* obj) const {
  NOT_DESTROYED();
  AncestorSkipInfo skip_info(obj);
  for (const LayoutObject* r = this; r; r = r->Container(&skip_info)) {
    if (r == obj) {
      return true;
    }
    if (skip_info.AncestorSkipped()) {
      return false;
    }
  }
  return false;
}

bool LayoutObject::IsInlineRuby() const {
  NOT_DESTROYED();
  return IsLayoutInline() && StyleRef().Display() == EDisplay::kRuby;
}

bool LayoutObject::IsInlineRubyText() const {
  NOT_DESTROYED();
  return IsLayoutInline() && StyleRef().Display() == EDisplay::kRubyText;
}

bool LayoutObject::IsHR() const {
  NOT_DESTROYED();
  return IsA<HTMLHRElement>(GetNode());
}

bool LayoutObject::IsButtonOrInputButton() const {
  NOT_DESTROYED();
  return IsInputButton() || IsA<HTMLButtonElement>(GetNode());
}

bool LayoutObject::IsInputButton() const {
  NOT_DESTROYED();
  if (const auto* input = DynamicTo<HTMLInputElement>(GetNode())) {
    return input->IsButton();
  }
  return false;
}

bool LayoutObject::IsMenuList() const {
  NOT_DESTROYED();
  if (const auto* select = DynamicTo<HTMLSelectElement>(GetNode())) {
    return select->UsesMenuList();
  }
  return false;
}

bool LayoutObject::IsStyleGenerated() const {
  NOT_DESTROYED();
  if (const auto* layout_text_fragment = DynamicTo<LayoutTextFragment>(this))
    return !layout_text_fragment->AssociatedTextNode();

  const Node* node = GetNode();
  return !node || node->IsPseudoElement();
}

void LayoutObject::MarkMayContainAnchor() {
  for (LayoutObject* runner = this; runner && !runner->MayContainAnchor();
       runner = runner->Parent()) {
    runner->SetSelfMayContainAnchor();
  }
}

void LayoutObject::SetIsInsideMulticolIncludingDescendants(
    bool inside_multicol) {
  NOT_DESTROYED();
  LayoutObject* next;
  for (LayoutObject* object = this; object; object = next) {
    bool was_inside_multicol = object->IsInsideMulticol();
    object->SetIsInsideMulticol(inside_multicol);
    if (inside_multicol == was_inside_multicol ||
        object->IsMulticolContainer()) {
      // Not changed, or nested multicol (in which case the truth is still in
      // there). Skip the subtree.
      next = object->NextInPreOrderAfterChildren(this);
    } else {
      next = object->NextInPreOrder(this);
    }
  }
}

bool LayoutObject::RequiresAnonymousTableWrappers(
    const LayoutObject* new_child) const {
  NOT_DESTROYED();
  // Check should agree with:
  // CSS 2.1 Tables: 17.2.1 Anonymous table objects
  // http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
  if (new_child->IsLayoutTableCol()) {
    bool is_column_in_column_group =
        new_child->StyleRef().Display() == EDisplay::kTableColumn &&
        IsLayoutTableCol();
    return !IsTable() && !is_column_in_column_group;
  }
  if (new_child->IsTableCaption())
    return !IsTable();
  if (new_child->IsTableSection())
    return !IsTable();
  if (new_child->IsTableRow())
    return !IsTableSection();
  if (new_child->IsTableCell())
    return !IsTableRow();
  return false;
}

#if EXPENSIVE_DCHECKS_ARE_ON()
void LayoutObject::AssertFragmentTree(bool display_locked) const {
  NOT_DESTROYED();
  for (const LayoutObject* layout_object = this; layout_object;) {
    // |LayoutNGMixin::UpdateInFlowBlockLayout| may |SetNeedsLayout| to its
    // containing block. Don't check if it will be re-laid out.
    if (layout_object->NeedsLayout()) {
      layout_object = layout_object->NextInPreOrderAfterChildren(this);
      continue;
    }

    // If display-locked, fragments may not be removed from the tree even after
    // the |LayoutObject| was destroyed, but still they should be consistent.
    if (!display_locked && layout_object->ChildLayoutBlockedByDisplayLock()) {
      layout_object->AssertFragmentTree(
          /* display_locked */ true);
      layout_object = layout_object->NextInPreOrderAfterChildren(this);
      continue;
    }

    // Check the direct children of the fragment. Grand-children and further
    // descendants will be checked by descendant LayoutObjects.
    if (const auto* box = DynamicTo<LayoutBox>(layout_object)) {
      for (const PhysicalBoxFragment& fragment : box->PhysicalFragments()) {
        DCHECK_EQ(box, fragment.OwnerLayoutBox());
        fragment.AssertFragmentTreeChildren(
            /* allow_destroyed_or_moved */ display_locked);
      }
    }
    layout_object = layout_object->NextInPreOrder(this);
  }
}

void LayoutObject::AssertClearedPaintInvalidationFlags() const {
  NOT_DESTROYED();
  if (ChildPrePaintBlockedByDisplayLock())
    return;

  if (PaintInvalidationStateIsDirty()) {
    ShowLayoutTreeForThis();
    NOTREACHED();
  }

  // Assert that the number of FragmentData and PhysicalBoxFragment objects
  // are identical. This was added as part of investigating crbug.com/1244130

  // Only LayoutBox has fragments. Bail if it's not a box, or if fragment
  // traversal isn't supported here.
  if (!IsBox() || !CanTraversePhysicalFragments())
    return;

  // Make an exception for table columns (unless they establish a layer, which
  // would be dangerous (but hopefully also impossible)), since they don't
  // produce fragments.
  if (IsLayoutTableCol() && !HasLayer())
    return;

  // Make an exception for <frameset> children, which don't produce fragments
  // if the number of children is larger than <rows count> * <cols count>.
  if (Parent() && Parent()->IsFrameSet()) {
    return;
  }

  // Sometimes we just have a Layout(NG)View with no children, and the view is
  // not marked for layout, even if it has never been laid out. It seems that we
  // don't actually paint under such circumstances, which means that it doesn't
  // matter whether we have fragments or not. See crbug.com/1288742
  if (IsLayoutView() && !EverHadLayout() && !SlowFirstChild())
    return;

  wtf_size_t fragment_count = FragmentList().size();
  if (fragment_count != To<LayoutBox>(this)->PhysicalFragmentCount()) {
    ShowLayoutTreeForThis();
    DCHECK_EQ(fragment_count, To<LayoutBox>(this)->PhysicalFragmentCount());
  }
}
#endif  // EXPENSIVE_DCHECKS_ARE_ON()

DISABLE_CFI_PERF
void LayoutObject::AddChild(LayoutObject* new_child,
                            LayoutObject* before_child) {
  NOT_DESTROYED();
#if DCHECK_IS_ON()
  DCHECK(IsAllowedToModifyLayoutTreeStructure(GetDocument()) ||
         IsInDetachedNonDomTree());
#endif

  LayoutObjectChildList* children = VirtualChildren();
  DCHECK(children);
  if (!children)
    return;

  if (RequiresAnonymousTableWrappers(new_child)) {
    // Generate an anonymous table or reuse existing one from previous child
    // Per: 17.2.1 Anonymous table objects 3. Generate missing parents
    // http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes
    LayoutObject* table = nullptr;
    LayoutObject* after_child =
        before_child ? before_child->PreviousSibling() : children->LastChild();
    if (after_child && after_child->IsAnonymous() && after_child->IsTable()) {
      table = after_child;
    } else {
      table = LayoutTable::CreateAnonymousWithParent(*this);
      children->InsertChildNode(this, table, before_child);
    }
    table->AddChild(new_child);
  } else if (!LayoutTextCombine::IsSupportedMode(
                 new_child->StyleRef().GetWritingMode()) ||
             !new_child->IsText()) [[likely]] {
    children->InsertChildNode(this, new_child, before_child);
  } else if (IsA<LayoutTextCombine>(*this)) {
    DCHECK(LayoutTextCombine::ShouldBeParentOf(*new_child)) << new_child;
    new_child->SetStyle(&StyleRef());
    children->InsertChildNode(this, new_child, before_child);
  } else if (!IsHorizontalTypographicMode() &&
             LayoutTextCombine::ShouldBeParentOf(*new_child)) {
    if (before_child) {
      if (IsA<LayoutTextCombine>(before_child)) {
        DCHECK(!DynamicTo<LayoutTextCombine>(before_child->PreviousSibling()))
            << before_child->PreviousSibling();
        before_child->AddChild(new_child, before_child->SlowFirstChild());
      } else if (auto* const previous_sibling = DynamicTo<LayoutTextCombine>(
                     before_child->PreviousSibling())) {
        previous_sibling->AddChild(new_child);
      } else {
        children->InsertChildNode(
            this, LayoutTextCombine::CreateAnonymous(To<LayoutText>(new_child)),
            before_child);
      }
    } else if (auto* const last_child =
                   DynamicTo<LayoutTextCombine>(SlowLastChild())) {
      last_child->AddChild(new_child);
    } else {
      children->AppendChildNode(
          this, LayoutTextCombine::CreateAnonymous(To<LayoutText>(new_child)));
    }
  } else {
    // In case of append/insert <br style="writing-mode:vertical-rl">
    // See http://crbug.com/1222121 and http://crbug.com/1258331
    DCHECK(!new_child->IsHorizontalWritingMode()) << new_child;
    DCHECK(new_child->IsText()) << new_child;
    children->InsertChildNode(this, new_child, before_child);
  }

  if (auto* text = DynamicTo<LayoutText>(new_child)) {
    if (EnumHasFlags(new_child->StyleRef().TextTransform(),
                     ETextTransform::kCapitalize)) {
      text->TransformAndSecureOriginalText();
    }
  }
}

void LayoutObject::RemoveChild(LayoutObject* old_child) {
  NOT_DESTROYED();
#if DCHECK_IS_ON()
  DCHECK(IsAllowedToModifyLayoutTreeStructure(GetDocument()) ||
         IsInDetachedNonDomTree());
#endif

  LayoutObjectChildList* children = VirtualChildren();
  DCHECK(children);
  if (!children)
    return;

  LayoutBoxModelObject* previous_sibling =
      DynamicTo<LayoutBoxModelObject>(old_child->PreviousSibling());
  LayoutBoxModelObject* next_sibling =
      DynamicTo<LayoutBoxModelObject>(old_child->NextSibling());

  children->RemoveChildNode(this, old_child);

  LayoutBoxModelObject::AttemptToMerge(previous_sibling, next_sibling);
}

bool LayoutObject::IsInTopOrViewTransitionLayer() const {
  NOT_DESTROYED();
  if (IsViewTransitionRoot()) {
    return true;
  }
  return !IsDocumentElement() && Parent()->IsLayoutView();
}

void LayoutObject::NotifyPriorityScrollAnchorStatusChanged() {
  NOT_DESTROYED();
  if (!Parent())
    return;
  for (auto* layer = Parent()->EnclosingLayer(); layer;
       layer = layer->Parent()) {
    if (PaintLayerScrollableArea* scrollable_area =
            layer->GetScrollableArea()) {
      DCHECK(scrollable_area->GetScrollAnchor());
      scrollable_area->GetScrollAnchor()->ClearSelf();
    }
  }
}

void LayoutObject::RegisterSubtreeChangeListenerOnDescendants(bool value) {
  NOT_DESTROYED();
  // If we're set to the same value then we're done as that means it's
  // set down the tree that way already.
  if (subtree_change_listener_registered_ == value) {
    return;
  }

  subtree_change_listener_registered_ = value;

  for (LayoutObject* curr = SlowFirstChild(); curr; curr = curr->NextSibling())
    curr->RegisterSubtreeChangeListenerOnDescendants(value);
}

bool LayoutObject::NotifyOfSubtreeChange() {
  NOT_DESTROYED();
  if (!subtree_change_listener_registered_ || notified_of_subtree_change_) {
    return false;
  }
  notified_of_subtree_change_ = true;
  return true;
}

void LayoutObject::HandleSubtreeModifications() {
  NOT_DESTROYED();
  if (ConsumesSubtreeChangeNotification())
    SubtreeDidChange();
  notified_of_subtree_change_ = false;
}

LayoutObject* LayoutObject::NextInPreOrder() const {
  NOT_DESTROYED();
  if (LayoutObject* o = SlowFirstChild())
    return o;

  return NextInPreOrderAfterChildren();
}

bool LayoutObject::HasClipRelatedProperty() const {
  NOT_DESTROYED();
  // This function detects a bunch of properties that can potentially affect
  // clip inheritance chain. However such generalization is practically useless
  // because these properties change clip inheritance in different way that
  // needs to be handled explicitly.
  // CSS clip applies clip to the current element and all descendants.
  // CSS overflow clip applies only to containing-block descendants.
  // CSS contain:paint applies to all descendants by making itself a containing
  // block for all descendants.
  // CSS clip-path/mask/filter induces a stacking context and applies inherited
  // clip to that stacking context, while resetting clip for descendants. This
  // special behavior is already handled elsewhere.
  if (HasCSSClip() || ShouldClipOverflowAlongEitherAxis()) {
    return true;
  }
  // Paint containment establishes isolation which creates clip isolation nodes.
  // Style & Layout containment also establish isolation (see
  // |NeedsIsolationNodes| in PaintPropertyTreeBuilder).
  if (ShouldApplyPaintContainment() ||
      (ShouldApplyStyleContainment() && ShouldApplyLayoutContainment())) {
    return true;
  }
  return false;
}

bool LayoutObject::IsRenderedLegendInternal() const {
  NOT_DESTROYED();
  DCHECK(IsBox());
  DCHECK(IsRenderedLegendCandidate());

  const auto* parent = Parent();
  // We may not be inserted into the tree yet.
  if (!parent)
    return false;

  const auto* parent_layout_block = DynamicTo<LayoutBlock>(parent);
  return parent_layout_block && IsA<HTMLFieldSetElement>(parent->GetNode()) &&
         LayoutFieldset::FindInFlowLegend(*parent_layout_block) == this;
}

bool LayoutObject::IsScrollMarker() const {
  NOT_DESTROYED();
  return GetNode() && GetNode()->IsScrollMarkerPseudoElement();
}

bool LayoutObject::IsScrollMarkerGroup() const {
  NOT_DESTROYED();
  return GetNode() && GetNode()->IsScrollMarkerGroupPseudoElement();
}

bool LayoutObject::IsScrollMarkerGroupBefore() const {
  NOT_DESTROYED();
  return GetNode() && GetNode()->IsScrollMarkerGroupBeforePseudoElement();
}

bool LayoutObject::IsListMarkerForSummary() const {
  NOT_DESTROYED();
  if (!IsListMarker()) {
    return false;
  }
  if (const auto* summary =
          DynamicTo<HTMLSummaryElement>(Parent()->GetNode())) {
    if (!summary->IsMainSummary())
      return false;
    if (ListMarker::GetListStyleCategory(GetDocument(), StyleRef()) !=
        ListMarker::ListStyleCategory::kSymbol)
      return false;
    return ListMarker::GetCounterStyle(GetDocument(), StyleRef())
        .IsDisclosureMarker();
  }
  return false;
}

bool LayoutObject::IsInListMarker() const {
  NOT_DESTROYED();
  // List markers are either leaf nodes (legacy LayoutListMarker), or have
  // exactly one leaf child. So there's no need to traverse ancestors.
  return Parent() && Parent()->IsListMarker();
}

LayoutObject* LayoutObject::NextInPreOrderAfterChildren() const {
  NOT_DESTROYED();
  LayoutObject* o = NextSibling();
  if (!o) {
    o = Parent();
    while (o && !o->NextSibling())
      o = o->Parent();
    if (o)
      o = o->NextSibling();
  }

  return o;
}

LayoutObject* LayoutObject::NextInPreOrder(
    const LayoutObject* stay_within) const {
  NOT_DESTROYED();
  if (LayoutObject* o = SlowFirstChild())
    return o;

  return NextInPreOrderAfterChildren(stay_within);
}

LayoutObject* LayoutObject::PreviousInPostOrder(
    const LayoutObject* stay_within) const {
  NOT_DESTROYED();
  if (LayoutObject* o = SlowLastChild())
    return o;

  return PreviousInPostOrderBeforeChildren(stay_within);
}

LayoutObject* LayoutObject::NextInPreOrderAfterChildren(
    const LayoutObject* stay_within) const {
  NOT_DESTROYED();
  if (this == stay_within)
    return nullptr;

  const LayoutObject* current = this;
  LayoutObject* next = current->NextSibling();
  for (; !next; next = current->NextSibling()) {
    current = current->Parent();
    if (!current || current == stay_within)
      return nullptr;
  }
  return next;
}

LayoutObject* LayoutObject::PreviousInPostOrderBeforeChildren(
    const LayoutObject* stay_within) const {
  NOT_DESTROYED();
  if (this == stay_within)
    return nullptr;

  const LayoutObject* current = this;
  LayoutObject* previous = current->PreviousSibling();
  for (; !previous; previous = current->PreviousSibling()) {
    current = current->Parent();
    if (!current || current == stay_within)
      return nullptr;
  }
  return previous;
}

LayoutObject* LayoutObject::PreviousInPreOrder() const {
  NOT_DESTROYED();
  if (LayoutObject* o = PreviousSibling()) {
    while (LayoutObject* last_child = o->SlowLastChild())
      o = last_child;
    return o;
  }

  return Parent();
}

LayoutObject* LayoutObject::PreviousInPreOrder(
    const LayoutObject* stay_within) const {
  NOT_DESTROYED();
  if (this == stay_within)
    return nullptr;

  return PreviousInPreOrder();
}

unsigned LayoutObject::DepthSlow() const {
  NOT_DESTROYED();
  DCHECK_EQ(depth_, kMaxLayoutObjectDepth);

  // Walk up the ancestor chain to determine our depth.
  unsigned extra = 0;
  for (const LayoutObject* object = this; object; object = object->Parent()) {
    if (object->depth_ < kMaxLayoutObjectDepth) {
      return object->depth_ + extra;
    }
    ++extra;
  }

  NOTREACHED();
}

void LayoutObject::SetDepthIncludingDescendants(unsigned depth) {
  NOT_DESTROYED();
  unsigned clamped_depth = std::min(depth, kMaxLayoutObjectDepth);
  if (depth_ == clamped_depth) {
    return;
  }
  depth_ = clamped_depth;

  // This appears to be slow, but is fine in practice. The loop is very rare.
  // Typically the layout-object tree is built from the root, e.g. when setting
  // the depth there typically aren't any children.
  //
  // We only really hit this when performing a reinsert or shifting objects
  // to/from anonymous objects.
  for (LayoutObject* child = SlowFirstChild(); child;
       child = child->NextSibling()) {
    child->SetDepthIncludingDescendants(clamped_depth + 1);
  }
}

namespace {

struct CommonAncestorResult {
  STACK_ALLOCATED();

 public:
  const LayoutObject* common_ancestor = nullptr;
  // The last objects before reaching the common ancestor.
  const LayoutObject* last = nullptr;
  const LayoutObject* other_last = nullptr;
};

CommonAncestorResult CommonAncestorInternal(const LayoutObject* object,
                                            const LayoutObject* other_object) {
  unsigned depth = object->Depth();
  unsigned other_depth = other_object->Depth();

  // Jump up to the same level in the tree.
  while (depth > other_depth) {
    object = object->Parent();
    --depth;
  }
  while (other_depth > depth) {
    other_object = other_object->Parent();
    --other_depth;
  }

  // Walk up until we hit the same object.
  const LayoutObject* last = nullptr;
  const LayoutObject* other_last = nullptr;
  while (object != other_object) {
    last = object;
    other_last = other_object;
    object = object->Parent();
    other_object = other_object->Parent();
  }

  return {object, last, other_last};
}

}  // namespace

const LayoutObject* LayoutObject::CommonAncestor(
    const LayoutObject& other) const {
  NOT_DESTROYED();
  return CommonAncestorInternal(this, &other).common_ancestor;
}

bool LayoutObject::IsBeforeInPreOrder(const LayoutObject& other,
                                      IndexCache* index_cache) const {
  NOT_DESTROYED();
  DCHECK_NE(this, &other);
  CommonAncestorResult result = CommonAncestorInternal(this, &other);

  // Check if `this` is a direct ancestor of `other`.
  if (this == result.common_ancestor) {
    return true;
  }
  // Check if `other` is a direct ancestor of `this`.
  if (&other == result.common_ancestor) {
    return false;
  }

  DCHECK(result.last);
  DCHECK(result.other_last);

  if (index_cache) {
    auto add_result = index_cache->insert(result.common_ancestor, nullptr);
    if (add_result.is_new_entry) {
      // Build up the index cache for this layout-object's children this
      // prevents the O(N) loop below if called multiple times.
      auto* index_map = MakeGarbageCollected<
          GCedHeapHashMap<Member<const LayoutObject>, unsigned>>();
      unsigned index = 0;
      for (const LayoutObject* child = result.common_ancestor->SlowFirstChild();
           child; child = child->NextSibling()) {
        index_map->insert(child, index++);
      }
      add_result.stored_value->value = index_map;
    }

    const auto* index_map = add_result.stored_value->value.Get();
    return index_map->find(result.last)->value <
           index_map->find(result.other_last)->value;
  }

  // Try and walk towards each other, if we encounter the other we are before.
  const LayoutObject* forward = result.last;
  const LayoutObject* backward = result.other_last;
  while (forward && backward) {
    forward = forward->NextSibling();
    if (forward && forward == backward) {
      return true;
    }
    backward = backward->PreviousSibling();
    if (forward && forward == backward) {
      return true;
    }
  }

  // Either `forward` or `backward` are null, we didn't hit the other object so
  // `this` must be after `other`.
  return false;
}

static void AddLayers(LayoutObject* obj,
                      PaintLayer* parent_layer,
                      LayoutObject*& new_object,
                      PaintLayer*& before_child) {
  if (obj->HasLayer()) {
    if (!before_child && new_object) {
      // We need to figure out the layer that follows newObject. We only do
      // this the first time we find a child layer, and then we update the
      // pointer values for newObject and beforeChild used by everyone else.
      before_child =
          new_object->Parent()->FindNextLayer(parent_layer, new_object);
      new_object = nullptr;
    }
    parent_layer->AddChild(To<LayoutBoxModelObject>(obj)->Layer(),
                           before_child);
    return;
  }

  for (LayoutObject* curr = obj->SlowFirstChild(); curr;
       curr = curr->NextSibling())
    AddLayers(curr, parent_layer, new_object, before_child);
}

void LayoutObject::AddLayers(PaintLayer* parent_layer) {
  NOT_DESTROYED();
  if (!parent_layer)
    return;

  LayoutObject* object = this;
  PaintLayer* before_child = nullptr;
  blink::AddLayers(this, parent_layer, object, before_child);
}

void LayoutObject::RemoveLayers(PaintLayer* parent_layer) {
  NOT_DESTROYED();
  if (!parent_layer)
    return;

  if (HasLayer()) {
    parent_layer->RemoveChild(To<LayoutBoxModelObject>(this)->Layer());
    return;
  }

  for (LayoutObject* curr = SlowFirstChild(); curr; curr = curr->NextSibling())
    curr->RemoveLayers(parent_layer);
}

void LayoutObject::MoveLayers(PaintLayer* old_parent, PaintLayer* new_parent) {
  NOT_DESTROYED();
  if (!new_parent)
    return;

  if (HasLayer()) {
    PaintLayer* layer = To<LayoutBoxModelObject>(this)->Layer();
    DCHECK_EQ(old_parent, layer->Parent());
    if (old_parent)
      old_parent->RemoveChild(layer);
    new_parent->AddChild(layer);
    return;
  }

  for (LayoutObject* curr = SlowFirstChild(); curr; curr = curr->NextSibling())
    curr->MoveLayers(old_parent, new_parent);
}

PaintLayer* LayoutObject::FindNextLayer(PaintLayer* parent_layer,
                                        LayoutObject* start_point,
                                        bool check_parent) {
  NOT_DESTROYED();
  // Error check the parent layer passed in. If it's null, we can't find
  // anything.
  if (!parent_layer)
    return nullptr;

  // Step 1: If our layer is a child of the desired parent, then return our
  // layer.
  PaintLayer* our_layer =
      HasLayer() ? To<LayoutBoxModelObject>(this)->Layer() : nullptr;
  if (our_layer && our_layer->Parent() == parent_layer)
    return our_layer;

  // Step 2: If we don't have a layer, or our layer is the desired parent, then
  // descend into our siblings trying to find the next layer whose parent is the
  // desired parent.
  if (!our_layer || our_layer == parent_layer) {
    for (LayoutObject* curr = start_point ? start_point->NextSibling()
                                          : SlowFirstChild();
         curr; curr = curr->NextSibling()) {
      PaintLayer* next_layer =
          curr->FindNextLayer(parent_layer, nullptr, false);
      if (next_layer)
        return next_layer;
    }
  }

  // Step 3: If our layer is the desired parent layer, then we're finished. We
  // didn't find anything.
  if (parent_layer == our_layer)
    return nullptr;

  // Step 4: If |checkParent| is set, climb up to our parent and check its
  // siblings that follow us to see if we can locate a layer.
  if (check_parent && Parent())
    return Parent()->FindNextLayer(parent_layer, this, true);

  return nullptr;
}

PaintLayer* LayoutObject::EnclosingLayer() const {
  NOT_DESTROYED();
  for (const LayoutObject* current = this; current;
       current = current->Parent()) {
    if (current->HasLayer())
      return To<LayoutBoxModelObject>(current)->Layer();
  }
  // TODO(crbug.com/365897): we should get rid of detached layout subtrees, at
  // which point this code should not be reached.
  return nullptr;
}

PaintLayer* LayoutObject::PaintingLayer(int max_depth) const {
  NOT_DESTROYED();
  auto FindContainer = [](const LayoutObject& object) -> const LayoutObject* {
    if (object.IsColumnSpanAll()) {
      // Column spanners paint through their multicolumn containers.
      return object.ContainerForColumnSpanner();
    }
    // Physical fragments and fragment items for ruby-text boxes are not
    // managed by inline parents, and stored in a separated line of the IFC.
    if (object.IsInlineRubyText()) {
      return object.ContainingBlock();
    }
    if (IsA<LayoutView>(object))
      return object.GetFrame()->OwnerLayoutObject();
    return object.Parent();
  };
  int depth = 0;
  const LayoutObject* outermost = nullptr;
  for (const LayoutObject* current = this; current;
       outermost = current, current = FindContainer(*current)) {
    if (max_depth != -1 && ++depth > max_depth) {
      return nullptr;
    }
    if (current->HasLayer() &&
        To<LayoutBoxModelObject>(current)->Layer()->IsSelfPaintingLayer())
      return To<LayoutBoxModelObject>(current)->Layer();
  }

  if (const auto* box = DynamicTo<LayoutBox>(outermost)) {
    if (box->PhysicalFragmentCount()) {
      // Only actual page content is attached to the layout tree. Page boxes and
      // margin boxes are not, since they are not part of the DOM. Return the
      // LayoutView paint layer for such objects.
      const PhysicalBoxFragment& fragment = *box->GetPhysicalFragment(0);
      if (fragment.GetBoxType() == PhysicalFragment::kPageContainer ||
          fragment.GetBoxType() == PhysicalFragment::kPageBorderBox ||
          fragment.GetBoxType() == PhysicalFragment::kPageMargin) {
        return box->View()->Layer();
      }
    }
  }

  // TODO(crbug.com/365897): we should get rid of detached layout subtrees, at
  // which point this code should not be reached.
  return nullptr;
}

LayoutBox* LayoutObject::EnclosingBox() const {
  NOT_DESTROYED();
  LayoutObject* curr = const_cast<LayoutObject*>(this);
  while (curr) {
    if (curr->IsBox())
      return To<LayoutBox>(curr);
    curr = curr->Parent();
  }

  return nullptr;
}

LayoutBlockFlow* LayoutObject::FragmentItemsContainer() const {
  NOT_DESTROYED();
  DCHECK(!IsOutOfFlowPositioned());
  auto* block_flow = DynamicTo<LayoutBlockFlow>(ContainingNGBox());
  if (!block_flow) {
    return nullptr;
  }
#if EXPENSIVE_DCHECKS_ARE_ON()
  // Make sure that we don't skip blocks in the ancestry chain (which might
  // happen if there are out-of-flow positioned objects, for instance). In this
  // method we don't want to escape the enclosing inline formatting context.
  for (const LayoutObject* walker = Parent(); walker != block_flow;
       walker = walker->Parent())
    DCHECK(!walker->IsLayoutBlock());
#endif
  return block_flow;
}

LayoutBox* LayoutObject::ContainingNGBox() const {
  NOT_DESTROYED();
  if (auto* parent = Parent()) {
    // Media and Canvas elements may have children that participate
    // in layout with fragments that need invalidation after subtree layout.
    if (parent->IsMedia()) {
      return To<LayoutBox>(parent);
    }
    if (parent->IsCanvas() && RuntimeEnabledFeatures::CanvasDrawElementEnabled(
                                  GetDocument().GetExecutionContext())) {
      return To<LayoutBox>(parent);
    }
  }
  return ContainingBlock();
}

LayoutBlock* LayoutObject::ContainingFragmentationContextRoot() const {
  NOT_DESTROYED();
  if (!MightBeInsideFragmentationContext())
    return nullptr;
  bool found_column_spanner = IsColumnSpanAll();
  for (LayoutBlock* ancestor = ContainingBlock(); ancestor;
       ancestor = ancestor->ContainingBlock()) {
    if (ancestor->IsFragmentationContextRoot()) {
      // Column spanners do not participate in the fragmentation context
      // of their nearest fragmentation context, but rather the next above,
      // if there is one.
      if (found_column_spanner)
        return ancestor->ContainingFragmentationContextRoot();
      return ancestor;
    }
    if (ancestor->IsColumnSpanAll())
      found_column_spanner = true;
  }
  return nullptr;
}

bool LayoutObject::IsFirstInlineFragmentSafe() const {
  NOT_DESTROYED();
  DCHECK(IsInline());
  LayoutBlockFlow* block_flow = FragmentItemsContainer();
  return block_flow && !block_flow->NeedsLayout();
}

static inline bool ObjectIsRelayoutBoundary(const LayoutObject* object) {
  // Only LayoutBox (and subclasses) are allowed to be relayout roots.
  const auto* box = DynamicTo<LayoutBox>(object);
  if (!box) {
    return false;
  }

  // We need a previous layout result to begin layout at a subtree root.
  const LayoutResult* layout_result = box->GetCachedLayoutResult(nullptr);
  if (!layout_result) {
    return false;
  }

  // Make sure our fragment is safe to use.
  {
    const auto& fragment = layout_result->GetPhysicalFragment();
    if (fragment.IsLayoutObjectDestroyedOrMoved()) {
      return false;
    }

    // Fragmented nodes cannot be relayout roots.
    if (fragment.GetBreakToken()) {
      return false;
    }

    // Any propagated layout-objects will affect the our container chain.
    if (fragment.HasPropagatedLayoutObjects()) {
      return false;
    }

    // If a box has any OOF descendants, they are propagated up the tree to
    // accumulate their static-position.
    if (fragment.HasOutOfFlowPositionedDescendants()) {
      return false;
    }

    // Anchors should be propagated across the layout boundaries, even when
    // `contain: strict` is explicitly set.
    if (fragment.HasChildAnchors()) {
      return false;
    }

    // A box which doesn't establish a new formatting context can pass a whole
    // bunch of state (floats, margins) to an arbitrary sibling, causing that
    // sibling to position/size differently.
    if (!fragment.IsFormattingContextRoot()) {
      return false;
    }
  }

  // Positioned objects always have self-painting layers and are safe to use as
  // relayout boundaries.
  bool is_svg_root = box->IsSVGRoot();
  bool has_self_painting_layer = box->HasLayer() && box->HasSelfPaintingLayer();
  if (!has_self_painting_layer && !is_svg_root)
    return false;

  // Table parts can't be relayout roots since the table is responsible for
  // layouting all the parts.
  if (box->IsTablePart()) {
    return false;
  }

  // OOF-positioned objects which rely on their static-position for placement
  // cannot be relayout boundaries (their final position would be incorrect).
  // TODO(crbug.com/40280256): Ignoring position-area means we may not allow
  // using the object as a relayout boundary even if position-area causes the
  // object to not rely on static position.
  const ComputedStyle& style = box->StyleRef();
  if (box->IsOutOfFlowPositioned() &&
      (style.HasAutoLeftAndRightIgnoringPositionArea() ||
       style.HasAutoTopAndBottomIgnoringPositionArea())) {
    return false;
  }

  // In general we can't relayout a flex item independently of its container;
  // not only is the result incorrect due to the override size that's set, it
  // also messes with the cached main size on the flexbox.
  if (box->IsFlexItem()) {
    return false;
  }

  // Similarly to flex items, we can't relayout a grid/grid-lanes item
  // independently of its container. This also applies to out of flow items of
  // the grid, as we need the cached information of the grid to recompute the
  // out of flow item's containing block rect.
  if (box->ContainingBlock()->IsLayoutGridOrGridLanes()) {
    return false;
  }

  // MathML subtrees can't be relayout roots because of the embellished operator
  // and space-like logic.
  if (box->IsMathML() && !box->IsMathMLRoot()) {
    return false;
  }

  if (box->ShouldApplyLayoutContainment() &&
      box->ShouldApplySizeContainment()) {
    return true;
  }

  // SVG roots are sufficiently self-contained to be a relayout boundary, even
  // if their size is non-fixed.
  if (is_svg_root)
    return true;

  // If either dimension is percent-based, intrinsic, or anything but fixed,
  // this object cannot form a re-layout boundary. A non-fixed computed logical
  // height will allow the object to grow and shrink based on the content
  // inside. The same goes for for logical width, if this objects is inside a
  // shrink-to-fit container, for instance.
  if (!style.Width().IsFixed() || !style.Height().IsFixed()) {
    return false;
  }

  if (box->IsTextControl()) {
    return true;
  }

  if (!box->ShouldClipOverflowAlongBothAxis()) {
    return false;
  }

  // Scrollbar parts can be removed during layout. Avoid the complexity of
  // having to deal with that.
  if (box->IsLayoutCustomScrollbarPart()) {
    return false;
  }

  // Inside block fragmentation it's generally problematic to allow relayout
  // roots. A multicol container ancestor may be scheduled for relayout as well
  // (due to other changes that may have happened since the previous layout
  // pass), which might affect the column heights, which may affect how this
  // object breaks across columns). Column spanners may also have been added or
  // removed since the previous layout pass, which is just another way of
  // affecting the column heights (and the number of rows). Another problematic
  // case is out-of-flow positioned objects, since they are being laid out by
  // the fragmentation context root (to become direct fragmentainer children),
  // rather than being laid out by their actual CSS containing block.
  //
  // Instead of identifying cases where it's safe to allow relayout roots, just
  // disallow them inside block fragmentation.
  if (box->MightBeInsideFragmentationContext()) {
    return false;
  }

  return true;
}

// Mark this object needing to re-run |CollectInlines()|.
//
// The flag is propagated to its container so that InlineNode that contains
// |this| is marked too. When |this| is a container, the propagation stops at
// |this|. When invalidating on inline blocks, floats, or OOF, caller need to
// pay attention whether it should mark its inner context or outer.
void LayoutObject::SetNeedsCollectInlines() {
  NOT_DESTROYED();
  if (NeedsCollectInlines())
    return;

  if (IsSVGChild() && !IsSVGText() && !IsSVGInline() && !IsSVGInlineText() &&
      !IsSVGForeignObject()) [[unlikely]] {
    return;
  }

  SetNeedsCollectInlines(true);

  if (LayoutObject* parent = Parent())
    parent->SetChildNeedsCollectInlines();
}

void LayoutObject::SetChildNeedsCollectInlines() {
  NOT_DESTROYED();
  LayoutObject* object = this;
  do {
    if (object->NeedsCollectInlines())
      break;
    object->SetNeedsCollectInlines(true);

    // Stop marking at the inline formatting context root. This is usually a
    // |LayoutBlockFlow|, but some other classes can have children; e.g.,
    // |LayoutButton| or |LayoutSVGRoot|. |LayoutInline| is the only class we
    // collect recursively (see |CollectInlines|). Use the same condition here.
    if (!object->IsLayoutInline())
      break;

    object = object->Parent();
  } while (object);
}

namespace {

bool HasPropagatedLayoutObjects(const LayoutObject* object) {
  if (auto* box = DynamicTo<LayoutBox>(object)) {
    for (const auto& fragment : box->PhysicalFragments()) {
      if (fragment.IsLayoutObjectDestroyedOrMoved()) {
        return true;
      }
      if (fragment.HasPropagatedLayoutObjects()) {
        return true;
      }
    }
  }
  return false;
}

}  // namespace

void LayoutObject::MarkContainerChainForLayout(bool schedule_relayout) {
  NOT_DESTROYED();
#if DCHECK_IS_ON()
  DCHECK(!IsSetNeedsLayoutForbidden());
  DCHECK(!GetDocument().InvalidationDisallowed());
#endif
  // When we're in layout, we're marking a descendant as needing layout with
  // the intention of visiting it during this layout. We shouldn't be
  // scheduling it to be laid out later. Also, scheduleRelayout() must not be
  // called while iterating LocalFrameView::layout_subtree_root_list_.
  schedule_relayout &= !GetFrameView()->IsInPerformLayout();

  const bool allow_subtree_layout_root =
      schedule_relayout && !View()->NeedsLayout();

  LayoutObject* object = Container();
  LayoutObject* last = this;

  bool simplified_normal_flow_layout = NeedsSimplifiedLayoutOnly();
  while (object) {
    if (object->SelfNeedsFullLayout()) {
      return;
    }

    // Note that if the last element we processed was blocked by a display lock,
    // and the reason we're propagating a change is that a subtree needed layout
    // (ie |last| doesn't need either self layout or positioned movement
    // layout), then we can return and stop the dirty bit propagation. Note that
    // it's not enough to check |object|, since the element that is actually
    // locked needs its child bits set properly, we need to go one more
    // iteration after that.
    if (!last->SelfNeedsFullLayout() &&
        last->ChildLayoutBlockedByDisplayLock() &&
        !HasPropagatedLayoutObjects(last)) {
      return;
    }

    // Don't mark the outermost object of an unrooted subtree. That object will
    // be marked when the subtree is added to the document.
    LayoutObject* container = object->Container();
    if (!container && !IsA<LayoutView>(object))
      return;

    if (!last->IsTextOrSVGChild() && last->StyleRef().HasOutOfFlowPosition()) {
      object = last->ContainingBlock();
      container = object->Container();
      simplified_normal_flow_layout = true;
    }

    if (simplified_normal_flow_layout) {
      if (object->NeedsSimplifiedLayout()) {
        return;
      }
      object->SetNeedsSimplifiedLayout(true);
    } else {
      if (object->ChildNeedsFullLayout()) {
        return;
      }
      object->SetChildNeedsFullLayout(true);
    }
#if DCHECK_IS_ON()
    DCHECK(!object->IsSetNeedsLayoutForbidden());
#endif

    object->MarkSelfPaintingLayerForVisualOverflowRecalc();

    last = object;
    if (allow_subtree_layout_root && ObjectIsRelayoutBoundary(last) &&
        last->IsRooted()) {
      GetFrameView()->ScheduleRelayoutOfSubtree(*last);
      return;
    }
    object = container;
  }

  if (schedule_relayout && IsA<LayoutView>(last)) {
    GetFrameView()->ScheduleRelayout();
  }
}

// LayoutNG has different OOF-positioned handling compared to the existing
// layout system. To correctly determine the static-position of the object,
// LayoutNG "bubbles" up the static-position inside the LayoutResult.
// See: |LayoutResult::OutOfFlowPositionedDescendants()|.
//
// Column spanners also have a bubbling mechanism, and therefore also need to
// mark ancestors between the element itself and the containing block (the
// multicol container).
//
// Whenever an OOF-positioned object is added/removed we need to invalidate
// layout for all the layout objects which may have stored a LayoutResult
// with this object contained in that list.
//
// In the future it may be possible to optimize this, e.g.
//  - For the removal case, add a pass which modifies the layout result to
//    remove the OOF-positioned descendant.
//  - For the adding case, if the OOF-positioned doesn't require a
//    static-position, simply insert the object up the LayoutResult chain with
//    an invalid static-position.
void LayoutObject::MarkParentForSpannerOrOutOfFlowPositionedChange() {
  NOT_DESTROYED();
#if DCHECK_IS_ON()
  DCHECK(!IsSetNeedsLayoutForbidden());
  DCHECK(!GetDocument().InvalidationDisallowed());
#endif

  LayoutObject* object = Parent();
  if (!object)
    return;

  // As OOF-positioned objects are represented as an object replacement
  // character in the inline items list. We need to ensure we collect the
  // inline items again to either collect or drop the OOF-positioned object.
  //
  // Note that this isn't necessary if we're dealing with a column spanner here,
  // but in order to keep things simple, we'll make no difference.
  if (RuntimeEnabledFeatures::LayoutOOFCollectInlinesFixEnabled()) {
    object->SetChildNeedsCollectInlines();
  } else {
    object->SetNeedsCollectInlines();
  }

  const LayoutBlock* containing_block = ContainingBlock();
  while (object != containing_block) {
    object->SetChildNeedsLayout(kMarkOnlyThis);
    object = object->Container();
  }
  // Finally mark the parent block for layout. This will mark everything which
  // has an OOF-positioned object or column spanner in a LayoutResult as
  // needing layout.
  if (object)
    object->SetChildNeedsLayout();
}

void LayoutObject::SetIntrinsicLogicalWidthsDirty(
    MarkingBehavior mark_parents) {
  NOT_DESTROYED();
  intrinsic_logical_widths_dirty_ = true;
  intrinsic_logical_widths_depends_on_block_constraints_ = true;
  indefinite_intrinsic_logical_widths_dirty_ = true;
  definite_intrinsic_logical_widths_dirty_ = true;
  if (mark_parents == kMarkContainerChain &&
      (IsText() || !StyleRef().HasOutOfFlowPosition()))
    InvalidateContainerIntrinsicLogicalWidths();
}

void LayoutObject::ClearIntrinsicLogicalWidthsDirty() {
  NOT_DESTROYED();
  intrinsic_logical_widths_dirty_ = false;
}

bool LayoutObject::IsFontFallbackValid() const {
  NOT_DESTROYED();
  return StyleRef().GetFont()->IsFallbackValid() &&
         FirstLineStyleRef().GetFont()->IsFallbackValid();
}

void LayoutObject::InvalidateSubtreeLayoutForFontUpdates() {
  NOT_DESTROYED();
  if (!IsFontFallbackValid()) {
    SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
        layout_invalidation_reason::kFontsChanged);
  }
  for (LayoutObject* child = SlowFirstChild(); child;
       child = child->NextSibling()) {
    child->InvalidateSubtreeLayoutForFontUpdates();
  }
}

static inline bool ShouldInvalidateBeyond(LayoutObject* o) {
  // We don't work on individual inline objects, instead at an IFC level. We
  // never clear these bits on inline elements so invalidate past them.
  if (o->IsLayoutInline() || o->IsText()) {
    return true;
  }

  // Similarly for tables we don't compute min/max sizes on rows/sections.
  // Invalidate past them.
  if (o->IsTableRow() || o->IsTableSection()) {
    return true;
  }

  // Invalidate past any subgrids. NOTE: we do this in both axes as we don't
  // know what writing-mode the root grid is in.
  if (o->IsLayoutGridOrGridLanes()) {
    const auto& style = o->StyleRef();
    // TODO(almaher): Grid-lanes can only be subgridded in the grid axis, so we
    // will only need to check in the grid/track sizing axis for grid-lanes.
    if (style.GridTemplateColumns().IsSubgriddedAxis() ||
        style.GridTemplateRows().IsSubgriddedAxis()) {
      return true;
    }
  }

  return false;
}

inline void LayoutObject::InvalidateContainerIntrinsicLogicalWidths() {
  NOT_DESTROYED();

  LayoutObject* o = Container();
  while (o &&
         (!o->IntrinsicLogicalWidthsDirty() || ShouldInvalidateBeyond(o))) {
    LayoutObject* container = o->Container();

    // Don't invalidate the outermost object of an unrooted subtree. That object
    // will be invalidated when the subtree is added to the document.
    if (!container && !IsA<LayoutView>(o))
      break;

    o->intrinsic_logical_widths_dirty_ = true;
    // A positioned object has no effect on the min/max width of its containing
    // block ever. We can optimize this case and not go up any further.
    if (o->StyleRef().HasOutOfFlowPosition())
      break;
    o = container;
  }
}

LayoutObject* LayoutObject::ContainerForAbsolutePosition(
    AncestorSkipInfo* skip_info) const {
  NOT_DESTROYED();
  return FindAncestorByPredicate(this, skip_info, [](LayoutObject* candidate) {
    return candidate->CanContainAbsolutePositionObjects();
  });
}

LayoutObject* LayoutObject::ContainerForFixedPosition(
    AncestorSkipInfo* skip_info) const {
  NOT_DESTROYED();
  DCHECK(!IsText());
  return FindAncestorByPredicate(this, skip_info, [](LayoutObject* candidate) {
    return candidate->CanContainFixedPositionObjects();
  });
}

LayoutObject* LayoutObject::ContainerForColumnSpanner(
    AncestorSkipInfo* skip_info) const {
  NOT_DESTROYED();
  return FindColumnSpannerContainer(this, skip_info);
}

LayoutBlock* LayoutObject::ContainingBlockForAbsolutePosition(
    AncestorSkipInfo* skip_info) const {
  NOT_DESTROYED();
  auto* container = ContainerForAbsolutePosition(skip_info);
  return container ? container->InclusiveContainingBlock(skip_info) : nullptr;
}

LayoutBlock* LayoutObject::ContainingBlockForFixedPosition(
    AncestorSkipInfo* skip_info) const {
  NOT_DESTROYED();
  auto* container = ContainerForFixedPosition(skip_info);
  return container ? container->InclusiveContainingBlock(skip_info) : nullptr;
}

LayoutBlock* LayoutObject::InclusiveContainingBlock(
    AncestorSkipInfo* skip_info) {
  NOT_DESTROYED();
  auto* layout_block = DynamicTo<LayoutBlock>(this);
  return layout_block ? layout_block : ContainingBlock(skip_info);
}

const PaintLayer* LayoutObject::ContainingScrollContainerLayer(
    bool ignore_layout_view_for_fixed_pos) const {
  NOT_DESTROYED();
  const PaintLayer* layer = EnclosingLayer();
  if (!layer) {
    return nullptr;
  }
  if (auto* box = layer->GetLayoutBox()) {
    if (box != this && box->IsScrollContainer()) {
      return layer;
    }
  }
  bool is_fixed_to_view = false;
  if (auto* scroll_container_layer =
          layer->ContainingScrollContainerLayer(&is_fixed_to_view)) {
    if (!is_fixed_to_view || !ignore_layout_view_for_fixed_pos) {
      return scroll_container_layer;
    }
  }
  return nullptr;
}

const PaintLayer* LayoutObject::ContainingScrollContainerLayer(
    PhysicalAxis axis,
    bool ignore_layout_view_for_fixed_pos) const {
  NOT_DESTROYED();
  // Walk the chain of nearest scroll containers, skipping those not
  // scrollable in the `axis`.
  const PaintLayer* container =
      ContainingScrollContainerLayer(ignore_layout_view_for_fixed_pos);
  while (container) {
    const auto& style = container->GetLayoutObject().StyleRef();
    if ((axis == PhysicalAxis::kVertical &&
         style.IsOverflowValueScrollableY()) ||
        ((axis == PhysicalAxis::kHorizontal &&
          style.IsOverflowValueScrollableX()))) {
      return container;
    }
    container = container->GetLayoutObject().ContainingScrollContainerLayer(
        ignore_layout_view_for_fixed_pos);
  }
  return nullptr;
}

const LayoutBox* LayoutObject::ContainingScrollContainer(
    bool ignore_layout_view_for_fixed_pos) const {
  NOT_DESTROYED();
  if (const PaintLayer* scroll_container_layer =
          ContainingScrollContainerLayer(ignore_layout_view_for_fixed_pos)) {
    return scroll_container_layer->GetLayoutBox();
  }
  return nullptr;
}

const LayoutBox* LayoutObject::ContainingScrollContainer(
    PhysicalAxis axis) const {
  NOT_DESTROYED();
  if (const PaintLayer* scroll_container_layer =
          ContainingScrollContainerLayer(axis)) {
    return scroll_container_layer->GetLayoutBox();
  }
  return nullptr;
}

LayoutObject* LayoutObject::ContainingBlockForTextOverflow() const {
  NOT_DESTROYED();
  LayoutObject* block = ContainingBlock();
  if (block && block->IsAnonymous()) {
    block = block->Parent();
  }
  if (block && !block->BehavesLikeBlockContainer()) {
    return nullptr;
  }
  return block;
}

LayoutObject* LayoutObject::NearestAncestorForElement() const {
  NOT_DESTROYED();
  LayoutObject* ancestor = Parent();
  while (ancestor && ancestor->IsAnonymous()) {
    ancestor = ancestor->Parent();
  }
  return ancestor;
}

bool LayoutObject::ComputeIsFixedContainer(const ComputedStyle& style) const {
  NOT_DESTROYED();
  if (IsViewTransitionRoot()) {
    return true;
  }
  bool is_document_element = IsDocumentElement();
  // https://www.w3.org/TR/filter-effects-1/#FilterProperty
  if (!is_document_element && style.HasNonInitialFilter()) {
    return true;
  }
  // Backdrop-filter creates a containing block for fixed and absolute
  // positioned elements:
  // https://drafts.fxtf.org/filter-effects-2/#backdrop-filter-operation
  if (!is_document_element && style.HasNonInitialBackdropFilter()) {
    return true;
  }
  // https://github.com/WICG/html-in-canvas
  if (const auto* element = DynamicTo<Element>(GetNode())) {
    if (element->CanvasForDrawing()) {
      return true;
    }
  }
  // The LayoutView is always a container of fixed positioned descendants. In
  // addition, SVG foreignObjects become such containers, so that descendants
  // of a foreignObject cannot escape it. Similarly, text controls let authors
  // select elements inside that are created by user agent shadow DOM, and we
  // have (C++) code that assumes that the elements are indeed contained by the
  // text control. So just make sure this is the case.
  if (IsA<LayoutView>(this) || IsSVGForeignObject() || IsTextControl()) {
    return true;
  }

  // crbug.com/1153042: If <fieldset> is a fixed container, its anonymous
  // content box should be a fixed container.
  if (IsAnonymous() && Parent() && Parent()->IsFieldset() &&
      Parent()->CanContainFixedPositionObjects()) {
    return true;
  }

  // https://www.w3.org/TR/css-transforms-1/#containing-block-for-all-descendants
  // For transform-style specifically, we want to consider the computed
  // value rather than the used value.
  if (style.HasTransformRelatedProperty() ||
      style.TransformStyle3D() == ETransformStyle3D::kPreserve3d) {
    if (IsBox()) {
      return true;
    }
  }
  // https://www.w3.org/TR/css-contain-1/#containment-layout
  if (IsEligibleForPaintOrLayoutContainment() &&
      (ShouldApplyPaintContainment(style) ||
       ShouldApplyLayoutContainment(style) ||
       style.HasWillChangeProperty(CSSPropertyID::kContain))) {
    return true;
  }

  return false;
}

bool LayoutObject::ComputeIsAbsoluteContainer(const ComputedStyle& style,
                                              bool is_fixed_container) const {
  NOT_DESTROYED();
  return is_fixed_container ||
         (style.GetPosition() != EPosition::kStatic ||
          style.HasWillChangeProperty(CSSPropertyID::kPosition)) ||
         // crbug.com/1153042: If <fieldset> is an absolute container, its
         // anonymous content box should be an absolute container.
         (IsAnonymous() && Parent() && Parent()->IsFieldset() &&
          Parent()->CanContainAbsolutePositionObjects());
}

const LayoutBoxModelObject* LayoutObject::FindFirstStickyContainer(
    const LayoutBox* below) const {
  NOT_DESTROYED();
  DCHECK(IsContainedBy(below));
  for (const LayoutObject* ancestor = this; ancestor != below;
       ancestor = ancestor->Container()) {
    if (ancestor->StyleRef().HasStickyConstrainedPosition()) {
      return To<LayoutBoxModelObject>(ancestor);
    }
  }
  return nullptr;
}

gfx::RectF LayoutObject::AbsoluteBoundingBoxRectF(
    MapCoordinatesFlags flags) const {
  NOT_DESTROYED();
  DCHECK(!flags.Has(MapCoordinatesMode::kIgnoreTransforms));
  Vector<gfx::QuadF> quads;
  AbsoluteQuads(quads, flags);

  wtf_size_t n = quads.size();
  if (n == 0)
    return gfx::RectF();

  gfx::RectF result = quads[0].BoundingBox();
  for (wtf_size_t i = 1; i < n; ++i)
    result.Union(quads[i].BoundingBox());
  return result;
}

gfx::Rect LayoutObject::AbsoluteBoundingBoxRect(
    MapCoordinatesFlags flags) const {
  NOT_DESTROYED();
  DCHECK(!flags.Has(MapCoordinatesMode::kIgnoreTransforms));
  Vector<gfx::QuadF> quads;
  AbsoluteQuads(quads, flags);

  wtf_size_t n = quads.size();
  if (!n)
    return gfx::Rect();

  gfx::RectF result;
  for (auto& quad : quads)
    result.Union(quad.BoundingBox());
  return gfx::ToEnclosingRect(result);
}

gfx::Rect LayoutObject::AbsoluteBoundingBoxRectForUnboundedElement() const {
  NOT_DESTROYED();
  DCHECK(RuntimeEnabledFeatures::UnboundedElementEnabled());
  if (const auto* box_model = DynamicTo<LayoutBoxModelObject>(this)) {
    PhysicalRect overflow = box_model->VisualOverflowRectIncludingFilters();
    // Intersect with the clip-path bounding box so that the browser-side window
    // bounds match the clipped/visible area of the element rather than its
    // unclipped layout size. This prevents size/origin mismatches in the
    // compositor.
    if (auto clip_path_bounds =
            ClipPathClipper::LocalClipPathBoundingBox(*this)) {
      overflow.Intersect(PhysicalRect::EnclosingRect(*clip_path_bounds));
    }
    return ToEnclosingRect(LocalToAbsoluteRect(overflow));
  }
  return AbsoluteBoundingBoxRect();
}

PhysicalRect LayoutObject::AbsoluteBoundingBoxRectHandlingEmptyInline(
    MapCoordinatesFlags flags) const {
  NOT_DESTROYED();
  return PhysicalRect::EnclosingRect(AbsoluteBoundingBoxRectF(flags));
}

PhysicalRect LayoutObject::AbsoluteBoundingBoxRectForScrollIntoView() const {
  NOT_DESTROYED();
  // Ignore sticky position offsets for the purposes of scrolling elements into
  // view. See https://www.w3.org/TR/css-position-3/#stickypos-scroll for
  // details

  const MapCoordinatesFlags flag =
      (RuntimeEnabledFeatures::CSSPositionStickyStaticScrollPositionEnabled())
          ? MapCoordinatesFlags{MapCoordinatesMode::kIgnoreStickyOffset}
          : MapCoordinatesFlags{};

  if (const auto* scroll_marker =
          DynamicTo<ScrollMarkerPseudoElement>(GetNode())) {
    // Scroll markers are reparented into a scroll marker group. We want the
    // rectangle of the originating element (or column).
    const Element& originating_element =
        scroll_marker->UltimateOriginatingElement();
    const auto* originating_object = originating_element.GetLayoutObject();
    const auto* column_pseudo =
        DynamicTo<ColumnPseudoElement>(scroll_marker->parentNode());
    if (!column_pseudo) {
      return originating_object->AbsoluteBoundingBoxRectForScrollIntoView();
    }
    // This is a ::column::scroll-marker
    if (const auto* scroller = originating_element.GetLayoutBoxForScrolling();
        scroller && scroller->GetScrollableArea()->ScrollableAxes()) {
      // The originating element (the multicol container) is also the scrollable
      // container.
      PhysicalRect bounds = column_pseudo->ColumnRect();
      bounds.offset -= PhysicalOffset::FromVector2dFRound(
          scroller->GetScrollableArea()->GetScrollOffset());
      return scroller->LocalToAbsoluteRect(bounds, flag);
    }
    gfx::QuadF quad = originating_object->LocalRectToAbsoluteQuad(
        column_pseudo->ColumnRect(), flag);
    return PhysicalRect::EnclosingRect(quad.BoundingBox());
  }

  return AbsoluteBoundingBoxRectHandlingEmptyInline(flag);
}

void LayoutObject::AddAbsoluteRectForLayer(gfx::Rect& result) {
  NOT_DESTROYED();
  if (HasLayer())
    result.Union(AbsoluteBoundingBoxRect());
  for (LayoutObject* current = SlowFirstChild(); current;
       current = current->NextSibling())
    current->AddAbsoluteRectForLayer(result);
}

gfx::Rect LayoutObject::AbsoluteBoundingBoxRectIncludingDescendants() const {
  NOT_DESTROYED();
  gfx::Rect result = AbsoluteBoundingBoxRect();
  for (LayoutObject* current = SlowFirstChild(); current;
       current = current->NextSibling())
    current->AddAbsoluteRectForLayer(result);
  return result;
}

void LayoutObject::Paint(const PaintInfo&) const {
  NOT_DESTROYED();
}

RecalcScrollableOverflowResult LayoutObject::RecalcScrollableOverflow() {
  NOT_DESTROYED();
  ClearSelfNeedsScrollableOverflowRecalc();
  if (!ChildNeedsScrollableOverflowRecalc()) {
    return RecalcScrollableOverflowResult();
  }

  ClearChildNeedsScrollableOverflowRecalc();
  bool children_scrollable_overflow_changed = false;
  for (LayoutObject* current = SlowFirstChild(); current;
       current = current->NextSibling()) {
    children_scrollable_overflow_changed |=
        current->RecalcScrollableOverflow().scrollable_overflow_changed;
  }
  return {children_scrollable_overflow_changed,
          /* rebuild_fragment_tree */ false};
}

void LayoutObject::RecalcVisualOverflow() {
  NOT_DESTROYED();
  for (LayoutObject* current = SlowFirstChild(); current;
       current = current->NextSibling()) {
    if (current->HasLayer() &&
        To<LayoutBoxModelObject>(current)->HasSelfPaintingLayer())
      continue;
    current->RecalcVisualOverflow();
  }
}

void LayoutObject::RecalcNormalFlowChildVisualOverflowIfNeeded() {
  NOT_DESTROYED();
  if (IsOutOfFlowPositioned() ||
      (HasLayer() && To<LayoutBoxModelObject>(this)->HasSelfPaintingLayer()))
    return;
  RecalcVisualOverflow();
}

void LayoutObject::InvalidateVisualOverflow() {
  NOT_DESTROYED();
  if (IsInLayoutNGInlineFormattingContext() && !NeedsLayout()) {
    if (auto* text = DynamicTo<LayoutText>(this)) {
      text->InvalidateVisualOverflow();
    }
  }
  if (auto* painting_layer = PaintingLayer()) {
    painting_layer->SetNeedsVisualOverflowRecalc();
  }
  // TODO(crbug.com/40246969): This looks like an over-invalidation.
  // visual overflow change should not require checking for layout change.
  SetShouldCheckForPaintInvalidation();
}

#if DCHECK_IS_ON()
void LayoutObject::InvalidateVisualOverflowForDCheck() {
  NOT_DESTROYED();
  if (auto* box = DynamicTo<LayoutBox>(this)) {
    for (const PhysicalBoxFragment& fragment : box->PhysicalFragments()) {
      fragment.GetMutableForPainting().InvalidateInkOverflow();
    }
  }
  // For now, we can only check |LayoutBox| laid out by NG.
}
#endif

bool LayoutObject::HasDistortingVisualEffects() const {
  NOT_DESTROYED();
  // TODO(szager): Check occlusion information propagated from out-of-process
  // parent frame.

  auto& first_fragment = EnclosingLayer()->GetLayoutObject().FirstFragment();
  // This can happen for an iframe element which is outside the viewport and has
  // therefore never been painted. In that case, we do the safe thing -- report
  // it as having distorting visual effects.
  if (!first_fragment.HasLocalBorderBoxProperties())
    return true;
  auto paint_properties = first_fragment.LocalBorderBoxProperties();

  // No filters, no blends, no opacity < 100%.
  for (const auto* effect = &paint_properties.Effect().Unalias(); effect;
       effect = effect->UnaliasedParent()) {
    if (effect->HasRealEffects())
      return true;
  }

  auto& local_frame_root = GetDocument().GetFrame()->LocalFrameRoot();
  auto& root_fragment = local_frame_root.ContentLayoutObject()->FirstFragment();
  CHECK(root_fragment.HasLocalBorderBoxProperties());
  const auto& root_properties = root_fragment.LocalBorderBoxProperties();

  // We conservatively consider any 3D transform in the container chain to be
  // disqualifying here, even if the net transform flattens out to an
  // acceptable 2D matrix. This could be relaxed, but would require an update
  // to IsHitCandidateForDepthOrder in the hit testing code. This must be
  // checked before `SourceToDestinationProjection`, which flattens.
  if (!paint_properties.Transform().Unalias().IsCoplanarWith(
          root_properties.Transform().Unalias())) {
    return true;
  }

  // The only allowed transforms are 2D translation and proportional up-scaling.
  gfx::Transform projection = GeometryMapper::SourceToDestinationProjection(
      paint_properties.Transform(), root_properties.Transform());
  if (!projection.Is2dProportionalUpscaleAndOr2dTranslation())
    return true;

  return false;
}

bool LayoutObject::HasNonZeroEffectiveOpacity() const {
  NOT_DESTROYED();
  const FragmentData& fragment =
      EnclosingLayer()->GetLayoutObject().FirstFragment();

  // This can happen for an iframe element which is outside the viewport and has
  // therefore never been painted. In that case, we do the safe thing -- report
  // it as having non-zero opacity -- since this method is used by
  // IntersectionObserver to detect occlusion.
  if (!fragment.HasLocalBorderBoxProperties())
    return true;

  const auto& paint_properties = fragment.LocalBorderBoxProperties();

  for (const auto* effect = &paint_properties.Effect().Unalias(); effect;
       effect = effect->UnaliasedParent()) {
    if (effect->Opacity() == 0.0)
      return false;
  }
  return true;
}

String LayoutObject::DecoratedName() const {
  NOT_DESTROYED();
  StringBuilder name;
  name.Append(GetName());

  Vector<const char*> attributes;
  if (ChildLayoutBlockedByDisplayLock()) {
    attributes.push_back("display-locked");
  }
  if (IsAnonymous()) {
    attributes.push_back("anonymous");
  }
  // FIXME: Remove the special case for LayoutView here (requires rebaseline of
  // all tests).
  if (IsOutOfFlowPositioned() && !IsA<LayoutView>(this)) {
    attributes.push_back("positioned");
  }
  if (IsRelPositioned()) {
    attributes.push_back("relative positioned");
  }
  if (IsStickyPositioned()) {
    attributes.push_back("sticky positioned");
  }
  if (IsFloating()) {
    attributes.push_back("floating");
  }
  if (IsColumnSpanAll()) {
    attributes.push_back("column spanner");
  }
  if (IsLayoutBlock() && IsInline()) {
    attributes.push_back("inline");
  }
  if (IsLayoutReplaced() && !IsInline()) {
    attributes.push_back("block");
  }
  if (IsLayoutBlockFlow() && ChildrenInline() && SlowFirstChild()) {
    attributes.push_back("children-inline");
  }
  if (IsMulticolContainer()) {
    attributes.push_back("multicol");
  }
  if (IsRelayoutBoundary()) {
    attributes.push_back("relayout-boundary");
  }
  if (!attributes.empty()) {
    name.Append(" (");
    name.Append(attributes[0]);
    for (wtf_size_t i = 1; i < attributes.size(); ++i) {
      name.Append(", ");
      name.Append(attributes[i]);
    }
    name.Append(")");
  }

  return name.ToString();
}

String LayoutObject::ToString() const {
  NOT_DESTROYED();
  StringBuilder builder;
  builder.Append(DecoratedName());
  if (const Node* node = GetNode()) {
    builder.Append(' ');
    builder.Append(node->ToString());
  }
  return builder.ToString();
}

String LayoutObject::DebugName() const {
  NOT_DESTROYED();
  StringBuilder name;
  name.Append(DecoratedName());

  if (const Node* node = GetNode()) {
    name.Append(' ');
    name.Append(node->DebugName());
  }
  return name.ToString();
}

DOMNodeId LayoutObject::OwnerNodeId(bool is_internal_content) const {
  NOT_DESTROYED();
  if (RuntimeEnabledFeatures::HTMLPrintingArtifactAnnotationsEnabled() &&
      is_internal_content) {
    if (auto* node = GetNode()) {
      if (node->IsTextNode()) {
        node = node->parentNode();
      }
      if (node->IsElementNode()) {
        auto* element = DynamicTo<Element>(node);
        if (element->GetIdAttribute() == keywords::kInternalPrintHeader) {
          return SkPDF::NodeID::PaginationHeaderArtifact;
        } else if (element->GetIdAttribute() ==
                   keywords::kInternalPrintFooter) {
          return SkPDF::NodeID::PaginationFooterArtifact;
        } else if (element->GetIdAttribute() ==
                   keywords::kInternalPrintPageNumber) {
          return SkPDF::NodeID::PaginationArtifact;
        }
      }
    }
  }

  // For SVG child elements inside an SVG root with role="img", use the SVG
  // root's DOM node ID. This ensures that painted content from SVG children
  // (e.g. <path>, <rect>) is associated with the SVG root's /Figure structure
  // element in the tagged PDF, rather than being orphaned with their own
  // node IDs that are not in the structure tree.
  // See https://crbug.com/40883733.
  if (IsSVGChild()) {
    for (const LayoutObject* obj = Parent(); obj; obj = obj->Parent()) {
      if (obj->IsSVGRoot()) {
        if (auto* svg_element = DynamicTo<Element>(obj->GetNode())) {
          const AtomicString& role =
              svg_element->FastGetAttribute(html_names::kRoleAttr);
          if (EqualIgnoringAsciiCase(role, "img")) {
            return svg_element->GetDomNodeId();
          }
        }
        break;
      }
    }
  }

  return GetNode() ? GetNode()->GetDomNodeId() : kInvalidDOMNodeId;
}

void LayoutObject::InvalidateDisplayItemClients(
    PaintInvalidationReason reason) const {
  NOT_DESTROYED();
  // This default implementation invalidates only the object itself as a
  // DisplayItemClient.
  ObjectPaintInvalidator(*this).InvalidateDisplayItemClient(*this, reason);
}

PhysicalRect LayoutObject::AbsoluteSelectionRect() const {
  NOT_DESTROYED();
  PhysicalRect selection_rect = LocalSelectionVisualRect();
  if (!selection_rect.IsEmpty())
    MapToVisualRectInAncestorSpace(View(), selection_rect);

  if (LocalFrameView* frame_view = GetFrameView())
    return frame_view->DocumentToFrame(selection_rect);

  return selection_rect;
}

DISABLE_CFI_PERF
void LayoutObject::InvalidatePaint(
    const PaintInvalidatorContext& context) const {
  NOT_DESTROYED();
  ObjectPaintInvalidatorWithContext(*this, context).InvalidatePaint();
}

namespace {

// Applies local-root viewport clipping and remote-root mapping for a rect that
// has already been mapped to the local root's coordinate space. Returns true
// on success. Sets |intersects| to false (and empties |rect|) when clipping
// removes all area, matching the inclusive/intersect semantics expected by
// callers.
bool ApplyViewportClippingAndOffsets(gfx::RectF& rect,
                                     LayoutView& layout_view,
                                     VisualRectFlags visual_rect_flags,
                                     bool& intersects) {
  PhysicalRect physical_rect = PhysicalRect::EnclosingRect(rect);
  // When requested, skip all ancestor + viewport clipping even for the
  // GeometryMapper viewport fast path. This keeps the fast path consistent with
  // the slow path (LayoutBox::ApplyBoxClips and LayoutView's viewport clip) so
  // callers can compute both unclipped and clipped rectangles using the same
  // mapping pipeline.
  const bool apply_local_root_clip =
      !visual_rect_flags.Has(VisualRectFlag::kSkipAncestorAndViewportClips) &&
      !visual_rect_flags.Has(VisualRectFlag::kDontApplyMainFrameOverflowClip);

  if (apply_local_root_clip) {
    PhysicalRect viewport_rect = layout_view.ViewRect();
    if (visual_rect_flags.Has(VisualRectFlag::kEdgeInclusive)) {
      if (!physical_rect.InclusiveIntersect(viewport_rect)) {
        rect = gfx::RectF();
        intersects = false;
        return true;
      }
    } else {
      physical_rect.Intersect(viewport_rect);
      if (physical_rect.IsEmpty()) {
        rect = gfx::RectF();
        intersects = false;
        return true;
      }
    }
  }

  LocalFrameView* frame_view = layout_view.GetFrameView();
  if (!frame_view) {
    return false;
  }
  if (!frame_view->MapToVisualRectInRemoteRootFrame(
          physical_rect, apply_local_root_clip,
          visual_rect_flags.Has(
              VisualRectFlag::kApplyRemoteViewportTransform))) {
    return false;
  }
  rect = gfx::RectF(physical_rect);
  return true;
}

}  // namespace

bool LayoutObject::MapToVisualRectInAncestorSpaceInternalFastPath(
    const LayoutBoxModelObject* ancestor_or_null_for_viewport,
    gfx::RectF& rect_out,
    VisualRectFlags visual_rect_flags,
    bool& intersects) const {
  NOT_DESTROYED();
  // Do all mapping work on a local copy so we can safely fall back to the slow
  // path without mutating the caller's input rect when a fast-path dependency
  // (e.g. remote root mapping) is unavailable.
  gfx::RectF rect = rect_out;
  Document& document = GetDocument();
  LayoutView* layout_view = document.GetLayoutView();
  if (!layout_view) {
    return false;
  }

  // Allow a null ancestor to mean the local root viewport when using the
  // GeometryMapper fast path, but keep using the slow path for embedded frames
  // so the embedder chain is walked.
  const bool map_to_viewport = !ancestor_or_null_for_viewport;
  if (map_to_viewport && document.LocalOwner()) {
    // TODO(crbug.com/40187338): Migrate embedded-frame viewport mapping to the
    // GeometryMapper fast path instead of falling back to the slow embedder
    // walk.
    return false;
  }
  // Use fast path for viewport mapping if enabled (fall back to slow path).
  if (map_to_viewport &&
      !RuntimeEnabledFeatures::BlinkGeometryMapperViewportFastPathEnabled()) {
    return false;
  }
  const LayoutBoxModelObject* ancestor =
      map_to_viewport ? layout_view : ancestor_or_null_for_viewport;

  intersects = true;
  if (!visual_rect_flags.Has(VisualRectFlag::kUseGeometryMapper) ||
      !ancestor->FirstFragment().HasLocalBorderBoxProperties()) {
    return false;
  }

  // Ensure that transforms are applied to the roots of frames and iframes.
  if (ancestor == this &&
      (!map_to_viewport || !RuntimeEnabledFeatures::
                               FixVisualRectRemoteViewportTransformEnabled())) {
    return true;
  }

  AncestorSkipInfo skip_info(ancestor);
  PropertyTreeStateOrAlias container_properties(
      PropertyTreeState::kUninitialized);
  const LayoutObject* property_container = GetPropertyContainer(
      &skip_info, &container_properties, visual_rect_flags);
  if (!property_container)
    return false;

  // This works because it's not possible to have any intervening clips,
  // effects, transforms between |this| and |property_container|, and therefore
  // FirstFragment().PaintOffset() is relative to the transform space defined by
  // FirstFragment().LocalBorderBoxProperties() (if this == property_container)
  // or property_container->FirstFragment().ContentsProperties().
  rect.Offset(gfx::Vector2dF(FirstFragment().PaintOffset()));

  // For viewport mapping, run GeometryMapper to reach the local root viewport.
  if (map_to_viewport) {
    PropertyTreeState unaliased_container = container_properties.Unalias();
    FloatClipRect clip_rect(rect);
    intersects = GeometryMapper::LocalToLocalRootViewportRect(
        unaliased_container, clip_rect, kIgnoreOverlayScrollbarSize,
        visual_rect_flags);
    rect = clip_rect.Rect();
    if (!ApplyViewportClippingAndOffsets(rect, *layout_view, visual_rect_flags,
                                         intersects)) {
      return false;
    }
  } else {
    // For ancestor mapping, only map when there's a distance to cover.
    if (property_container != ancestor) {
      PropertyTreeState unaliased_container = container_properties.Unalias();
      PropertyTreeState ancestor_state =
          ancestor->FirstFragment().ContentsProperties().Unalias();
      FloatClipRect clip_rect(rect);
      intersects = GeometryMapper::LocalToAncestorVisualRect(
          unaliased_container, ancestor_state, clip_rect,
          kIgnoreOverlayScrollbarSize, visual_rect_flags);
      rect = clip_rect.Rect();
    }
    rect.Offset(-gfx::Vector2dF(ancestor->FirstFragment().PaintOffset()));
  }

  rect_out = rect;
  return true;
}

bool LayoutObject::MapToVisualRectInAncestorSpace(
    const LayoutBoxModelObject* ancestor,
    PhysicalRect& rect,
    VisualRectFlags visual_rect_flags) const {
  NOT_DESTROYED();
  gfx::RectF float_rect(rect);

  bool intersects = true;
  if (MapToVisualRectInAncestorSpaceInternalFastPath(
          ancestor, float_rect, visual_rect_flags, intersects)) {
    rect = PhysicalRect::EnclosingRect(float_rect);
    return intersects;
  }
  TransformState transform_state(TransformState::kApplyTransformDirection,
                                 gfx::QuadF(float_rect));
  intersects = MapToVisualRectInAncestorSpaceInternal(ancestor, transform_state,
                                                      visual_rect_flags);
  transform_state.Flatten();
  rect = PhysicalRect::EnclosingRect(
      transform_state.LastPlanarQuad().BoundingBox());
  return intersects;
}

bool LayoutObject::MapToVisualRectInAncestorSpace(
    const LayoutBoxModelObject* ancestor,
    gfx::RectF& rect,
    VisualRectFlags visual_rect_flags) const {
  NOT_DESTROYED();
  bool intersects = true;
  if (MapToVisualRectInAncestorSpaceInternalFastPath(
          ancestor, rect, visual_rect_flags, intersects)) {
    return intersects;
  }

  TransformState transform_state(TransformState::kApplyTransformDirection,
                                 gfx::QuadF(rect));
  intersects = MapToVisualRectInAncestorSpaceInternal(ancestor, transform_state,
                                                      visual_rect_flags);
  transform_state.Flatten();
  rect = transform_state.LastPlanarQuad().BoundingBox();
  return intersects;
}

bool LayoutObject::MapToVisualRectInAncestorSpaceInternal(
    const LayoutBoxModelObject* ancestor,
    TransformState& transform_state,
    VisualRectFlags visual_rect_flags) const {
  NOT_DESTROYED();
  // For any layout object that doesn't override this method (the main example
  // is LayoutText), the rect is assumed to be in the parent's coordinate space.

  if (ancestor == this)
    return true;

  if (LayoutObject* parent = Parent()) {
    if (parent->IsBox()) {
      bool preserve3d = parent->StyleRef().Preserves3D() && !parent->IsText();
      TransformState::TransformAccumulation accumulation =
          preserve3d ? TransformState::kAccumulateTransform
                     : TransformState::kFlattenTransform;

      if (parent != ancestor &&
          !To<LayoutBox>(parent)->MapContentsRectToBoxSpace(
              transform_state, accumulation, *this, visual_rect_flags))
        return false;
    }
    return parent->MapToVisualRectInAncestorSpaceInternal(
        ancestor, transform_state, visual_rect_flags);
  }
  return true;
}

const LayoutObject* LayoutObject::GetPropertyContainer(
    AncestorSkipInfo* skip_info,
    PropertyTreeStateOrAlias* container_properties,
    VisualRectFlags visual_rect_flags) const {
  NOT_DESTROYED();
  const LayoutObject* property_container = this;
  while (!property_container->FirstFragment().HasLocalBorderBoxProperties()) {
    property_container = property_container->Container(skip_info);
    if (!property_container || (skip_info && skip_info->AncestorSkipped()) ||
        property_container->IsFragmented()) {
      return nullptr;
    }
  }
  if (container_properties) {
    if (property_container == this) {
      *container_properties = FirstFragment().LocalBorderBoxProperties();

      if (visual_rect_flags.Has(VisualRectFlag::kIgnoreLocalClipPath)) {
        if (auto* properties =
                property_container->FirstFragment().PaintProperties()) {
          if (auto* clip_path_clip = properties->ClipPathClip()) {
            container_properties->SetClip(*clip_path_clip->Parent());
          }
        }
      }
    } else {
      *container_properties =
          property_container->FirstFragment().ContentsProperties();
    }
  }

  return property_container;
}

HitTestResult LayoutObject::HitTestForOcclusion(
    const PhysicalRect& hit_rect) const {
  NOT_DESTROYED();
  LocalFrame* frame = GetDocument().GetFrame();
  DCHECK(!frame->View()->NeedsLayout());
  HitTestRequest::HitTestRequestType hit_type =
      HitTestRequest::kIgnorePointerEventsNone | HitTestRequest::kReadOnly |
      HitTestRequest::kIgnoreClipping |
      HitTestRequest::kIgnoreZeroOpacityObjects |
      HitTestRequest::kHitTestVisualOverflow;
  HitTestLocation location(hit_rect);
  return frame->GetEventHandler().HitTestResultAtLocation(location, hit_type,
                                                          this, true);
}

std::ostream& operator<<(std::ostream& out, const LayoutObject& object) {
  String info;
#if DCHECK_IS_ON()
  StringBuilder string_builder;
  object.DumpLayoutObject(string_builder, false, 0);
  info = string_builder.ToString();
#else
  info = object.DebugName();
#endif
  return out << static_cast<const void*>(&object) << ":" << info.Utf8();
}

std::ostream& operator<<(std::ostream& out, const LayoutObject* object) {
  if (!object)
    return out << "<null>";
  return out << *object;
}

#if DCHECK_IS_ON()

void LayoutObject::ShowTreeForThis() const {
  NOT_DESTROYED();
  if (GetNode())
    ::ShowTree(GetNode());
}

void LayoutObject::ShowLayoutTreeForThis() const {
  NOT_DESTROYED();
  ShowLayoutTree(this, nullptr);
}

void LayoutObject::ShowLayoutObject() const {
  NOT_DESTROYED();

  if (getenv("RUNNING_UNDER_RR")) {
    // Printing timestamps requires an IPC to get the local time, which
    // does not work in an rr replay session. Just disable timestamp printing
    // globally, since we don't need them. Affecting global state isn't a
    // problem because invoking this from a rr session creates a temporary
    // program environment that will be destroyed as soon as the invocation
    // completes.
    logging::SetLogItems(true, true, false, false);
  }

  StringBuilder string_builder;
  DumpLayoutObject(string_builder, true, kShowTreeCharacterOffset);
  DLOG(INFO) << "\n" << string_builder.ToString().Utf8();
}

void LayoutObject::DumpLayoutObject(StringBuilder& string_builder,
                                    bool dump_address,
                                    unsigned show_tree_character_offset) const {
  // This function doesn't call `NOT_DESTROYED()` to aid debugging.
#if DCHECK_IS_ON()
  std::optional<base::AutoReset<bool>> is_destroyed;
  if (is_destroyed_) {
    string_builder.Append("[DESTROYED] ");

    // Temporarily reset `is_destroyed_` to make dumping possible. Code and
    // functions in this function must be safe to call for a destroyed object.
    is_destroyed.emplace(const_cast<bool*>(&is_destroyed_), false);
  }
#endif  // DCHECK_IS_ON()

  string_builder.Append(DecoratedName());

  if (dump_address)
    FormatTo(string_builder, " {}", this);

  if (IsText() && To<LayoutText>(this)->IsTextFragment()) {
    FormatTo(string_builder, " \"{}\" ",
             To<LayoutText>(this)->TransformedText());
  }

  if (GetNode()) {
    while (string_builder.length() < show_tree_character_offset)
      string_builder.Append(' ');
    string_builder.Append('\t');
    string_builder.Append(GetNode()->ToString());
  }
}

void LayoutObject::DumpLayoutTreeAndMark(StringBuilder& string_builder,
                                         const LayoutObject* marked_object1,
                                         const char* marked_label1,
                                         const LayoutObject* marked_object2,
                                         const char* marked_label2,
                                         unsigned depth) const {
  NOT_DESTROYED();
  StringBuilder object_info;
  if (marked_object1 == this && marked_label1)
    object_info.Append(marked_label1);
  if (marked_object2 == this && marked_label2)
    object_info.Append(marked_label2);
  while (object_info.length() < depth * 2)
    object_info.Append(' ');

  DumpLayoutObject(object_info, true, kShowTreeCharacterOffset);
  string_builder.Append(object_info);

  if (!ChildLayoutBlockedByDisplayLock()) {
    for (const LayoutObject* child = SlowFirstChild(); child;
         child = child->NextSibling()) {
      string_builder.Append('\n');
      child->DumpLayoutTreeAndMark(string_builder, marked_object1,
                                   marked_label1, marked_object2, marked_label2,
                                   depth + 1);
    }
  }
}

#endif  // DCHECK_IS_ON()

bool LayoutObject::IsSelected() const {
  NOT_DESTROYED();
  // Keep this fast and small, used in very hot functions to skip computing
  // selection when this is not selected. This function may be inlined in
  // link-optimized builds, but keeping fast and small helps running perf
  // tests.
  return GetSelectionState() != SelectionState::kNone ||
         // TODO(kojii): Can't we set SelectionState() properly to
         // LayoutTextFragment too?
         (IsA<LayoutTextFragment>(*this) && LayoutSelection::IsSelected(*this));
}

bool LayoutObject::IsSelectable() const {
  NOT_DESTROYED();
  return StyleRef().IsSelectable();
}

const ComputedStyle& LayoutObject::SlowEffectiveStyle(
    StyleVariant style_variant) const {
  NOT_DESTROYED();
  switch (style_variant) {
    case StyleVariant::kStandard:
      return StyleRef();
    case StyleVariant::kFirstLine:
      return IsAtomicInline() ? StyleRef() : FirstLineStyleRef();
    case StyleVariant::kStandardEllipsis:
      // The ellipsis is styled according to the line style.
      // https://www.w3.org/TR/css-overflow-3/#ellipsing-details
      // For the line-clamp ellipsis, we can end up with a FragmentItem whose
      // LayoutObject is the containing LayoutBlockFlow, because there is no
      // inline layout object that it belongs to. In that case, we must also use
      // the line style, which is this LayoutObject's style.
      DCHECK(
          IsInline() ||
          (RuntimeEnabledFeatures::CSSLineClampLineBreakingEllipsisEnabled() &&
           IsLayoutBlockFlow() && ChildrenInline()));
      if (!IsLayoutBlockFlow()) {
        if (const LayoutObject* block = ContainingBlock()) {
          return block->StyleRef();
        }
      }
      return StyleRef();
    case StyleVariant::kFirstLineEllipsis:
      DCHECK(IsInline());
      if (const LayoutObject* block = ContainingBlock()) {
        return block->FirstLineStyleRef();
      }
      return FirstLineStyleRef();
  }
  NOTREACHED();
}

StyleDifference LayoutObject::AdjustStyleDifference(
    StyleDifference diff) const {
  NOT_DESTROYED();
  if (diff.transform_changed && IsSVG()) {
    // Skip a full layout for transforms at the html/svg boundary which do not
    // affect sizes inside SVG.
    if (!IsSVGRoot())
      diff.SetNeedsFullLayout();
  }

  // Optimization: for decoration/color property changes, invalidation is only
  // needed if we have style or text affected by these properties.
  if (diff.text_decoration_or_color_changed &&
      !diff.NeedsNormalPaintInvalidation() &&
      !diff.NeedsSimplePaintInvalidation()) {
    if (StyleRef().HasOutlineWithCurrentColor() ||
        StyleRef().HasBackgroundRelatedColorReferencingCurrentColor() ||
        // Skip any text nodes that do not contain text boxes. Whitespace cannot
        // be skipped or we will miss invalidating decorations (e.g.,
        // underlines). MathML elements are not skipped either as some of them
        // do special painting (e.g. fraction bar).
        (IsText() && !IsBR() && To<LayoutText>(this)->HasInlineFragments()) ||
        (IsSVG() && StyleRef().IsFillColorCurrentColor()) ||
        (IsSVG() && StyleRef().IsStrokeColorCurrentColor()) || IsMathML()) {
      diff.SetNeedsSimplePaintInvalidation();
    }
  }

  // The answer to LayerTypeRequired() for plugins, iframes, and canvas can
  // change without the actual style changing, since it depends on whether we
  // decide to composite these elements. When the layer status of one of these
  // elements changes, we need to force a layout.
  if (!diff.NeedsFullLayout() && HasStyle() && IsBoxModelObject()) {
    bool requires_layer =
        To<LayoutBoxModelObject>(this)->LayerTypeRequired() != kNoPaintLayer;
    if (HasLayer() != requires_layer)
      diff.SetNeedsFullLayout();
  }

  return diff;
}

void LayoutObject::SetPseudoElementStyle(const LayoutObject& owner,
                                         bool match_parent_size) {
  NOT_DESTROYED();
  const ComputedStyle& pseudo_style = owner.StyleRef();
  DCHECK(pseudo_style.StyleType() == kPseudoIdCheckMark ||
         pseudo_style.StyleType() == kPseudoIdBefore ||
         pseudo_style.StyleType() == kPseudoIdAfter ||
         pseudo_style.StyleType() == kPseudoIdExpandIcon ||
         pseudo_style.StyleType() == kPseudoIdPickerIcon ||
         pseudo_style.StyleType() == kPseudoIdInterestButton ||
         pseudo_style.StyleType() == kPseudoIdMarker ||
         pseudo_style.StyleType() == kPseudoIdFirstLetter ||
         pseudo_style.StyleType() == kPseudoIdScrollMarkerGroup ||
         pseudo_style.IsPageMarginBox() ||
         pseudo_style.StyleType() == kPseudoIdScrollMarker ||
         pseudo_style.StyleType() == kPseudoIdScrollButtonBlockStart ||
         pseudo_style.StyleType() == kPseudoIdScrollButtonInlineStart ||
         pseudo_style.StyleType() == kPseudoIdScrollButtonInlineEnd ||
         pseudo_style.StyleType() == kPseudoIdScrollButtonBlockEnd);

  InheritIsInDetachedNonDomTree(owner);

  // FIXME: We should consider just making all pseudo items use an inherited
  // style.

  // Images are special and must inherit the pseudoStyle so the width and height
  // of the pseudo-element doesn't change the size of the image. In all other
  // cases we can just share the style.
  //
  // Quotes are also LayoutInline, so we need to create an inherited style to
  // avoid getting an inline with positioning or an invalid display.
  //
  if (IsImage() || IsQuote()) {
    ComputedStyleBuilder builder =
        GetDocument()
            .GetStyleResolver()
            .CreateComputedStyleBuilderInheritingFrom(pseudo_style);
    if (match_parent_size) {
      DCHECK(IsImage());
      builder.SetWidth(Length::Percent(100));
      builder.SetHeight(Length::Percent(100));
    }
    SetStyle(builder.TakeStyle());
    return;
  }

  if (IsText() && Parent() && Parent()->IsInitialLetterBox()) [[unlikely]] {
    // Note: `Parent()` can be null for text for generated contents.
    // See "accessibility/css-generated-content.html"
    const ComputedStyle* initial_letter_text_style =
        GetDocument().GetStyleResolver().StyleForInitialLetterText(
            pseudo_style, Parent()->ContainingBlock()->StyleRef());
    SetStyle(std::move(initial_letter_text_style));
    return;
  }

  if (IsText() && IsA<LayoutTextCombine>(Parent())) [[unlikely]] {
    // See http://crbug.com/1222640
    ComputedStyleBuilder combined_text_style_builder =
        GetDocument()
            .GetStyleResolver()
            .CreateComputedStyleBuilderInheritingFrom(pseudo_style);
    StyleAdjuster::AdjustStyleForCombinedText(combined_text_style_builder);
    SetStyle(combined_text_style_builder.TakeStyle());
    return;
  }

  SetStyle(&pseudo_style);
}

DISABLE_CFI_PERF
void LayoutObject::SetStyle(const ComputedStyle* style,
                            ApplyStyleChanges apply_changes) {
  NOT_DESTROYED();
  if (style_ == style)
    return;

  if (apply_changes == ApplyStyleChanges::kNo) {
    const ComputedStyle* old_style = style_;
    SetStyleInternal(style);
    // Ideally we shouldn't have to do this, but new CSSImageGeneratorValues are
    // generated on recalc for custom properties, which means we need to call
    // UpdateImageObservers to keep CSSImageGeneratorValue::clients_ up-to-date.
    if (!IsText()) {
      UpdateImageObservers(old_style, style_.Get());
      // Ditto for CSSURIValues.
      if (HasLayer()) {
        PaintLayer* layer = To<LayoutBoxModelObject>(*this).Layer();
        layer->UpdateFilters({}, old_style, *style_);
        layer->UpdateBackdropFilters(old_style, *style_);
        layer->UpdateClipPath(old_style, *style_);
      }
    }
    return;
  }

  DCHECK(style);

  StyleDifference diff;
  if (style_) {
    diff = style_->VisualInvalidationDiff(GetDocument(), *style);
    if (const auto* cached_inherited_first_line_style =
            style_->GetCachedPseudoElementStyle(kPseudoIdFirstLineInherited)) {
      // Merge the difference to the first line style because even if the new
      // style is the same as the old style, the new style may have some higher
      // priority properties overriding first line style.
      // See external/wpt/css/css-pseudo/first-line-change-inline-color*.html.
      diff.Merge(cached_inherited_first_line_style->VisualInvalidationDiff(
          GetDocument(), *style));
    }

    auto HighlightPseudoUpdateDiff =
        [this, style, &diff](const PseudoId pseudo,
                             const ComputedStyle* pseudo_old_style,
                             const ComputedStyle* pseudo_new_style) {
          DCHECK(pseudo == kPseudoIdSearchText ||
                 pseudo == kPseudoIdTargetText ||
                 pseudo == kPseudoIdSpellingError ||
                 pseudo == kPseudoIdGrammarError);

          if (style_->HasPseudoElementStyle(pseudo) ||
              style->HasPseudoElementStyle(pseudo)) {
            if (pseudo_old_style && pseudo_new_style) {
              diff.Merge(pseudo_old_style->VisualInvalidationDiff(
                  GetDocument(), *pseudo_new_style));
            } else {
              diff.SetNeedsNormalPaintInvalidation();
            }
          }
        };

    // See HighlightRegistry for ::highlight() paint invalidation.
    // We don't do anything regarding ::selection, as ::selection
    // uses its own mechanism for this (see
    // LayoutObject::InvalidateSelectionOnStyleChange()).
    if (RuntimeEnabledFeatures::SearchTextHighlightPseudoEnabled()) {
      HighlightPseudoUpdateDiff(kPseudoIdSearchText,
                                style_->HighlightData().SearchTextCurrent(),
                                style->HighlightData().SearchTextCurrent());
      HighlightPseudoUpdateDiff(kPseudoIdSearchText,
                                style_->HighlightData().SearchTextNotCurrent(),
                                style->HighlightData().SearchTextNotCurrent());
    }
    HighlightPseudoUpdateDiff(kPseudoIdTargetText,
                              style_->HighlightData().TargetText(),
                              style->HighlightData().TargetText());
    HighlightPseudoUpdateDiff(kPseudoIdSpellingError,
                              style_->HighlightData().SpellingError(),
                              style->HighlightData().SpellingError());
    HighlightPseudoUpdateDiff(kPseudoIdGrammarError,
                              style_->HighlightData().GrammarError(),
                              style->HighlightData().GrammarError());
  }

  diff = AdjustStyleDifference(diff);

  // A change to a property that can be animated on the compositor or an
  // animation affecting that property may require paint invalidation.
  diff = AdjustForCompositableAnimationPaint(style_, style, GetNode(), diff);

  StyleChangeContext style_change_context;

  const ComputedStyle* old_style = style_.Get();
  StyleWillChange(diff, old_style, *style, style_change_context);

  SetStyleInternal(std::move(style));

  if (!IsText()) {
    UpdateImageObservers(old_style, style_.Get());
  }

  bool does_not_need_layout_or_paint_invalidation = !parent_;

  StyleDidChange(diff, old_style, *style_, style_change_context);

  // FIXME: |this| might be destroyed here. This can currently happen for a
  // LayoutTextFragment when its first-letter block gets an update in
  // LayoutTextFragment::styleDidChange. For LayoutTextFragment(s),
  // we will safely bail out with the doesNotNeedLayoutOrPaintInvalidation flag.
  // We might want to broaden this condition in the future as we move
  // layoutObject changes out of layout and into style changes.
  if (does_not_need_layout_or_paint_invalidation)
    return;

  // Now that the layer (if any) has been updated, we need to adjust the diff
  // again, check whether we should layout now, and decide if we need to
  // invalidate paints.
  StyleDifference updated_diff = AdjustStyleDifference(diff);

  if (updated_diff.NeedsSimplePaintInvalidation()) {
    DCHECK(!diff.NeedsNormalPaintInvalidation());
    constexpr int kMaxDepth = 5;
    if (auto* painting_layer = PaintingLayer(kMaxDepth)) {
      painting_layer->SetNeedsRepaint();
      InvalidateDisplayItemClients(PaintInvalidationReason::kStyle);
      GetFrameView()->ScheduleVisualUpdateForPaintInvalidationIfNeeded();
    } else {
      updated_diff.SetNeedsNormalPaintInvalidation();
    }
  }

  if (!diff.NeedsFullLayout()) {
    if (updated_diff.NeedsFullLayout()) {
      SetNeedsLayoutAndIntrinsicWidthsRecalc(
          layout_invalidation_reason::kStyleChange);
    } else if (updated_diff.NeedsPositionedLayout() ||
               StyleRef().HasAnchorFunctionsWithoutEvaluator()) {
      if (StyleRef().HasOutOfFlowPosition()) {
        ContainingBlock()->SetNeedsSimplifiedLayout();
      } else {
        ContainingBlock()->SetChildNeedsLayout();
        Parent()->DirtyLinesFromChangedChild(this);
      }
    }
  }

  // TODO(cbiesinger): Shouldn't this check container->NeedsLayout, since that's
  // the one we'll mark for NeedsOverflowRecalc()?
  if (diff.transform_changed && !NeedsLayout()) {
    if (LayoutBlock* container = ContainingBlock())
      container->SetNeedsOverflowRecalc();
  }

  if (diff.needs_recompute_visual_overflow) {
    InvalidateVisualOverflow();
#if DCHECK_IS_ON()
    InvalidateVisualOverflowForDCheck();
#endif
  }

  if (diff.NeedsNormalPaintInvalidation() ||
      updated_diff.NeedsNormalPaintInvalidation()) {
    if (IsSVGRoot()) {
      // LayoutSVGRoot::LocalVisualRect() depends on some styles.
      SetShouldDoFullPaintInvalidation();
    } else {
      // We'll set needing geometry change later if the style change does cause
      // possible layout change or visual overflow change.
      SetShouldDoFullPaintInvalidationWithoutLayoutChange(
          PaintInvalidationReason::kStyle);
    }
  }

  // Main thread clip path animations always require paint property updates,
  // and cc thread clip path animations require updates when stopping or
  // starting. See: AdjustForCompositableAnimationPaint.
  if (old_style && diff.NeedsNormalPaintInvalidation() &&
      diff.clip_path_changed) {
    SetNeedsPaintPropertyUpdate();
    PaintingLayer()->SetNeedsCompositingInputsUpdate();
  }

  // Text nodes share style with their parents but the paint properties don't
  // apply to them, hence the !isText() check. If property nodes are added or
  // removed as a result of these style changes, PaintPropertyTreeBuilder will
  // call SetNeedsRepaint to cause re-generation of PaintChunks.
  // This is skipped if no layer is present because |PaintLayer::StyleDidChange|
  // will handle this invalidation.
  if (!IsText() && !HasLayer() &&
      (diff.transform_changed || diff.opacity_changed || diff.z_index_changed ||
       diff.filter_changed || diff.clip_property_changed ||
       diff.blend_mode_changed || diff.mask_changed ||
       diff.compositing_reasons_changed)) {
    SetNeedsPaintPropertyUpdate();
  }
}

void LayoutObject::UpdateFirstLineImageObservers(
    const ComputedStyle* new_style) {
  NOT_DESTROYED();
  bool has_new_first_line_style =
      new_style && new_style->HasPseudoElementStyle(kPseudoIdFirstLine) &&
      BehavesLikeBlockContainer();
  DCHECK(!has_new_first_line_style || new_style == &StyleRef());

  if (!registered_as_first_line_image_observer_ && !has_new_first_line_style) {
    return;
  }

  using FirstLineStyleMap =
      HeapHashMap<WeakMember<const LayoutObject>, Member<const ComputedStyle>>;
  using FirstLineStyleMapHolder = DisallowNewWrapper<FirstLineStyleMap>;
  DEFINE_STATIC_LOCAL(Persistent<FirstLineStyleMapHolder>,
                      first_line_style_map_holder,
                      (MakeGarbageCollected<FirstLineStyleMapHolder>()));
  auto& first_line_style_map = first_line_style_map_holder->Value();
  DCHECK_EQ(registered_as_first_line_image_observer_,
            first_line_style_map.Contains(this));
  const auto* old_first_line_style = registered_as_first_line_image_observer_
                                         ? first_line_style_map.at(this)
                                         : nullptr;

  // UpdateFillImages() may indirectly call LayoutBlock::ImageChanged() which
  // will invalidate the first line style cache and remove a reference to
  // new_first_line_style, so hold a reference here.
  const ComputedStyle* new_first_line_style =
      has_new_first_line_style ? FirstLineStyleWithoutFallback() : nullptr;

  if (new_first_line_style && !new_first_line_style->HasBackgroundImage())
    new_first_line_style = nullptr;

  if (old_first_line_style || new_first_line_style) {
    UpdateFillImages(
        old_first_line_style ? &old_first_line_style->BackgroundLayers()
                             : nullptr,
        new_first_line_style ? &new_first_line_style->BackgroundLayers()
                             : nullptr);
    if (new_first_line_style) {
      // The cached first line style may have been invalidated during
      // UpdateFillImages, so get it again. However, the new cached first line
      // style should be the same as the previous new_first_line_style.
      DCHECK(FillLayer::ImagesIdentical(
          &new_first_line_style->BackgroundLayers(),
          &FirstLineStyleWithoutFallback()->BackgroundLayers()));
      new_first_line_style = FirstLineStyleWithoutFallback();
      registered_as_first_line_image_observer_ = true;
      first_line_style_map.Set(this, std::move(new_first_line_style));
    } else {
      registered_as_first_line_image_observer_ = false;
      first_line_style_map.erase(this);
    }
    DCHECK_EQ(registered_as_first_line_image_observer_,
              first_line_style_map.Contains(this));
  }
}

void LayoutObject::StyleWillChange(StyleDifference diff,
                                   const ComputedStyle* old_style,
                                   const ComputedStyle& new_style,
                                   StyleChangeContext& style_change_context) {
  NOT_DESTROYED();
  DCHECK(!IsText());

  if (old_style) {
    bool visibility_changed = old_style->Visibility() != new_style.Visibility();
    // If our z-index changes value or our visibility changes,
    // we need to dirty our stacking context's z-order list.
    if (visibility_changed ||
        old_style->EffectiveZIndex() != new_style.EffectiveZIndex() ||
        IsStackingContext(*old_style) != IsStackingContext(new_style)) {
      GetDocument().SetDraggableRegionsDirty(true);
    }

    // Keep layer hierarchy visibility bits up to date if visibility changes.
    if (visibility_changed) {
      // We might not have an enclosing layer yet because we might not be in the
      // tree.
      if (PaintLayer* layer = EnclosingLayer())
        layer->DirtyVisibleContentStatus();
      GetDocument().GetFrame()->GetInputMethodController().DidChangeVisibility(
          *this);
    }
  }
}

static inline bool AreCursorsEqual(const ComputedStyle& a,
                                   const ComputedStyle& b) {
  return a.Cursor() == b.Cursor() &&
         base::ValuesEquivalent(a.Cursors(), b.Cursors());
}

void LayoutObject::SetScrollAnchorDisablingStyleChangedOnAncestor() {
  NOT_DESTROYED();
  // Walk up the parent chain and find the first scrolling block to disable
  // scroll anchoring on.
  LayoutObject* object = Parent();
  Element* viewport_defining_element = GetDocument().ViewportDefiningElement();
  while (object) {
    auto* block = DynamicTo<LayoutBlock>(object);
    if (block && (block->IsScrollContainer() ||
                  block->GetNode() == viewport_defining_element)) {
      block->SetScrollAnchorDisablingStyleChanged(true);
      return;
    }
    object = object->Parent();
  }
}

static void ClearAncestorScrollAnchors(LayoutObject* layout_object) {
  PaintLayer* layer = nullptr;
  if (LayoutObject* parent = layout_object->Parent())
    layer = parent->EnclosingLayer();

  while (layer) {
    if (PaintLayerScrollableArea* scrollable_area =
            layer->GetScrollableArea()) {
      ScrollAnchor* anchor = scrollable_area->GetScrollAnchor();
      DCHECK(anchor);
      anchor->Clear();
    }
    layer = layer->Parent();
  }
}

void LayoutObject::UpdateAfterReinsert(const ComputedStyle& old_style) {
  NOT_DESTROYED();

  // Now that we are in the layout-tree, disable scroll-anchoring on our scroll
  // container as per:
  // https://drafts.csswg.org/css-scroll-anchoring-1/#suppression-triggers
  if (old_style.HasOutOfFlowPosition() != StyleRef().HasOutOfFlowPosition()) {
    SetScrollAnchorDisablingStyleChangedOnAncestor();
  }
}

void LayoutObject::StyleDidChange(
    StyleDifference diff,
    const ComputedStyle* old_style,
    const ComputedStyle& new_style,
    const StyleChangeContext& style_change_context) {
  NOT_DESTROYED();
  if (HasHiddenBackface()) {
    if (Parent() && Parent()->StyleRef().UsedTransformStyle3D() ==
                        ETransformStyle3D::kPreserve3d) {
      UseCounter::Count(GetDocument(),
                        WebFeature::kHiddenBackfaceWithPossible3D);
      UseCounter::Count(GetDocument(), WebFeature::kHiddenBackfaceWith3D);
      UseCounter::Count(GetDocument(),
                        WebFeature::kHiddenBackfaceWithPreserve3D);
    } else if (new_style.HasTransform()) {
      UseCounter::Count(GetDocument(),
                        WebFeature::kHiddenBackfaceWithPossible3D);
      // For consistency with existing code usage, this uses
      // Has3DTransformOperation rather than the slightly narrower
      // HasNonTrivial3DTransformOperation (which used to exist, and was only
      // web-exposed for compositing decisions on low-end devices).  However,
      // given the discussion in
      // https://github.com/w3c/csswg-drafts/issues/3305 it's possible we may
      // want to tie backface-visibility behavior to something closer to the
      // latter.
      if (new_style.Has3DTransformOperation()) {
        UseCounter::Count(GetDocument(), WebFeature::kHiddenBackfaceWith3D);
      }
    }
  }

  if (ShouldApplyStrictContainment() &&
      new_style.IsContentVisibilityVisible()) {
    if (ShouldApplyStyleContainment()) {
      UseCounter::Count(GetDocument(),
                        WebFeature::kCSSContainAllWithoutContentVisibility);
    }
    UseCounter::Count(GetDocument(),
                      WebFeature::kCSSContainStrictWithoutContentVisibility);
  }

  // First assume the outline will be affected. It may be updated when we know
  // it's not affected.
  SetOutlineMayBeAffectedByDescendants(new_style.HasOutline());

  if (diff.NeedsFullLayout()) {
    SetNeedsLayoutAndIntrinsicWidthsRecalc(
        layout_invalidation_reason::kStyleChange);
  } else if (diff.NeedsPositionedLayout()) {
    if (auto* containing_block = ContainingBlock()) {
      if (new_style.HasOutOfFlowPosition()) {
        containing_block->SetNeedsSimplifiedLayout();
      } else {
        containing_block->SetChildNeedsLayout();
      }
    }
  }

  if (diff.ax_visibility_or_inert_changed) {
    if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
      cache->StyleChanged(this, /*visibility_or_inertness_changed*/ true);
    }
  }

  if (diff.ax_style_changed) {
    if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
      cache->StyleChanged(this);
    }
  }

  if (old_style &&
      old_style->ContentVisibility() != new_style.ContentVisibility()) {
    if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
      if (const Node* node = GetNode()) {
        cache->RemoveSubtree(node, /* remove_root */ false);
      }
    }
  }

  if (diff.disable_scroll_anchoring) {
    SetScrollAnchorDisablingStyleChanged(true);
  }

  if (diff.opacity_changed && IsDocumentElement() &&
      old_style->Opacity() == 0.f && new_style.Opacity() != 0.f) {
    PaintTimingDetector::From(GetDocument()).ReportIgnoredContent();
  }

  // Don't check for paint invalidation here; we need to wait until the layer
  // has been updated by subclasses before we know if we have to invalidate
  // paints (in setStyle()).

  if (old_style && !AreCursorsEqual(*old_style, new_style)) {
    if (LocalFrame* frame = GetFrame()) {
      // Cursor update scheduling is done by the local root, which is the main
      // frame if there are no RemoteFrame ancestors in the frame tree. Use of
      // localFrameRoot() is discouraged but will change when cursor update
      // scheduling is moved from EventHandler to PageEventHandler.
      frame->LocalFrameRoot().GetEventHandler().ScheduleCursorUpdate();
    }
  }

  if (diff.NeedsNormalPaintInvalidation() && old_style) {
    if (ResolveColor(*old_style, GetCSSPropertyBackgroundColor()) !=
            ResolveColor(new_style, GetCSSPropertyBackgroundColor()) ||
        old_style->BackgroundLayers() != new_style.BackgroundLayers()) {
      SetBackgroundNeedsFullPaintInvalidation();
    }
  }

  ApplyPseudoElementStyleChanges(old_style);

  if (old_style &&
      old_style->UsedTransformStyle3D() != new_style.UsedTransformStyle3D()) {
    // Change of transform-style may affect descendant transform property nodes.
    AddSubtreePaintPropertyUpdateReason(
        SubtreePaintPropertyUpdateReason::kTransformStyleChanged);
  }

  if (old_style && old_style->OverflowAnchor() != new_style.OverflowAnchor()) {
    ClearAncestorScrollAnchors(this);
  }

  if (old_style &&
      old_style->UsedPointerEvents() != new_style.UsedPointerEvents()) {
    // UsedPointerEvents affects hit test opacity.
    SetShouldInvalidatePaintForHitTest();
  }

  if (new_style.AnchorName()) {
    MarkMayContainAnchor();
  }

  if (MayContainAnchor() && old_style) {
    // If there's an anchor here, and the new style might want to run animations
    // on the compositor, anchors may affect layout of the anchored elements.
    // Mark for layout to update the anchor references and thus request main
    // frame animations if needed.
    if (new_style.IsRunningTransformRelatedAnimationOnCompositor() &&
        !old_style->IsRunningTransformRelatedAnimationOnCompositor()) {
      SetNeedsLayout(layout_invalidation_reason::kStyleChange);
    }
  }
  const bool style_focusability = new_style.IsFocusable();
  const bool old_style_focusability = old_style && old_style->IsFocusable();
  if (!style_focusability && old_style_focusability) {
    node_->FocusabilityLost();
  }

  // Elements with non-auto touch-action will send a SetTouchAction message on
  // touchstart in EventHandler::handleTouchEvent, and so effectively have a
  // touchstart handler that must be reported.
  //
  // Since a CSS property cannot be applied directly to a text node, a handler
  // will have already been added for its parent so ignore it.
  //
  // Elements may inherit touch action from parent frame, so we need to report
  // touchstart handler if the root layout object has non-auto touch action.
  const bool is_old_touch_action_auto =
      !old_style || old_style->EffectiveTouchAction() == TouchAction::kAuto;
  const bool is_new_touch_action_auto =
      new_style.EffectiveTouchAction() == TouchAction::kAuto;
  if (GetNode() && is_old_touch_action_auto != is_new_touch_action_auto) {
    EventHandlerRegistry& registry =
        GetDocument().GetFrame()->GetEventHandlerRegistry();
    if (is_new_touch_action_auto) {
      registry.DidRemoveEventHandler(*GetNode(),
                                     EventHandlerRegistry::kTouchAction);
    } else {
      registry.DidAddEventHandler(*GetNode(),
                                  EventHandlerRegistry::kTouchAction);
    }
    MarkEffectiveAllowedTouchActionChanged();
  }
}

void LayoutObject::ApplyPseudoElementStyleChanges(
    const ComputedStyle* old_style) {
  NOT_DESTROYED();
  ApplyFirstLineChanges(old_style);

  if ((old_style && old_style->HasPseudoElementStyle(kPseudoIdSelection)) ||
      StyleRef().HasPseudoElementStyle(kPseudoIdSelection))
    InvalidateSelectionOnStyleChange();
}

void LayoutObject::ApplyFirstLineChanges(const ComputedStyle* old_style) {
  NOT_DESTROYED();
  bool has_old_first_line_style =
      old_style && old_style->HasPseudoElementStyle(kPseudoIdFirstLine);
  bool has_new_first_line_style =
      StyleRef().HasPseudoElementStyle(kPseudoIdFirstLine);
  if (!has_old_first_line_style && !has_new_first_line_style)
    return;

  StyleDifference diff;
  bool has_diff = false;
  bool first_line_highlight_changed = false;
  if (Parent() && has_old_first_line_style && has_new_first_line_style) {
    if (const auto* old_first_line_style =
            old_style->GetCachedPseudoElementStyle(kPseudoIdFirstLine)) {
      if (const auto* new_first_line_style = FirstLineStyleWithoutFallback()) {
        diff = old_first_line_style->VisualInvalidationDiff(
            GetDocument(), *new_first_line_style);
        diff = AdjustForCompositableAnimationPaint(
            old_first_line_style, new_first_line_style, GetNode(), diff);
        // Highlight pseudo styles are stored  in StyleHighlightData and are
        // intentionally ignored by VisualInvalidationDiff, because highlight
        // repaints are normally driven by HighlightRegistry.
        first_line_highlight_changed = old_first_line_style->HighlightData() !=
                                       new_first_line_style->HighlightData();
        has_diff = true;
      }
    }
  }
  if (!has_diff) {
    diff.SetNeedsNormalPaintInvalidation();
    diff.SetNeedsFullLayout();
  }

  if (BehavesLikeBlockContainer() &&
      (diff.NeedsNormalPaintInvalidation() ||
       diff.text_decoration_or_color_changed || first_line_highlight_changed)) {
    if (auto* first_line_container =
            To<LayoutBlock>(this)->NearestInnerBlockWithFirstLine())
      first_line_container->SetShouldDoFullPaintInvalidationForFirstLine();
  }

  if (diff.NeedsFullLayout()) {
    SetNeedsCollectInlines();
    SetNeedsLayoutAndIntrinsicWidthsRecalc(
        layout_invalidation_reason::kStyleChange);
  }
}

void LayoutObject::AddAsImageObserver(StyleImage* image) {
  NOT_DESTROYED();
  if (!image)
    return;
#if DCHECK_IS_ON()
  ++as_image_observer_count_;
#endif
  image->AddClient(this);
}

void LayoutObject::RemoveAsImageObserver(StyleImage* image) {
  NOT_DESTROYED();
  if (!image)
    return;
#if DCHECK_IS_ON()
  SECURITY_DCHECK(as_image_observer_count_ > 0u);
  --as_image_observer_count_;
#endif
  image->RemoveClient(this);
}

void LayoutObject::UpdateFillImages(const FillLayer* old_layers,
                                    const FillLayer* new_layers) {
  NOT_DESTROYED();
  // Optimize the common case
  if (FillLayer::ImagesIdentical(old_layers, new_layers))
    return;

  // Go through the new layers and AddAsImageObserver() first, to avoid removing
  // all clients of an image.
  for (const FillLayer* curr_new = new_layers; curr_new;
       curr_new = curr_new->Next())
    AddAsImageObserver(curr_new->GetImage());

  for (const FillLayer* curr_old = old_layers; curr_old;
       curr_old = curr_old->Next())
    RemoveAsImageObserver(curr_old->GetImage());
}

void LayoutObject::UpdateCursorImages(const CursorList* old_cursors,
                                      const CursorList* new_cursors) {
  NOT_DESTROYED();
  if (old_cursors && new_cursors && *old_cursors == *new_cursors)
    return;

  if (new_cursors) {
    for (const auto& cursor : *new_cursors)
      AddAsImageObserver(cursor.GetImage());
  }
  if (old_cursors) {
    for (const auto& cursor : *old_cursors)
      RemoveAsImageObserver(cursor.GetImage());
  }
}

void LayoutObject::UpdateImage(StyleImage* old_image, StyleImage* new_image) {
  NOT_DESTROYED();
  if (old_image != new_image) {
    // AddAsImageObserver first, to avoid removing all clients of an image.
    AddAsImageObserver(new_image);
    RemoveAsImageObserver(old_image);
  }
}

void LayoutObject::UpdateShapeImage(const ShapeValue* old_shape_value,
                                    const ShapeValue* new_shape_value) {
  NOT_DESTROYED();
  if (old_shape_value || new_shape_value) {
    UpdateImage(old_shape_value ? old_shape_value->GetImage() : nullptr,
                new_shape_value ? new_shape_value->GetImage() : nullptr);
  }
}

PhysicalRect LayoutObject::ViewRect() const {
  NOT_DESTROYED();
  return View()->ViewRect();
}

gfx::PointF LayoutObject::AncestorToLocalPoint(
    const LayoutBoxModelObject* ancestor,
    const gfx::PointF& container_point,
    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  TransformState transform_state(
      TransformState::kUnapplyInverseTransformDirection, container_point);
  MapAncestorToLocal(ancestor, transform_state, mode);
  transform_state.Flatten();

  return transform_state.LastPlanarPoint();
}

gfx::QuadF LayoutObject::AncestorToLocalQuad(
    const LayoutBoxModelObject* ancestor,
    const gfx::QuadF& quad,
    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  TransformState transform_state(
      TransformState::kUnapplyInverseTransformDirection,
      quad.BoundingBox().CenterPoint(), quad);
  MapAncestorToLocal(ancestor, transform_state, mode);
  transform_state.Flatten();
  return transform_state.LastPlanarQuad();
}

LayoutObject* LayoutObject::CanvasForDrawingLayoutObject() const {
  NOT_DESTROYED();
  if (!IsBoxModelObject()) {
    return nullptr;
  }
  if (const auto* element = DynamicTo<Element>(GetNode())) {
    if (HTMLCanvasElement* canvas = element->CanvasForDrawing()) {
      return canvas->GetLayoutObject();
    }
  }
  return nullptr;
}

void LayoutObject::MapLocalToAncestor(const LayoutBoxModelObject* ancestor,
                                      TransformState& transform_state,
                                      MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  CHECK_EQ(transform_state.Direction(),
           TransformState::kApplyTransformDirection);
  if (ancestor == this)
    return;

  if (LayoutObject* canvas_layout_object = CanvasForDrawingLayoutObject()) {
    bool use_transforms = !mode.Has(MapCoordinatesMode::kIgnoreTransforms);
    const bool preserve3d = use_transforms && StyleRef().Preserves3D();
    if (use_transforms &&
        ShouldUseTransformFromContainer(canvas_layout_object)) {
      gfx::Transform t;
      GetTransformFromContainer(canvas_layout_object, PhysicalOffset(), t);
      transform_state.ApplyTransform(
          t, preserve3d ? TransformState::kAccumulateTransform
                        : TransformState::kFlattenTransform);
    }
    if (canvas_layout_object != ancestor) {
      canvas_layout_object->MapLocalToAncestor(ancestor, transform_state, mode);
    }
    return;
  }

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

  // If we skipped an ancestor, it must mean we have an ancestor that was passed
  // in.
  CHECK(!skip_info.AncestorSkipped() || ancestor);

  PhysicalOffset container_offset = OffsetFromContainer(container, mode);

  bool use_transforms = !mode.Has(MapCoordinatesMode::kIgnoreTransforms);

  // Text objects just copy their parent's computed style, so we need to ignore
  // them.
  const bool container_preserves_3d =
      container->StyleRef().Preserves3D() && !container->IsText();
  // Just because container and this have preserve-3d doesn't mean all
  // the DOM elements between them do.  (We know they don't have a
  // transform, though, since otherwise they'd be the container.)
  const bool path_preserves_3d = container == NearestAncestorForElement();
  const bool preserve3d =
      use_transforms && container_preserves_3d && path_preserves_3d;

  if (use_transforms && ShouldUseTransformFromContainer(container)) {
    gfx::Transform t;
    GetTransformFromContainer(container, container_offset, t);
    transform_state.ApplyTransform(t, preserve3d
                                          ? TransformState::kAccumulateTransform
                                          : TransformState::kFlattenTransform);
  } else {
    transform_state.Move(container_offset,
                         preserve3d ? TransformState::kAccumulateTransform
                                    : TransformState::kFlattenTransform);
  }

  if (skip_info.AncestorSkipped()) {
    // There can't be a transform between |ancestor| and |o|, because transforms
    // create containers, so it should be safe to just subtract the delta
    // between the ancestor and |o|.
    transform_state.Move(-ancestor->OffsetFromAncestor(container),
                         preserve3d ? TransformState::kAccumulateTransform
                                    : TransformState::kFlattenTransform);
    return;
  }

  container->MapLocalToAncestor(ancestor, transform_state, mode);
}

void LayoutObject::MapAncestorToLocal(const LayoutBoxModelObject* ancestor,
                                      TransformState& transform_state,
                                      MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  CHECK_EQ(transform_state.Direction(),
           TransformState::kUnapplyInverseTransformDirection);
  if (this == ancestor)
    return;

  if (LayoutObject* canvas_layout_object = CanvasForDrawingLayoutObject()) {
    if (canvas_layout_object != ancestor) {
      canvas_layout_object->MapAncestorToLocal(ancestor, transform_state, mode);
    }
    bool use_transforms = !mode.Has(MapCoordinatesMode::kIgnoreTransforms);
    const bool preserve3d = use_transforms && StyleRef().Preserves3D();
    if (use_transforms &&
        ShouldUseTransformFromContainer(canvas_layout_object)) {
      gfx::Transform t;
      GetTransformFromContainer(canvas_layout_object, PhysicalOffset(), t);
      transform_state.ApplyTransform(
          t, preserve3d ? TransformState::kAccumulateTransform
                        : TransformState::kFlattenTransform);
    }
    return;
  }

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

  if (!skip_info.AncestorSkipped())
    container->MapAncestorToLocal(ancestor, transform_state, mode);

  PhysicalOffset container_offset = OffsetFromContainer(container, mode);
  bool use_transforms = !mode.Has(MapCoordinatesMode::kIgnoreTransforms);

  // Just because container and this have preserve-3d doesn't mean all
  // the DOM elements between them do.  (We know they don't have a
  // transform, though, since otherwise they'd be the container.)
  if (container != NearestAncestorForElement()) {
    transform_state.Move(PhysicalOffset(), TransformState::kFlattenTransform);
  }

  const bool preserve3d = use_transforms && StyleRef().Preserves3D();
  if (use_transforms && ShouldUseTransformFromContainer(container)) {
    gfx::Transform t;
    GetTransformFromContainer(container, container_offset, t);
    transform_state.ApplyTransform(t, preserve3d
                                          ? TransformState::kAccumulateTransform
                                          : TransformState::kFlattenTransform);
  } else {
    transform_state.Move(container_offset,
                         preserve3d ? TransformState::kAccumulateTransform
                                    : TransformState::kFlattenTransform);
  }

  if (skip_info.AncestorSkipped()) {
    container_offset = ancestor->OffsetFromAncestor(container);
    transform_state.Move(-container_offset);
  }
}

bool LayoutObject::ShouldUseTransformFromContainer(
    const LayoutObject* container_object) const {
  NOT_DESTROYED();
  // hasTransform() indicates whether the object has transform, transform-style
  // or perspective. We just care about transform, so check the layer's
  // transform directly.
  return (HasLayer() && To<LayoutBoxModelObject>(this)->Layer()->Transform()) ||
         (container_object && container_object->HasPerspective());
}

void LayoutObject::GetTransformFromContainer(
    const LayoutObject* container_object,
    const PhysicalOffset& offset_in_container,
    gfx::Transform& transform,
    const PhysicalSize* size,
    const gfx::Transform* fragment_transform) const {
  NOT_DESTROYED();
  transform.MakeIdentity();
  if (fragment_transform) {
    transform.PreConcat(*fragment_transform);
  } else {
    PaintLayer* layer =
        HasLayer() ? To<LayoutBoxModelObject>(this)->Layer() : nullptr;
    if (layer && layer->Transform()) {
      transform.PreConcat(layer->CurrentTransform());
    }
  }

  transform.PostTranslate(offset_in_container.left.ToFloat(),
                          offset_in_container.top.ToFloat());

  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);
    }
  }

  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(size);

    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;
  }
}

gfx::PointF LayoutObject::LocalToAncestorPoint(
    const gfx::PointF& local_point,
    const LayoutBoxModelObject* ancestor,
    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  TransformState transform_state(TransformState::kApplyTransformDirection,
                                 local_point);
  MapLocalToAncestor(ancestor, transform_state, mode);
  transform_state.Flatten();

  return transform_state.LastPlanarPoint();
}

PhysicalRect LayoutObject::LocalToAncestorRect(
    const PhysicalRect& rect,
    const LayoutBoxModelObject* ancestor,
    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  return PhysicalRect::EnclosingRect(
      LocalToAncestorQuad(gfx::QuadF(gfx::RectF(rect)), ancestor, mode)
          .BoundingBox());
}

gfx::QuadF LayoutObject::LocalToAncestorQuad(
    const gfx::QuadF& local_quad,
    const LayoutBoxModelObject* ancestor,
    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  // Track the point at the center of the quad's bounding box. As
  // MapLocalToAncestor() calls OffsetFromContainer(), it will use that point
  // as the reference point to decide which column's transform to apply in
  // multiple-column blocks.
  TransformState transform_state(TransformState::kApplyTransformDirection,
                                 local_quad.BoundingBox().CenterPoint(),
                                 local_quad);
  MapLocalToAncestor(ancestor, transform_state, mode);
  transform_state.Flatten();

  return transform_state.LastPlanarQuad();
}

gfx::Transform LayoutObject::LocalToAncestorTransform(
    const LayoutBoxModelObject* ancestor,
    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  DCHECK(!mode.Has(MapCoordinatesMode::kIgnoreTransforms));
  TransformState transform_state(TransformState::kApplyTransformDirection);
  MapLocalToAncestor(ancestor, transform_state, mode);
  return transform_state.AccumulatedTransform();
}

PhysicalOffset LayoutObject::OffsetFromContainerInternal(
    const LayoutObject* o,
    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  CHECK(o);
  DCHECK_EQ(o, Container());
  PhysicalOffset offset;
  if (o->IsScrollContainer()) {
    offset += OffsetFromScrollableContainer(o, mode);
  }
  offset += OffsetFromOverscrollContainer(o, mode);
  return offset;
}

PhysicalOffset LayoutObject::OffsetFromScrollableContainer(
    const LayoutObject* container,
    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  CHECK(container);
  DCHECK(container->IsScrollContainer());

  if (IsFixedPositioned() && container->IsLayoutView())
    return PhysicalOffset();

  if (mode.Has(MapCoordinatesMode::kIgnoreScrollOriginAndOffset)) {
    return PhysicalOffset();
  }

  const auto* box = To<LayoutBox>(container);
  if (!mode.Has(MapCoordinatesMode::kIgnoreScrollOffset)) {
    return -box->ScrolledContentOffset();
  }

  // ScrollOrigin accounts for other writing modes whose content's origin is not
  // at the top-left.
  CHECK(box->GetScrollableArea());
  return PhysicalOffset(box->GetScrollableArea()->ScrollOrigin());
}

PhysicalOffset LayoutObject::OffsetFromOverscrollContainer(
    const LayoutObject* container,
    MapCoordinatesFlags mode) const {
  // If either container is not a shifting overscroll area container or we need
  // to ignore scroll offsets, then we can early out.
  if (!container->IsContentMovingOverscrollContainer() ||
      mode.Has(MapCoordinatesMode::kIgnoreScrollOffset)) {
    return PhysicalOffset();
  }

  OverscrollAreaTracker* tracker =
      To<Element>(container->GetNode())->GetOverscrollAreaTracker();
  if (!tracker) {
    return PhysicalOffset();
  }
  const VectorOf<Element>& overscroll_areas = tracker->DOMSortedElements();
  // If we have a non-overlay overscroll area, the content is shifted by the
  // scroll of all overscroll areas, and each individual overscroll area is
  // shifted by each one before it.
  wtf_size_t affecting_overscroll_areas =
      IsOverscrollAreaParent()
          ? overscroll_areas.Find(
                &To<PseudoElement>(GetNode())->UltimateOriginatingElement())
          : overscroll_areas.size();
  CHECK_LE(affecting_overscroll_areas, overscroll_areas.size());

  PhysicalOffset offset;
  for (wtf_size_t i = 0; i < affecting_overscroll_areas; ++i) {
    auto* area_parent =
        overscroll_areas[i]->GetPseudoElement(kPseudoIdOverscrollAreaParent);
    if (auto* area_parent_object = area_parent->GetLayoutObject()) {
      offset += OffsetFromScrollableContainer(area_parent_object, mode);
    }
  }
  return offset;
}

PhysicalOffset LayoutObject::OffsetFromAncestor(
    const LayoutObject* ancestor_container) const {
  NOT_DESTROYED();
  if (ancestor_container == this)
    return PhysicalOffset();

  PhysicalOffset offset;
  PhysicalOffset reference_point;
  const LayoutObject* curr_container = this;
  AncestorSkipInfo skip_info(ancestor_container);
  do {
    const LayoutObject* next_container = curr_container->Container(&skip_info);

    // This means we reached the top without finding container.
    CHECK(next_container);
    if (!next_container)
      break;
    DCHECK(!curr_container->HasTransformRelatedProperty());
    PhysicalOffset current_offset =
        curr_container->OffsetFromContainer(next_container);
    offset += current_offset;
    reference_point += current_offset;
    curr_container = next_container;
  } while (curr_container != ancestor_container &&
           !skip_info.AncestorSkipped());
  if (skip_info.AncestorSkipped()) {
    DCHECK(curr_container);
    offset -= ancestor_container->OffsetFromAncestor(curr_container);
  }

  return offset;
}

PhysicalRect LayoutObject::LocalCaretRect(int, CaretShape) const {
  NOT_DESTROYED();
  return PhysicalRect();
}

bool LayoutObject::IsRooted() const {
  NOT_DESTROYED();
  const LayoutObject* object = this;
  while (object->Parent() && !object->HasLayer())
    object = object->Parent();
  if (object->HasLayer())
    return To<LayoutBoxModelObject>(object)->Layer()->Root()->IsRootLayer();
  return false;
}

Node* LayoutObject::GeneratingNode() const {
  NOT_DESTROYED();
  Node* node = GetNode();
  if (!node) {
    return Parent() ? Parent()->GeneratingNode() : nullptr;
  }
  if (node->IsPseudoElement()) {
    return &To<PseudoElement>(node)->UltimateOriginatingElement();
  }
  return node;
}

Node* LayoutObject::EnclosingNode() const {
  NOT_DESTROYED();
  Node* node = GetNode();
  return node ? node : Parent()->EnclosingNode();
}

RespectImageOrientationEnum LayoutObject::GetImageOrientation(
    const LayoutObject* layout_object) {
  return layout_object ? layout_object->StyleRef().ImageOrientation()
                       : ComputedStyleInitialValues::InitialImageOrientation();
}

void LayoutObject::WillBeDestroyed(const ComputedStyle* style) {
  NOT_DESTROYED();
  DCHECK(!IsText());

  const LocalFrame* frame = GetFrame();
  if (frame) {
    frame->GetInputMethodController().LayoutObjectWillBeDestroyed(*this);
  }

  // Destroy any leftover anonymous children.
  LayoutObjectChildList* children = VirtualChildren();
  if (children)
    children->DestroyLeftoverChildren();

  if (frame) {
    // If this layoutObject is being autoscrolled, stop the autoscrolling.
    if (const Page* page = frame->GetPage()) {
      page->GetAutoscrollController().StopAutoscrollIfNeeded(this);
    }
  }

  Remove();

  // Remove the handler if node had touch-action set. Handlers are not added
  // for text nodes so don't try removing for one too. Need to check if
  // m_style is null in cases of partial construction. Any handler we added
  // previously may have already been removed by the Document independently.
  if (GetNode() && style && style->GetTouchAction() != TouchAction::kAuto) {
    EventHandlerRegistry& registry =
        GetDocument().GetFrame()->GetEventHandlerRegistry();
    if (registry.EventHandlerTargets(EventHandlerRegistry::kTouchAction)
            ->Contains(GetNode())) {
      registry.DidRemoveEventHandler(*GetNode(),
                                     EventHandlerRegistry::kTouchAction);
    }
  }

  // Remove this object as ImageResourceObserver.
  if (style) {
    UpdateImageObservers(style, nullptr);
  }

  // We must have removed all image observers.
  SECURITY_CHECK(!registered_as_first_line_image_observer_);
#if DCHECK_IS_ON()
  SECURITY_DCHECK(as_image_observer_count_ == 0u);
#endif

  if (LocalFrameView* view = GetFrameView()) {
    view->ClearLayoutSubtreeRoot(*this);
    view->RemovePendingTransformUpdate(*this);
    view->RemovePendingOpacityUpdate(*this);
  }
}

DISABLE_CFI_PERF
void LayoutObject::InsertedIntoTree() {
  NOT_DESTROYED();
  // FIXME: We should DCHECK(isRooted()) here but generated content makes some
  // out-of-order insertion.

  can_traverse_physical_fragments_ =
      CalculateCanTraversePhysicalFragments(*this);

  // Keep our layer hierarchy updated. Optimize for the common case where we
  // don't have any children and don't have a layer attached to ourselves.
  PaintLayer* layer = nullptr;
  if (SlowFirstChild() || HasLayer()) {
    layer = Parent()->EnclosingLayer();
    AddLayers(layer);
  }

  // If |this| is visible but this object was not, tell the layer it has some
  // visible content that needs to be drawn and layer visibility optimization
  // can't be used
  if (Parent()->StyleRef().Visibility() != EVisibility::kVisible &&
      StyleRef().Visibility() == EVisibility::kVisible && !HasLayer()) {
    if (!layer)
      layer = Parent()->EnclosingLayer();
    if (layer)
      layer->DirtyVisibleContentStatus();
  }

  // |FirstInlineFragment()| should be cleared. |LayoutObjectChildList| does
  // this, just check here for all new objects in the tree.
  DCHECK(!HasInlineFragments());

  if (Parent()->ChildrenInline())
    Parent()->DirtyLinesFromChangedChild(this);

  if (const Element* element = DynamicTo<Element>(GetNode());
      element && element->MayBeImplicitAnchor()) {
    MarkMayContainAnchor();
  } else if (MayContainAnchor()) {
    Parent()->MarkMayContainAnchor();
  }
}

enum FindReferencingScrollAnchorsBehavior { kDontClear, kClear };

static bool FindReferencingScrollAnchors(
    LayoutObject* layout_object,
    FindReferencingScrollAnchorsBehavior behavior) {
  PaintLayer* layer = nullptr;
  if (LayoutObject* parent = layout_object->Parent())
    layer = parent->EnclosingLayer();
  bool found = false;

  // Walk up the layer tree to clear any scroll anchors that reference us.
  while (layer) {
    if (PaintLayerScrollableArea* scrollable_area =
            layer->GetScrollableArea()) {
      ScrollAnchor* anchor = scrollable_area->GetScrollAnchor();
      DCHECK(anchor);
      if (anchor->RefersTo(layout_object)) {
        found = true;
        if (behavior == kClear)
          anchor->NotifyRemoved(layout_object);
        else
          return true;
      }
    }
    layer = layer->Parent();
  }
  return found;
}

void LayoutObject::WillBeRemovedFromTree() {
  NOT_DESTROYED();
  // FIXME: We should DCHECK(isRooted()) but we have some out-of-order removals
  // which would need to be fixed first.

  // If we remove a visible child from an invisible parent, we don't know the
  // layer visibility any more.
  PaintLayer* layer = nullptr;
  if (Parent()->StyleRef().Visibility() != EVisibility::kVisible &&
      StyleRef().Visibility() == EVisibility::kVisible && !HasLayer()) {
    layer = Parent()->EnclosingLayer();
    if (layer)
      layer->DirtyVisibleContentStatus();
  }

  // Keep our layer hierarchy updated.
  if (SlowFirstChild() || HasLayer()) {
    if (!layer)
      layer = Parent()->EnclosingLayer();
    RemoveLayers(layer);
  }

  if (Parent()->ChildrenInline()) {
    Parent()->DirtyLinesFromChangedChild(this);
  }

  if (is_scroll_anchor_object_) {
    // Clear the bit first so that anchor.clear() doesn't recurse into
    // findReferencingScrollAnchors.
    is_scroll_anchor_object_ = false;
    FindReferencingScrollAnchors(this, kClear);
  }

  if (LocalFrameView* frame_view = GetFrameView()) {
    frame_view->SetIntersectionObservationState(LocalFrameView::kDesired);
  }
}

void LayoutObject::SetNeedsPaintPropertyUpdate() {
  NOT_DESTROYED();
  DCHECK(!GetDocument().InvalidationDisallowed());
  if (needs_paint_property_update_ || !GetDocument().IsActive()) {
    return;
  }

  // If we're an overscroll container or an ::-internal-overscroll-area-parent,
  // then under a paint property update, we have to make sure that all of our
  // ::-internal-overscroll-area-parent siblings/children and the container
  // itself are also updated. This is due to the fact that we do some
  // reparenting in PaintPropertyTreeBuilder. Without this, we can end up with
  // cycles if only *some* of the related objects are dirtied.
  if (IsOverscrollContainer()) {
    LayoutObject* container =
        IsOverscrollAreaParent() ? ContainingBlock() : this;
    if (container) {
      Element* container_element = DynamicTo<Element>(container->GetNode());
      CHECK(container_element);

      if (OverscrollAreaTracker* overscroll_area_tracker =
              container_element->GetOverscrollAreaTracker()) {
        for (Element* overscroll_area :
             overscroll_area_tracker->DOMSortedElements()) {
          if (PseudoElement* overscroll_area_parent =
                  overscroll_area->GetPseudoElement(
                      kPseudoIdOverscrollAreaParent)) {
            if (auto* object = overscroll_area_parent->GetLayoutObject()) {
              object->needs_paint_property_update_ = true;
              object->SetDescendantNeedsPaintPropertyUpdate();
            }
          }
        }

        container->needs_paint_property_update_ = true;
        // Note that we mark descendants needing property update starting from
        // container, as opposed to container's parent, since we invalidated the
        // direct children of the container
        // (::-internal-overscroll-area-parent).
        container->SetDescendantNeedsPaintPropertyUpdate();
        return;
      }
    }
  }

  needs_paint_property_update_ = true;
  if (Parent())
    Parent()->SetDescendantNeedsPaintPropertyUpdate();
}

void LayoutObject::SetDescendantNeedsPaintPropertyUpdate() {
  NOT_DESTROYED();
  for (auto* ancestor = this;
       ancestor && !ancestor->DescendantNeedsPaintPropertyUpdate();
       ancestor = ancestor->Parent()) {
    ancestor->descendant_needs_paint_property_update_ = true;
  }
}

void LayoutObject::MaybeClearIsScrollAnchorObject() {
  NOT_DESTROYED();
  if (!is_scroll_anchor_object_) {
    return;
  }
  is_scroll_anchor_object_ = FindReferencingScrollAnchors(this, kDontClear);
}

void LayoutObject::DestroyAndCleanupAnonymousWrappers(
    bool performing_reattach) {
  NOT_DESTROYED();

  LayoutObject* destroy_root = this;
  LayoutObject* destroy_root_parent = destroy_root->Parent();
  for (; destroy_root_parent && destroy_root_parent->IsAnonymous();
       destroy_root = destroy_root_parent,
       destroy_root_parent = destroy_root_parent->Parent()) {
    // The anonymous fieldset contents wrapper should be kept.
    if (destroy_root_parent->Parent() &&
        destroy_root_parent->Parent()->IsFieldset()) {
      break;
    }

    // We need to keep the anonymous parent, if it won't become empty by the
    // removal of this LayoutObject.
    if (destroy_root->PreviousSibling()) {
      break;
    }
    if (destroy_root->NextSibling()) {
      break;
    }
  }

  if (!performing_reattach && destroy_root_parent) {
    while (destroy_root_parent->IsAnonymous())
      destroy_root_parent = destroy_root_parent->Parent();
    GetDocument().GetStyleEngine().DetachedFromParent(destroy_root_parent);
  }

  destroy_root->Destroy();

  // WARNING: |this| is deleted here.
}

void LayoutObject::Destroy() {
  NOT_DESTROYED();
  DCHECK(g_allow_destroying_layout_object_in_finalizer ||
         !ThreadState::Current()->IsSweepingOnOwningThread());

  // Mark as being destroyed to avoid trouble with merges in |RemoveChild()| and
  // other house keepings.
  being_destroyed_ = true;

  // This is one of the few places we may have a nullable style (a LayoutObject
  // may be created, then immediately destroyed before a style is set). Pass
  // the style into WillBeDestroyed so that the overrides explicitly check this.
  WillBeDestroyed(style_.Get());

#if DCHECK_IS_ON()
  DCHECK(!has_ax_object_) << this;
  is_destroyed_ = true;
#endif
}

PositionWithAffinity LayoutObject::PositionForPoint(
    const PhysicalOffset&) const {
  NOT_DESTROYED();
  // NG codepath requires |kPrePaintClean|.
  // |SelectionModifier| calls this only in legacy codepath.
  DCHECK(GetDocument().Lifecycle().GetState() >=
         DocumentLifecycle::kPrePaintClean);
  return CreatePositionWithAffinity(0);
}

bool LayoutObject::CanHaveAdditionalCompositingReasons() const {
  NOT_DESTROYED();
  return false;
}

CompositingReasons LayoutObject::AdditionalCompositingReasons() const {
  NOT_DESTROYED();
  return {};
}

bool LayoutObject::HitTestAllPhases(HitTestResult& result,
                                    const HitTestLocation& hit_test_location,
                                    const PhysicalOffset& accumulated_offset) {
  NOT_DESTROYED();
  if (NodeAtPoint(result, hit_test_location, accumulated_offset,
                  HitTestPhase::kForeground)) {
    return true;
  }
  if (NodeAtPoint(result, hit_test_location, accumulated_offset,
                  HitTestPhase::kFloat)) {
    return true;
  }
  if (NodeAtPoint(result, hit_test_location, accumulated_offset,
                  HitTestPhase::kDescendantBlockBackgrounds)) {
    return true;
  }
  if (NodeAtPoint(result, hit_test_location, accumulated_offset,
                  HitTestPhase::kSelfBlockBackground)) {
    return true;
  }
  return false;
}

Node* LayoutObject::NodeForHitTest() const {
  NOT_DESTROYED();
  if (Node* node = GetNode())
    return node;

  const LayoutObject* parent = Parent();
  if (!parent) {
    return nullptr;
  }

  // If we hit the anonymous layout-objects within generated content we should
  // actually hit the generated content so walk up to the PseudoElement.
  const bool use_parent_node = ([&]() {
    switch (parent->StyleRef().StyleType()) {
      case kPseudoIdAfter:
      case kPseudoIdBefore:
      case kPseudoIdFirstLetter:
      case kPseudoIdInterestButton:
      case kPseudoIdMarker:
      case kPseudoIdScrollButton:
      case kPseudoIdScrollButtonBlockStart:
      case kPseudoIdScrollButtonInlineStart:
      case kPseudoIdScrollButtonInlineEnd:
      case kPseudoIdScrollButtonBlockEnd:
      case kPseudoIdScrollMarker:
        return true;
      default:
        return false;
    }
  })();

  if (use_parent_node) {
    for (; parent; parent = parent->Parent()) {
      if (Node* node = parent->GetNode()) {
        return node;
      }
    }
  }

  return nullptr;
}

void LayoutObject::UpdateHitTestResult(HitTestResult& result,
                                       const PhysicalOffset& point) const {
  NOT_DESTROYED();
  if (result.InnerNode())
    return;

  if (Node* n = NodeForHitTest())
    result.SetNodeAndPosition(n, point);
}

bool LayoutObject::NodeAtPoint(HitTestResult&,
                               const HitTestLocation&,
                               const PhysicalOffset&,
                               HitTestPhase) {
  NOT_DESTROYED();
  return false;
}

const ComputedStyle* LayoutObject::FirstLineStyleWithoutFallback() const {
  NOT_DESTROYED();
  DCHECK(GetDocument().GetStyleEngine().UsesFirstLineRules());

  if (IsText()) {
    if (!Parent())
      return nullptr;
    return Parent()->FirstLineStyleWithoutFallback();
  }

  // Normal markers don't use ::first-line styles in Chromium, so be consistent
  // and return null for content markers. This may need to change depending on
  // https://github.com/w3c/csswg-drafts/issues/4506
  if (StyleRef().StyleType() == kPseudoIdMarker) {
    return nullptr;
  }

  if (BehavesLikeBlockContainer()) {
    if (const ComputedStyle* cached =
            StyleRef().GetCachedPseudoElementStyle(kPseudoIdFirstLine)) {
      // If the style is cached by getComputedStyle(element, "::first-line"), it
      // is marked with IsEnsuredInDisplayNone(). In that case we might not have
      // the correct ::first-line style for laying out the ::first-line. Ignore
      // the cached ComputedStyle and overwrite it using
      // ReplaceCachedPseudoElementStyle() below.
      if (!cached->IsEnsuredInDisplayNone())
        return cached;
    }

    if (Element* element = DynamicTo<Element>(GetNode())) {
      if (element->ShadowPseudoId() ==
          shadow_element_names::kPseudoInternalInputSuggested) {
        // Disable ::first-line style for autofill previews. See
        // crbug.com/1227170.
        return nullptr;
      }
    }

    for (const LayoutBlock* first_line_block = To<LayoutBlock>(this);
         first_line_block;
         first_line_block = first_line_block->FirstLineStyleParentBlock()) {
      const ComputedStyle& style = first_line_block->StyleRef();
      if (!style.HasPseudoElementStyle(kPseudoIdFirstLine))
        continue;
      if (first_line_block == this) {
        if (const ComputedStyle* cached =
                first_line_block->GetCachedPseudoElementStyle(
                    kPseudoIdFirstLine)) {
          return cached;
        }
        continue;
      }

      // We can't use first_line_block->GetCachedPseudoElementStyle() because
      // it's based on first_line_block's style. We need to get the uncached
      // first line style based on this object's style and cache the result in
      // it.
      if (const ComputedStyle* first_line_style =
              first_line_block->GetUncachedPseudoElementStyle(
                  StyleRequest(kPseudoIdFirstLine, &StyleRef()))) {
        return StyleRef().ReplaceCachedPseudoElementStyle(
            std::move(first_line_style), kPseudoIdFirstLine, g_null_atom);
      }
    }
  } else if ((!IsAnonymous() && IsLayoutInline() &&
              !GetNode()->IsFirstLetterPseudoElement()) ||
             (RuntimeEnabledFeatures::QuoteFirstLineStyleEnabled() &&
              IsQuote())) {
    if (const ComputedStyle* cached =
            StyleRef().GetCachedPseudoElementStyle(kPseudoIdFirstLineInherited))
      return cached;

    // Quote doesn't have an associated Node because it's generated thus always
    // anonymous. So, we need to access a parent LayoutObject.
    const auto* layout_object =
        RuntimeEnabledFeatures::QuoteFirstLineStyleEnabled() && IsQuote()
            ? Parent()
            : this;
    if (layout_object->Parent()) {
      if (const ComputedStyle* parent_first_line_style =
              layout_object->Parent()->FirstLineStyleWithoutFallback()) {
        // A first-line style is in effect. Get the uncached first-line style
        // based on parent_first_line_style and cache the result in this
        // object's style.
        if (const ComputedStyle* first_line_style =
                layout_object->GetUncachedPseudoElementStyle(StyleRequest(
                    kPseudoIdFirstLineInherited, parent_first_line_style))) {
          return StyleRef().AddCachedPseudoElementStyle(
              std::move(first_line_style), kPseudoIdFirstLineInherited,
              g_null_atom);
        }
      }
    }
  }
  return nullptr;
}

const ComputedStyle* LayoutObject::GetCachedPseudoElementStyle(
    PseudoId pseudo) const {
  NOT_DESTROYED();
  DCHECK_NE(pseudo, kPseudoIdBefore);
  DCHECK_NE(pseudo, kPseudoIdCheckMark);
  DCHECK_NE(pseudo, kPseudoIdAfter);
  DCHECK_NE(pseudo, kPseudoIdExpandIcon);
  DCHECK_NE(pseudo, kPseudoIdPickerIcon);
  DCHECK_NE(pseudo, kPseudoIdInterestButton);
  if (!GetNode())
    return nullptr;

  Element* element = Traversal<Element>::FirstAncestorOrSelf(*GetNode());
  if (!element)
    return nullptr;

  return element->CachedStyleForPseudoElement(pseudo);
}

const ComputedStyle* LayoutObject::GetUncachedPseudoElementStyle(
    const StyleRequest& request) const {
  NOT_DESTROYED();
  DCHECK_NE(request.pseudo_id, kPseudoIdBefore);
  DCHECK_NE(request.pseudo_id, kPseudoIdCheckMark);
  DCHECK_NE(request.pseudo_id, kPseudoIdAfter);
  DCHECK_NE(request.pseudo_id, kPseudoIdExpandIcon);
  DCHECK_NE(request.pseudo_id, kPseudoIdPickerIcon);
  DCHECK_NE(request.pseudo_id, kPseudoIdInterestButton);
  if (!GetNode())
    return nullptr;

  Element* element = Traversal<Element>::FirstAncestorOrSelf(*GetNode());
  if (!element)
    return nullptr;
  if (element->IsPseudoElement() &&
      request.pseudo_id != kPseudoIdFirstLineInherited)
    return nullptr;

  return element->UncachedStyleForPseudoElement(request);
}

void LayoutObject::AddDraggableRegions(Vector<DraggableRegionValue>& regions) {
  NOT_DESTROYED();
  // Convert the style regions to absolute coordinates.
  if (StyleRef().Visibility() != EVisibility::kVisible || !IsBox()) {
    return;
  }

  if (StyleRef().DraggableRegionMode() == EDraggableRegionMode::kNone) {
    return;
  }

  auto* box = To<LayoutBox>(this);
  PhysicalRect local_bounds = box->PhysicalBorderBoxRect();
  PhysicalRect abs_bounds = LocalToAbsoluteRect(local_bounds);

  DraggableRegionValue region;
  region.draggable =
      StyleRef().DraggableRegionMode() == EDraggableRegionMode::kMove;
  region.bounds = abs_bounds;
  regions.push_back(region);
}

bool LayoutObject::WillRenderImage() {
  NOT_DESTROYED();
  // Without visibility we won't render (and therefore don't care about
  // animation).
  if (StyleRef().Visibility() != EVisibility::kVisible) {
    return false;
  }
  // We will not render a new image when ExecutionContext is paused
  if (GetDocument().GetExecutionContext()->IsContextPaused()) {
    return false;
  }
  // Suspend animations when the page is not visible.
  if (GetDocument().hidden()) {
    return false;
  }
  // If we're not in a window (i.e., we're dormant from being in a background
  // tab) then we don't want to render either.
  if (!GetDocument().View()->IsVisible()) {
    return false;
  }
  // If paint invalidation of this object is delayed, animations can be
  // suspended. When the object is painted the next time, the animations will
  // be started again. Only suspend if the object is marked for paint
  // invalidation in the future, or else may not end up being painted.
  if (ShouldDelayFullPaintInvalidation() && ShouldCheckForPaintInvalidation()) {
    return false;
  }
  return true;
}

bool LayoutObject::GetImageAnimationPolicy(
    mojom::blink::ImageAnimationPolicy& policy) {
  NOT_DESTROYED();
  if (!GetDocument().GetSettings())
    return false;
  policy = GetDocument().GetSettings()->GetImageAnimationPolicy();
  return true;
}

InterpolationQuality LayoutObject::ComputeSpeculativeDecodeQuality() const {
  NOT_DESTROYED();
  return StyleRef().GetInterpolationQuality();
}

void LayoutObject::ImageChanged(ImageResourceContent* image,
                                CanDeferInvalidation defer) {
  NOT_DESTROYED();
  DCHECK(node_);

  // Image change notifications should not be received during paint because
  // the resulting invalidations will be cleared following paint. This can also
  // lead to modifying the tree out from under paint(), see: crbug.com/616700.
  DCHECK_NE(GetDocument().Lifecycle().GetState(),
            DocumentLifecycle::LifecycleState::kInPaint);

  ImageChanged(static_cast<WrappedImagePtr>(image), defer);
}

void LayoutObject::ImageNotifyFinished(ImageResourceContent* image) {
  NOT_DESTROYED();
  if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
    cache->ImageLoaded(this);

  if (GetDocument().domWindow()) {
    PaintTimingDetector::From(GetDocument()).NotifyImageFinished(*this, image);
  }

  if (!image->ErrorOccurred()) {
    Element* element = DynamicTo<Element>(GetNode());
    if (const std::optional<AdProvenance>& ad_provenance =
            image->GetAdProvenance()) {
      if (element) {
        element->SetIsAdRelated(*ad_provenance);
      }
    }

    LocalFrame* frame = GetDocument().GetFrame();
    bool is_ad = image->GetAdProvenance().has_value() ||
                 (element && element->IsAdRelated()) ||
                 (frame && frame->IsAdFrame());

    if (is_ad) {
      if (Image* img = image->GetImage()) {
        // Headroom is on a log2 scale. 0.5f corresponds to 2^0.5 ~= 1.41x SDR
        // white.
        if (img->PaintImageForCurrentFrame().GetMaximumRenderedHdrHeadroom() >=
            0.5f) {
          UseCounter::Count(GetDocument(), WebFeature::kAdImageHDR);
        }
      }
    }
  }
}

Element* LayoutObject::ScrollParent(const Element* base) const {
  NOT_DESTROYED();

  // 1. If any of the following holds true,
  //    return null and terminate this algorithm...
  if (IsDocumentElement()) {
    return nullptr;
  }

  // Body element should only return null iif it's the scrolling element.
  // See:
  // https://github.com/w3c/csswg-drafts/issues/12723#issuecomment-3998966905
  if (IsBody() && GetDocument().ScrollingElementNoLayout() == GetNode()) {
    return nullptr;
  }

  bool last_container_fixed = IsFixedPositioned();
  HeapHashSet<Member<TreeScope>> ancestor_tree_scopes;
  if (base) {
    ancestor_tree_scopes = base->GetAncestorTreeScopes();
  }

  // 2. Let ancestor be the containing block of the element in the flat tree and
  //    repeat these substeps:
  for (LayoutObject* ancestor = ContainingBlock(); ancestor;
       ancestor = ancestor->ContainingBlock()) {
    Node* ancestor_node = ancestor->GetNode();
    if (!ancestor_node) {
      continue;
    }

    // 1. If ancestor is the initial containing block, return the
    //    scrollingElement for the element’s document if it is not
    //    closed-shadow-hidden from the element, otherwise return null.
    if (IsA<LayoutView>(ancestor)) {
      Element* scrolling_element = GetDocument().scrollingElement();
      if (!last_container_fixed && scrolling_element &&
          (!base ||
           ancestor_tree_scopes.Contains(&scrolling_element->GetTreeScope()))) {
        return scrolling_element;
      }
      return nullptr;
    }

    // 2. If ancestor is not closed-shadow-hidden from the element, and is a
    //    scroll container, terminate this algorithm and return ancestor.
    if ((!base ||
         ancestor_tree_scopes.Contains(&ancestor_node->GetTreeScope())) &&
        ancestor->IsScrollContainer()) {
      return DynamicTo<Element>(ancestor_node);
    }

    // 3. If the computed value of the position property of ancestor is fixed,
    //    and no ancestor establishes a fixed position containing block,
    //    terminate this algorithm and return null.
    //
    // This is effectively covered by the next iteration if we get
    // to the root.
    last_container_fixed = ancestor->IsFixedPositioned();

    // 4. Let ancestor be the containing block of ancestor in the flat tree.
    //
    // Handled by the loop update
  }

  return nullptr;
}

Element* LayoutObject::OffsetParent(const Element* base) const {
  NOT_DESTROYED();

  // Spec: https://drafts.csswg.org/cssom-view-1/#dom-htmlelement-offsetparent
  if (IsDocumentElement() || IsBody())
    return nullptr;

  bool in_position_fixed = IsFixedPositioned();
  HeapHashSet<Member<TreeScope>> ancestor_tree_scopes;
  if (base) {
    ancestor_tree_scopes = base->GetAncestorTreeScopes();
  }
  float effective_zoom = StyleRef().EffectiveZoom();
  Node* node = nullptr;
  for (LayoutObject* ancestor = Parent(); ancestor;
       ancestor = ancestor->Parent()) {
    node = ancestor->GetNode();

    if (!node)
      continue;

    // If |base| was provided, then we should not return an Element which is
    // closed shadow hidden from |base|. If we keep going up the flat tree, then
    // we will eventually get to a node which is not closed shadow hidden from
    // |base|. https://github.com/w3c/csswg-drafts/issues/159
    if (base && !ancestor_tree_scopes.Contains(&node->GetTreeScope())) {
      // If a fixed position containing block is found within the shadow tree,
      // we should treat it as exiting the fixed position mode.
      if (ancestor->CanContainFixedPositionObjects()) {
        in_position_fixed = false;
      } else if (ancestor->IsFixedPositioned()) {
        in_position_fixed = true;
      }
      continue;
    }

    if (in_position_fixed) {
      // If the computed value of the position property of ancestor is fixed,
      // and no ancestor establishes a fixed position containing block,
      // terminate this algorithm and return null.
      if (ancestor->CanContainFixedPositionObjects()) {
        break;
      }
    } else {
      if (ancestor->CanContainAbsolutePositionObjects()) {
        break;
      }

      if (IsA<HTMLBodyElement>(*node)) {
        break;
      }

      if (!IsPositioned() &&
          (IsA<HTMLTableElement>(*node) || IsA<HTMLTableCellElement>(*node))) {
        break;
      }
    }

    // Webkit specific extension where offsetParent stops at zoom level changes.
    if (effective_zoom != ancestor->StyleRef().EffectiveZoom())
      break;

    in_position_fixed |= ancestor->IsFixedPositioned();
  }

  return DynamicTo<Element>(node);
}

void LayoutObject::NotifyImageFullyRemoved(ImageResourceContent* image) {
  NOT_DESTROYED();
  if (GetDocument().domWindow()) {
    PaintTimingDetector::From(GetDocument()).NotifyImageRemoved(*this, image);
  }
}

PositionWithAffinity LayoutObject::CreatePositionWithAffinity(
    int offset,
    TextAffinity affinity) const {
  NOT_DESTROYED();
  // If this is a non-anonymous layoutObject in an editable area, then it's
  // simple.
  Node* const node = NonPseudoNode();
  if (!node)
    return FindPosition();
  return AdjustForEditingBoundary(
      PositionWithAffinity(Position(node, offset), affinity));
}

PositionWithAffinity LayoutObject::FindPosition() const {
  NOT_DESTROYED();
  // We don't want to cross the boundary between editable and non-editable
  // regions of the document, but that is either impossible or at least
  // extremely unlikely in any normal case because we stop as soon as we
  // find a single non-anonymous layoutObject.

  // Find a nearby non-anonymous layoutObject.
  const LayoutObject* child = this;
  while (const LayoutObject* parent = child->Parent()) {
    // Find non-anonymous content after.
    for (const LayoutObject* layout_object = child->NextInPreOrder(parent);
         layout_object; layout_object = layout_object->NextInPreOrder(parent)) {
      if (const Node* node = layout_object->NonPseudoNode()) {
        return PositionWithAffinity(FirstPositionInOrBeforeNode(*node));
      }
    }

    // Find non-anonymous content before.
    for (const LayoutObject* layout_object = child->PreviousInPreOrder();
         layout_object; layout_object = layout_object->PreviousInPreOrder()) {
      if (layout_object == parent)
        break;
      if (const Node* node = layout_object->NonPseudoNode())
        return PositionWithAffinity(LastPositionInOrAfterNode(*node));
    }

    // Use the parent itself unless it too is anonymous.
    if (const Node* node = parent->NonPseudoNode())
      return PositionWithAffinity(FirstPositionInOrBeforeNode(*node));

    // Repeat at the next level up.
    child = parent;
  }

  // Everything was anonymous. Give up.
  return PositionWithAffinity();
}

PositionWithAffinity LayoutObject::FirstPositionInOrBeforeThis() const {
  NOT_DESTROYED();
  if (Node* node = NonPseudoNode())
    return AdjustForEditingBoundary(FirstPositionInOrBeforeNode(*node));
  return FindPosition();
}

PositionWithAffinity LayoutObject::LastPositionInOrAfterThis() const {
  NOT_DESTROYED();
  if (Node* node = NonPseudoNode())
    return AdjustForEditingBoundary(LastPositionInOrAfterNode(*node));
  return FindPosition();
}

PositionWithAffinity LayoutObject::PositionAfterThis() const {
  NOT_DESTROYED();
  if (Node* node = NonPseudoNode())
    return AdjustForEditingBoundary(Position::AfterNode(*node));
  return FindPosition();
}

PositionWithAffinity LayoutObject::PositionBeforeThis() const {
  NOT_DESTROYED();
  if (Node* node = NonPseudoNode())
    return AdjustForEditingBoundary(Position::BeforeNode(*node));
  return FindPosition();
}

PositionWithAffinity LayoutObject::CreatePositionWithAffinity(
    int offset) const {
  NOT_DESTROYED();
  return CreatePositionWithAffinity(offset, TextAffinity::kDownstream);
}

CursorDirective LayoutObject::GetCursor(const PhysicalOffset&,
                                        ui::Cursor&) const {
  NOT_DESTROYED();
  return kSetCursorBasedOnStyle;
}

bool LayoutObject::CanUpdateSelectionOnRootLineBoxes() const {
  NOT_DESTROYED();
  if (NeedsLayout())
    return false;

  const LayoutBlock* containing_block = ContainingBlock();
  return containing_block && !containing_block->NeedsLayout();
}

SVGLayoutResult LayoutObject::UpdateSVGLayout(const SVGLayoutInfo&) {
  NOT_DESTROYED();
  NOTREACHED();
}

gfx::RectF LayoutObject::ObjectBoundingBox() const {
  NOT_DESTROYED();
  NOTREACHED();
}

gfx::RectF LayoutObject::StrokeBoundingBox() const {
  NOT_DESTROYED();
  NOTREACHED();
}

gfx::RectF LayoutObject::DecoratedBoundingBox() const {
  NOT_DESTROYED();
  NOTREACHED();
}

gfx::RectF LayoutObject::VisualRectInLocalSVGCoordinates() const {
  NOT_DESTROYED();
  NOTREACHED();
}

AffineTransform LayoutObject::LocalSVGTransform() const {
  NOT_DESTROYED();
  return AffineTransform();
}

bool LayoutObject::IsRelayoutBoundary() const {
  NOT_DESTROYED();
  return ObjectIsRelayoutBoundary(this);
}

void LayoutObject::SetShouldInvalidateSelection() {
  NOT_DESTROYED();
  should_invalidate_selection_ = true;
  SetShouldCheckForPaintInvalidation();
  // Invalidate overflow for ::selection styles that contain overflowing
  // effects.
  if (IsText()) {
    if (auto* computed_style = StyleRef().HighlightData().Selection()) {
      if (computed_style->HasAppliedTextDecorations() ||
          computed_style->HasVisualOverflowingEffect()) {
        InvalidateVisualOverflow();
      }
    }
  }
}

void LayoutObject::SetShouldDoFullPaintInvalidation(
    PaintInvalidationReason reason) {
  NOT_DESTROYED();
  DCHECK(IsLayoutFullPaintInvalidationReason(reason));
  SetShouldCheckForPaintInvalidation();
  SetShouldDoFullPaintInvalidationWithoutLayoutChangeInternal(reason);
}

void LayoutObject::SetShouldDoFullPaintInvalidationWithoutLayoutChange(
    PaintInvalidationReason reason) {
  NOT_DESTROYED();
  DCHECK(IsNonLayoutFullPaintInvalidationReason(reason));
  // Use SetBackgroundNeedsFullPaintInvalidation() instead. See comment of the
  // function.
  DCHECK_NE(reason, PaintInvalidationReason::kBackground);
  SetShouldDoFullPaintInvalidationWithoutLayoutChangeInternal(reason);
}

void LayoutObject::SetShouldDoFullPaintInvalidationWithoutLayoutChangeInternal(
    PaintInvalidationReason reason) {
  NOT_DESTROYED();
  // Only full invalidation reasons are allowed.
  DCHECK(IsFullPaintInvalidationReason(reason));
  const bool was_delayed = should_delay_full_paint_invalidation_;
  should_delay_full_paint_invalidation_ = false;
  const bool should_upgrade_reason =
      reason > PaintInvalidationReasonForPrePaint();
  if (was_delayed || should_upgrade_reason) {
    SetShouldCheckForPaintInvalidationWithoutLayoutChange();
  }
  if (should_upgrade_reason) {
    paint_invalidation_reason_for_pre_paint_ = static_cast<unsigned>(reason);
    DCHECK_EQ(reason, PaintInvalidationReasonForPrePaint());
  }
}

void LayoutObject::SetShouldInvalidatePaintForHitTest() {
  NOT_DESTROYED();
  if (PaintInvalidationReasonForPrePaint() <
      PaintInvalidationReason::kHitTest) {
    SetShouldCheckForPaintInvalidationWithoutLayoutChange();
    paint_invalidation_reason_for_pre_paint_ =
        static_cast<unsigned>(PaintInvalidationReason::kHitTest);
    DCHECK(ShouldInvalidatePaintForHitTestOnly());
  }
}

void LayoutObject::SetShouldCheckForPaintInvalidation() {
  NOT_DESTROYED();
  if (ShouldCheckLayoutForPaintInvalidation()) {
    DCHECK(ShouldCheckForPaintInvalidation());
    return;
  }
  GetFrameView()->ScheduleVisualUpdateForPaintInvalidationIfNeeded();

  should_check_for_paint_invalidation_ = true;
  should_check_layout_for_paint_invalidation_ = true;

  // This is not a good place to be during pre-paint. Marking the the ancestry
  // for paint invalidation checking during pre-paint is bad, since we may
  // already be done with those objects, and never get to visit them again in
  // the pre-paint phase. LayoutObject ancestors as they may be, the structure
  // of the physical fragment tree could be different.
  DCHECK(GetDocument().Lifecycle().GetState() !=
         DocumentLifecycle::kInPrePaint);

  for (LayoutObject* ancestor = Parent();
       ancestor && !ancestor->DescendantShouldCheckLayoutForPaintInvalidation();
       ancestor = ancestor->Parent()) {
    ancestor->should_check_for_paint_invalidation_ = true;
    ancestor->descendant_should_check_layout_for_paint_invalidation_ = true;
  }
}

void LayoutObject::SetShouldCheckForPaintInvalidationWithoutLayoutChange() {
  NOT_DESTROYED();
  if (ShouldCheckForPaintInvalidation()) {
    return;
  }
  GetFrameView()->ScheduleVisualUpdateForPaintInvalidationIfNeeded();

  should_check_for_paint_invalidation_ = true;
  for (LayoutObject* ancestor = Parent();
       ancestor && !ancestor->ShouldCheckForPaintInvalidation();
       ancestor = ancestor->Parent()) {
    ancestor->should_check_for_paint_invalidation_ = true;
  }
}

void LayoutObject::SetSubtreeShouldCheckForPaintInvalidation() {
  NOT_DESTROYED();
  if (SubtreeShouldCheckForPaintInvalidation()) {
    DCHECK(ShouldCheckForPaintInvalidation());
    return;
  }
  SetShouldCheckForPaintInvalidation();
  subtree_should_check_for_paint_invalidation_ = true;
}

void LayoutObject::SetMayNeedPaintInvalidationAnimatedBackgroundImage() {
  NOT_DESTROYED();
  if (MayNeedPaintInvalidationAnimatedBackgroundImage())
    return;
  may_need_paint_invalidation_animated_background_image_ = true;
  SetShouldCheckForPaintInvalidationWithoutLayoutChange();
}

void LayoutObject::SetShouldDelayFullPaintInvalidation() {
  NOT_DESTROYED();
  // Should have already set a full paint invalidation reason.
  DCHECK(IsFullPaintInvalidationReason(PaintInvalidationReasonForPrePaint()));
  // Subtree full paint invalidation can't be delayed.
  if (subtree_should_do_full_paint_invalidation_) {
    return;
  }

  should_delay_full_paint_invalidation_ = true;
  if (!ShouldCheckForPaintInvalidation()) {
    // This will also schedule a visual update.
    SetShouldCheckForPaintInvalidationWithoutLayoutChange();
  } else {
    // Schedule visual update for the next document cycle in which we will
    // check if the delayed invalidation should be promoted to a real
    // invalidation.
    GetFrameView()->ScheduleVisualUpdateForPaintInvalidationIfNeeded();
  }
}

void LayoutObject::ClearShouldDelayFullPaintInvalidation() {
  NOT_DESTROYED();
  // This will clear ShouldDelayFullPaintInvalidation() flag.
  SetShouldDoFullPaintInvalidationWithoutLayoutChangeInternal(
      PaintInvalidationReasonForPrePaint());
}

void LayoutObject::ClearPaintInvalidationFlags() {
  NOT_DESTROYED();
// PaintInvalidationStateIsDirty should be kept in sync with the
// booleans that are cleared below.
#if DCHECK_IS_ON()
  DCHECK(!ShouldCheckForPaintInvalidation() || PaintInvalidationStateIsDirty());
#endif
  if (!ShouldDelayFullPaintInvalidation()) {
    paint_invalidation_reason_for_pre_paint_ =
        static_cast<unsigned>(PaintInvalidationReason::kNone);
    background_needs_full_paint_invalidation_ = false;
  }
  should_check_for_paint_invalidation_ = false;
  subtree_should_check_for_paint_invalidation_ = false;
  subtree_should_do_full_paint_invalidation_ = false;
  may_need_paint_invalidation_animated_background_image_ = false;
  should_check_layout_for_paint_invalidation_ = false;
  descendant_should_check_layout_for_paint_invalidation_ = false;
  should_invalidate_selection_ = false;
}

#if DCHECK_IS_ON()
bool LayoutObject::PaintInvalidationStateIsDirty() const {
  NOT_DESTROYED();
  return BackgroundNeedsFullPaintInvalidation() ||
         ShouldCheckForPaintInvalidation() || ShouldInvalidateSelection() ||
         ShouldCheckLayoutForPaintInvalidation() ||
         DescendantShouldCheckLayoutForPaintInvalidation() ||
         ShouldDoFullPaintInvalidation() ||
         SubtreeShouldDoFullPaintInvalidation() ||
         MayNeedPaintInvalidationAnimatedBackgroundImage();
}
#endif

void LayoutObject::EnsureIsReadyForPaintInvalidation() {
  NOT_DESTROYED();
  DCHECK(!NeedsLayout() || ChildLayoutBlockedByDisplayLock());

  // Force full paint invalidation if the outline may be affected by descendants
  // and this object is marked for checking paint invalidation for any reason.
  if (outline_may_be_affected_by_descendants_ ||
      previous_outline_may_be_affected_by_descendants_) {
    SetShouldDoFullPaintInvalidationWithoutLayoutChange(
        PaintInvalidationReason::kOutline);
  }
  previous_outline_may_be_affected_by_descendants_ =
      outline_may_be_affected_by_descendants_;
}

void LayoutObject::ClearPaintFlags() {
  NOT_DESTROYED();
  DCHECK_EQ(GetDocument().Lifecycle().GetState(),
            DocumentLifecycle::kInPrePaint);
  ClearPaintInvalidationFlags();
  needs_paint_property_update_ = false;
  pre_paint_subtree_walk_reasons_ = 0;

  if (!ChildPrePaintBlockedByDisplayLock()) {
    descendant_needs_paint_property_update_ = false;
    descendant_pre_paint_subtree_walk_reasons_ = 0;
    subtree_paint_property_update_reasons_ =
        static_cast<unsigned>(SubtreePaintPropertyUpdateReason::kNone);
  }
}

bool LayoutObject::IsAllowedToModifyLayoutTreeStructure(Document& document) {
  return document.Lifecycle().StateAllowsLayoutTreeMutations() ||
         document.GetStyleEngine().InInterleavedStyleRecalc() ||
         document.GetStyleEngine().InScrollMarkersAttachment();
}

void LayoutObject::SetSubtreeShouldDoFullPaintInvalidation(
    PaintInvalidationReason reason) {
  NOT_DESTROYED();
  SetShouldDoFullPaintInvalidation(reason);
  subtree_should_do_full_paint_invalidation_ = true;
}

void LayoutObject::SetIsBackgroundAttachmentFixedObject(
    bool is_background_attachment_fixed_object) {
  NOT_DESTROYED();
  DCHECK(GetFrameView());
  DCHECK(IsBoxModelObject());
  if (is_background_attachment_fixed_object_ ==
      is_background_attachment_fixed_object) {
    return;
  }
  is_background_attachment_fixed_object_ =
      is_background_attachment_fixed_object;
  if (is_background_attachment_fixed_object) {
    GetFrameView()->AddBackgroundAttachmentFixedObject(
        To<LayoutBoxModelObject>(*this));
  } else {
    SetCanCompositeBackgroundAttachmentFixed(false);
    GetFrameView()->RemoveBackgroundAttachmentFixedObject(
        To<LayoutBoxModelObject>(*this));
  }
}

void LayoutObject::SetCanCompositeBackgroundAttachmentFixed(
    bool can_composite) {
  NOT_DESTROYED();
  if (can_composite != can_composite_background_attachment_fixed_) {
    can_composite_background_attachment_fixed_ = can_composite;
    SetNeedsPaintPropertyUpdate();
  }
}

PhysicalRect LayoutObject::DebugRect() const {
  NOT_DESTROYED();
  return PhysicalRect();
}

void LayoutObject::InvalidateSelectionOnStyleChange() {
  NOT_DESTROYED();
  // LayoutSelection::Commit() propagates the state up the containing node
  // chain to tell if a block contains selected nodes or not. If this layout
  // object is not a block, we need to get the selection state from the
  // containing block to tell if we have any selected node children.
  LayoutBlock* block =
      IsLayoutBlock() ? To<LayoutBlock>(this) : ContainingBlock();
  if (!block)
    return;
  if (!block->IsSelected())
    return;

  InvalidateSelectedChildrenOnStyleChange();
}

void LayoutObject::InvalidateSelectedChildrenOnStyleChange() {
  NOT_DESTROYED();
  // Process the entire subtree for selected nodes, marking those that are
  // leaves as needing invalidation. This is necessary because ::selection
  // styling may apply to the entire subtree.
  for (LayoutObject* child = SlowFirstChild(); child;
       child = child->NextSibling()) {
    // Anonymous objects never be IsSelected(). See SetSelectionStateIfNeeded().
    if (!child->IsSelected() && !child->IsAnonymous()) {
      continue;
    }
    if (child->CanBeSelectionLeaf()) {
      child->SetShouldInvalidateSelection();
      continue;
    }
    child->InvalidateSelectedChildrenOnStyleChange();
  }
}

void LayoutObject::SetNeedsPrePaintSubtreeWalk(
    PrePaintSubtreeWalkReasons reasons) {
  NOT_DESTROYED();
  DCHECK(!GetDocument().InvalidationDisallowed());
  CHECK(!reasons.empty());
  pre_paint_subtree_walk_reasons_ |= reasons.ToEnumBitmask();
  // If we're locked, mark our descendants as needing pre-paint subtree walk.
  // This is used a signal to ensure we mark the element as needing pre-paint
  // subtree walk when the element becomes unlocked.
  if (ChildPrePaintBlockedByDisplayLock()) {
    descendant_pre_paint_subtree_walk_reasons_ |= reasons.ToEnumBitmask();
    return;
  }

  if (Parent()) {
    Parent()->SetDescendantNeedsPrePaintSubtreeWalk(reasons);
  }
}

void LayoutObject::SetDescendantNeedsPrePaintSubtreeWalk(
    PrePaintSubtreeWalkReasons reasons) {
  NOT_DESTROYED();
  DCHECK(!GetDocument().InvalidationDisallowed());
  for (LayoutObject* obj = this;
       obj && !obj->GetDescendantPrePaintSubtreeWalkReasons().HasAll(reasons);
       obj = obj->Parent()) {
    obj->descendant_pre_paint_subtree_walk_reasons_ |= reasons.ToEnumBitmask();
    if (obj->ChildPrePaintBlockedByDisplayLock()) {
      break;
    }
  }
}

void LayoutObject::MarkEffectiveAllowedTouchActionChanged() {
  NOT_DESTROYED();
  SetNeedsPrePaintSubtreeWalk(
      {PrePaintSubtreeWalkReason::kEffectiveAllowedTouchAction});
}

void LayoutObject::MarkBlockingWheelEventHandlerChanged() {
  NOT_DESTROYED();
  SetNeedsPrePaintSubtreeWalk(
      {PrePaintSubtreeWalkReason::kBlockingWheelEventHandler});
}

void LayoutObject::MarkSoftNavigationContextChanged() {
  NOT_DESTROYED();
  SetNeedsPrePaintSubtreeWalk(
      {PrePaintSubtreeWalkReason::kSoftNavigationContext});
}

void LayoutObject::MarkContainerTimingChanged() {
  NOT_DESTROYED();
  DCHECK(RuntimeEnabledFeatures::ContainerTimingEnabled(
      GetDocument().GetExecutionContext()));
  SetNeedsPrePaintSubtreeWalk(
      {PrePaintSubtreeWalkReason::kContainerTimingContext});
}

// Note about ::first-letter pseudo-element:
//   When an element has ::first-letter pseudo-element, first letter characters
//   are taken from |Text| node and first letter characters are considered
//   as content of <pseudo:first-letter>.
//   For following HTML,
//      <style>div::first-letter {color: red}</style>
//      <div>abc</div>
//   we have following layout tree:
//      LayoutBlockFlow {DIV} at (0,0) size 784x55
//        LayoutInline {<pseudo:first-letter>} at (0,0) size 22x53
//          LayoutTextFragment (anonymous) at (0,1) size 22x53
//            text run at (0,1) width 22: "a"
//        LayoutTextFragment {#text} at (21,30) size 16x17
//          text run at (21,30) width 16: "bc"
//  In this case, |Text::layoutObject()| for "abc" returns |LayoutTextFragment|
//  containing "bc", and it is called remaining part.
//
//  Even if |Text| node contains only first-letter characters, e.g. just "a",
//  remaining part of |LayoutTextFragment|, with |fragmentLength()| == 0, is
//  appeared in layout tree.
//
//  When |Text| node contains only first-letter characters and whitespaces, e.g.
//  "B\n", associated |LayoutTextFragment| is first-letter part instead of
//  remaining part.
//
//  Punctuation characters are considered as first-letter. For "(1)ab",
//  "(1)" are first-letter part and "ab" are remaining part.
const LayoutObject* AssociatedLayoutObjectOf(const Node& node,
                                             int offset_in_node,
                                             LayoutObjectSide object_side) {
  DCHECK_GE(offset_in_node, 0);
  LayoutObject* layout_object = node.GetLayoutObject();
  if (!node.IsTextNode() || !layout_object ||
      !To<LayoutText>(layout_object)->IsTextFragment())
    return layout_object;
  auto* layout_text_fragment = To<LayoutTextFragment>(layout_object);
  if (!layout_text_fragment->IsRemainingTextLayoutObject()) {
    DCHECK_LE(
        static_cast<unsigned>(offset_in_node),
        layout_text_fragment->Start() + layout_text_fragment->FragmentLength());
    return layout_text_fragment;
  }
  if (layout_text_fragment->FragmentLength()) {
    const unsigned threshold =
        object_side == LayoutObjectSide::kRemainingTextIfOnBoundary
            ? layout_text_fragment->Start()
            : layout_text_fragment->Start() + 1;
    if (static_cast<unsigned>(offset_in_node) >= threshold)
      return layout_object;
  }
  return layout_text_fragment->GetFirstLetterPart();
}

bool LayoutObject::CanBeSelectionLeaf() const {
  NOT_DESTROYED();
  if (SlowFirstChild() || StyleRef().Visibility() != EVisibility::kVisible ||
      DisplayLockUtilities::LockedAncestorPreventingPaint(*this)) {
    return false;
  }
  return CanBeSelectionLeafInternal();
}

Vector<PhysicalRect> LayoutObject::CollectOutlineRectsAndAdvance(
    OutlineType outline_type,
    AccompaniedFragmentIterator& iterator) const {
  NOT_DESTROYED();
  Vector<PhysicalRect> outline_rects;
  PhysicalOffset paint_offset = iterator.GetFragmentData()->PaintOffset();

  VectorOutlineRectCollector collector;
  if (iterator.Cursor()) {
    wtf_size_t fragment_index = iterator.Cursor()->ContainerFragmentIndex();
    do {
      const FragmentItem* item = iterator.Cursor()->Current().Item();
      if (!item)
        continue;
      if (const PhysicalBoxFragment* box_fragment = item->BoxFragment()) {
        box_fragment->AddSelfOutlineRects(
            paint_offset + item->OffsetInContainerFragment(), outline_type,
            collector, nullptr);
      } else {
        PhysicalRect rect;
        rect = item->RectInContainerFragment();
        rect.Move(paint_offset);
        collector.AddRect(rect);
      }
      // Keep going as long as we're within the same container fragment. If
      // we're block-fragmented, there will be multiple container fragments,
      // each with their own FragmentData object.
    } while (iterator.Advance() &&
             iterator.Cursor()->ContainerFragmentIndex() == fragment_index);
    outline_rects = collector.TakeRects();
  } else {
    if (const auto* box_fragment = iterator.GetPhysicalBoxFragment()) {
      box_fragment->AddSelfOutlineRects(paint_offset, outline_type, collector,
                                        nullptr);
      outline_rects = collector.TakeRects();
    } else {
      outline_rects = OutlineRects(nullptr, paint_offset, outline_type);
    }
    iterator.Advance();
  }

  return outline_rects;
}

Vector<PhysicalRect> LayoutObject::OutlineRects(
    OutlineInfo* info,
    const PhysicalOffset& additional_offset,
    OutlineType outline_type) const {
  NOT_DESTROYED();
  VectorOutlineRectCollector collector;
  AddOutlineRects(collector, info, additional_offset, outline_type);
  return collector.TakeRects();
}

void LayoutObject::SetModifiedStyleOutsideStyleRecalc(
    const ComputedStyle* style,
    ApplyStyleChanges apply_changes) {
  NOT_DESTROYED();
  SetStyle(style, apply_changes);
  if (IsAnonymous()) {
    return;
  }
  if (auto* element = DynamicTo<Element>(GetNode())) {
    element->SetComputedStyle(style);
  }
}

void LayoutObject::MarkSelfPaintingLayerForVisualOverflowRecalc() {
  NOT_DESTROYED();
  DCHECK(!GetDocument().InvalidationDisallowed());
  if (HasLayer()) {
    auto* box_model_object = To<LayoutBoxModelObject>(this);
    if (box_model_object->HasSelfPaintingLayer())
      box_model_object->Layer()->SetNeedsVisualOverflowRecalc();
  }
#if DCHECK_IS_ON()
  InvalidateVisualOverflowForDCheck();
#endif
}

void LayoutObject::SetSVGDescendantMayHaveTransformRelatedOperations() {
  NOT_DESTROYED();
  auto* object = this;
  while (!object->IsSVGRoot()) {
    DCHECK(object->IsSVGChild());
    if (object->SVGDescendantMayHaveTransformRelatedOperations()) {
      break;
    }
    if (object->IsSVGHiddenContainer()) {
      return;
    }
    object->svg_descendant_may_have_transform_related_operations_ = true;
    object = object->Parent();
    if (!object) {
      return;
    }
  }
  // If we have set SetSVGDescendantMayHaveTransformRelatedOperations() for
  // any object, set the enclosing layer needs repaint because some
  // LayoutSVGContainer may paint differently by ignoring the cull rect.
  // See SVGContainerPainter.
  if (object != this) {
    if (auto* layer = object->EnclosingLayer())
      layer->SetNeedsRepaint();
  }
}

void LayoutObject::InvalidateSubtreePositionTry(bool mark_style_dirty) {
  NOT_DESTROYED();

  bool invalidate = StyleRef().GetPositionTryFallbacks() != nullptr;
  if (invalidate) {
    // Invalidate layout as @position-fallback styles are applied during layout.
    SetNeedsLayout(layout_invalidation_reason::kStyleChange);
  }

  if (mark_style_dirty) {
    if (Node* node = GetNode()) {
      if (node->GetStyleChangeType() == kSubtreeStyleChange) {
        // No need to further mark for style recalc inside this subtree.
        mark_style_dirty = false;
      }
      if (invalidate && mark_style_dirty) {
        // Need to invalidate style to avoid using stale cached position
        // fallback styles.
        node->SetNeedsStyleRecalc(kLocalStyleChange,
                                  StyleChangeReasonForTracing::Create(
                                      style_change_reason::kPositionTryChange));
      }
    }
  }

  for (LayoutObject* child = SlowFirstChild(); child;
       child = child->NextSibling()) {
    child->InvalidateSubtreePositionTry(mark_style_dirty);
  }
}

bool LayoutObject::IsBackdropForOverscrollAreaParent() const {
  NOT_DESTROYED();
  const auto* pseudo = DynamicTo<PseudoElement>(GetNode());
  return pseudo && pseudo->GetPseudoId() == kPseudoIdOverscrollBackdrop;
}

bool LayoutObject::IsCanvasOrInCanvasSubtree() const {
  if (auto* element = DynamicTo<Element>(GetNode())) {
    return element->IsCanvasOrInCanvasSubtree();
  }
  return IsInCanvasSubtree();
}

bool LayoutObject::IsInCanvasSubtree() const {
  if (auto* node = GetNode()) {
    if (auto* element = DynamicTo<Element>(node)) {
      return element->IsInCanvasSubtree();
    }
    if (auto* document = DynamicTo<Document>(node)) {
      auto* owner = document->LocalOwner();
      return owner && owner->IsCanvasOrInCanvasSubtree();
    }
  }
  return Parent() && Parent()->IsCanvasOrInCanvasSubtree();
}

}  // namespace blink

#if DCHECK_IS_ON()

void ShowTree(const blink::LayoutObject* object) {
  if (getenv("RUNNING_UNDER_RR")) {
    // Printing timestamps requires an IPC to get the local time, which
    // does not work in an rr replay session. Just disable timestamp printing
    // globally, since we don't need them. Affecting global state isn't a
    // problem because invoking this from a rr session creates a temporary
    // program environment that will be destroyed as soon as the invocation
    // completes.
    logging::SetLogItems(true, true, false, false);
  }

  if (object)
    object->ShowTreeForThis();
  else
    DLOG(INFO) << "Cannot showTree. Root is (nil)";
}

void ShowLayoutTree(const blink::LayoutObject* object1) {
  ShowLayoutTree(object1, nullptr);
}

void ShowLayoutTree(const blink::LayoutObject* object1,
                    const blink::LayoutObject* object2) {
  if (getenv("RUNNING_UNDER_RR")) {
    // Printing timestamps requires an IPC to get the local time, which
    // does not work in an rr replay session. Just disable timestamp printing
    // globally, since we don't need them. Affecting global state isn't a
    // problem because invoking this from a rr session creates a temporary
    // program environment that will be destroyed as soon as the invocation
    // completes.
    logging::SetLogItems(true, true, false, false);
  }

  if (object1) {
    const blink::LayoutObject* root = object1;
    while (root->Parent())
      root = root->Parent();
    if (object1) {
      blink::StringBuilder string_builder;
      root->DumpLayoutTreeAndMark(string_builder, object1, "*", object2, "-",
                                  0);
      DLOG(INFO) << "\n" << string_builder.ToString().Utf8();
    }
  } else {
    DLOG(INFO) << "Cannot showLayoutTree. Root is (nil)";
  }
}

#endif  // DCHECK_IS_ON()
