// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/view.h"

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

#include "base/check_op.h"
#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/debug/dump_without_crashing.h"
#include "base/feature_list.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/observer_list.h"
#include "base/ranges/algorithm.h"
#include "base/scoped_observation.h"
#include "base/strings/utf_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkRect.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_id_forward.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/mojom/drag_drop_types.mojom.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/color/color_provider_manager.h"
#include "ui/compositor/clip_recorder.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/paint_context.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/compositor/transform_recorder.h"
#include "ui/display/screen.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_target_iterator.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/angle_conversions.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/interpolated_transform.h"
#include "ui/gfx/scoped_canvas.h"
#include "ui/native_theme/native_theme.h"
#include "ui/views/accessibility/accessibility_paint_checks.h"
#include "ui/views/accessibility/ax_event_manager.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/buildflags.h"
#include "ui/views/context_menu_controller.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/drag_controller.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/view_observer.h"
#include "ui/views/view_tracker.h"
#include "ui/views/view_utils.h"
#include "ui/views/views_features.h"
#include "ui/views/views_switches.h"
#include "ui/views/widget/native_widget_private.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/tooltip_manager.h"
#include "ui/views/widget/widget.h"

#if BUILDFLAG(IS_WIN)
#include "base/win/scoped_gdi_object.h"
#include "ui/native_theme/native_theme_win.h"
#endif

namespace views {

namespace {

#if BUILDFLAG(IS_WIN)
constexpr bool kContextMenuOnMousePress = false;
#else
constexpr bool kContextMenuOnMousePress = true;
#endif

// Having UseDefaultFillLayout true by default wreaks a bit of havoc right now,
// so it is false for the time being. Once the various sites which currently use
// FillLayout are converted to using this and the other places that either
// override Layout() or do nothing are also validated, this can be switched to
// true.
constexpr bool kUseDefaultFillLayout = false;

// Default horizontal drag threshold in pixels.
// Same as what gtk uses.
constexpr int kDefaultHorizontalDragThreshold = 8;

// Default vertical drag threshold in pixels.
// Same as what gtk uses.
constexpr int kDefaultVerticalDragThreshold = 8;

// The following are used to offset the keys for the callbacks associated with
// the bounds element callbacks.
constexpr int kXChangedKey = sizeof(int) * 0;
constexpr int kYChangedKey = sizeof(int) * 1;
constexpr int kWidthChangedKey = sizeof(int) * 2;
constexpr int kHeightChangedKey = sizeof(int) * 3;

// Returns the top view in |view|'s hierarchy.
const View* GetHierarchyRoot(const View* view) {
  const View* root = view;
  while (root && root->parent())
    root = root->parent();
  return root;
}

}  // namespace

namespace internal {

#if DCHECK_IS_ON()
class ScopedChildrenLock {
 public:
  explicit ScopedChildrenLock(const View* view)
      : reset_(&view->iterating_, true) {}

  ScopedChildrenLock(const ScopedChildrenLock&) = delete;
  ScopedChildrenLock& operator=(const ScopedChildrenLock&) = delete;

  ~ScopedChildrenLock() = default;

 private:
  base::AutoReset<bool> reset_;
};
#else
class ScopedChildrenLock {
 public:
  explicit ScopedChildrenLock(const View* view) {}
  ~ScopedChildrenLock() {}
};
#endif

}  // namespace internal

////////////////////////////////////////////////////////////////////////////////
// ViewMaskLayer
// This class is responsible for creating a masking layer for a view that paints
// to a layer. It tracks the size of the layer it is masking.
class VIEWS_EXPORT ViewMaskLayer : public ui::LayerDelegate,
                                   public ViewObserver {
 public:
  // Note that |observed_view| must outlive the ViewMaskLayer instance.
  ViewMaskLayer(const SkPath& path, View* observed_view);
  ViewMaskLayer(const ViewMaskLayer& mask_layer) = delete;
  ViewMaskLayer& operator=(const ViewMaskLayer& mask_layer) = delete;
  ~ViewMaskLayer() override;

  ui::Layer* layer() { return &layer_; }

 private:
  // ui::LayerDelegate:
  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
                                  float new_device_scale_factor) override;
  void OnPaintLayer(const ui::PaintContext& context) override;

  // views::ViewObserver:
  void OnViewBoundsChanged(View* observed_view) override;

  base::ScopedObservation<View, ViewObserver> observed_view_{this};

  SkPath path_;
  ui::Layer layer_;
};

ViewMaskLayer::ViewMaskLayer(const SkPath& path, View* observed_view)
    : path_{path} {
  layer_.set_delegate(this);
  layer_.SetFillsBoundsOpaquely(false);
  layer_.SetName("ViewMaskLayer");
  observed_view_.Observe(observed_view);
  OnViewBoundsChanged(observed_view);
}

ViewMaskLayer::~ViewMaskLayer() {
  layer_.set_delegate(nullptr);
}

void ViewMaskLayer::OnDeviceScaleFactorChanged(float old_device_scale_factor,
                                               float new_device_scale_factor) {}

void ViewMaskLayer::OnPaintLayer(const ui::PaintContext& context) {
  cc::PaintFlags flags;
  flags.setAlphaf(1.0f);
  flags.setStyle(cc::PaintFlags::kFill_Style);
  flags.setAntiAlias(true);

  ui::PaintRecorder recorder(context, layer()->size());
  recorder.canvas()->DrawPath(path_, flags);
}

void ViewMaskLayer::OnViewBoundsChanged(View* observed_view) {
  layer_.SetBounds(observed_view->GetLocalBounds());
}

////////////////////////////////////////////////////////////////////////////////
// View, public:

// Creation and lifetime -------------------------------------------------------

View::View() {
  SetTargetHandler(this);
  if (kUseDefaultFillLayout)
    default_fill_layout_.emplace(DefaultFillLayout());

  static bool capture_stack_trace =
      base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kViewStackTraces);
  if (capture_stack_trace) {
    SetProperty(kViewStackTraceKey,
                std::make_unique<base::debug::StackTrace>());
  }

  ax_node_data_ = std::make_unique<ui::AXNodeData>();
}

View::~View() {
  life_cycle_state_ = LifeCycleState::kDestroying;

  if (parent_)
    parent_->RemoveChildView(this);

  // This view should have been removed from the focus list by now.
  DCHECK_EQ(next_focusable_view_, nullptr);
  DCHECK_EQ(previous_focusable_view_, nullptr);

  // Need to remove layout manager before deleting children because if we do not
  // it is possible for layout-related calls (e.g. CalculatePreferredSize()) to
  // be called on this view during one of the callbacks below. Since most
  // layout managers access child view properties, this would result in a
  // use-after-free error.
  layout_manager_.reset();

  {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : children_) {
      child->parent_ = nullptr;

      // Remove any references to |child| to avoid holding a dangling ptr.
      if (child->previous_focusable_view_)
        child->previous_focusable_view_->next_focusable_view_ = nullptr;
      if (child->next_focusable_view_)
        child->next_focusable_view_->previous_focusable_view_ = nullptr;

      // Since all children are removed here, it is safe to set
      // |child|'s focus list pointers to null and expect any references
      // to |child| will be removed subsequently.
      child->next_focusable_view_ = nullptr;
      child->previous_focusable_view_ = nullptr;

      if (!child->owned_by_client_)
        delete child;
    }

    // Clear `children_` to prevent UAFs from observers and properties that may
    // end up looking at children(), directly or indirectly, before ~View() goes
    // out of scope.
    children_.clear();
  }

  for (ViewObserver& observer : observers_)
    observer.OnViewIsDeleting(this);

  for (ui::Layer* layer : GetLayersInOrder(ViewLayer::kExclude)) {
    layer->RemoveObserver(this);
  }

  // Clearing properties explicitly here lets us guarantee that properties
  // outlive |this| (at least the View part of |this|). This is intentionally
  // called at the end so observers can examine properties inside
  // OnViewIsDeleting(), for instance.
  ClearProperties();

  life_cycle_state_ = LifeCycleState::kDestroyed;
}

// Tree operations -------------------------------------------------------------

const Widget* View::GetWidget() const {
  // The root view holds a reference to this view hierarchy's Widget.
  return parent_ ? parent_->GetWidget() : nullptr;
}

Widget* View::GetWidget() {
  return const_cast<Widget*>(const_cast<const View*>(this)->GetWidget());
}

void View::ReorderChildView(View* view, size_t index) {
  DCHECK_EQ(view->parent_, this);
  const auto i = base::ranges::find(children_, view);
  DCHECK(i != children_.end());

  // If |view| is already at the desired position, there's nothing to do.
  const auto pos =
      std::next(children_.begin(),
                static_cast<ptrdiff_t>(std::min(index, children_.size() - 1)));
  if (i == pos)
    return;

    // Rotate |view| to be at the desired position.
#if DCHECK_IS_ON()
  DCHECK(!iterating_);
#endif
  if (pos < i)
    std::rotate(pos, i, std::next(i));
  else
    std::rotate(i, std::next(i), std::next(pos));

  // Update focus siblings.  Unhook |view| from the focus cycle first so
  // SetFocusSiblings() won't traverse through it.
  view->RemoveFromFocusList();
  SetFocusSiblings(view, pos);

  for (ViewObserver& observer : observers_)
    observer.OnChildViewReordered(this, view);

  ReorderLayers();
  InvalidateLayout();
}

void View::RemoveChildView(View* view) {
  DoRemoveChildView(view, true, false, nullptr);
}

void View::RemoveAllChildViews() {
  while (!children_.empty())
    DoRemoveChildView(children_.front(), false, true, nullptr);
  UpdateTooltip();
}

void View::RemoveAllChildViewsWithoutDeleting() {
  while (!children_.empty())
    DoRemoveChildView(children_.front(), false, false, nullptr);
  UpdateTooltip();
}

bool View::Contains(const View* view) const {
  for (const View* v = view; v; v = v->parent_) {
    if (v == this)
      return true;
  }
  return false;
}

View::Views::const_iterator View::FindChild(const View* view) const {
  return base::ranges::find(children_, view);
}

absl::optional<size_t> View::GetIndexOf(const View* view) const {
  const auto i = FindChild(view);
  return i == children_.cend() ? absl::nullopt
                               : absl::make_optional(static_cast<size_t>(
                                     std::distance(children_.cbegin(), i)));
}

// Size and disposition --------------------------------------------------------

void View::SetBounds(int x, int y, int width, int height) {
  SetBoundsRect(gfx::Rect(x, y, std::max(0, width), std::max(0, height)));
}

void View::SetBoundsRect(const gfx::Rect& bounds) {
  if (bounds == bounds_) {
    if (needs_layout_) {
      needs_layout_ = false;
      TRACE_EVENT1("views", "View::Layout(set_bounds)", "class",
                   GetClassName());
      Layout();
    }
    return;
  }

  bool is_size_changed = bounds_.size() != bounds.size();
  // Paint where the view is currently.
  SchedulePaintBoundsChanged(is_size_changed);

  gfx::Rect prev = bounds_;
  bounds_ = bounds;

  // Paint the new bounds.
  SchedulePaintBoundsChanged(is_size_changed);

  if (layer()) {
    if (parent_) {
      LayerOffsetData offset_data(
          parent_->CalculateOffsetToAncestorWithLayer(nullptr));
      offset_data += GetMirroredPosition().OffsetFromOrigin();
      SetLayerBounds(size(), offset_data);
    } else {
      SetLayerBounds(bounds_.size(),
                     LayerOffsetData() + bounds_.OffsetFromOrigin());
    }

    // In RTL mode, if our width has changed, our children's mirrored bounds
    // will have changed. Update the child's layer bounds, or if it is not a
    // layer, the bounds of any layers inside the child.
    if (GetMirrored() && bounds_.width() != prev.width()) {
      for (View* child : children_) {
        child->UpdateChildLayerBounds(
            LayerOffsetData(layer()->device_scale_factor(),
                            child->GetMirroredPosition().OffsetFromOrigin()));
      }
    }
  } else {
    // If our bounds have changed, then any descendant layer bounds may have
    // changed. Update them accordingly.
    UpdateChildLayerBounds(CalculateOffsetToAncestorWithLayer(nullptr));
  }

  OnBoundsChanged(prev);
  NotifyAccessibilityEvent(ax::mojom::Event::kLocationChanged, false);

  if (needs_layout_ || is_size_changed) {
    needs_layout_ = false;
    TRACE_EVENT1("views", "View::Layout(bounds_changed)", "class",
                 GetClassName());
    Layout();
  }

  if (GetNeedsNotificationWhenVisibleBoundsChange())
    OnVisibleBoundsChanged();

  // Notify interested Views that visible bounds within the root view may have
  // changed.
  if (descendants_to_notify_) {
    for (auto* i : *descendants_to_notify_) {
      i->OnVisibleBoundsChanged();
    }
  }

  for (ViewObserver& observer : observers_)
    observer.OnViewBoundsChanged(this);

  // The property effects have already been taken into account above. No need to
  // redo them here.
  if (prev.x() != bounds_.x())
    OnPropertyChanged(&bounds_ + kXChangedKey, kPropertyEffectsNone);
  if (prev.y() != bounds_.y())
    OnPropertyChanged(&bounds_ + kYChangedKey, kPropertyEffectsNone);
  if (prev.width() != bounds_.width())
    OnPropertyChanged(&bounds_ + kWidthChangedKey, kPropertyEffectsNone);
  if (prev.height() != bounds_.height())
    OnPropertyChanged(&bounds_ + kHeightChangedKey, kPropertyEffectsNone);
}

void View::SetSize(const gfx::Size& size) {
  SetBounds(x(), y(), size.width(), size.height());
}

void View::SetPosition(const gfx::Point& position) {
  SetBounds(position.x(), position.y(), width(), height());
}

void View::SetX(int x) {
  SetBounds(x, y(), width(), height());
}

void View::SetY(int y) {
  SetBounds(x(), y, width(), height());
}

gfx::Rect View::GetContentsBounds() const {
  gfx::Rect contents_bounds(GetLocalBounds());
  contents_bounds.Inset(GetInsets());
  return contents_bounds;
}

gfx::Rect View::GetLocalBounds() const {
  return gfx::Rect(size());
}

gfx::Insets View::GetInsets() const {
  return border_ ? border_->GetInsets() : gfx::Insets();
}

