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

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

#include <inttypes.h>

#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/scroll/scrollbar_mode.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/web/web_print_page_description.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/markers/document_marker_controller.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/frame/visual_viewport.h"
#include "third_party/blink/renderer/core/html/html_iframe_element.h"
#include "third_party/blink/renderer/core/html/plugin_document.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/layout/block_node.h"
#include "third_party/blink/renderer/core/layout/constraint_space_builder.h"
#include "third_party/blink/renderer/core/layout/geometry/transform_state.h"
#include "third_party/blink/renderer/core/layout/hit_test_cache.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
#include "third_party/blink/renderer/core/layout/layout_counter.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.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_view_transition_root.h"
#include "third_party/blink/renderer/core/layout/length_utils.h"
#include "third_party/blink/renderer/core/layout/list/layout_inline_list_item.h"
#include "third_party/blink/renderer/core/layout/list/layout_list_item.h"
#include "third_party/blink/renderer/core/layout/physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_root.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/scrolling/root_scroller_controller.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/view_painter.h"
#include "third_party/blink/renderer/core/svg/svg_document_extensions.h"
#include "third_party/blink/renderer/core/view_transition/view_transition.h"
#include "third_party/blink/renderer/core/view_transition/view_transition_skip_reason.h"
#include "third_party/blink/renderer/core/view_transition/view_transition_utils.h"
#include "third_party/blink/renderer/platform/geometry/infinite_int_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
#include "ui/display/screen_info.h"
#include "ui/gfx/geometry/quad_f.h"

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#endif