gfx::Rect View::GetVisibleBounds() const {
  if (!IsDrawn())
    return gfx::Rect();
  gfx::Rect vis_bounds(GetLocalBounds());
  gfx::Rect ancestor_bounds;
  const View* view = this;
  gfx::Transform transform;

  while (view != nullptr && !vis_bounds.IsEmpty()) {
    transform.PostConcat(view->GetTransform());
    gfx::Transform translation;
    translation.Translate(static_cast<float>(view->GetMirroredX()),
                          static_cast<float>(view->y()));
    transform.PostConcat(translation);

    vis_bounds = view->ConvertRectToParent(vis_bounds);
    const View* ancestor = view->parent_;
    if (ancestor != nullptr) {
      ancestor_bounds.SetRect(0, 0, ancestor->width(), ancestor->height());
      vis_bounds.Intersect(ancestor_bounds);
    } else if (!view->GetWidget()) {
      // If the view has no Widget, we're not visible. Return an empty rect.
      return gfx::Rect();
    }
    view = ancestor;
  }
  if (vis_bounds.IsEmpty())
    return vis_bounds;
  // Convert back to this views coordinate system. This mapping returns the
  // enclosing rect, which is good because partially visible pixels should
  // be considered visible.
  return transform.InverseMapRect(vis_bounds).value_or(vis_bounds);
}

gfx::Rect View::GetBoundsInScreen() const {
  gfx::Point origin;
  View::ConvertPointToScreen(this, &origin);
  return gfx::Rect(origin, size());
}

gfx::Rect View::GetAnchorBoundsInScreen() const {
  return GetBoundsInScreen();
}

gfx::Size View::GetPreferredSize() const {
  if (preferred_size_)
    return *preferred_size_;
  return CalculatePreferredSize();
}

gfx::Size View::GetPreferredSize(const SizeBounds& available_size) const {
  if (preferred_size_)
    return *preferred_size_;
  return CalculatePreferredSize(available_size);
}

int View::GetBaseline() const {
  return -1;
}

void View::SetPreferredSize(absl::optional<gfx::Size> size) {
  if (preferred_size_ == size)
    return;

  preferred_size_ = std::move(size);
  PreferredSizeChanged();
}

void View::SizeToPreferredSize() {
  SetSize(GetPreferredSize());
}

gfx::Size View::GetMinimumSize() const {
  if (HasLayoutManager())
    return GetLayoutManager()->GetMinimumSize(this);

  return GetPreferredSize();
}

gfx::Size View::GetMaximumSize() const {
  return gfx::Size();
}

int View::GetHeightForWidth(int w) const {
  if (HasLayoutManager())
    return GetLayoutManager()->GetPreferredHeightForWidth(this, w);
  return GetPreferredSize().height();
}

SizeBounds View::GetAvailableSize(const View* child) const {
  if (HasLayoutManager())
    return GetLayoutManager()->GetAvailableSize(this, child);
  return SizeBounds();
}

bool View::GetVisible() const {
  return visible_;
}

void View::SetVisible(bool visible) {
  const bool was_visible = visible_;
  if (was_visible != visible) {
    // If the View was visible, schedule paint to refresh parent.
    // TODO(beng): not sure we should be doing this if we have a layer.
    if (was_visible)
      SchedulePaint();

    visible_ = visible;
    AdvanceFocusIfNecessary();

    // Notify the parent.
    if (parent_) {
      parent_->ChildVisibilityChanged(this);
      if (!view_accessibility_ || !view_accessibility_->IsIgnored()) {
        parent_->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged,
                                          true);
      }
    }

    // This notifies all sub-views recursively.
    PropagateVisibilityNotifications(this, visible_);
    UpdateLayerVisibility();

    // Notify all other subscriptions of the change.
    OnPropertyChanged(&visible_, kPropertyEffectsPaint);

    if (was_visible)
      UpdateTooltip();
  }

  if (parent_) {
    LayoutManager* const layout_manager = parent_->GetLayoutManager();
    if (layout_manager && layout_manager->view_setting_visibility_on_ != this)
      layout_manager->ViewVisibilitySet(parent_, this, was_visible, visible);
  }
}

base::CallbackListSubscription View::AddVisibleChangedCallback(
    PropertyChangedCallback callback) {
  return AddPropertyChangedCallback(&visible_, std::move(callback));
}

bool View::IsDrawn() const {
  return visible_ && parent_ ? parent_->IsDrawn() : false;
}

bool View::GetIsDrawn() const {
  return IsDrawn();
}

bool View::GetEnabled() const {
  return enabled_;
}

void View::SetEnabled(bool enabled) {
  if (enabled_ == enabled)
    return;

  enabled_ = enabled;
  AdvanceFocusIfNecessary();
  // TODO(crbug.com/1421682): We need a specific enabled-changed event for this.
  // Some platforms have specific state-changed events and this generic event
  // does not suggest what changed.
  NotifyAccessibilityEvent(ax::mojom::Event::kStateChanged, true);
  OnPropertyChanged(&enabled_, kPropertyEffectsPaint);
}

base::CallbackListSubscription View::AddEnabledChangedCallback(
    PropertyChangedCallback callback) {
  return AddPropertyChangedCallback(&enabled_, std::move(callback));
}

View::Views View::GetChildrenInZOrder() {
  if (HasLayoutManager()) {
    const auto result = GetLayoutManager()->GetChildViewsInPaintOrder(this);
    DCHECK_EQ(children_.size(), result.size());
    return result;
  }
  return children_;
}

// Transformations -------------------------------------------------------------

gfx::Transform View::GetTransform() const {
  if (!layer())
    return gfx::Transform();

  gfx::Transform transform = layer()->transform();
  gfx::PointF scroll_offset = layer()->CurrentScrollOffset();
  // Offsets for layer-based scrolling are never negative, but the horizontal
  // scroll direction is reversed in RTL via canvas flipping.
  transform.Translate((GetMirrored() ? 1 : -1) * scroll_offset.x(),
                      -scroll_offset.y());
  return transform;
}

void View::SetClipPath(const SkPath& path) {
  clip_path_ = path;
  if (layer())
    CreateMaskLayer();
}

void View::SetTransform(const gfx::Transform& transform) {
  if (transform.IsIdentity()) {
    if (layer())
      layer()->SetTransform(transform);
    paint_to_layer_for_transform_ = false;
    CreateOrDestroyLayer();
  } else {
    paint_to_layer_for_transform_ = true;
    CreateOrDestroyLayer();
    DCHECK_NE(layer(), nullptr);
    layer()->SetTransform(transform);
    layer()->ScheduleDraw();
  }

  for (ui::Layer* layer : GetLayersInOrder(ViewLayer::kExclude)) {
    layer->SetTransform(transform);
  }
}

void View::SetPaintToLayer(ui::LayerType layer_type) {
  // Avoid re-creating the layer if unnecessary.
  if (paint_to_layer_explicitly_set_) {
    DCHECK_NE(layer(), nullptr);
    if (layer()->type() == layer_type)
      return;
  }

  DestroyLayerImpl(LayerChangeNotifyBehavior::DONT_NOTIFY);
  paint_to_layer_explicitly_set_ = true;

  // We directly call |CreateLayer()| here to pass |layer_type|. A call to
  // |CreateOrDestroyLayer()| is therefore not necessary.
  CreateLayer(layer_type);

  if (!clip_path_.isEmpty() && !mask_layer_)
    CreateMaskLayer();

  // Notify the parent chain about the layer change.
  NotifyParentsOfLayerChange();
}

void View::DestroyLayer() {
  paint_to_layer_explicitly_set_ = false;
  CreateOrDestroyLayer();
}

void View::AddLayerToRegion(ui::Layer* new_layer, LayerRegion region) {
  AddLayerToRegionImpl(
      new_layer, region == LayerRegion::kAbove ? layers_above_ : layers_below_);
}

void View::RemoveLayerFromRegions(ui::Layer* old_layer) {
  RemoveLayerFromRegionsKeepInLayerTree(old_layer);

  // Note that |old_layer| may have already been removed from its parent.
  ui::Layer* parent_layer = layer()->parent();
  if (parent_layer && parent_layer == old_layer->parent())
    parent_layer->Remove(old_layer);

  CreateOrDestroyLayer();
}

void View::RemoveLayerFromRegionsKeepInLayerTree(ui::Layer* old_layer) {
  auto remove_layer = [old_layer, this](std::vector<ui::Layer*>& layer_vector) {
    auto layer_pos = base::ranges::find(layer_vector, old_layer);
    if (layer_pos == layer_vector.end()) {
      return false;
    }
    layer_vector.erase(layer_pos);
    old_layer->RemoveObserver(this);
    return true;
  };
  const bool layer_removed =
      remove_layer(layers_below_) || remove_layer(layers_above_);
  DCHECK(layer_removed) << "Attempted to remove a layer that was never added.";
}

std::vector<ui::Layer*> View::GetLayersInOrder(ViewLayer view_layer) {
  // If not painting to a layer, there are no layers immediately related to this
  // view.
  if (!layer()) {
    // If there is no View layer, there should be no layers above or below.
    DCHECK(layers_above_.empty() && layers_below_.empty());
    return {};
  }

  std::vector<ui::Layer*> result;
  for (ui::Layer* layer_below : layers_below_) {
    result.push_back(layer_below);
  }
  if (view_layer == ViewLayer::kInclude) {
    result.push_back(layer());
  }
  for (auto* layer_above : layers_above_) {
    result.push_back(layer_above);
  }

  return result;
}

void View::LayerDestroyed(ui::Layer* layer) {
  // Only layers added with |AddLayerToRegion()| or |AddLayerAboveView()|
  // are observed so |layer| can safely be removed.
  RemoveLayerFromRegions(layer);
}

std::unique_ptr<ui::Layer> View::RecreateLayer() {
  std::unique_ptr<ui::Layer> old_layer = LayerOwner::RecreateLayer();
  Widget* widget = GetWidget();
  if (widget)
    widget->LayerTreeChanged();
  return old_layer;
}

// RTL positioning -------------------------------------------------------------

gfx::Rect View::GetMirroredBounds() const {
  gfx::Rect bounds(bounds_);
  bounds.set_x(GetMirroredX());
  return bounds;
}

gfx::Rect View::GetMirroredContentsBounds() const {
  gfx::Rect bounds(bounds_);
  bounds.Inset(GetInsets());
  bounds.set_x(GetMirroredX());
  return bounds;
}

gfx::Point View::GetMirroredPosition() const {
  return gfx::Point(GetMirroredX(), y());
}

int View::GetMirroredX() const {
  return parent_ ? parent_->GetMirroredXForRect(bounds_) : x();
}

int View::GetMirroredXForRect(const gfx::Rect& rect) const {
  return GetMirrored() ? (width() - rect.x() - rect.width()) : rect.x();
}

gfx::Rect View::GetMirroredRect(const gfx::Rect& rect) const {
  gfx::Rect mirrored_rect = rect;
  mirrored_rect.set_x(GetMirroredXForRect(rect));
  return mirrored_rect;
}

int View::GetMirroredXInView(int x) const {
  return GetMirrored() ? width() - x : x;
}

int View::GetMirroredXWithWidthInView(int x, int w) const {
  return GetMirrored() ? width() - x - w : x;
}

// Layout ----------------------------------------------------------------------

void View::Layout() {
  needs_layout_ = false;

  // If we have a layout manager, let it handle the layout for us.
  if (HasLayoutManager())
    GetLayoutManager()->Layout(this);

  // Make sure to propagate the Layout() call to any children that haven't
  // received it yet through the layout manager and need to be laid out. This
  // is needed for the case when the child requires a layout but its bounds
  // weren't changed by the layout manager. If there is no layout manager, we
  // just propagate the Layout() call down the hierarchy, so whoever receives
  // the call can take appropriate action.
  internal::ScopedChildrenLock lock(this);
  for (auto* child : children_) {
    if (child->needs_layout_ || !HasLayoutManager()) {
      TRACE_EVENT1("views", "View::LayoutChildren", "class",
                   child->GetClassName());
      child->needs_layout_ = false;
      child->Layout();
    }
  }
}

void View::InvalidateLayout() {
  // Always invalidate up. This is needed to handle the case of us already being
  // valid, but not our parent.
  needs_layout_ = true;
  if (HasLayoutManager())
    GetLayoutManager()->InvalidateLayout();

  if (parent_) {
    parent_->InvalidateLayout();
  } else {
    Widget* widget = GetWidget();
    if (widget)
      widget->ScheduleLayout();
  }
}

LayoutManager* View::GetLayoutManager() const {
  if (layout_manager_)
    return layout_manager_.get();
  if (default_fill_layout_.has_value())
    return &const_cast<View*>(this)->default_fill_layout_.value();
  return nullptr;
}

void View::SetLayoutManager(std::nullptr_t) {
  SetLayoutManagerImpl(nullptr);
}

bool View::GetUseDefaultFillLayout() const {
  if (layout_manager_)
    return false;
  return default_fill_layout_.has_value();
}

void View::SetUseDefaultFillLayout(bool value) {
  if (value == default_fill_layout_.has_value())
    return;

  if (value) {
    default_fill_layout_.emplace(DefaultFillLayout());
    // Kill the currently assigned layout manager if one had been assigned.
    layout_manager_.reset();
  } else {
    default_fill_layout_.reset();
  }
  OnPropertyChanged(&default_fill_layout_, kPropertyEffectsLayout);
}

// Attributes ------------------------------------------------------------------

const View* View::GetViewByID(int id) const {
  if (id == id_)
    return const_cast<View*>(this);

  internal::ScopedChildrenLock lock(this);
  for (auto* child : children_) {
    const View* view = child->GetViewByID(id);
    if (view)
      return view;
  }
  return nullptr;
}

View* View::GetViewByID(int id) {
  return const_cast<View*>(const_cast<const View*>(this)->GetViewByID(id));
}

void View::SetID(int id) {
  if (id == id_)
    return;

  id_ = id;

  OnPropertyChanged(&id_, kPropertyEffectsNone);
}

base::CallbackListSubscription View::AddIDChangedCallback(
    PropertyChangedCallback callback) {
  return AddPropertyChangedCallback(&id_, callback);
}

void View::SetGroup(int gid) {
  // Don't change the group id once it's set.
  DCHECK(group_ == -1 || group_ == gid);
  if (group_ != gid) {
    group_ = gid;
    OnPropertyChanged(&group_, kPropertyEffectsNone);
  }
}

int View::GetGroup() const {
  return group_;
}

base::CallbackListSubscription View::AddGroupChangedCallback(
    PropertyChangedCallback callback) {
  return AddPropertyChangedCallback(&group_, callback);
}

bool View::IsGroupFocusTraversable() const {
  return true;
}

void View::GetViewsInGroup(int group, Views* views) {
  if (group_ == group)
    views->push_back(this);

  internal::ScopedChildrenLock lock(this);
  for (auto* child : children_)
    child->GetViewsInGroup(group, views);
}

View* View::GetSelectedViewForGroup(int group) {
  Views views;
  GetWidget()->GetRootView()->GetViewsInGroup(group, &views);
  return views.empty() ? nullptr : views[0];
}

// Coordinate conversion -------------------------------------------------------

// static
void View::ConvertPointToTarget(const View* source,
                                const View* target,
                                gfx::Point* point) {
  DCHECK(source);
  DCHECK(target);
  if (source == target)
    return;

  const View* root = GetHierarchyRoot(target);
#if BUILDFLAG(IS_MAC)
  // If the root views don't match make sure we are in the same widget tree.
  // Full screen in macOS creates a child widget that hosts top chrome.
  // TODO(bur): Remove this check when top chrome can be composited into its own
  // NSView without the need for a new widget.
  if (GetHierarchyRoot(source) != root) {
    const Widget* source_top_level_widget =
        source->GetWidget()->GetTopLevelWidget();
    const Widget* target_top_level_widget =
        target->GetWidget()->GetTopLevelWidget();
    CHECK_EQ(source_top_level_widget, target_top_level_widget);
  }
#else  // IS_MAC
  CHECK_EQ(GetHierarchyRoot(source), root);
#endif

  if (source != root)
    source->ConvertPointForAncestor(root, point);

  if (target != root)
    target->ConvertPointFromAncestor(root, point);
}

// static
gfx::Point View::ConvertPointToTarget(const View* source,
                                      const View* target,
                                      const gfx::Point& point) {
  gfx::Point local_point = point;
  ConvertPointToTarget(source, target, &local_point);
  return local_point;
}

// static
void View::ConvertRectToTarget(const View* source,
                               const View* target,
                               gfx::RectF* rect) {
  DCHECK(source);
  DCHECK(target);
  if (source == target)
    return;

  const View* root = GetHierarchyRoot(target);
  CHECK_EQ(GetHierarchyRoot(source), root);

  if (source != root)
    source->ConvertRectForAncestor(root, rect);

  if (target != root)
    target->ConvertRectFromAncestor(root, rect);
}

// static
gfx::RectF View::ConvertRectToTarget(const View* source,
                                     const View* target,
                                     const gfx::RectF& rect) {
  gfx::RectF local_rect = rect;
  ConvertRectToTarget(source, target, &local_rect);
  return local_rect;
}

// static
gfx::Rect View::ConvertRectToTarget(const View* source,
                                    const View* target,
                                    gfx::Rect& rect) {
  constexpr float kDefaultAllowedConversionError = 0.00001f;
  return gfx::ToEnclosedRectIgnoringError(
      ConvertRectToTarget(source, target, gfx::RectF(rect)),
      kDefaultAllowedConversionError);
}

// static
void View::ConvertPointToWidget(const View* src, gfx::Point* p) {
  DCHECK(src);
  DCHECK(p);

  src->ConvertPointForAncestor(nullptr, p);
}

// static
void View::ConvertPointFromWidget(const View* dest, gfx::Point* p) {
  DCHECK(dest);
  DCHECK(p);

  dest->ConvertPointFromAncestor(nullptr, p);
}

// static
void View::ConvertPointToScreen(const View* src, gfx::Point* p) {
  DCHECK(src);
  DCHECK(p);

  // If the view is not connected to a tree, there's nothing we can do.
  const Widget* widget = src->GetWidget();
  if (widget) {
    ConvertPointToWidget(src, p);
    *p += widget->GetClientAreaBoundsInScreen().OffsetFromOrigin();
  }
}

// static
gfx::Point View::ConvertPointToScreen(const View* src, const gfx::Point& p) {
  gfx::Point screen_pt = p;
  ConvertPointToScreen(src, &screen_pt);
  return screen_pt;
}

// static
void View::ConvertPointFromScreen(const View* dst, gfx::Point* p) {
  DCHECK(dst);
  DCHECK(p);

  const views::Widget* widget = dst->GetWidget();
  if (!widget)
    return;
  *p -= widget->GetClientAreaBoundsInScreen().OffsetFromOrigin();
  ConvertPointFromWidget(dst, p);
}

// static
gfx::Point View::ConvertPointFromScreen(const View* src, const gfx::Point& p) {
  gfx::Point local_pt = p;
  ConvertPointFromScreen(src, &local_pt);
  return local_pt;
}

// static
void View::ConvertRectToScreen(const View* src, gfx::Rect* rect) {
  DCHECK(src);
  DCHECK(rect);

  gfx::Point new_origin = rect->origin();
  views::View::ConvertPointToScreen(src, &new_origin);
  rect->set_origin(new_origin);
}

gfx::Rect View::ConvertRectToParent(const gfx::Rect& rect) const {
  // This mapping returns the enclosing rect, which is good because pixels that
  // partially occupy in the parent should be included.
  gfx::Rect x_rect = GetTransform().MapRect(rect);
  x_rect.Offset(GetMirroredPosition().OffsetFromOrigin());
  return x_rect;
}

gfx::Rect View::ConvertRectToWidget(const gfx::Rect& rect) const {
  gfx::Rect x_rect = rect;
  for (const View* v = this; v; v = v->parent_)
    x_rect = v->ConvertRectToParent(x_rect);
  return x_rect;
}

// Painting --------------------------------------------------------------------

void View::SchedulePaint() {
  SchedulePaintInRect(GetLocalBounds());
}

void View::SchedulePaintInRect(const gfx::Rect& rect) {
  needs_paint_ = true;
  SchedulePaintInRectImpl(rect);
}

void View::Paint(const PaintInfo& parent_paint_info) {
  CHECK_EQ(life_cycle_state_, LifeCycleState::kAlive);

  if (!ShouldPaint())
    return;

  if (!has_run_accessibility_paint_checks_) {
    RunAccessibilityPaintChecks(this);
    has_run_accessibility_paint_checks_ = true;
  }

  const gfx::Rect& parent_bounds =
      !parent() ? GetMirroredBounds() : parent()->GetMirroredBounds();

  PaintInfo paint_info = PaintInfo::CreateChildPaintInfo(
      parent_paint_info, GetMirroredBounds(), parent_bounds.size(),
      GetPaintScaleType(), !!layer(), needs_paint_);

  needs_paint_ = false;

  const ui::PaintContext& context = paint_info.context();
  bool is_invalidated = true;
  if (paint_info.context().CanCheckInvalid() ||
      base::FeatureList::IsEnabled(features::kEnableViewPaintOptimization)) {
    // For View paint optimization, do not default to repainting every View in
    // the View hierarchy if the invalidation rect is empty. Repainting does not
    // depend on the invalidation rect for View paint optimization.
#if DCHECK_IS_ON()
    if (!context.is_pixel_canvas()) {
      gfx::Vector2d offset;
      context.Visited(this);
      View* view = this;
      while (view->parent() && !view->layer()) {
        DCHECK(view->GetTransform().IsIdentity());
        offset += view->GetMirroredPosition().OffsetFromOrigin();
        view = view->parent();
      }
      // The offset in the PaintContext should be the offset up to the paint
      // root, which we compute and verify here.
      DCHECK_EQ(context.PaintOffset().x(), offset.x());
      DCHECK_EQ(context.PaintOffset().y(), offset.y());
      // The above loop will stop when |view| is the paint root, which should be
      // the root of the current paint walk, as verified by storing the root in
      // the PaintContext.
      DCHECK_EQ(context.RootVisited(), view);
    }
#endif

    // If the View wasn't invalidated, don't waste time painting it, the output
    // would be culled.
    is_invalidated = paint_info.ShouldPaint();
  }

  TRACE_EVENT1("views", "View::Paint", "class", GetClassName());

  // If the view is backed by a layer, it should paint with itself as the origin
  // rather than relative to its parent.
  // TODO(danakj): Rework clip and transform recorder usage here to use
  // std::optional once we can do so.
  ui::ClipRecorder clip_recorder(parent_paint_info.context());
  if (!layer()) {
    // Set the clip rect to the bounds of this View, or |clip_path_| if it's
    // been set. Note that the X (or left) position we pass to ClipRect takes
    // into consideration whether or not the View uses a right-to-left layout so
    // that we paint the View in its mirrored position if need be.
    if (clip_path_.isEmpty()) {
      clip_recorder.ClipRect(gfx::Rect(paint_info.paint_recording_size()) +
                             paint_info.offset_from_parent());
    } else {
      SkPath clip_path_in_parent = clip_path_;

      // Transform |clip_path_| from local space to parent recording space.
      gfx::Transform to_parent_recording_space;

      to_parent_recording_space.Translate(paint_info.offset_from_parent());
      to_parent_recording_space.Scale(
          SkFloatToScalar(paint_info.paint_recording_scale_x()),
          SkFloatToScalar(paint_info.paint_recording_scale_y()));

      clip_path_in_parent.transform(
          gfx::TransformToFlattenedSkMatrix(to_parent_recording_space));
      clip_recorder.ClipPathWithAntiAliasing(clip_path_in_parent);
    }
  }

  ui::TransformRecorder transform_recorder(context);
  SetUpTransformRecorderForPainting(paint_info.offset_from_parent(),
                                    &transform_recorder);

  // Note that the cache is not aware of the offset of the view
  // relative to the parent since painting is always done relative to
  // the top left of the individual view.
  if (is_invalidated ||
      !paint_cache_.UseCache(context, paint_info.paint_recording_size())) {
    ui::PaintRecorder recorder(context, paint_info.paint_recording_size(),
                               paint_info.paint_recording_scale_x(),
                               paint_info.paint_recording_scale_y(),
                               &paint_cache_);
    gfx::Canvas* canvas = recorder.canvas();
    gfx::ScopedCanvas scoped_canvas(canvas);
    if (flip_canvas_on_paint_for_rtl_ui_)
      scoped_canvas.FlipIfRTL(width());

    // Delegate painting the contents of the View to the virtual OnPaint method.
    OnPaint(canvas);
  }

  CHECK_EQ(life_cycle_state_, LifeCycleState::kAlive);

  // View::Paint() recursion over the subtree.
  PaintChildren(paint_info);
}

void View::SetBackground(std::unique_ptr<Background> b) {
  background_ = std::move(b);
  if (background_ && GetWidget())
    background_->OnViewThemeChanged(this);
  SchedulePaint();
}

Background* View::GetBackground() const {
  return background_.get();
}

void View::SetBorder(std::unique_ptr<Border> b) {
  const gfx::Rect old_contents_bounds = GetContentsBounds();
  border_ = std::move(b);
  if (border_ && GetWidget())
    border_->OnViewThemeChanged(this);

  // Conceptually, this should be PreferredSizeChanged(), but for some view
  // hierarchies that triggers synchronous add/remove operations that are unsafe
  // in some contexts where SetBorder is called.
  //
  // InvalidateLayout() still triggers a re-layout of the view, which should
  // include re-querying its preferred size so in practice this is both safe and
  // has the intended effect.
  if (old_contents_bounds != GetContentsBounds())
    InvalidateLayout();

  SchedulePaint();
}

Border* View::GetBorder() const {
  return border_.get();
}

const ui::ThemeProvider* View::GetThemeProvider() const {
  const auto* widget = GetWidget();
  return widget ? widget->GetThemeProvider() : nullptr;
}

const LayoutProvider* View::GetLayoutProvider() const {
  if (!GetWidget())
    return nullptr;
  // TODO(pbos): Ask the widget for a layout provider.
  return LayoutProvider::Get();
}

const ui::ColorProvider* View::GetColorProvider() const {
  const auto* widget = GetWidget();
  return widget ? widget->GetColorProvider() : nullptr;
}

const ui::NativeTheme* View::GetNativeTheme() const {
  if (native_theme_)
    return native_theme_;

  if (parent())
    return parent()->GetNativeTheme();

  const Widget* widget = GetWidget();
  if (widget)
    return widget->GetNativeTheme();

  static bool has_crashed_reported = false;
  // Crash on debug builds and dump without crashing on release builds to ensure
  // we catch fallthrough to the global NativeTheme instance on all Chromium
  // builds (crbug.com/1056756).
  if (!has_crashed_reported) {
    DCHECK(false);
    base::debug::DumpWithoutCrashing();
    has_crashed_reported = true;
  }

  return ui::NativeTheme::GetInstanceForNativeUi();
}

void View::SetNativeThemeForTesting(ui::NativeTheme* theme) {
  ui::NativeTheme* original_native_theme = GetNativeTheme();
  native_theme_ = theme;
  if (native_theme_ != original_native_theme)
    PropagateThemeChanged();
}

// RTL painting ----------------------------------------------------------------

bool View::GetFlipCanvasOnPaintForRTLUI() const {
  return flip_canvas_on_paint_for_rtl_ui_;
}

void View::SetFlipCanvasOnPaintForRTLUI(bool enable) {
  if (enable == flip_canvas_on_paint_for_rtl_ui_)
    return;
  flip_canvas_on_paint_for_rtl_ui_ = enable;

  OnPropertyChanged(&flip_canvas_on_paint_for_rtl_ui_, kPropertyEffectsPaint);
}

base::CallbackListSubscription
View::AddFlipCanvasOnPaintForRTLUIChangedCallback(
    PropertyChangedCallback callback) {
  return AddPropertyChangedCallback(&flip_canvas_on_paint_for_rtl_ui_,
                                    std::move(callback));
}

void View::SetMirrored(bool is_mirrored) {
  if (is_mirrored_ && is_mirrored_.value() == is_mirrored)
    return;
  is_mirrored_ = is_mirrored;

  OnPropertyChanged(&is_mirrored_, kPropertyEffectsPaint);
}

bool View::GetMirrored() const {
  return is_mirrored_.value_or(base::i18n::IsRTL());
}

// Input -----------------------------------------------------------------------

View* View::GetEventHandlerForPoint(const gfx::Point& point) {
  return GetEventHandlerForRect(gfx::Rect(point, gfx::Size(1, 1)));
}

View* View::GetEventHandlerForRect(const gfx::Rect& rect) {
  return GetEffectiveViewTargeter()->TargetForRect(this, rect);
}

bool View::GetCanProcessEventsWithinSubtree() const {
  return can_process_events_within_subtree_;
}

void View::SetCanProcessEventsWithinSubtree(bool can_process) {
  if (can_process_events_within_subtree_ == can_process)
    return;
  can_process_events_within_subtree_ = can_process;
  OnPropertyChanged(&can_process_events_within_subtree_, kPropertyEffectsNone);
}

View* View::GetTooltipHandlerForPoint(const gfx::Point& point) {
  // TODO(tdanderson): Move this implementation into ViewTargetDelegate.
  if (!HitTestPoint(point) || !GetCanProcessEventsWithinSubtree())
    return nullptr;

  // Walk the child Views recursively looking for the View that most
  // tightly encloses the specified point.
  View::Views children = GetChildrenInZOrder();
  DCHECK_EQ(children_.size(), children.size());
  for (auto* child : base::Reversed(children)) {
    if (!child->GetVisible())
      continue;

    gfx::Point point_in_child_coords(point);
    ConvertPointToTarget(this, child, &point_in_child_coords);
    View* handler = child->GetTooltipHandlerForPoint(point_in_child_coords);
    if (handler)
      return handler;
  }
  return this;
}