namespace blink {

LayoutView::LayoutView(ContainerNode* document)
    : LayoutBlockFlow(document),
      frame_view_(To<Document>(document)->View()),
      layout_counter_count_(0),
      hit_test_count_(0),
      hit_test_cache_hits_(0),
      hit_test_cache_(MakeGarbageCollected<HitTestCache>()),
      autosize_h_scrollbar_mode_(mojom::blink::ScrollbarMode::kAuto),
      autosize_v_scrollbar_mode_(mojom::blink::ScrollbarMode::kAuto) {
  DCHECK(document->IsDocumentNode());
  // init LayoutObject attributes
  SetInline(false);

  SetIntrinsicLogicalWidthsDirty(kMarkOnlyThis);

  SetPositionState(kIsOutOfFlowPositioned);

  // Update the cached bit here since the Document is made the effective root
  // scroller before we've created the layout tree.
  if (GetDocument().GetRootScrollerController().EffectiveRootScroller() ==
      GetDocument()) {
    SetIsEffectiveRootScroller(true);
  }
}

LayoutView::~LayoutView() = default;

void LayoutView::Trace(Visitor* visitor) const {
  visitor->Trace(frame_view_);
  visitor->Trace(svg_text_descendants_);
  visitor->Trace(text_to_variable_length_transform_result_);
  visitor->Trace(hit_test_cache_);
  visitor->Trace(initial_containing_block_resize_handled_list_);
  LayoutBlockFlow::Trace(visitor);
}

bool LayoutView::HitTest(const HitTestLocation& location,
                         HitTestResult& result) {
  NOT_DESTROYED();
  TRACE_EVENT0("blink", "LayoutView::HitTest");
  if (HasSVGTextDescendants()) {
    // This is necessary because SVG <text> might have obsolete geometry after
    // scale-only changes.  See crbug.com/1296089#c16
    auto it = svg_text_descendants_.find(this);
    if (it != svg_text_descendants_.end()) {
      for (LayoutSVGText* svg_text : *it->value) {
        if (svg_text->NeedsTextMetricsUpdate()) {
          svg_text->SetNeedsLayout(layout_invalidation_reason::kStyleChange);
        }
      }
    }
  }

  // We have to recursively update layout/style here because otherwise, when the
  // hit test recurses into a child document, it could trigger a layout on the
  // parent document, which can destroy PaintLayer that are higher up in the
  // call stack, leading to crashes.
  // Note that Document::UpdateLayout calls its parent's UpdateLayout.
  // Note that if an iframe has its render pipeline throttled, it will not
  // update layout here, and it will also not propagate the hit test into the
  // iframe's inner document.
  if (!GetFrameView()->UpdateAllLifecyclePhasesExceptPaint(
          DocumentUpdateReason::kHitTest))
    return false;

  // This means the LayoutView is not updated for PrePaint above, probably
  // because the frame is detached.
  if (!FirstFragment().HasLocalBorderBoxProperties())
    return false;

  return HitTestNoLifecycleUpdate(location, result);
}

bool LayoutView::HitTestNoLifecycleUpdate(const HitTestLocation& location,
                                          HitTestResult& result) {
  NOT_DESTROYED();
  TRACE_EVENT_BEGIN("blink,devtools.timeline", "HitTest");
  hit_test_count_++;

  uint64_t dom_tree_version = GetDocument().DomTreeVersion();
  HitTestResult cache_result = result;
  bool hit_layer = false;
  if (hit_test_cache_->LookupCachedResult(location, cache_result,
                                          dom_tree_version)) {
    hit_test_cache_hits_++;
    hit_layer = true;
    result = cache_result;
  } else {
    LocalFrameView* frame_view = GetFrameView();
    PhysicalRect hit_test_area;
    if (frame_view) {
      // Start with a rect sized to the frame, to ensure we include the
      // scrollbars.
      hit_test_area.size = PhysicalSize(frame_view->Size());
      if (result.GetHitTestRequest().IgnoreClipping()) {
        hit_test_area.Unite(
            frame_view->DocumentToFrame(PhysicalRect(DocumentRect())));
      } else if (RuntimeEnabledFeatures::UnboundedElementEnabled() &&
                 GetDocument().HasActiveUnboundedElements()) {
        hit_test_area = PhysicalRect(InfiniteIntRect());
      }
    }

    hit_layer = Layer()->HitTest(location, result, hit_test_area);

    // If hitTestResult include scrollbar, innerNode should be the parent of the
    // scrollbar.
    if (result.GetScrollbar()) {
      // Clear innerNode if we hit a scrollbar whose ScrollableArea isn't
      // associated with a LayoutBox so we aren't hitting some random element
      // below too.
      result.SetInnerNode(nullptr);
      result.SetURLElement(nullptr);
      if (LayoutBox* layout_box = result.GetScrollbar()->GetLayoutBox()) {
        if (Node* node = layout_box->GetNode()) {
          // If scrollbar belongs to Document, we should set innerNode to the
          // <html> element to match other browser.
          if (node->IsDocumentNode()) {
            node = node->GetDocument().documentElement();
          }

          result.SetInnerNode(node);
          result.SetURLElement(node->EnclosingLinkEventParentOrSelf());
        }
      }
    }

    if (hit_layer)
      hit_test_cache_->AddCachedResult(location, result, dom_tree_version);
  }

  TRACE_EVENT_END(
      "blink,devtools.timeline", "endData", [&](perfetto::TracedValue context) {
        inspector_hit_test_event::EndData(
            std::move(context), result.GetHitTestRequest(), location, result);
      });
  return hit_layer;
}

void LayoutView::ClearHitTestCache() {
  NOT_DESTROYED();
  hit_test_cache_->Clear();
  auto* object = GetFrame()->OwnerLayoutObject();
  if (object)
    object->View()->ClearHitTestCache();
}

LayoutUnit LayoutView::ComputeMinimumWidth() {
  NOT_DESTROYED();
  const ComputedStyle& style = StyleRef();
  WritingMode mode = style.GetWritingMode();
  ConstraintSpaceBuilder builder(mode, style.GetWritingDirection(),
                                 /* is_new_fc */ true);
  builder.SetContainsAnnotations(contains_annotations_);
  return BlockNode(this)
      .ComputeMinMaxSizes(mode, SizeType::kIntrinsic,
                          builder.ToConstraintSpace())
      .sizes.min_size;
}

void LayoutView::AddChild(LayoutObject* new_child, LayoutObject* before_child) {
  NOT_DESTROYED();
  if (new_child->StyleRef().StyleType() == kPseudoIdViewTransition) {
    // The view-transition pseudo tree is needs to be laid out within the
    // "snapshot containing block". This is implemented by inserting an
    // anonymous LayoutViewTransitionRoot between the ::view-transition and
    // LayoutView.
    CHECK(!before_child);

    // The view-transition root may already exist if the pseudo is being
    // reinserted due to a positioned state change.
    if (!GetViewTransitionRoot()) {
      LayoutViewTransitionRoot* snapshot_containing_block =
          MakeGarbageCollected<LayoutViewTransitionRoot>(GetDocument());
      LayoutBlockFlow::AddChild(snapshot_containing_block,
                                /*before_child=*/nullptr);
    }
    GetViewTransitionRoot()->AddChild(new_child);

    ViewTransition* transition =
        ViewTransitionUtils::GetTransition(GetDocument());
    CHECK(transition);
    transition->UpdateSnapshotContainingBlockStyle();
    return;
  }

  LayoutBlockFlow::AddChild(new_child, before_child);
}

bool LayoutView::IsChildAllowed(LayoutObject* child,
                                const ComputedStyle&) const {
  NOT_DESTROYED();
  return child->IsBox();
}

bool LayoutView::CanHaveChildren() const {
  NOT_DESTROYED();
  FrameOwner* owner = GetFrame()->Owner();
  if (!owner)
    return true;
  // Although it is not spec compliant, many websites intentionally call
  // Window.print() on display:none iframes. https://crbug.com/819327.
  if (GetDocument().Printing())
    return true;
  // A PluginDocument needs a layout tree during loading, even if it is inside a
  // display: none iframe.  This is because WebLocalFrameImpl::DidFinish expects
  // the PluginDocument's <embed> element to have an EmbeddedContentView, which
  // it acquires during LocalFrameView::UpdatePlugins, which operates on the
  // <embed> element's layout object (LayoutEmbeddedObject).
  if (IsA<PluginDocument>(GetDocument()) ||
      GetDocument().IsForExternalHandler())
    return true;
  return !owner->IsDisplayNone();
}

bool LayoutView::ShouldPlaceBlockDirectionScrollbarOnLogicalLeft() const {
  NOT_DESTROYED();
  LocalFrame& frame = GetFrameView()->GetFrame();
  // See crbug.com/249860
  if (frame.IsOutermostMainFrame()) {
    Settings* settings = GetDocument().GetSettings();
    if (!settings || !settings->GetPlaceRTLScrollbarsOnLeftSideInMainFrame())
      return false;
  }
  // <body> inherits 'direction' from <html>, so checking style on the body is
  // sufficient.
  if (Element* body = GetDocument().body()) {
    if (LayoutObject* body_layout_object = body->GetLayoutObject()) {
      return body_layout_object->StyleRef()
          .ShouldPlaceBlockDirectionScrollbarOnLogicalLeft();
    }
  }
  return false;
}

void LayoutView::MapLocalToAncestor(const LayoutBoxModelObject* ancestor,
                                    TransformState& transform_state,
                                    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  CHECK_EQ(transform_state.Direction(),
           TransformState::kApplyTransformDirection);
  if (!ancestor && !mode.Has(MapCoordinatesMode::kIgnoreTransforms) &&
      ShouldUseTransformFromContainer(nullptr)) {
    gfx::Transform t;
    GetTransformFromContainer(nullptr, PhysicalOffset(), t);
    transform_state.ApplyTransform(t);
  }

  if (ancestor == this)
    return;

  if (mode.Has(MapCoordinatesMode::kTraverseDocumentBoundaries)) {
    auto* parent_doc_layout_object = GetFrame()->OwnerLayoutObject();
    if (parent_doc_layout_object) {
      transform_state.Move(
          parent_doc_layout_object->PhysicalContentBoxRect().offset);
      parent_doc_layout_object->MapLocalToAncestor(ancestor, transform_state,
                                                   mode);
    } else {
      DCHECK(!ancestor);
      if (mode.HasAny({MapCoordinatesMode::kApplyRemoteMainFrameTransform,
                       MapCoordinatesMode::kApplyRemoteViewportTransform})) {
        GetFrameView()->MapLocalToRemoteMainFrame(
            transform_state,
            mode.Has(MapCoordinatesMode::kApplyRemoteViewportTransform));
      }
    }
  }
}

void LayoutView::MapAncestorToLocal(const LayoutBoxModelObject* ancestor,
                                    TransformState& transform_state,
                                    MapCoordinatesFlags mode) const {
  NOT_DESTROYED();
  CHECK_EQ(transform_state.Direction(),
           TransformState::kUnapplyInverseTransformDirection);
  if (this != ancestor &&
      mode.Has(MapCoordinatesMode::kTraverseDocumentBoundaries)) {
    if (auto* parent_doc_layout_object = GetFrame()->OwnerLayoutObject()) {
      parent_doc_layout_object->MapAncestorToLocal(ancestor, transform_state,
                                                   mode);

      transform_state.Move(
          parent_doc_layout_object->PhysicalContentBoxRect().offset);
    } else {
      DCHECK(!ancestor);
      // Note that MapLocalToRemoteMainFrame is correct here because
      // transform_state has kUnapplyInverseTransformDirection.
      if (mode.HasAny({MapCoordinatesMode::kApplyRemoteMainFrameTransform,
                       MapCoordinatesMode::kApplyRemoteViewportTransform}) &&
          GetFrame()->IsLocalRoot()) {
        GetFrameView()->MapLocalToRemoteMainFrame(
            transform_state,
            mode.Has(MapCoordinatesMode::kApplyRemoteViewportTransform));
      }
    }
  } else {
    DCHECK(this == ancestor || !ancestor);
  }
}

PhysicalSize LayoutView::InitialContainingBlockSize() const {
  NOT_DESTROYED();
  return PhysicalSize(GetLayoutSize(kIncludeScrollbars));
}

void LayoutView::RegisterVariableLengthTransformResult(
    const LayoutText& text,
    const VariableLengthTransformResult& result) {
  NOT_DESTROYED();
  CHECK(text.HasVariableLengthTransform());
  text_to_variable_length_transform_result_.Set(&text, result);
}

void LayoutView::UnregisterVariableLengthTransformResult(
    const LayoutText& text) {
  NOT_DESTROYED();
  text_to_variable_length_transform_result_.erase(&text);
}

VariableLengthTransformResult LayoutView::GetVariableLengthTransformResult(
    const LayoutText& text) {
  NOT_DESTROYED();
  CHECK(text.HasVariableLengthTransform());
  return text_to_variable_length_transform_result_.at(&text);
}

LayoutViewTransitionRoot* LayoutView::GetViewTransitionRoot() const {
  NOT_DESTROYED();
  // Returns nullptr if LastChild isn't a ViewTransitionRoot.
  return DynamicTo<LayoutViewTransitionRoot>(LastChild());
}

void LayoutView::InvalidatePaintForViewAndDescendants() {
  NOT_DESTROYED();
  SetSubtreeShouldDoFullPaintInvalidation();
}

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