ui::Cursor View::GetCursor(const ui::MouseEvent& event) {
  return ui::Cursor();
}

bool View::HitTestPoint(const gfx::Point& point) const {
  return HitTestRect(gfx::Rect(point, gfx::Size(1, 1)));
}

bool View::HitTestRect(const gfx::Rect& rect) const {
  return GetEffectiveViewTargeter()->DoesIntersectRect(this, rect);
}

bool View::IsMouseHovered() const {
  // If we haven't yet been placed in an onscreen view hierarchy, we can't be
  // hovered.
  if (!GetWidget())
    return false;

  // If mouse events are disabled, then the mouse cursor is invisible and
  // is therefore not hovering over this button.
  if (!GetWidget()->IsMouseEventsEnabled())
    return false;

  gfx::Point cursor_pos(display::Screen::GetScreen()->GetCursorScreenPoint());
  ConvertPointFromScreen(this, &cursor_pos);
  return HitTestPoint(cursor_pos);
}

bool View::OnMousePressed(const ui::MouseEvent& event) {
  return false;
}

bool View::OnMouseDragged(const ui::MouseEvent& event) {
  return false;
}

void View::OnMouseReleased(const ui::MouseEvent& event) {}

void View::OnMouseCaptureLost() {}

void View::OnMouseMoved(const ui::MouseEvent& event) {}

void View::OnMouseEntered(const ui::MouseEvent& event) {}

void View::OnMouseExited(const ui::MouseEvent& event) {}

void View::SetMouseAndGestureHandler(View* new_handler) {
  // |new_handler| may be nullptr.
  if (parent_)
    parent_->SetMouseAndGestureHandler(new_handler);
}

void View::SetMouseHandler(View* new_handler) {
  if (parent_)
    parent_->SetMouseHandler(new_handler);
}

bool View::OnKeyPressed(const ui::KeyEvent& event) {
  return false;
}

bool View::OnKeyReleased(const ui::KeyEvent& event) {
  return false;
}

bool View::OnMouseWheel(const ui::MouseWheelEvent& event) {
  return false;
}

void View::OnKeyEvent(ui::KeyEvent* event) {
  bool consumed = (event->type() == ui::ET_KEY_PRESSED) ? OnKeyPressed(*event)
                                                        : OnKeyReleased(*event);
  if (consumed)
    event->StopPropagation();
}

void View::OnMouseEvent(ui::MouseEvent* event) {
  switch (event->type()) {
    case ui::ET_MOUSE_PRESSED:
      if (ProcessMousePressed(*event))
        event->SetHandled();
      return;

    case ui::ET_MOUSE_MOVED:
      if ((event->flags() &
           (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_RIGHT_MOUSE_BUTTON |
            ui::EF_MIDDLE_MOUSE_BUTTON)) == 0) {
        OnMouseMoved(*event);
        return;
      }
      [[fallthrough]];
    case ui::ET_MOUSE_DRAGGED:
      ProcessMouseDragged(event);
      return;

    case ui::ET_MOUSE_RELEASED:
      ProcessMouseReleased(*event);
      return;

    case ui::ET_MOUSEWHEEL:
      if (OnMouseWheel(*event->AsMouseWheelEvent()))
        event->SetHandled();
      break;

    case ui::ET_MOUSE_ENTERED:
      if (event->flags() & ui::EF_TOUCH_ACCESSIBILITY)
        NotifyAccessibilityEvent(ax::mojom::Event::kHover, true);
      OnMouseEntered(*event);
      break;

    case ui::ET_MOUSE_EXITED:
      OnMouseExited(*event);
      break;

    default:
      return;
  }
}

void View::OnScrollEvent(ui::ScrollEvent* event) {}

void View::OnTouchEvent(ui::TouchEvent* event) {
  NOTREACHED_NORETURN() << "Views should not receive touch events.";
}

void View::OnGestureEvent(ui::GestureEvent* event) {}

base::StringPiece View::GetLogContext() const {
  return GetClassName();
}

void View::SetNotifyEnterExitOnChild(bool notify) {
  notify_enter_exit_on_child_ = notify;
}

bool View::GetNotifyEnterExitOnChild() const {
  return notify_enter_exit_on_child_;
}

const ui::InputMethod* View::GetInputMethod() const {
  Widget* widget = const_cast<Widget*>(GetWidget());
  return widget ? const_cast<const ui::InputMethod*>(widget->GetInputMethod())
                : nullptr;
}

std::unique_ptr<ViewTargeter> View::SetEventTargeter(
    std::unique_ptr<ViewTargeter> targeter) {
  std::unique_ptr<ViewTargeter> old_targeter = std::move(targeter_);
  targeter_ = std::move(targeter);
  return old_targeter;
}

ViewTargeter* View::GetEffectiveViewTargeter() const {
  DCHECK(GetWidget());
  ViewTargeter* view_targeter = targeter();
  if (!view_targeter)
    view_targeter = GetWidget()->GetRootView()->targeter();
  CHECK(view_targeter);
  return view_targeter;
}

WordLookupClient* View::GetWordLookupClient() {
  return nullptr;
}

bool View::CanAcceptEvent(const ui::Event& event) {
  return IsDrawn();
}

ui::EventTarget* View::GetParentTarget() {
  return parent_;
}

std::unique_ptr<ui::EventTargetIterator> View::GetChildIterator() const {
  return std::make_unique<ui::EventTargetIteratorPtrImpl<View>>(children_);
}

ui::EventTargeter* View::GetEventTargeter() {
  return targeter_.get();
}

void View::ConvertEventToTarget(const ui::EventTarget* target,
                                ui::LocatedEvent* event) const {
  event->ConvertLocationToTarget(this, static_cast<const View*>(target));
}

gfx::PointF View::GetScreenLocationF(const ui::LocatedEvent& event) const {
  DCHECK_EQ(this, event.target());
  gfx::Point screen_location(event.location());
  ConvertPointToScreen(this, &screen_location);
  return gfx::PointF(screen_location);
}

// Accelerators ----------------------------------------------------------------

void View::AddAccelerator(const ui::Accelerator& accelerator) {
  if (!accelerators_)
    accelerators_ = std::make_unique<std::vector<ui::Accelerator>>();

  if (!base::Contains(*accelerators_, accelerator))
    accelerators_->push_back(accelerator);

  RegisterPendingAccelerators();
}

void View::RemoveAccelerator(const ui::Accelerator& accelerator) {
  CHECK(accelerators_) << "Removing non-existent accelerator";

  auto i(base::ranges::find(*accelerators_, accelerator));
  CHECK(i != accelerators_->end()) << "Removing non-existent accelerator";

  auto index = static_cast<size_t>(i - accelerators_->begin());
  accelerators_->erase(i);
  if (index >= registered_accelerator_count_) {
    // The accelerator is not registered to FocusManager.
    return;
  }
  --registered_accelerator_count_;

  // Providing we are attached to a Widget and registered with a focus manager,
  // we should de-register from that focus manager now.
  if (GetWidget() && accelerator_focus_manager_)
    accelerator_focus_manager_->UnregisterAccelerator(accelerator, this);
}

void View::ResetAccelerators() {
  if (accelerators_)
    UnregisterAccelerators(false);
}

bool View::AcceleratorPressed(const ui::Accelerator& accelerator) {
  return false;
}

bool View::CanHandleAccelerators() const {
  const Widget* widget = GetWidget();
  if (!GetEnabled() || !IsDrawn() || !widget || !widget->IsVisible())
    return false;
#if BUILDFLAG(ENABLE_DESKTOP_AURA)
  // Non-ChromeOS aura windows have an associated FocusManagerEventHandler which
  // adds currently focused view as an event PreTarget (see
  // DesktopNativeWidgetAura::InitNativeWidget). However, the focused view isn't
  // always the right view to handle accelerators: It should only handle them
  // when active. Only top level widgets can be active, so for child widgets
  // check if they are focused instead. ChromeOS also behaves different than
  // Linux when an extension popup is about to handle the accelerator.
  bool child = widget && widget->GetTopLevelWidget() != widget;
  bool focus_in_child = widget && widget->GetRootView()->Contains(
                                      GetFocusManager()->GetFocusedView());
  if ((child && !focus_in_child) || (!child && !widget->IsActive()))
    return false;
#endif
  return true;
}

// Focus -----------------------------------------------------------------------

bool View::HasFocus() const {
  const FocusManager* focus_manager = GetFocusManager();
  return focus_manager && (focus_manager->GetFocusedView() == this);
}

View* View::GetNextFocusableView() {
  return next_focusable_view_;
}

const View* View::GetNextFocusableView() const {
  return next_focusable_view_;
}

View* View::GetPreviousFocusableView() {
  return previous_focusable_view_;
}

void View::RemoveFromFocusList() {
  View* const old_prev = previous_focusable_view_;
  View* const old_next = next_focusable_view_;

  previous_focusable_view_ = nullptr;
  next_focusable_view_ = nullptr;

  if (old_prev)
    old_prev->next_focusable_view_ = old_next;

  if (old_next)
    old_next->previous_focusable_view_ = old_prev;
}

void View::InsertBeforeInFocusList(View* view) {
  DCHECK(view);
  DCHECK_EQ(parent_, view->parent_);

  if (view == next_focusable_view_)
    return;

  RemoveFromFocusList();

  next_focusable_view_ = view;
  previous_focusable_view_ = view->previous_focusable_view_;

  if (previous_focusable_view_)
    previous_focusable_view_->next_focusable_view_ = this;

  next_focusable_view_->previous_focusable_view_ = this;
}

void View::InsertAfterInFocusList(View* view) {
  DCHECK(view);
  DCHECK_EQ(parent_, view->parent_);

  if (view == previous_focusable_view_)
    return;

  RemoveFromFocusList();

  if (view->next_focusable_view_) {
    InsertBeforeInFocusList(view->next_focusable_view_);
    return;
  }

  view->next_focusable_view_ = this;
  previous_focusable_view_ = view;
}

View::Views View::GetChildrenFocusList() {
  View* starting_focus_view = nullptr;

  Views children_views = children();
  for (View* child : children_views) {
    if (child->GetPreviousFocusableView() == nullptr) {
      starting_focus_view = child;
      break;
    }
  }

  if (starting_focus_view == nullptr)
    return {};

  Views result;

  // Tracks the views traversed so far. Used to check for cycles.
  base::flat_set<View*> seen_views;

  View* cur = starting_focus_view;
  while (cur != nullptr) {
    // Views are not supposed to have focus cycles, but just in case, fail
    // gracefully to avoid a crash.
    if (seen_views.contains(cur)) {
      LOG(ERROR) << "View focus cycle detected.";
      return {};
    }

    seen_views.insert(cur);
    result.push_back(cur);

    cur = cur->GetNextFocusableView();
  }

  return result;
}

View::FocusBehavior View::GetFocusBehavior() const {
  return focus_behavior_;
}

void View::SetFocusBehavior(FocusBehavior focus_behavior) {
  if (GetFocusBehavior() == focus_behavior)
    return;

  focus_behavior_ = focus_behavior;
  AdvanceFocusIfNecessary();

  OnPropertyChanged(&focus_behavior_, kPropertyEffectsNone);
}

bool View::IsFocusable() const {
  return GetFocusBehavior() == FocusBehavior::ALWAYS && GetEnabled() &&
         IsDrawn();
}

bool View::IsAccessibilityFocusable() const {
  return GetViewAccessibility().IsAccessibilityFocusable();
}

FocusManager* View::GetFocusManager() {
  Widget* widget = GetWidget();
  return widget ? widget->GetFocusManager() : nullptr;
}

const FocusManager* View::GetFocusManager() const {
  const Widget* widget = GetWidget();
  return widget ? widget->GetFocusManager() : nullptr;
}

void View::RequestFocus() {
  FocusManager* focus_manager = GetFocusManager();
  if (focus_manager) {
    bool focusable = focus_manager->keyboard_accessible()
                         ? IsAccessibilityFocusable()
                         : IsFocusable();
    if (focusable)
      focus_manager->SetFocusedView(this);
  }
}

bool View::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
  return false;
}

FocusTraversable* View::GetFocusTraversable() {
  return nullptr;
}

FocusTraversable* View::GetPaneFocusTraversable() {
  return nullptr;
}

// Tooltips --------------------------------------------------------------------

std::u16string View::GetTooltipText(const gfx::Point& p) const {
  return std::u16string();
}

// Context menus ---------------------------------------------------------------

void View::ShowContextMenu(const gfx::Point& p,
                           ui::MenuSourceType source_type) {
  if (!context_menu_controller_)
    return;

  context_menu_controller_->ShowContextMenuForView(this, p, source_type);
}

gfx::Point View::GetKeyboardContextMenuLocation() {
  gfx::Rect vis_bounds = GetVisibleBounds();
  gfx::Point screen_point(vis_bounds.x() + vis_bounds.width() / 2,
                          vis_bounds.y() + vis_bounds.height() / 2);
  ConvertPointToScreen(this, &screen_point);
  return screen_point;
}

// Drag and drop ---------------------------------------------------------------

bool View::GetDropFormats(int* formats,
                          std::set<ui::ClipboardFormatType>* format_types) {
  return false;
}

bool View::AreDropTypesRequired() {
  return false;
}

bool View::CanDrop(const OSExchangeData& data) {
  // TODO(sky): when I finish up migration, this should default to true.
  return false;
}

void View::OnDragEntered(const ui::DropTargetEvent& event) {}

int View::OnDragUpdated(const ui::DropTargetEvent& event) {
  return ui::DragDropTypes::DRAG_NONE;
}

void View::OnDragExited() {}

void View::OnDragDone() {}

View::DropCallback View::GetDropCallback(const ui::DropTargetEvent& event) {
  return base::NullCallback();
}

// static
bool View::ExceededDragThreshold(const gfx::Vector2d& delta) {
  return (abs(delta.x()) > GetHorizontalDragThreshold() ||
          abs(delta.y()) > GetVerticalDragThreshold());
}

// Accessibility----------------------------------------------------------------

ViewAccessibility& View::GetViewAccessibility() const {
  if (!view_accessibility_)
    view_accessibility_ = ViewAccessibility::Create(const_cast<View*>(this));
  return *view_accessibility_;
}

void View::GetAccessibleNodeData(ui::AXNodeData* node_data) {
  // `ViewAccessibility::GetAccessibleNodeData` populates the id and classname
  // values prior to asking the View for its data. We don't want to stomp on
  // those values.
  ax_node_data_->id = node_data->id;
  ax_node_data_->AddStringAttribute(
      ax::mojom::StringAttribute::kClassName,
      node_data->GetStringAttribute(ax::mojom::StringAttribute::kClassName));

  // Copy everything set by the property setters.
  *node_data = *ax_node_data_;
}

void View::SetAccessibilityProperties(
    absl::optional<ax::mojom::Role> role,
    absl::optional<std::u16string> name,
    absl::optional<std::u16string> description,
    absl::optional<std::u16string> role_description,
    absl::optional<ax::mojom::NameFrom> name_from,
    absl::optional<ax::mojom::DescriptionFrom> description_from) {
  base::AutoReset<bool> initializing(&pause_accessibility_events_, true);
  if (role.has_value()) {
    if (role_description.has_value()) {
      SetAccessibleRole(role.value(), role_description.value());
    } else {
      SetAccessibleRole(role.value());
    }
  }

  // Defining the NameFrom value without specifying the name doesn't make much
  // sense. The only exception might be if the NameFrom is setting the name to
  // explicitly empty. In order to prevent surprising/confusing behavior, we
  // only use the NameFrom value if we have an explicit name. As a result, any
  // caller setting the name to explicitly empty must set the name to an empty
  // string.
  if (name.has_value()) {
    if (name_from.has_value()) {
      SetAccessibleName(name.value(), name_from.value());
    } else {
      SetAccessibleName(name.value());
    }
  }

  // See the comment above regarding the NameFrom value.
  if (description.has_value()) {
    if (description_from.has_value()) {
      SetAccessibleDescription(description.value(), description_from.value());
    } else {
      SetAccessibleDescription(description.value());
    }
  }
}

void View::SetAccessibleName(const std::u16string& name) {
  SetAccessibleName(
      name, static_cast<ax::mojom::NameFrom>(ax_node_data_->GetIntAttribute(
                ax::mojom::IntAttribute::kNameFrom)));
}

void View::SetAccessibleName(std::u16string name,
                             ax::mojom::NameFrom name_from) {
  // Allow subclasses to adjust the name.
  AdjustAccessibleName(name, name_from);

  // Ensure we have a current `name_from` value. For instance, the name might
  // still be an empty string, but a view is now indicating that this is by
  // design by setting `NameFrom::kAttributeExplicitlyEmpty`.
  ax_node_data_->SetNameFrom(name_from);

  if (name == accessible_name_) {
    return;
  }

  if (name.empty()) {
    ax_node_data_->RemoveStringAttribute(ax::mojom::StringAttribute::kName);
  } else if (ax_node_data_->role != ax::mojom::Role::kUnknown &&
             ax_node_data_->role != ax::mojom::Role::kNone) {
    // TODO(accessibility): This is to temporarily work around the DCHECK
    // in `AXNodeData` that wants to have a role to calculate a name-from.
    // If we don't have a role yet, don't add it to the data until we do.
    // See `SetAccessibleRole` where we check for and handle this condition.
    // Also note that the `SetAccessibilityProperties` function allows view
    // authors to set the role and name at once, if all views use it, we can
    // remove this workaround.
    ax_node_data_->SetName(name);
  }

  accessible_name_ = name;
  OnPropertyChanged(&accessible_name_, kPropertyEffectsNone);
  OnAccessibleNameChanged(name);
  NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true);
}

void View::SetAccessibleName(View* naming_view) {
  DCHECK(naming_view);
  DCHECK_NE(this, naming_view);

  const std::u16string& name = naming_view->GetAccessibleName();
  DCHECK(!name.empty());

  SetAccessibleName(name, ax::mojom::NameFrom::kRelatedElement);
  ax_node_data_->AddIntListAttribute(
      ax::mojom::IntListAttribute::kLabelledbyIds,
      {naming_view->GetViewAccessibility().GetUniqueId().Get()});
}

const std::u16string& View::GetAccessibleName() const {
  return accessible_name_;
}

void View::SetAccessibleRole(const ax::mojom::Role role) {
  if (role == accessible_role_) {
    return;
  }

  ax_node_data_->role = role;
  if (role != ax::mojom::Role::kUnknown && role != ax::mojom::Role::kNone) {
    if (ax_node_data_->GetStringAttribute(ax::mojom::StringAttribute::kName)
            .empty() &&
        !accessible_name_.empty()) {
      // TODO(accessibility): This is to temporarily work around the DCHECK
      // that wants to have a role to calculate a name-from. If we have a
      // name in our properties but not in our `AXNodeData`, the name was
      // set prior to the role. Now that we have a valid role, we can set
      // the name. See `SetAccessibleName` for where we delayed setting it.
      ax_node_data_->SetName(accessible_name_);
    }
  }

  accessible_role_ = role;
  OnPropertyChanged(&accessible_role_, kPropertyEffectsNone);
}

void View::SetAccessibleRole(const ax::mojom::Role role,
                             const std::u16string& role_description) {
  if (!role_description.empty()) {
    ax_node_data_->AddStringAttribute(
        ax::mojom::StringAttribute::kRoleDescription,
        base::UTF16ToUTF8(role_description));
  } else {
    ax_node_data_->RemoveStringAttribute(
        ax::mojom::StringAttribute::kRoleDescription);
  }

  SetAccessibleRole(role);
}

ax::mojom::Role View::GetAccessibleRole() const {
  return accessible_role_;
}

void View::SetAccessibleDescription(const std::u16string& description) {
  if (description.empty()) {
    ax_node_data_->RemoveStringAttribute(
        ax::mojom::StringAttribute::kDescription);
    ax_node_data_->RemoveIntAttribute(
        ax::mojom::IntAttribute::kDescriptionFrom);
    accessible_description_ = description;
    return;
  }

  SetAccessibleDescription(description,
                           ax::mojom::DescriptionFrom::kAriaDescription);
}

void View::SetAccessibleDescription(
    const std::u16string& description,
    ax::mojom::DescriptionFrom description_from) {
  // Ensure we have a current `description_from` value. For instance, the
  // description might still be an empty string, but a view is now indicating
  // that this is by design by setting
  // `DescriptionFrom::kAttributeExplicitlyEmpty`.
  ax_node_data_->SetDescriptionFrom(description_from);

  if (description == accessible_description_) {
    return;
  }

  // `AXNodeData::SetDescription` DCHECKs that the description is not empty
  // unless it has `DescriptionFrom::kAttributeExplicitlyEmpty`.
  if (!description.empty() ||
      ax_node_data_->GetDescriptionFrom() ==
          ax::mojom::DescriptionFrom::kAttributeExplicitlyEmpty) {
    ax_node_data_->SetDescription(description);
  }

  accessible_description_ = description;
  OnPropertyChanged(&accessible_description_, kPropertyEffectsNone);
}

void View::SetAccessibleDescription(View* describing_view) {
  DCHECK(describing_view);
  DCHECK_NE(this, describing_view);

  const std::u16string& name = describing_view->GetAccessibleName();
  DCHECK(!name.empty());

  SetAccessibleDescription(name, ax::mojom::DescriptionFrom::kRelatedElement);
  ax_node_data_->AddIntListAttribute(
      ax::mojom::IntListAttribute::kDescribedbyIds,
      {describing_view->GetViewAccessibility().GetUniqueId().Get()});
}

const std::u16string& View::GetAccessibleDescription() const {
  return accessible_description_;
}

bool View::HandleAccessibleAction(const ui::AXActionData& action_data) {
  switch (action_data.action) {
    case ax::mojom::Action::kBlur:
      if (HasFocus()) {
        GetFocusManager()->ClearFocus();
        return true;
      }
      break;
    case ax::mojom::Action::kDoDefault: {
      const gfx::Point center = GetLocalBounds().CenterPoint();
      ui::MouseEvent press(ui::ET_MOUSE_PRESSED, center, center,
                           ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                           ui::EF_LEFT_MOUSE_BUTTON);
      OnEvent(&press);
      ui::MouseEvent release(ui::ET_MOUSE_RELEASED, center, center,
                             ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
                             ui::EF_LEFT_MOUSE_BUTTON);
      OnEvent(&release);
      return true;
    }
    case ax::mojom::Action::kFocus:
      if (IsAccessibilityFocusable()) {
        RequestFocus();
        return true;
      }
      break;
    case ax::mojom::Action::kScrollToMakeVisible:
      ScrollRectToVisible(GetLocalBounds());
      return true;
    case ax::mojom::Action::kShowContextMenu:
      ShowContextMenu(GetBoundsInScreen().CenterPoint(),
                      ui::MENU_SOURCE_KEYBOARD);
      return true;
    default:
      // Some actions are handled by subclasses of View.
      break;
  }

  return false;
}

gfx::NativeViewAccessible View::GetNativeViewAccessible() {
  return GetViewAccessibility().GetNativeObject();
}

void View::NotifyAccessibilityEvent(ax::mojom::Event event_type,
                                    bool send_native_event) {
  // If it belongs to a widget but its native widget is already destructed, do
  // not send such accessibility event as it's unexpected to send such events
  // during destruction, and is likely to lead to crashes/problems.
  if (GetWidget() && !GetWidget()->GetNativeView())
    return;

  // If `pause_accessibility_events_` is true, it means we are initializing
  // property values. In this specific case, we do not want to notify platform
  // assistive technologies that a property has changed.
  if (pause_accessibility_events_) {
    return;
  }

  AXEventManager::Get()->NotifyViewEvent(this, event_type);

  if (send_native_event && GetWidget())
    GetViewAccessibility().NotifyAccessibilityEvent(event_type);

  OnAccessibilityEvent(event_type);
}

void View::OnAccessibilityEvent(ax::mojom::Event event_type) {}

// Scrolling -------------------------------------------------------------------

void View::ScrollRectToVisible(const gfx::Rect& rect) {
  if (parent_)
    parent_->ScrollRectToVisible(rect + bounds().OffsetFromOrigin());
}

void View::ScrollViewToVisible() {
  ScrollRectToVisible(GetLocalBounds());
}

void View::AddObserver(ViewObserver* observer) {
  CHECK(observer);
  observers_.AddObserver(observer);
}

void View::RemoveObserver(ViewObserver* observer) {
  observers_.RemoveObserver(observer);
}

bool View::HasObserver(const ViewObserver* observer) const {
  return observers_.HasObserver(observer);
}

////////////////////////////////////////////////////////////////////////////////
// View, protected:

// Size and disposition --------------------------------------------------------

gfx::Size View::CalculatePreferredSize() const {
  if (HasLayoutManager())
    return GetLayoutManager()->GetPreferredSize(this);
  return gfx::Size();
}

gfx::Size View::CalculatePreferredSize(const SizeBounds& available_size) const {
  return CalculatePreferredSize();
}

void View::PreferredSizeChanged() {
  if (parent_)
    parent_->ChildPreferredSizeChanged(this);
  // Since some layout managers (specifically AnimatingLayoutManager) can react
  // to InvalidateLayout() by doing calculations and since the parent can
  // potentially change preferred size, etc. as a result of calling
  // ChildPreferredSizeChanged(), postpone invalidation until the events have
  // run all the way up the hierarchy.
  InvalidateLayout();
  for (ViewObserver& observer : observers_)
    observer.OnViewPreferredSizeChanged(this);
}

bool View::GetNeedsNotificationWhenVisibleBoundsChange() const {
  return false;
}

void View::OnVisibleBoundsChanged() {}

// Tree operations -------------------------------------------------------------

void View::ViewHierarchyChanged(const ViewHierarchyChangedDetails& details) {}

void View::VisibilityChanged(View* starting_from, bool is_visible) {}

void View::NativeViewHierarchyChanged() {
  FocusManager* focus_manager = GetFocusManager();
  if (accelerator_focus_manager_ != focus_manager) {
    UnregisterAccelerators(true);

    if (focus_manager)
      RegisterPendingAccelerators();
  }
}

void View::AddedToWidget() {}

void View::RemovedFromWidget() {}

// Painting --------------------------------------------------------------------

void View::OnDidSchedulePaint(const gfx::Rect& rect) {}

void View::PaintChildren(const PaintInfo& paint_info) {
  TRACE_EVENT1("views", "View::PaintChildren", "class", GetClassName());
  RecursivePaintHelper(&View::Paint, paint_info);
}

void View::OnPaint(gfx::Canvas* canvas) {
  TRACE_EVENT1("views", "View::OnPaint", "class", GetClassName());
  OnPaintBackground(canvas);
  OnPaintBorder(canvas);
}

void View::OnPaintBackground(gfx::Canvas* canvas) {
  if (background_) {
    TRACE_EVENT0("views", "View::OnPaintBackground");
    background_->Paint(canvas, this);
  }
}

void View::OnPaintBorder(gfx::Canvas* canvas) {
  if (border_) {
    TRACE_EVENT0("views", "View::OnPaintBorder");
    border_->Paint(*this, canvas);
  }
}

// Accelerated Painting --------------------------------------------------------

View::LayerOffsetData View::CalculateOffsetToAncestorWithLayer(
    ui::Layer** layer_parent) {
  if (layer()) {
    if (layer_parent)
      *layer_parent = layer();
    return LayerOffsetData(layer()->device_scale_factor());
  }
  if (!parent_)
    return LayerOffsetData();

  LayerOffsetData offset_data =
      parent_->CalculateOffsetToAncestorWithLayer(layer_parent);

  return offset_data + GetMirroredPosition().OffsetFromOrigin();
}

void View::UpdateParentLayer() {
  if (!layer())
    return;

  ui::Layer* parent_layer = nullptr;

  if (parent_)
    parent_->CalculateOffsetToAncestorWithLayer(&parent_layer);

  ReparentLayer(parent_layer);
}

void View::MoveLayerToParent(ui::Layer* parent_layer,
                             const LayerOffsetData& offset_data) {
  LayerOffsetData local_offset_data(offset_data);
  if (parent_layer != layer())
    local_offset_data += GetMirroredPosition().OffsetFromOrigin();

  if (layer() && parent_layer != layer()) {
    SetLayerParent(parent_layer);
    SetLayerBounds(size(), local_offset_data);
  } else {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : GetChildrenInZOrder())
      child->MoveLayerToParent(parent_layer, local_offset_data);
  }
}

void View::UpdateLayerVisibility() {
  bool visible = visible_;
  for (const View* v = parent_; visible && v && !v->layer(); v = v->parent_)
    visible = v->GetVisible();

  UpdateChildLayerVisibility(visible);
}

void View::UpdateChildLayerVisibility(bool ancestor_visible) {
  const bool layers_visible = ancestor_visible && visible_;
  if (layer()) {
    for (ui::Layer* layer : GetLayersInOrder()) {
      layer->SetVisible(layers_visible);
    }
  }
  {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : children_)
      child->UpdateChildLayerVisibility(layers_visible);
  }
}