  // Apply our transform if we have one (because of full page zooming).
  if (Layer() && Layer()->Transform()) {
    transform_state.ApplyTransform(Layer()->CurrentTransform(),
                                   TransformState::kFlattenTransform);
  }

  transform_state.Flatten();

  if (ancestor == this)
    return true;

  const bool apply_viewport_clip =
      !visual_rect_flags.Has(VisualRectFlag::kSkipAncestorAndViewportClips);

  Element* owner = GetDocument().LocalOwner();
  if (!owner) {
    PhysicalRect rect = PhysicalRect::EnclosingRect(
        transform_state.LastPlanarQuad().BoundingBox());
    const bool apply_overflow_clip =
        apply_viewport_clip &&
        !visual_rect_flags.Has(VisualRectFlag::kDontApplyMainFrameOverflowClip);
    const bool apply_viewport_transform =
        visual_rect_flags.Has(VisualRectFlag::kApplyRemoteViewportTransform);

    // When mapping into the viewport space (ancestor == nullptr) for the
    // outermost main frame, apply the local visual viewport transform (page
    // scale + visual viewport location). The GeometryMapper viewport fast path
    // includes this transform; keep the slow path consistent.
    if (apply_viewport_transform &&
        GetFrameView()->GetFrame().IsOutermostMainFrame() &&
        base::FeatureList::IsEnabled(
            blink::features::
                kVisualRectMappingApplyLocalVisualViewportTransform)) {
      // Convert from root-frame coordinates into visual-viewport coordinates.
      // This applies the visual viewport's location and page scale (pinch-zoom)
      // so viewport mapping remains consistent between the slow path and the
      // GeometryMapper fast path.
      VisualViewport& visual_viewport =
          GetFrameView()->GetFrame().GetPage()->GetVisualViewport();
      gfx::RectF rect_f(rect);
      rect_f = visual_viewport.RootFrameToViewport(rect_f);
      rect = PhysicalRect::EnclosingRect(rect_f);

      // RootFrameToViewport can yield negative coordinates when the visual
      // viewport is offset (e.g. browser controls animation or pinch-zoom).
      // Apply the same local-root viewport clipping semantics as the
      // GeometryMapper viewport fast path. This does not duplicate clipping
      // performed by MapToVisualRectInRemoteRootFrame(): in the outermost main
      // frame that method is a no-op, and the slow path does not otherwise
      // apply LayoutView::ViewRect() clipping for ancestor == nullptr.
      if (apply_overflow_clip) {
        PhysicalRect view_rectangle = ViewRect();
        if (visual_rect_flags.Has(VisualRectFlag::kEdgeInclusive)) {
          if (!rect.InclusiveIntersect(view_rectangle)) {
            transform_state.SetQuad(gfx::QuadF(gfx::RectF(rect)));
            return false;
          }
        } else {
          rect.Intersect(view_rectangle);
        }
      }
    }

    bool retval = GetFrameView()->MapToVisualRectInRemoteRootFrame(
        rect, apply_overflow_clip, apply_viewport_transform,
        apply_viewport_clip);
    transform_state.SetQuad(gfx::QuadF(gfx::RectF(rect)));
    return retval;
  }