void View::DestroyLayerImpl(LayerChangeNotifyBehavior notify_parents) {
  // Normally, adding layers above or below will trigger painting to a layer.
  // It would leave this view in an inconsistent state if its layer were
  // destroyed while layers beneath were still present. So, assume this doesn't
  // happen.
  DCHECK(layers_below_.empty() && layers_above_.empty());

  if (!layer())
    return;

  // Copy children(), since the loop below will mutate its result.
  std::vector<ui::Layer*> children = layer()->children();
  ui::Layer* new_parent = layer()->parent();
  for (auto* child : children) {
    layer()->Remove(child);
    if (new_parent)
      new_parent->Add(child);
  }

  mask_layer_.reset();

  LayerOwner::DestroyLayer();

  if (new_parent)
    ReorderLayers();

  UpdateChildLayerBounds(CalculateOffsetToAncestorWithLayer(nullptr));

  SchedulePaint();

  // Notify the parent chain about the layer change.
  if (notify_parents == LayerChangeNotifyBehavior::NOTIFY)
    NotifyParentsOfLayerChange();

  Widget* widget = GetWidget();
  if (widget)
    widget->LayerTreeChanged();
}

void View::NotifyParentsOfLayerChange() {
  // Notify the parent chain about the layer change.
  View* view_parent = parent();
  while (view_parent) {
    view_parent->OnChildLayerChanged(this);
    view_parent = view_parent->parent();
  }
}

void View::UpdateChildLayerBounds(const LayerOffsetData& offset_data) {
  if (layer()) {
    SetLayerBounds(size(), offset_data);
  } else {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : children_) {
      child->UpdateChildLayerBounds(
          offset_data + child->GetMirroredPosition().OffsetFromOrigin());
    }
  }
}

void View::OnPaintLayer(const ui::PaintContext& context) {
  PaintFromPaintRoot(context);
}

void View::OnLayerTransformed(const gfx::Transform& old_transform,
                              ui::PropertyChangeReason reason) {
  NotifyAccessibilityEvent(ax::mojom::Event::kLocationChanged, false);

  for (ViewObserver& observer : observers_)
    observer.OnViewLayerTransformed(this);
}

void View::OnLayerClipRectChanged(const gfx::Rect& old_rect,
                                  ui::PropertyChangeReason reason) {
  for (ViewObserver& observer : observers_)
    observer.OnViewLayerClipRectChanged(this);
}

void View::OnDeviceScaleFactorChanged(float old_device_scale_factor,
                                      float new_device_scale_factor) {
  snap_layer_to_pixel_boundary_ =
      (new_device_scale_factor - std::floor(new_device_scale_factor)) != 0.0f;

  if (!layer())
    return;

  // There can be no subpixel offset if the layer has no parent.
  if (!parent() || !layer()->parent())
    return;

  if (layer()->parent() && layer()->GetCompositor() &&
      layer()->GetCompositor()->is_pixel_canvas()) {
    LayerOffsetData offset_data(
        parent()->CalculateOffsetToAncestorWithLayer(nullptr));
    offset_data += GetMirroredPosition().OffsetFromOrigin();
    SnapLayerToPixelBoundary(offset_data);
  } else {
    SnapLayerToPixelBoundary(LayerOffsetData());
  }
}

void View::CreateOrDestroyLayer() {
  if (paint_to_layer_explicitly_set_ || paint_to_layer_for_transform_ ||
      !layers_below_.empty() || !layers_above_.empty()) {
    // If we need to paint to a layer, make sure we have one.
    if (!layer())
      CreateLayer(ui::LAYER_TEXTURED);
  } else if (layer()) {
    // If we don't, make sure we delete our layer.
    DestroyLayerImpl(LayerChangeNotifyBehavior::NOTIFY);
  }
}

void View::ReorderLayers() {
  View* v = this;
  while (v && !v->layer())
    v = v->parent();

  Widget* widget = GetWidget();
  if (!v) {
    if (widget) {
      ui::Layer* layer = widget->GetLayer();
      if (layer)
        widget->GetRootView()->ReorderChildLayers(layer);
    }
  } else {
    v->ReorderChildLayers(v->layer());
  }

  if (widget) {
    // Reorder the widget's child NativeViews in case a child NativeView is
    // associated with a view (e.g. via a NativeViewHost). Always do the
    // reordering because the associated NativeView's layer (if it has one)
    // is parented to the widget's layer regardless of whether the host view has
    // an ancestor with a layer.
    widget->ReorderNativeViews();
  }
}

void View::AddLayerToRegionImpl(ui::Layer* new_layer,
                                std::vector<ui::Layer*>& layer_vector) {
  DCHECK(new_layer);
  DCHECK(!base::Contains(layer_vector, new_layer)) << "Layer already added.";

  new_layer->AddObserver(this);
  new_layer->SetVisible(GetVisible());
  layer_vector.push_back(new_layer);

  // If painting to a layer already, ensure |new_layer| gets added and stacked
  // correctly. If not, this will happen on layer creation.
  if (layer()) {
    ui::Layer* const parent_layer = layer()->parent();
    // Note that |new_layer| may have already been added to the parent, for
    // example when the layer of a LayerOwner is recreated.
    if (parent_layer && parent_layer != new_layer->parent()) {
      parent_layer->Add(new_layer);
    }
    new_layer->SetBounds(gfx::Rect(new_layer->size()) +
                         layer()->bounds().OffsetFromOrigin());
    if (parent()) {
      parent()->ReorderLayers();
    }
  }

  CreateOrDestroyLayer();

  layer()->SetFillsBoundsOpaquely(false);
}

void View::SetLayerParent(ui::Layer* parent_layer) {
  // Adding the main layer can trigger a call to |SnapLayerToPixelBoundary()|.
  // That method assumes layers beneath have already been added. Therefore
  // layers above and below must be added first here. See crbug.com/961212.
  for (ui::Layer* extra_layer : GetLayersInOrder(ViewLayer::kExclude)) {
    parent_layer->Add(extra_layer);
  }
  parent_layer->Add(layer());
  // After adding the main layer, it's relative position in the stack needs
  // to be adjusted. This will ensure the layer is between any of the layers
  // above and below the main layer.
  if (!layers_below_.empty()) {
    parent_layer->StackAbove(layer(), layers_below_.back());
  } else if (!layers_above_.empty()) {
    parent_layer->StackBelow(layer(), layers_above_.front());
  }
}

void View::ReorderChildLayers(ui::Layer* parent_layer) {
  if (layer() && layer() != parent_layer) {
    DCHECK_EQ(parent_layer, layer()->parent());
    for (ui::Layer* layer_above : layers_above_) {
      parent_layer->StackAtBottom(layer_above);
    }
    parent_layer->StackAtBottom(layer());
    for (ui::Layer* layer_below : layers_below_) {
      parent_layer->StackAtBottom(layer_below);
    }
  } else {
    // Iterate backwards through the children so that a child with a layer
    // which is further to the back is stacked above one which is further to
    // the front.
    View::Views children = GetChildrenInZOrder();
    DCHECK_EQ(children_.size(), children.size());
    for (auto* child : base::Reversed(children))
      child->ReorderChildLayers(parent_layer);
  }
}

void View::OnChildLayerChanged(View* child) {}

// Input -----------------------------------------------------------------------

View::DragInfo* View::GetDragInfo() {
  return parent_ ? parent_->GetDragInfo() : nullptr;
}

// Focus -----------------------------------------------------------------------

void View::OnFocus() {}

void View::OnBlur() {}

void View::Focus() {
  OnFocus();

  // TODO(pbos): Investigate if parts of this can run unconditionally.
  if (!suppress_default_focus_handling_) {
    // Clear the native focus. This ensures that no visible native view has the
    // focus and that we still receive keyboard inputs.
    FocusManager* focus_manager = GetFocusManager();
    if (focus_manager)
      focus_manager->ClearNativeFocus();

    // Notify assistive technologies of the focus change.
    AXVirtualView* const focused_virtual_child =
        view_accessibility_ ? view_accessibility_->FocusedVirtualChild()
                            : nullptr;
    if (focused_virtual_child) {
      focused_virtual_child->NotifyAccessibilityEvent(ax::mojom::Event::kFocus);
    } else {
      NotifyAccessibilityEvent(ax::mojom::Event::kFocus, true);
    }
  }

  // If this is the contents root of a |ScrollView|, focus should bring the
  // |ScrollView| to visible rather than resetting its content scroll position.
  ScrollView* const scroll_view = ScrollView::GetScrollViewForContents(this);
  if (scroll_view) {
    scroll_view->ScrollViewToVisible();
  } else {
    ScrollViewToVisible();
  }

  for (ViewObserver& observer : observers_)
    observer.OnViewFocused(this);
}

void View::Blur() {
  ViewTracker tracker(this);
  OnBlur();

  if (tracker.view()) {
    for (ViewObserver& observer : observers_)
      observer.OnViewBlurred(this);
  }
}

// System events ---------------------------------------------------------------

void View::OnThemeChanged() {
#if DCHECK_IS_ON()
  on_theme_changed_called_ = true;
#endif
}

// Tooltips --------------------------------------------------------------------

void View::TooltipTextChanged() {
  Widget* widget = GetWidget();
  // TooltipManager may be null if there is a problem creating it.
  if (widget && widget->GetTooltipManager())
    widget->GetTooltipManager()->TooltipTextChanged(this);
}

// Drag and drop ---------------------------------------------------------------

int View::GetDragOperations(const gfx::Point& press_pt) {
  return drag_controller_
             ? drag_controller_->GetDragOperationsForView(this, press_pt)
             : ui::DragDropTypes::DRAG_NONE;
}

void View::WriteDragData(const gfx::Point& press_pt, OSExchangeData* data) {
  DCHECK(drag_controller_);
  drag_controller_->WriteDragDataForView(this, press_pt, data);
}

bool View::InDrag() const {
  const Widget* widget = GetWidget();
  return widget ? widget->dragged_view() == this : false;
}

int View::GetHorizontalDragThreshold() {
  // TODO(jennyz): This value may need to be adjusted for different platforms
  // and for different display density.
  return kDefaultHorizontalDragThreshold;
}

int View::GetVerticalDragThreshold() {
  // TODO(jennyz): This value may need to be adjusted for different platforms
  // and for different display density.
  return kDefaultVerticalDragThreshold;
}

PaintInfo::ScaleType View::GetPaintScaleType() const {
  return PaintInfo::ScaleType::kScaleWithEdgeSnapping;
}

void View::HandlePropertyChangeEffects(PropertyEffects effects) {
  if (effects & kPropertyEffectsPreferredSizeChanged)
    PreferredSizeChanged();
  if (effects & kPropertyEffectsLayout)
    InvalidateLayout();
  if (effects & kPropertyEffectsPaint)
    SchedulePaint();
}

void View::AfterPropertyChange(const void* key, int64_t old_value) {
  if (key == kElementIdentifierKey) {
    const ui::ElementIdentifier old_element_id =
        ui::ElementIdentifier::FromRawValue(
            base::checked_cast<intptr_t>(old_value));
    if (old_element_id) {
      views::ElementTrackerViews::GetInstance()->UnregisterView(old_element_id,
                                                                this);
    }
    const ui::ElementIdentifier new_element_id =
        GetProperty(kElementIdentifierKey);
    if (new_element_id) {
      views::ElementTrackerViews::GetInstance()->RegisterView(new_element_id,
                                                              this);
    }
  }
}

void View::OnPropertyChanged(ui::metadata::PropertyKey property,
                             PropertyEffects property_effects) {
  if (property_effects != kPropertyEffectsNone)
    HandlePropertyChangeEffects(property_effects);
  TriggerChangedCallback(property);
}

int View::GetX() const {
  return x();
}

int View::GetY() const {
  return y();
}

int View::GetWidth() const {
  return width();
}

int View::GetHeight() const {
  return height();
}

void View::SetWidth(int width) {
  SetBounds(x(), y(), width, height());
}

void View::SetHeight(int height) {
  SetBounds(x(), y(), width(), height);
}

std::u16string View::GetTooltip() const {
  return GetTooltipText(gfx::Point());
}

////////////////////////////////////////////////////////////////////////////////
// View, private:

// DropInfo --------------------------------------------------------------------

void View::DragInfo::Reset() {
  possible_drag = false;
  start_pt = gfx::Point();
}

void View::DragInfo::PossibleDrag(const gfx::Point& p) {
  possible_drag = true;
  start_pt = p;
}

// Painting --------------------------------------------------------------------

void View::SchedulePaintInRectImpl(const gfx::Rect& rect) {
  OnDidSchedulePaint(rect);
  if (!visible_)
    return;
  if (layer()) {
    layer()->SchedulePaint(rect);
  } else if (parent_) {
    // Translate the requested paint rect to the parent's coordinate system
    // then pass this notification up to the parent.
    parent_->SchedulePaintInRectImpl(ConvertRectToParent(rect));
  }
}

void View::SchedulePaintBoundsChanged(bool size_changed) {
  if (!visible_)
    return;

  // If we have a layer and the View's size did not change, we do not need to
  // schedule any paints since the layer will be redrawn at its new location
  // during the next Draw() cycle in the compositor.
  if (!layer() || size_changed) {
    // Otherwise, if the size changes or we don't have a layer then we need to
    // use SchedulePaint to invalidate the area occupied by the View.
    SchedulePaint();
  } else {
    // The compositor doesn't Draw() until something on screen changes, so
    // if our position changes but nothing is being animated on screen, then
    // tell the compositor to redraw the scene. We know layer() exists due to
    // the above if clause.
    layer()->ScheduleDraw();
  }
}

void View::SchedulePaintOnParent() {
  if (parent_) {
    // Translate the requested paint rect to the parent's coordinate system
    // then pass this notification up to the parent.
    parent_->SchedulePaintInRect(ConvertRectToParent(GetLocalBounds()));
  }
}

bool View::ShouldPaint() const {
  return visible_ && !size().IsEmpty();
}

void View::SetUpTransformRecorderForPainting(
    const gfx::Vector2d& offset_from_parent,
    ui::TransformRecorder* recorder) const {
  // If the view is backed by a layer, it should paint with itself as the origin
  // rather than relative to its parent.
  if (layer())
    return;

  // Translate the graphics such that 0,0 corresponds to where this View is
  // located relative to its parent.
  gfx::Transform transform_from_parent;
  transform_from_parent.Translate(offset_from_parent.x(),
                                  offset_from_parent.y());
  recorder->Transform(transform_from_parent);
}

void View::RecursivePaintHelper(void (View::*func)(const PaintInfo&),
                                const PaintInfo& info) {
  View::Views children = GetChildrenInZOrder();
  DCHECK_EQ(children_.size(), children.size());
  for (auto* child : children) {
    if (!child->layer())
      (child->*func)(info);
  }
}

void View::PaintFromPaintRoot(const ui::PaintContext& parent_context) {
  PaintInfo paint_info = PaintInfo::CreateRootPaintInfo(
      parent_context, layer() ? layer()->size() : size());
  Paint(paint_info);
  static bool draw_view_bounds_rects =
      base::CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kDrawViewBoundsRects);
  if (draw_view_bounds_rects)
    PaintDebugRects(paint_info);
}