  if (LayoutBox* obj = owner->GetLayoutBox()) {
    PhysicalRect rect = PhysicalRect::EnclosingRect(
        transform_state.LastPlanarQuad().BoundingBox());
    PhysicalRect view_rectangle = ViewRect();
    if (apply_viewport_clip) {
      if (visual_rect_flags.Has(VisualRectFlag::kEdgeInclusive)) {
        if (!rect.InclusiveIntersect(view_rectangle)) {
          transform_state.SetQuad(gfx::QuadF(gfx::RectF(rect)));
          return false;
        }
      } else {
        rect.Intersect(view_rectangle);
      }
    }
    // Frames are painted at rounded-int position. Since we cannot efficiently
    // compute the subpixel offset of painting at this point in a a bottom-up
    // walk, round to the enclosing int rect, which will enclose the actual
    // visible rect.
    rect.ExpandEdgesToPixelBoundaries();

    // Adjust for frame border.
    rect.Move(obj->PhysicalContentBoxRect().offset);
    transform_state.SetQuad(gfx::QuadF(gfx::RectF(rect)));

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

  // This can happen, e.g., if the iframe element has display:none.
  transform_state.SetQuad(gfx::QuadF(gfx::RectF()));
  return false;
}

PhysicalOffset LayoutView::OffsetForFixedPosition() const {
  NOT_DESTROYED();
  return IsScrollContainer() ? ScrolledContentOffset() : PhysicalOffset();
}

void LayoutView::QuadsInAncestorInternal(Vector<gfx::QuadF>& quads,
                                         const LayoutBoxModelObject* ancestor,
                                         MapCoordinatesFlags mode,
                                         BoxQuadType) const {
  NOT_DESTROYED();
  quads.push_back(LocalRectToAncestorQuad(
      PhysicalRect(PhysicalOffset(), GetScrollableArea()->Size()), ancestor,
      mode));
}

void LayoutView::CommitPendingSelection() {
  NOT_DESTROYED();
  TRACE_EVENT0("blink", "LayoutView::commitPendingSelection");
  DCHECK(!NeedsLayout());
  frame_view_->GetFrame().Selection().CommitAppearanceIfNeeded();
}

bool LayoutView::ShouldUsePaginatedLayout(const Document& document) {
  if (!document.Printing())
    return false;
  const LocalFrameView* frame_view = document.View();
  if (!frame_view)
    return false;
  return frame_view->GetFrame().ShouldUsePaginatedLayout();
}

PhysicalRect LayoutView::ViewRect() const {
  NOT_DESTROYED();
  if (GetDocument().Printing()) {
    return PhysicalRect(PhysicalOffset(), StitchedSize());
  }

  if (!frame_view_)
    return PhysicalRect();

  // TODO(bokan): This shouldn't be just for the outermost main frame, we
  // should do it for all frames. crbug.com/1311518.
  if (frame_view_->GetFrame().IsOutermostMainFrame()) {
    if (auto* transition = ViewTransitionUtils::GetTransition(GetDocument());
        transition && transition->IsRootTransitioning()) {
      // If we're capturing a transition snapshot, the root transition
      // needs to produce the snapshot at a known stable size, excluding
      // all insetting UI like mobile URL bars and virtual keyboards.

      // This adjustment should always be an expansion of the current
      // viewport.
      if (transition->GetSnapshotRootSize().width() <
              frame_view_->Size().width() ||
          transition->GetSnapshotRootSize().height() <
              frame_view_->Size().height()) {
        // TODO(https://issues.chromium.org/362991812) This can happen when
        // layout is deferred during a resize or rotation, causing a temporary
        // mismatch. We need skip the transition which would have happened later
        // anyway.
        transition->SkipTransitionSoon(
            ViewTransition::PromiseResponse::kRejectInvalidState,
            ViewTransitionSkipReason::kSnapshotRootChangedSize);
        return PhysicalRect(PhysicalOffset(),
                            PhysicalSize(frame_view_->Size()));
      }

      return PhysicalRect(
          PhysicalOffset(transition->GetFrameToSnapshotRootOffset()),
          PhysicalSize(transition->GetSnapshotRootSize()));
    }
  }

  return PhysicalRect(PhysicalOffset(), PhysicalSize(frame_view_->Size()));
}

PhysicalRect LayoutView::OverflowClipRect(
    OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior) const {
  NOT_DESTROYED();
  return OverflowClipRectInternal(overlay_scrollbar_clip_behavior,
                                  false /* for_scroll_node */);
}

PhysicalRect LayoutView::OverflowClipRectForScrollNode() const {
  NOT_DESTROYED();
  return OverflowClipRectInternal(kIgnoreOverlayScrollbarSize,
                                  true /* for_scroll_node */);
}

PhysicalRect LayoutView::OverflowClipRectInternal(
    OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior,
    bool for_scroll_node) const {
  NOT_DESTROYED();
  PhysicalRect rect = ViewRect();
  if (rect.IsEmpty()) {
    return LayoutBox::OverflowClipRect(overlay_scrollbar_clip_behavior);
  }

  // When capturing the root snapshot for a transition, we paint the
  // background color where the scrollbar would be so keep the clip rect
  // the full ViewRect size.
  // NOTE: When calculating the rect for scroll node, we don't want this
  // behavior because scroll node dimensions (e.g. thumb length) shouldn't
  // be affected by the view transition.
  auto* transition = ViewTransitionUtils::GetTransition(GetDocument());
  bool is_in_transition = transition && transition->IsRootTransitioning();
  if (IsScrollContainer() && (for_scroll_node || !is_in_transition)) {
    ExcludeScrollbars(rect, overlay_scrollbar_clip_behavior);
  }

  return rect;
}

void LayoutView::SetAutosizeScrollbarModes(mojom::blink::ScrollbarMode h_mode,
                                           mojom::blink::ScrollbarMode v_mode) {
  NOT_DESTROYED();
  DCHECK_EQ(v_mode == mojom::blink::ScrollbarMode::kAuto,
            h_mode == mojom::blink::ScrollbarMode::kAuto);
  autosize_v_scrollbar_mode_ = v_mode;
  autosize_h_scrollbar_mode_ = h_mode;
}

void LayoutView::CalculateScrollbarModes(
    mojom::blink::ScrollbarMode& h_mode,
    mojom::blink::ScrollbarMode& v_mode,
    std::optional<EOverflow> overflow_x,
    std::optional<EOverflow> overflow_y) const {
  NOT_DESTROYED();
  DCHECK_EQ(overflow_x.has_value(), overflow_y.has_value());

#define RETURN_SCROLLBAR_MODE(mode) \
  {                                 \
    h_mode = v_mode = mode;         \
    return;                         \
  }

  // FrameViewAutoSizeInfo manually controls the appearance of the main frame's
  // scrollbars so defer to those if we're in AutoSize mode.
  if (AutosizeVerticalScrollbarMode() != mojom::blink::ScrollbarMode::kAuto ||
      AutosizeHorizontalScrollbarMode() != mojom::blink::ScrollbarMode::kAuto) {
    h_mode = AutosizeHorizontalScrollbarMode();
    v_mode = AutosizeVerticalScrollbarMode();
    return;
  }

  LocalFrame* frame = GetFrame();
  if (!frame) {
    RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
  }

  // ClipsContent() is false means that the client wants to paint the whole
  // contents of the frame without scrollbars, which is for printing etc.
  if (!frame->ClipsContent()) {
    // Don't disable scrollbars in paint preview capture. This will make sure
    // that page content will not be shifted during the paint preview capture.
    bool disable_scrollbars =
        !GetDocument().AreScrollbarsAllowedInPaintPreview();
#if BUILDFLAG(IS_ANDROID)
    // However, Android WebView has a setting recordFullDocument. When it's set
    // to true, ClipsContent() is false here, while WebView still expects blink
    // to provide scrolling mechanism. The flag can be set through WebView API,
    // or is forced if the app's target SDK version < LOLLIPOP.
    // Synchronous compositing indicates Android WebView.
    if (Platform::Current()
            ->IsSynchronousCompositingEnabledForAndroidWebView() &&
        !GetDocument().IsPrintingOrPaintingPreview()) {
      disable_scrollbars = false;
    }
#endif
    if (disable_scrollbars) {
      RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
    }
  }

  if (FrameOwner* owner = frame->Owner()) {
    // Setting scrolling="no" on an iframe element disables scrolling.
    if (owner->ScrollbarMode() == mojom::blink::ScrollbarMode::kAlwaysOff) {
      RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
    }
  }

  Document& document = GetDocument();
  if (Node* body = document.body()) {
    // Framesets can't scroll.
    if (body->GetLayoutObject() && body->GetLayoutObject()->IsFrameSet()) {
      RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
    }
  }

  if (LocalFrameView* frameView = GetFrameView()) {
    // Scrollbars can be disabled by LocalFrameView::setCanHaveScrollbars.
    if (!frameView->CanHaveScrollbars()) {
      RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
    }
  }

  const bool is_after_layout_tree_constructed = !overflow_x.has_value();
  if (is_after_layout_tree_constructed) {
    Element* viewport_defining_element = document.ViewportDefiningElement();

    if (!viewport_defining_element) {
      RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAuto);
    }

    const LayoutObject* viewport = viewport_defining_element->GetLayoutObject();
    if (!viewport) {
      RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAuto);
    }