void View::PaintDebugRects(const PaintInfo& parent_paint_info) {
  if (!ShouldPaint())
    return;

  const gfx::Rect& parent_bounds = (layer() || !parent())
                                       ? GetMirroredBounds()
                                       : parent()->GetMirroredBounds();
  PaintInfo paint_info = PaintInfo::CreateChildPaintInfo(
      parent_paint_info, GetMirroredBounds(), parent_bounds.size(),
      GetPaintScaleType(), !!layer());

  const ui::PaintContext& context = paint_info.context();

  ui::TransformRecorder transform_recorder(context);
  SetUpTransformRecorderForPainting(paint_info.offset_from_parent(),
                                    &transform_recorder);

  RecursivePaintHelper(&View::PaintDebugRects, paint_info);

  // Draw outline rects for debugging.
  ui::PaintRecorder recorder(context, paint_info.paint_recording_size(),
                             paint_info.paint_recording_scale_x(),
                             paint_info.paint_recording_scale_y(),
                             &paint_cache_);
  gfx::Canvas* canvas = recorder.canvas();
  const float scale = canvas->UndoDeviceScaleFactor();
  gfx::RectF outline_rect(ScaleToEnclosedRect(GetLocalBounds(), scale));
  gfx::RectF content_outline_rect(
      ScaleToEnclosedRect(GetContentsBounds(), scale));
  const auto* color_provider = GetColorProvider();
  if (content_outline_rect != outline_rect) {
    content_outline_rect.Inset(0.5f);
    canvas->DrawRect(content_outline_rect,
                     color_provider->GetColor(ui::kColorDebugContentOutline));
  }
  outline_rect.Inset(0.5f);
  canvas->DrawRect(outline_rect,
                   color_provider->GetColor(ui::kColorDebugBoundsOutline));
}

// Tree operations -------------------------------------------------------------

void View::AddChildViewAtImpl(View* view, size_t index) {
  CHECK_NE(view, this) << "You cannot add a view as its own child";
  DCHECK_LE(index, children_.size());

  // TODO(https://crbug.com/942298): Should just DCHECK(!view->parent_);.
  View* parent = view->parent_;
  if (parent == this) {
    ReorderChildView(view, index);
    return;
  }

  // Remove |view| from its parent, if any.
  Widget* old_widget = view->GetWidget();
  ui::NativeTheme* old_theme = old_widget ? view->GetNativeTheme() : nullptr;
  if (parent)
    parent->DoRemoveChildView(view, true, false, this);

  view->parent_ = this;
#if DCHECK_IS_ON()
  DCHECK(!iterating_);
#endif
  const auto pos = children_.insert(
      std::next(children_.cbegin(), static_cast<ptrdiff_t>(index)), view);

  view->RemoveFromFocusList();
  SetFocusSiblings(view, pos);

  // Ensure the layer tree matches the view tree before calling to any client
  // code. This way if client code further modifies the view tree we are in a
  // sane state.
  const bool did_reparent_any_layers = view->UpdateParentLayers();
  Widget* widget = GetWidget();
  if (did_reparent_any_layers && widget)
    widget->LayerTreeChanged();

  ReorderLayers();

  // Make sure the visibility of the child layers are correct.
  // If any of the parent View is hidden, then the layers of the subtree
  // rooted at |this| should be hidden. Otherwise, all the child layers should
  // inherit the visibility of the owner View.
  view->UpdateLayerVisibility();

  // Need to notify the layout manager because one of the callbacks below might
  // want to know the view's new preferred size, minimum size, etc.
  if (HasLayoutManager())
    GetLayoutManager()->ViewAdded(this, view);

  if (widget && (view->GetNativeTheme() != old_theme))
    view->PropagateThemeChanged();

  ViewHierarchyChangedDetails details(true, this, view, parent);

  for (View* v = this; v; v = v->parent_)
    v->ViewHierarchyChangedImpl(details);

  view->PropagateAddNotifications(details, widget && widget != old_widget);

  UpdateTooltip();

  if (widget) {
    RegisterChildrenForVisibleBoundsNotification(view);

    if (view->GetVisible())
      view->SchedulePaint();
  }

  for (ViewObserver& observer : observers_)
    observer.OnChildViewAdded(this, view);
}

void View::DoRemoveChildView(View* view,
                             bool update_tool_tip,
                             bool delete_removed_view,
                             View* new_parent) {
  DCHECK(view);

  const auto i = FindChild(view);
  if (i == children_.cend())
    return;

  std::unique_ptr<View> view_to_be_deleted;
  view->RemoveFromFocusList();

  Widget* widget = GetWidget();
  bool is_removed_from_widget = false;
  if (widget) {
    UnregisterChildrenForVisibleBoundsNotification(view);
    if (view->GetVisible())
      view->SchedulePaint();

    is_removed_from_widget = !new_parent || new_parent->GetWidget() != widget;
    if (is_removed_from_widget)
      widget->NotifyWillRemoveView(view);
  }

  // Make sure the layers belonging to the subtree rooted at |view| get
  // removed.
  view->OrphanLayers();
  if (widget)
    widget->LayerTreeChanged();

  // Need to notify the layout manager because one of the callbacks below might
  // want to know the view's new preferred size, minimum size, etc.
  if (HasLayoutManager())
    GetLayoutManager()->ViewRemoved(this, view);

  view->PropagateRemoveNotifications(this, new_parent, is_removed_from_widget);
  view->parent_ = nullptr;

  if (delete_removed_view && !view->owned_by_client_)
    view_to_be_deleted.reset(view);

#if DCHECK_IS_ON()
  DCHECK(!iterating_);
#endif
  children_.erase(i);

  if (update_tool_tip)
    UpdateTooltip();

  for (ViewObserver& observer : observers_)
    observer.OnChildViewRemoved(this, view);
}

void View::PropagateRemoveNotifications(View* old_parent,
                                        View* new_parent,
                                        bool is_removed_from_widget) {
  {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : children_) {
      child->PropagateRemoveNotifications(old_parent, new_parent,
                                          is_removed_from_widget);
    }
  }

  // When a view is removed from a hierarchy, UnregisterAccelerators() is called
  // for the removed view and all descendant views in post-order.
  UnregisterAccelerators(true);

  ViewHierarchyChangedDetails details(false, old_parent, this, new_parent);
  for (View* v = this; v; v = v->parent_)
    v->ViewHierarchyChangedImpl(details);

  if (is_removed_from_widget) {
    RemovedFromWidget();
    for (ViewObserver& observer : observers_)
      observer.OnViewRemovedFromWidget(this);
  }
}

void View::PropagateAddNotifications(const ViewHierarchyChangedDetails& details,
                                     bool is_added_to_widget) {
  // When a view is added to a Widget hierarchy, RegisterPendingAccelerators()
  // will be called for the added view and all its descendants in pre-order.
  // This means that descendant views will register their accelerators after
  // their parents. This allows children to override accelerators registered by
  // their parents as accelerators registered later take priority over those
  // registered earlier.
  if (GetFocusManager())
    RegisterPendingAccelerators();

  {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : children_)
      child->PropagateAddNotifications(details, is_added_to_widget);
  }

  ViewHierarchyChangedImpl(details);
  if (is_added_to_widget) {
    AddedToWidget();
    for (ViewObserver& observer : observers_)
      observer.OnViewAddedToWidget(this);
  }
}

void View::PropagateNativeViewHierarchyChanged() {
  {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : children_)
      child->PropagateNativeViewHierarchyChanged();
  }
  NativeViewHierarchyChanged();
}

void View::ViewHierarchyChangedImpl(
    const ViewHierarchyChangedDetails& details) {
  ViewHierarchyChanged(details);

  for (ViewObserver& observer : observers_)
    observer.OnViewHierarchyChanged(this, details);

  details.parent->needs_layout_ = true;
}

// Size and disposition --------------------------------------------------------

void View::PropagateVisibilityNotifications(View* start, bool is_visible) {
  {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : children_)
      child->PropagateVisibilityNotifications(start, is_visible);
  }
  VisibilityChangedImpl(start, is_visible);
}

void View::VisibilityChangedImpl(View* starting_from, bool is_visible) {
  VisibilityChanged(starting_from, is_visible);
  for (ViewObserver& observer : observers_)
    observer.OnViewVisibilityChanged(this, starting_from);
}

void View::SnapLayerToPixelBoundary(const LayerOffsetData& offset_data) {
  if (!layer())
    return;

#if DCHECK_IS_ON()
  // We rely on our layers above and below being parented correctly at this
  // point.
  for (ui::Layer* layer_above_below : GetLayersInOrder(ViewLayer::kExclude)) {
    DCHECK_EQ(layer()->parent(), layer_above_below->parent());
  }
#endif  // DCHECK_IS_ON()

  if (layer()->GetCompositor() && layer()->GetCompositor()->is_pixel_canvas()) {
    gfx::Vector2dF offset = snap_layer_to_pixel_boundary_ && layer()->parent()
                                ? offset_data.GetSubpixelOffset()
                                : gfx::Vector2dF();
    layer()->SetSubpixelPositionOffset(offset);
    for (ui::Layer* layer : GetLayersInOrder(ViewLayer::kExclude)) {
      layer->SetSubpixelPositionOffset(offset);
    }
  }
}

// static
void View::RegisterChildrenForVisibleBoundsNotification(View* view) {
  if (view->GetNeedsNotificationWhenVisibleBoundsChange())
    view->RegisterForVisibleBoundsNotification();
  for (View* child : view->children_)
    RegisterChildrenForVisibleBoundsNotification(child);
}

// static
void View::UnregisterChildrenForVisibleBoundsNotification(View* view) {
  if (view->GetNeedsNotificationWhenVisibleBoundsChange())
    view->UnregisterForVisibleBoundsNotification();
  for (View* child : view->children_)
    UnregisterChildrenForVisibleBoundsNotification(child);
}

void View::RegisterForVisibleBoundsNotification() {
  if (registered_for_visible_bounds_notification_)
    return;

  registered_for_visible_bounds_notification_ = true;
  for (View* ancestor = parent_; ancestor; ancestor = ancestor->parent_)
    ancestor->AddDescendantToNotify(this);
}

void View::UnregisterForVisibleBoundsNotification() {
  if (!registered_for_visible_bounds_notification_)
    return;

  registered_for_visible_bounds_notification_ = false;
  for (View* ancestor = parent_; ancestor; ancestor = ancestor->parent_)
    ancestor->RemoveDescendantToNotify(this);
}

void View::AddDescendantToNotify(View* view) {
  DCHECK(view);
  if (!descendants_to_notify_)
    descendants_to_notify_ = std::make_unique<Views>();
  descendants_to_notify_->push_back(view);
}

void View::RemoveDescendantToNotify(View* view) {
  DCHECK(view && descendants_to_notify_);
  auto i = base::ranges::find(*descendants_to_notify_, view);
  DCHECK(i != descendants_to_notify_->end());
  descendants_to_notify_->erase(i);
  if (descendants_to_notify_->empty())
    descendants_to_notify_.reset();
}

void View::SetLayoutManagerImpl(std::unique_ptr<LayoutManager> layout_manager) {
  // Some code keeps a bare pointer to the layout manager for calling
  // derived-class-specific-functions. It's an easy mistake to create a new
  // unique_ptr and re-set the layout manager with a new unique_ptr, which
  // will cause a crash. Re-setting to nullptr is OK.
  CHECK(!layout_manager || layout_manager_ != layout_manager);

  layout_manager_ = std::move(layout_manager);
  if (layout_manager_)
    layout_manager_->Installed(this);
  // Only reset |default_fill_layout_| if it's already been set and there is a
  // layout manager.
  if (default_fill_layout_.has_value() && layout_manager_)
    default_fill_layout_.reset();
  else if (kUseDefaultFillLayout)
    default_fill_layout_.emplace(DefaultFillLayout());
}

void View::SetLayerBounds(const gfx::Size& size,
                          const LayerOffsetData& offset_data) {
  const gfx::Rect bounds = gfx::Rect(size) + offset_data.offset();
  const bool bounds_changed = (bounds != layer()->GetTargetBounds());
  layer()->SetBounds(bounds);
  for (ui::Layer* layer : GetLayersInOrder(ViewLayer::kExclude)) {
    layer->SetBounds(gfx::Rect(layer->size()) + bounds.OffsetFromOrigin());
  }
  SnapLayerToPixelBoundary(offset_data);
  if (bounds_changed) {
    for (ViewObserver& observer : observers_)
      observer.OnLayerTargetBoundsChanged(this);
  }
}

// Transformations -------------------------------------------------------------

bool View::GetTransformRelativeTo(const View* ancestor,
                                  gfx::Transform* transform) const {
  const View* p = this;

  while (p && p != ancestor) {
    transform->PostConcat(p->GetTransform());
    gfx::Transform translation;
    translation.Translate(static_cast<float>(p->GetMirroredX()),
                          static_cast<float>(p->y()));
    transform->PostConcat(translation);

    p = p->parent_;
  }

  return p == ancestor;
}

// Coordinate conversion -------------------------------------------------------

bool View::ConvertPointForAncestor(const View* ancestor,
                                   gfx::Point* point) const {
  gfx::Transform trans;
  // TODO(sad): Have some way of caching the transformation results.
  bool result = GetTransformRelativeTo(ancestor, &trans);
  *point = gfx::ToFlooredPoint(trans.MapPoint(gfx::PointF(*point)));
  return result;
}

bool View::ConvertPointFromAncestor(const View* ancestor,
                                    gfx::Point* point) const {
  gfx::Transform trans;
  bool result = GetTransformRelativeTo(ancestor, &trans);
  if (const absl::optional<gfx::PointF> transformed_point =
          trans.InverseMapPoint(gfx::PointF(*point))) {
    *point = gfx::ToFlooredPoint(transformed_point.value());
  }
  return result;
}

bool View::ConvertRectForAncestor(const View* ancestor,
                                  gfx::RectF* rect) const {
  gfx::Transform trans;
  // TODO(sad): Have some way of caching the transformation results.
  bool result = GetTransformRelativeTo(ancestor, &trans);
  *rect = trans.MapRect(*rect);
  return result;
}

bool View::ConvertRectFromAncestor(const View* ancestor,
                                   gfx::RectF* rect) const {
  gfx::Transform trans;
  bool result = GetTransformRelativeTo(ancestor, &trans);
  *rect = trans.InverseMapRect(*rect).value_or(*rect);
  return result;
}

// Accelerated painting --------------------------------------------------------

void View::CreateLayer(ui::LayerType layer_type) {
  // A new layer is being created for the view. So all the layers of the
  // sub-tree can inherit the visibility of the corresponding view.
  {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : children_)
      child->UpdateChildLayerVisibility(true);
  }

  SetLayer(std::make_unique<ui::Layer>(layer_type));
  layer()->set_delegate(this);
  layer()->SetName(GetClassName());

  UpdateParentLayers();
  UpdateLayerVisibility();

  // The new layer needs to be ordered in the layer tree according
  // to the view tree. Children of this layer were added in order
  // in UpdateParentLayers().
  if (parent())
    parent()->ReorderLayers();

  Widget* widget = GetWidget();
  if (widget)
    widget->LayerTreeChanged();

  // Before having its own Layer, this View may have painted in to a Layer owned
  // by an ancestor View. Scheduling a paint on the parent View will erase this
  // View's painting effects on the ancestor View's Layer.
  // (See crbug.com/551492)
  SchedulePaintOnParent();
}

bool View::UpdateParentLayers() {
  // Attach all top-level un-parented layers.
  if (layer()) {
    if (!layer()->parent()) {
      UpdateParentLayer();
      return true;
    }
    // The layers of any child views are already in place, so we can stop
    // iterating here.
    return false;
  }
  bool result = false;
  internal::ScopedChildrenLock lock(this);
  for (auto* child : children_) {
    if (child->UpdateParentLayers())
      result = true;
  }
  return result;
}

void View::OrphanLayers() {
  if (layer()) {
    ui::Layer* parent = layer()->parent();
    if (parent) {
      for (ui::Layer* layer : GetLayersInOrder()) {
        parent->Remove(layer);
      }
    }

    // The layer belonging to this View has already been orphaned. It is not
    // necessary to orphan the child layers.
    return;
  }
  internal::ScopedChildrenLock lock(this);
  for (auto* child : children_)
    child->OrphanLayers();
}

void View::ReparentLayer(ui::Layer* parent_layer) {
  DCHECK_NE(layer(), parent_layer);
  if (parent_layer) {
    SetLayerParent(parent_layer);
  }
  // Update the layer bounds; this needs to be called after this layer is added
  // to the new parent layer since snapping to pixel boundary will be affected
  // by the layer hierarchy.
  LayerOffsetData offset =
      parent_ ? parent_->CalculateOffsetToAncestorWithLayer(nullptr)
              : LayerOffsetData(layer()->device_scale_factor());
  SetLayerBounds(size(), offset + GetMirroredBounds().OffsetFromOrigin());
  layer()->SchedulePaint(GetLocalBounds());
  MoveLayerToParent(layer(), LayerOffsetData(layer()->device_scale_factor()));
}

void View::CreateMaskLayer() {
  DCHECK(layer());
  mask_layer_ = std::make_unique<views::ViewMaskLayer>(clip_path_, this);
  layer()->SetMaskLayer(mask_layer_->layer());
}

// Layout ----------------------------------------------------------------------

bool View::HasLayoutManager() const {
  return ((default_fill_layout_.has_value() && !children_.empty()) ||
          layout_manager_);
}

// Input -----------------------------------------------------------------------

bool View::ProcessMousePressed(const ui::MouseEvent& event) {
  int drag_operations = (enabled_ && event.IsOnlyLeftMouseButton() &&
                         HitTestPoint(event.location()))
                            ? GetDragOperations(event.location())
                            : 0;
  ContextMenuController* context_menu_controller =
      event.IsRightMouseButton() ? context_menu_controller_.get() : nullptr;
  View::DragInfo* drag_info = GetDragInfo();

  const bool was_enabled = GetEnabled();
  const bool result = OnMousePressed(event);

  if (!was_enabled)
    return result;

  if (event.IsOnlyRightMouseButton() && context_menu_controller &&
      kContextMenuOnMousePress) {
    // Assume that if there is a context menu controller we won't be deleted
    // from mouse pressed.
    if (HitTestPoint(event.location())) {
      gfx::Point location(event.location());
      ConvertPointToScreen(this, &location);
      ShowContextMenu(location, ui::MENU_SOURCE_MOUSE);
      return true;
    }
  }

  // WARNING: we may have been deleted, don't use any View variables.
  if (drag_operations != ui::DragDropTypes::DRAG_NONE) {
    drag_info->PossibleDrag(event.location());
    return true;
  }
  return !!context_menu_controller || result;
}

void View::ProcessMouseDragged(ui::MouseEvent* event) {
  // Copy the field, that way if we're deleted after drag and drop no harm is
  // done.
  ContextMenuController* context_menu_controller = context_menu_controller_;
  const bool possible_drag = GetDragInfo()->possible_drag;
  if (possible_drag &&
      ExceededDragThreshold(GetDragInfo()->start_pt - event->location()) &&
      (!drag_controller_ ||
       drag_controller_->CanStartDragForView(this, GetDragInfo()->start_pt,
                                             event->location()))) {
    if (DoDrag(*event, GetDragInfo()->start_pt,
               ui::mojom::DragEventSource::kMouse)) {
      event->StopPropagation();
      return;
    }
  } else {
    if (OnMouseDragged(*event)) {
      event->SetHandled();
      return;
    }
    // Fall through to return value based on context menu controller.
  }
  // WARNING: we may have been deleted.
  if ((context_menu_controller != nullptr) || possible_drag)
    event->SetHandled();
}

void View::ProcessMouseReleased(const ui::MouseEvent& event) {
  if (!kContextMenuOnMousePress && context_menu_controller_ &&
      event.IsOnlyRightMouseButton()) {
    // Assume that if there is a context menu controller we won't be deleted
    // from mouse released.
    gfx::Point location(event.location());
    OnMouseReleased(event);
    if (HitTestPoint(location)) {
      ConvertPointToScreen(this, &location);
      ShowContextMenu(location, ui::MENU_SOURCE_MOUSE);
    }
  } else {
    OnMouseReleased(event);
  }
  // WARNING: we may have been deleted.
}

// Accelerators ----------------------------------------------------------------

void View::RegisterPendingAccelerators() {
  if (!accelerators_ ||
      registered_accelerator_count_ == accelerators_->size()) {
    // No accelerators are waiting for registration.
    return;
  }

  if (!GetWidget()) {
    // The view is not yet attached to a widget, defer registration until then.
    return;
  }

  accelerator_focus_manager_ = GetFocusManager();
  CHECK(accelerator_focus_manager_);
  for (std::vector<ui::Accelerator>::const_iterator i =
           accelerators_->begin() +
           static_cast<ptrdiff_t>(registered_accelerator_count_);
       i != accelerators_->end(); ++i) {
    accelerator_focus_manager_->RegisterAccelerator(
        *i, ui::AcceleratorManager::kNormalPriority, this);
  }
  registered_accelerator_count_ = accelerators_->size();
}

void View::UnregisterAccelerators(bool leave_data_intact) {
  if (!accelerators_)
    return;

  if (GetWidget()) {
    if (accelerator_focus_manager_) {
      accelerator_focus_manager_->UnregisterAccelerators(this);
      accelerator_focus_manager_ = nullptr;
    }
    if (!leave_data_intact) {
      accelerators_->clear();
      accelerators_.reset();
    }
    registered_accelerator_count_ = 0;
  }
}

// Focus -----------------------------------------------------------------------

void View::SetFocusSiblings(View* view, Views::const_iterator pos) {
  // |view| was just inserted at |pos|, so all of these conditions must hold.
  DCHECK(!children_.empty());
  DCHECK(pos != children_.cend());
  DCHECK_EQ(view, *pos);

  if (children_.size() > 1) {
    if (std::next(pos) == children_.cend()) {
      // |view| was inserted at the end, but the end of the child list may not
      // be the last focusable element. Try to hook in after the last focusable
      // child.
      View* const old_last = *base::ranges::find_if_not(
          children_.cbegin(), pos, &View::next_focusable_view_);
      DCHECK_NE(old_last, view);
      view->InsertAfterInFocusList(old_last);
    } else {
      // |view| was inserted somewhere other than the end.  Hook in before the
      // subsequent child.
      view->InsertBeforeInFocusList(*std::next(pos));
    }
  }
}

void View::AdvanceFocusIfNecessary() {
  // Focus should only be advanced if this is the focused view and has become
  // unfocusable. If the view is still focusable or is not focused, we can
  // return early avoiding further unnecessary checks. Focusability check is
  // performed first as it tends to be faster.
  if (IsAccessibilityFocusable() || !HasFocus())
    return;

  FocusManager* focus_manager = GetFocusManager();
  if (focus_manager)
    focus_manager->AdvanceFocusIfNecessary();
}

// System events ---------------------------------------------------------------

void View::PropagateThemeChanged() {
  {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : base::Reversed(children_))
      child->PropagateThemeChanged();
  }
  OnThemeChanged();
  if (border_)
    border_->OnViewThemeChanged(this);
  if (background_)
    background_->OnViewThemeChanged(this);
#if DCHECK_IS_ON()
  DCHECK(on_theme_changed_called_)
      << "views::View::OnThemeChanged() has not been called. This means that "
         "some class in the hierarchy is not calling their direct parent's "
         "OnThemeChanged(). Please fix this by adding the missing call. Do not "
         "call views::View::OnThemeChanged() directly unless views::View is "
         "the direct parent class.";
  on_theme_changed_called_ = false;
#endif
  for (ViewObserver& observer : observers_)
    observer.OnViewThemeChanged(this);
}

void View::PropagateDeviceScaleFactorChanged(float old_device_scale_factor,
                                             float new_device_scale_factor) {
  {
    internal::ScopedChildrenLock lock(this);
    for (auto* child : base::Reversed(children_)) {
      child->PropagateDeviceScaleFactorChanged(old_device_scale_factor,
                                               new_device_scale_factor);
    }
  }

  // If the view is drawing to the layer, OnDeviceScaleFactorChanged() is called
  // through LayerDelegate callback.
  if (!layer())
    OnDeviceScaleFactorChanged(old_device_scale_factor,
                               new_device_scale_factor);
}

// Tooltips --------------------------------------------------------------------

void View::UpdateTooltip() {
  Widget* widget = GetWidget();
  // TODO(beng): The TooltipManager nullptr check can be removed when we
  //             consolidate Init() methods and make views_unittests Init() all
  //             Widgets that it uses.
  if (widget && widget->GetTooltipManager())
    widget->GetTooltipManager()->UpdateTooltip();
}

// Drag and drop ---------------------------------------------------------------

bool View::DoDrag(const ui::LocatedEvent& event,
                  const gfx::Point& press_pt,
                  ui::mojom::DragEventSource source) {
  int drag_operations = GetDragOperations(press_pt);
  if (drag_operations == ui::DragDropTypes::DRAG_NONE)
    return false;

  Widget* widget = GetWidget();
  // We should only start a drag from an event, implying we have a widget.
  DCHECK(widget);

  // Don't attempt to start a drag while in the process of dragging. This is
  // especially important on X where we can get multiple mouse move events when
  // we start the drag.
  if (widget->dragged_view())
    return false;

  std::unique_ptr<OSExchangeData> data(std::make_unique<OSExchangeData>());
  WriteDragData(press_pt, data.get());

  // Message the RootView to do the drag and drop. That way if we're removed
  // the RootView can detect it and avoid calling us back.
  gfx::Point widget_location(event.location());
  ConvertPointToWidget(this, &widget_location);
  widget->RunShellDrag(this, std::move(data), widget_location, drag_operations,
                       source);
  // WARNING: we may have been deleted.
  return true;
}

View::DefaultFillLayout::DefaultFillLayout() = default;

View::DefaultFillLayout::~DefaultFillLayout() = default;

void View::DefaultFillLayout::Layout(View* host) {
  const gfx::Rect contents_bounds = host->GetContentsBounds();
  for (auto* child : host->children()) {
    if (!child->GetProperty(kViewIgnoredByLayoutKey))
      child->SetBoundsRect(contents_bounds);
  }
}

gfx::Size View::DefaultFillLayout::GetPreferredSize(const View* host) const {
  gfx::Size preferred_size;
  for (auto* child : host->children()) {
    if (!child->GetProperty(kViewIgnoredByLayoutKey))
      preferred_size.SetToMax(child->GetPreferredSize());
  }
  return preferred_size;
}

int View::DefaultFillLayout::GetPreferredHeightForWidth(const View* host,
                                                        int width) const {
  const gfx::Insets insets = host->GetInsets();
  int preferred_height = 0;
  for (auto* child : host->children()) {
    if (!child->GetProperty(kViewIgnoredByLayoutKey)) {
      preferred_height = std::max(
          preferred_height,
          child->GetHeightForWidth(width - insets.width()) + insets.height());
    }
  }
  return preferred_height;
}

// This block requires the existence of METADATA_HEADER(View) in the class
// declaration for View.
BEGIN_METADATA_BASE(View)
ADD_PROPERTY_METADATA(std::unique_ptr<Background>, Background)
ADD_PROPERTY_METADATA(std::unique_ptr<Border>, Border)
ADD_READONLY_PROPERTY_METADATA(const char*, ClassName)
ADD_PROPERTY_METADATA(bool, Enabled)
ADD_PROPERTY_METADATA(View::FocusBehavior, FocusBehavior)
ADD_PROPERTY_METADATA(bool, FlipCanvasOnPaintForRTLUI)
ADD_PROPERTY_METADATA(int, Group)
ADD_PROPERTY_METADATA(int, Height)
ADD_PROPERTY_METADATA(int, ID)
ADD_READONLY_PROPERTY_METADATA(bool, IsDrawn);
ADD_READONLY_PROPERTY_METADATA(gfx::Size, MaximumSize)
ADD_READONLY_PROPERTY_METADATA(gfx::Size, MinimumSize)
ADD_PROPERTY_METADATA(bool, Mirrored)
ADD_PROPERTY_METADATA(bool, NotifyEnterExitOnChild)
ADD_READONLY_PROPERTY_METADATA(std::u16string, Tooltip)
ADD_PROPERTY_METADATA(bool, Visible)
ADD_PROPERTY_METADATA(bool, CanProcessEventsWithinSubtree)
ADD_PROPERTY_METADATA(bool, UseDefaultFillLayout)
ADD_PROPERTY_METADATA(int, Width)
ADD_PROPERTY_METADATA(int, X)
ADD_PROPERTY_METADATA(int, Y)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kMarginsKey)
ADD_CLASS_PROPERTY_METADATA(gfx::Insets, kInternalPaddingKey)
ADD_CLASS_PROPERTY_METADATA(LayoutAlignment, kCrossAxisAlignmentKey)
ADD_CLASS_PROPERTY_METADATA(bool, kViewIgnoredByLayoutKey)
END_METADATA

}  // namespace views

DEFINE_ENUM_CONVERTERS(views::View::FocusBehavior,
                       {views::View::FocusBehavior::ACCESSIBLE_ONLY,
                        u"ACCESSIBLE_ONLY"},
                       {views::View::FocusBehavior::ALWAYS, u"ALWAYS"},
                       {views::View::FocusBehavior::NEVER, u"NEVER"})