    if (viewport->IsSVGRoot()) {
      // Don't allow overflow to affect <img> and css backgrounds
      if (To<LayoutSVGRoot>(viewport)->IsEmbeddedThroughSVGImage()) {
        RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAuto);
      }

      // FIXME: evaluate if we can allow overflow for these cases too.
      // Overflow is always hidden when stand-alone SVG documents are embedded.
      if (To<LayoutSVGRoot>(viewport)
              ->IsEmbeddedThroughFrameContainingSVGDocument()) {
        RETURN_SCROLLBAR_MODE(mojom::blink::ScrollbarMode::kAlwaysOff);
      }
    }

    const ComputedStyle& style = viewport->StyleRef();
    overflow_x = style.OverflowX();
    overflow_y = style.OverflowY();
  }

  h_mode = v_mode = mojom::blink::ScrollbarMode::kAuto;

  bool should_ignore_overflow_hidden = false;
  if (Settings* settings = document.GetSettings()) {
    if (settings->GetIgnoreMainFrameOverflowHiddenQuirk() &&
        frame->IsMainFrame())
      should_ignore_overflow_hidden = true;
  }
  if (!should_ignore_overflow_hidden) {
    if (overflow_x == EOverflow::kHidden || overflow_x == EOverflow::kClip)
      h_mode = mojom::blink::ScrollbarMode::kAlwaysOff;
    if (overflow_y == EOverflow::kHidden || overflow_y == EOverflow::kClip)
      v_mode = mojom::blink::ScrollbarMode::kAlwaysOff;
  }

  if (overflow_x == EOverflow::kScroll)
    h_mode = mojom::blink::ScrollbarMode::kAlwaysOn;
  if (overflow_y == EOverflow::kScroll)
    v_mode = mojom::blink::ScrollbarMode::kAlwaysOn;

#undef RETURN_SCROLLBAR_MODE
}

AtomicString LayoutView::NamedPageAtIndex(wtf_size_t page_index) const {
  NOT_DESTROYED();
  // If layout is dirty, it's not possible to look up page names reliably.
  DCHECK_GE(GetDocument().Lifecycle().GetState(),
            DocumentLifecycle::kLayoutClean);

  if (!PhysicalFragmentCount()) {
    return AtomicString();
  }
  DCHECK_EQ(PhysicalFragmentCount(), 1u);
  const PhysicalBoxFragment& view_fragment = *GetPhysicalFragment(0);
  const auto children = view_fragment.Children();
  if (page_index >= children.size()) {
    return AtomicString();
  }
  const auto& page_fragment = To<PhysicalBoxFragment>(*children[page_index]);
  return page_fragment.PropagatedPageName();
}

PhysicalRect LayoutView::DocumentRect() const {
  NOT_DESTROYED();
  return ScrollableOverflowRect();
}

gfx::Size LayoutView::GetLayoutSize(
    IncludeScrollbarsInRect scrollbar_inclusion) const {
  NOT_DESTROYED();
  if (GetDocument().Printing()) {
    return ToFlooredSize(initial_containing_block_size_for_printing_);
  }
  return GetNonPrintingLayoutSize(scrollbar_inclusion);
}

gfx::Size LayoutView::GetNonPrintingLayoutSize(
    IncludeScrollbarsInRect scrollbar_inclusion) const {
  NOT_DESTROYED();
  if (!frame_view_)
    return gfx::Size();

  gfx::Size result = frame_view_->GetLayoutSize();
  if (scrollbar_inclusion == kExcludeScrollbars &&
      frame_view_->LayoutViewport()) {
    result = frame_view_->LayoutViewport()->ExcludeScrollbars(result);
  }
  return result;
}

LayoutUnit LayoutView::ViewLogicalHeightForPercentages() const {
  NOT_DESTROYED();
  PhysicalSize size;
  if (GetDocument().Printing()) {
    size = initial_containing_block_size_for_printing_;
  } else {
    size = PhysicalSize(GetLayoutSize(kExcludeScrollbars));
  }
  return IsHorizontalWritingMode() ? size.height : size.width;
}

const LayoutBox& LayoutView::RootBox() const {
  NOT_DESTROYED();
  Element* document_element = GetDocument().documentElement();
  DCHECK(document_element);
  DCHECK(document_element->GetLayoutObject());
  return To<LayoutBox>(*document_element->GetLayoutObject());
}

void LayoutView::LayoutRoot() {
  NOT_DESTROYED();
  if (ShouldUsePaginatedLayout()) {
    intrinsic_logical_widths_ = LogicalWidth();
  }

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
  // The font code in FontPlatformData does not have a direct connection to the
  // document, the frame or anything from which we could retrieve the device
  // scale factor. After using zoom for DSF, the GraphicsContext does only ever
  // have a DSF of 1 on Linux. In order for the font code to be aware of an up
  // to date DSF when layout happens, we plumb this through to the FontCache, so
  // that we can correctly retrieve RenderStyleForStrike from out of
  // process. crbug.com/845468
  LocalFrame& frame = GetFrameView()->GetFrame();
  ChromeClient& chrome_client = frame.GetChromeClient();
  FontCache::SetDeviceScaleFactor(
      chrome_client.GetScreenInfo(frame).device_scale_factor);
#endif

  const ComputedStyle& style = StyleRef();
  const WritingMode writing_mode = style.GetWritingMode();
  const WritingDirectionMode writing_direction = style.GetWritingDirection();
  const LogicalSize original_size =
      ToLogicalSize(InitialContainingBlockSize(), writing_mode);

  // If we are being auto-sized, we want to ensure that we are at least our
  // min-content size in the inline-axis.
  LayoutUnit min_size;
  if (RuntimeEnabledFeatures::AutoSizeUsesScrollWidthForOverflowEnabled() &&
      GetFrameView()->IsBeingAutoSized()) {
    ConstraintSpaceBuilder builder(writing_mode, writing_direction,
                                   /* is_new_fc */ true);
    builder.SetAvailableSize({kIndefiniteSize, original_size.block_size});
    builder.SetIsFixedBlockSize(true);
    builder.SetContainsAnnotations(contains_annotations_);
    min_size = BlockNode(this)
                   .ComputeMinMaxSizes(writing_mode, SizeType::kIntrinsic,
                                       builder.ToConstraintSpace())
                   .sizes.min_size;
    if (style.OverflowBlockDirection() == EOverflow::kAuto) {
      min_size -= ComputeLogicalScrollbars().InlineSum();
    }
  }

  const LogicalSize initial_size(std::max(original_size.inline_size, min_size),
                                 original_size.block_size);
  const bool is_resizing_initial_containing_block =
      ToLogicalSize(StitchedSize(), writing_mode) != initial_size;
  DCHECK(!initial_containing_block_resize_handled_list_);
  if (is_resizing_initial_containing_block) {
    initial_containing_block_resize_handled_list_ =
        MakeGarbageCollected<GCedHeapHashSet<Member<const LayoutObject>>>();
  }

  ConstraintSpaceBuilder builder(writing_mode, writing_direction,
                                 /* is_new_fc */ true);
  builder.SetAvailableSize(initial_size);
  builder.SetIsFixedInlineSize(true);
  builder.SetIsFixedBlockSize(true);
  builder.SetContainsAnnotations(contains_annotations_);

  BlockNode(this).Layout(builder.ToConstraintSpace());
  initial_containing_block_resize_handled_list_ = nullptr;
}

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

  Node* node = GetDocument().documentElement();
  if (node) {
    PhysicalOffset adjusted_point = point;
    if (const auto* layout_box = node->GetLayoutBox())
      adjusted_point -= layout_box->PhysicalLocation();
    if (IsScrollContainer()) {
      adjusted_point += PhysicalOffset(PixelSnappedScrolledContentOffset());
    }
    result.SetNodeAndPosition(node, adjusted_point);
  }
}

bool LayoutView::BackgroundIsKnownToBeOpaqueInRect(const PhysicalRect&) const {
  NOT_DESTROYED();
  // The base background color applies to the main frame only.
  return GetFrame()->IsMainFrame() &&
         frame_view_->BaseBackgroundColor().IsOpaque();
}

gfx::SizeF LayoutView::SubtractUnconditionalScrollbarsFromViewportUnits(
    const gfx::SizeF& viewport_size) const {
  if (!RuntimeEnabledFeatures::SmallerViewportUnitsEnabled()) {
    return viewport_size;
  }
  gfx::SizeF scrollbars_removed =
      viewport_size -
      gfx::SizeF(vertical_scrollbar_width_for_viewport_units_,
                 horizontal_scrollbar_height_for_viewport_units_);
  scrollbars_removed.SetToMax(gfx::SizeF(0, 0));
  return scrollbars_removed;
}

gfx::SizeF LayoutView::SmallViewportSizeForViewportUnits() const {
  NOT_DESTROYED();
  return GetFrameView()
             ? SubtractUnconditionalScrollbarsFromViewportUnits(
                   GetFrameView()->SmallViewportSizeForViewportUnits())
             : gfx::SizeF();
}

gfx::SizeF LayoutView::LargeViewportSizeForViewportUnits() const {
  NOT_DESTROYED();
  return GetFrameView()
             ? SubtractUnconditionalScrollbarsFromViewportUnits(
                   GetFrameView()->LargeViewportSizeForViewportUnits())
             : gfx::SizeF();
}

gfx::SizeF LayoutView::DynamicViewportSizeForViewportUnits() const {
  NOT_DESTROYED();
  return GetFrameView()
             ? SubtractUnconditionalScrollbarsFromViewportUnits(
                   GetFrameView()->DynamicViewportSizeForViewportUnits())
             : gfx::SizeF();
}

gfx::SizeF LayoutView::PaginationViewportSizeForMediaQueries() const {
  NOT_DESTROYED();
  // The spec says to use the page *box* size when evaluating width and height
  // media queries: https://drafts.csswg.org/mediaqueries-3/#width
  //
  // Nobody has ever done that, though. It's always been about the page
  // *area*.
  // General discussion: https://github.com/w3c/csswg-drafts/issues/5437
  //
  // Furthermore, declarations in @page rules that affect the size must be
  // ignored, to avoid circular dependencies.
  // See https://drafts.csswg.org/css-page-3/#page-size-prop
  //
  // Therefore use the default page area size, as provided by the system and
  // print settings (i.e. unaffected by CSS).
  const WebPrintParams& params = frame_view_->GetFrame().GetPrintParams();
  const WebPrintPageDescription& default_page_description =
      params.default_page_description;
  gfx::SizeF size(std::max(.0f, default_page_description.size.width() -
                                    (default_page_description.margin_left +
                                     default_page_description.margin_right)),
                  std::max(.0f, default_page_description.size.height() -
                                    (default_page_description.margin_top +
                                     default_page_description.margin_bottom)));

  // If the paginated content is scaled, the number of pixels that can fit
  // within the page area is inversely proportional to the scale factor.
  size.Scale(1.0f / params.scale_factor);

  return size;
}

void LayoutView::WillBeDestroyed(const ComputedStyle* style) {
  NOT_DESTROYED();
  // TODO(wangxianzhu): This is a workaround of crbug.com/570706.
  // Should find and fix the root cause.
  if (PaintLayer* layer = Layer())
    layer->SetNeedsRepaint();
  LayoutBlockFlow::WillBeDestroyed(style);
}

void LayoutView::UpdateFromStyle() {
  NOT_DESTROYED();
  LayoutBlockFlow::UpdateFromStyle();

  // LayoutView of the main frame is responsible for painting base background.
  if (GetFrameView()->ShouldPaintBaseBackgroundColor())
    SetHasBoxDecorationBackground(true);
}

void LayoutView::UpdateAfterLayout() {
  NOT_DESTROYED();
  LayoutBlockFlow::UpdateAfterLayout();
  if (cached_scroll_dimensions_.has_value() && GetFrame()->Owner() &&
      !GetFrame()->Owner()->IsDisplayNone() && !GetDocument().Printing()) {
    auto* scrollable_area = GetScrollableArea();
    CHECK(scrollable_area);
    // Only restore scroll offset if the scroll dimensions match
    if (scrollable_area->ScrollWidth() == cached_scroll_dimensions_->width &&
        scrollable_area->ScrollHeight() == cached_scroll_dimensions_->height &&
        scrollable_area->ScrollOrigin() == cached_scroll_dimensions_->origin) {
      scrollable_area->SetScrollOffset(cached_scroll_dimensions_->offset,
                                       mojom::blink::ScrollType::kProgrammatic,
                                       cc::ScrollSourceType::kAbsoluteScroll);
    }
    cached_scroll_dimensions_.reset();
  }
}

void LayoutView::CacheScrollDimensions() {
  NOT_DESTROYED();
  auto* scrollable_area = GetScrollableArea();
  CHECK(scrollable_area);
  cached_scroll_dimensions_.emplace(CachedScrollDimensions{
      scrollable_area->ScrollWidth(), scrollable_area->ScrollHeight(),
      scrollable_area->ScrollOrigin(), scrollable_area->GetScrollOffset()});
}

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

  LocalFrame& frame = GetFrameView()->GetFrame();
  VisualViewport& visual_viewport = frame.GetPage()->GetVisualViewport();
  if (frame.IsMainFrame() && visual_viewport.IsActiveViewport()) {
    // |VisualViewport::UsedColorScheme| depends on the LayoutView's used
    // color scheme.
    if (!old_style || old_style->UsedColorScheme() !=
                          visual_viewport.UsedColorSchemeScrollbars()) {
      visual_viewport.UsedColorSchemeChanged();
    }
    if (old_style && old_style->ScrollbarThumbColorResolved() !=
                         visual_viewport.CSSScrollbarThumbColor()) {
      visual_viewport.ScrollbarColorChanged();
    }
  }
}

PhysicalRect LayoutView::DebugRect() const {
  NOT_DESTROYED();
  return PhysicalRect(gfx::Rect(GetLayoutSize(kIncludeScrollbars)));
}

CompositingReasons LayoutView::AdditionalCompositingReasons() const {
  NOT_DESTROYED();
  // TODO(lfg): Audit for portals
  const LocalFrame& frame = frame_view_->GetFrame();
  if (frame.OwnerLayoutObject() &&
      frame.IsCrossOriginToParentOrOuterDocument()) {
    return {CompositingReason::kIFrame};
  }
  return {};
}

bool LayoutView::AffectedByResizedInitialContainingBlock(
    const LayoutResult& layout_result) {
  NOT_DESTROYED();
  if (!initial_containing_block_resize_handled_list_) {
    return false;
  }
  const LayoutObject* layout_object =
      layout_result.GetPhysicalFragment().GetLayoutObject();
  DCHECK(layout_object);
  auto add_result =
      initial_containing_block_resize_handled_list_->insert(layout_object);
  return add_result.is_new_entry;
}

void LayoutView::InvalidateLayoutForCounterStyleChanges() {
  NOT_DESTROYED();
  if (!HasLayoutCounters() && !HasLayoutListItems()) {
    return;
  }

  for (LayoutObject* layout_object = this; layout_object;
       layout_object = layout_object->NextInPreOrder()) {
    if (auto* ng_list_item = DynamicTo<LayoutListItem>(layout_object)) {
      ng_list_item->UpdateCounterStyle();
    } else if (auto* inline_list_item =
                   DynamicTo<LayoutInlineListItem>(layout_object)) {
      inline_list_item->UpdateCounterStyle();
    }
  }
}

bool LayoutView::HasTickmarks() const {
  NOT_DESTROYED();
  return GetDocument().Markers().PossiblyHasTextMatchMarkers();
}

Vector<gfx::Rect> LayoutView::GetTickmarks() const {
  NOT_DESTROYED();
  return GetDocument().Markers().LayoutRectsForTextMatchMarkers();
}

bool LayoutView::IsFragmentationContextRoot() const {
  NOT_DESTROYED();
  return ShouldUsePaginatedLayout();
}

bool LayoutView::SetScrollbarSizesForViewportUnits(const gfx::Size& size) {
  NOT_DESTROYED();
  bool changed = false;
  if (size.width() != vertical_scrollbar_width_for_viewport_units_) {
    vertical_scrollbar_width_for_viewport_units_ = size.width();
    changed = true;
  }
  if (size.height() != horizontal_scrollbar_height_for_viewport_units_) {
    horizontal_scrollbar_height_for_viewport_units_ = size.height();
    changed = true;
  }
  return changed;
}

void LayoutView::SetContainsAnnotations() {
  NOT_DESTROYED();
  if (contains_annotations_) {
    return;
  }
  contains_annotations_ = true;
  SetNeedsLayout(layout_invalidation_reason::kStyleChange);
}

}  // namespace blink
