chromium/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc

// 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 "content/browser/renderer_host/render_widget_host_view_aura.h"

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <string>
#include <tuple>
#include <utility>

#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/sanitizer_buildflags.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/null_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/test/with_feature_override.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "cc/trees/render_frame_metadata.h"
#include "components/input/input_router.h"
#include "components/input/mouse_wheel_event_queue.h"
#include "components/viz/common/features.h"
#include "components/viz/common/frame_sinks/begin_frame_args.h"
#include "components/viz/common/surfaces/child_local_surface_id_allocator.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/test/begin_frame_args_test.h"
#include "components/viz/test/fake_external_begin_frame_source.h"
#include "components/viz/test/fake_surface_observer.h"
#include "components/viz/test/test_latest_local_surface_id_lookup_delegate.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/gpu/compositor_util.h"
#include "content/browser/renderer_host/delegated_frame_host.h"
#include "content/browser/renderer_host/delegated_frame_host_client_aura.h"
#include "content/browser/renderer_host/frame_token_message_queue.h"
#include "content/browser/renderer_host/frame_tree.h"
#include "content/browser/renderer_host/overscroll_controller.h"
#include "content/browser/renderer_host/overscroll_controller_delegate.h"
#include "content/browser/renderer_host/render_frame_metadata_provider_impl.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_widget_host_delegate.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/browser/renderer_host/render_widget_host_view_event_handler.h"
#include "content/browser/renderer_host/text_input_manager.h"
#include "content/browser/site_instance_group.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/web_contents/web_contents_view_aura.h"
#include "content/common/features.h"
#include "content/public/browser/context_menu_params.h"
#include "content/public/browser/keyboard_event_processing_result.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_view_delegate.h"
#include "content/public/test/fake_frame_widget.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_image_transport_factory.h"
#include "content/test/mock_render_input_router.h"
#include "content/test/mock_render_widget_host_delegate.h"
#include "content/test/mock_widget.h"
#include "content/test/mock_widget_input_handler.h"
#include "content/test/test_overscroll_delegate.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_test_sink.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/input/synthetic_web_input_event_builders.h"
#include "third_party/blink/public/mojom/input/touch_event.mojom.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/window_parenting_client.h"
#include "ui/aura/env.h"
#include "ui/aura/layout_manager.h"
#include "ui/aura/scoped_keyboard_hook.h"
#include "ui/aura/test/aura_test_helper.h"
#include "ui/aura/test/aura_test_utils.h"
#include "ui/aura/test/test_cursor_client.h"
#include "ui/aura/test/test_screen.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_observer.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/ime/init/input_method_factory.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/mock_input_method.h"
#include "ui/base/ime/mojom/text_input_state.mojom.h"
#include "ui/base/ime/virtual_keyboard_controller.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/ui_base_switches.h"
#include "ui/base/ui_base_types.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer_tree_owner.h"
#include "ui/compositor/test/draw_waiter_for_test.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/blink/blink_event_util.h"
#include "ui/events/blink/blink_features.h"
#include "ui/events/blink/web_input_event_traits.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/gesture_detection/gesture_configuration.h"
#include "ui/events/gestures/motion_event_aura.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/selection_bound.h"
#include "ui/wm/core/window_util.h"

#if BUILDFLAG(IS_WIN)
#include "content/browser/renderer_host/legacy_render_widget_host_win.h"
#include "ui/base/view_prop.h"
#include "ui/base/win/window_event_target.h"
#endif

_;

WebGestureEvent;
WebInputEvent;
WebMouseEvent;
WebMouseWheelEvent;
WebTouchEvent;
WebTouchPoint;
WebInputEventTraits;
FrameEvictionManager;

#define EXPECT_EVICTED(view)

#define EXPECT_HAS_FRAME(view)

namespace content {

void ParentHostView(RenderWidgetHostView* host_view,
                    RenderWidgetHostView* parent_host_view,
                    const gfx::Rect& bounds = gfx::Rect()) {}

void InstallDelegatedFrameHostClient(
    RenderWidgetHostViewAura* render_widget_host_view);

const viz::LocalSurfaceId kArbitraryLocalSurfaceId(
    1,
    base::UnguessableToken::CreateForTesting(2, 3));

std::string GetMessageNames(
    const MockWidgetInputHandler::MessageVector& events) {}

// Simple observer that keeps track of changes to a window for tests.
class TestWindowObserver : public aura::WindowObserver {};

class FakeWindowEventDispatcher : public aura::WindowEventDispatcher {};

class FakeRenderWidgetHostViewAura : public RenderWidgetHostViewAura {};

// A layout manager that always resizes a child to the root window size.
class FullscreenLayoutManager : public aura::LayoutManager {};

class MockRenderWidgetHostImpl : public RenderWidgetHostImpl {};

class TestScopedKeyboardHook : public aura::ScopedKeyboardHook {};

TestScopedKeyboardHook::TestScopedKeyboardHook() = default;

TestScopedKeyboardHook::~TestScopedKeyboardHook() = default;

bool TestScopedKeyboardHook::IsKeyLocked(ui::DomCode dom_code) {}

void TestScopedKeyboardHook::LockAllKeys() {}

void TestScopedKeyboardHook::LockSpecificKey(ui::DomCode dom_code) {}

class RenderWidgetHostViewAuraTest : public testing::Test {};

void InstallDelegatedFrameHostClient(
    RenderWidgetHostViewAura* render_widget_host_view) {}

// TODO(mohsen): Consider moving these tests to OverscrollControllerTest if
// appropriate.
class RenderWidgetHostViewAuraOverscrollTest
    : public RenderWidgetHostViewAuraTest {};

class RenderWidgetHostViewAuraShutdownTest
    : public RenderWidgetHostViewAuraTest {};

// Checks that a popup is positioned correctly relative to its parent using
// screen coordinates.
TEST_F(RenderWidgetHostViewAuraTest, PositionChildPopup) {}

// Checks that moving parent sends new screen bounds.
TEST_F(RenderWidgetHostViewAuraTest, ParentMovementUpdatesScreenRect) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
// Checks that a popup view is destroyed when a user clicks outside of the popup
// view and focus does not change. This is the case when the user clicks on the
// desktop background on Chrome OS.
TEST_F(RenderWidgetHostViewAuraTest, DestroyPopupClickOutsidePopup) {
  parent_view_->SetBounds(gfx::Rect(10, 10, 400, 400));
  parent_view_->Focus();
  EXPECT_TRUE(parent_view_->HasFocus());

  InitViewForPopup(parent_view_, gfx::Rect(10, 10, 100, 100));
  aura::Window* window = view_->GetNativeView();
  ASSERT_TRUE(window != nullptr);

  gfx::Point click_point(0, 0);
  EXPECT_FALSE(window->GetBoundsInRootWindow().Contains(click_point));
  aura::Window* parent_window = parent_view_->GetNativeView();
  EXPECT_FALSE(parent_window->GetBoundsInRootWindow().Contains(click_point));

  TestWindowObserver observer(window);
  ui::test::EventGenerator generator(window->GetRootWindow(), click_point);
  widget_host_ = nullptr;  // Owned by `view_`.
  view_ = nullptr;         // Self destroying during `ClickLeftButton`.
  generator.ClickLeftButton();
  ASSERT_TRUE(parent_view_->HasFocus());
  ASSERT_TRUE(observer.destroyed());
}

// Checks that a popup view is destroyed when a user taps outside of the popup
// view and focus does not change. This is the case when the user taps the
// desktop background on Chrome OS.
TEST_F(RenderWidgetHostViewAuraTest, DestroyPopupTapOutsidePopup) {
  parent_view_->SetBounds(gfx::Rect(10, 10, 400, 400));
  parent_view_->Focus();
  EXPECT_TRUE(parent_view_->HasFocus());

  InitViewForPopup(parent_view_, gfx::Rect(10, 10, 100, 100));
  aura::Window* window = view_->GetNativeView();
  ASSERT_TRUE(window != nullptr);

  gfx::Point tap_point(0, 0);
  EXPECT_FALSE(window->GetBoundsInRootWindow().Contains(tap_point));
  aura::Window* parent_window = parent_view_->GetNativeView();
  EXPECT_FALSE(parent_window->GetBoundsInRootWindow().Contains(tap_point));

  TestWindowObserver observer(window);
  ui::test::EventGenerator generator(window->GetRootWindow(), tap_point);
  widget_host_ = nullptr;  // Owned by `view_`.
  view_ = nullptr;         // Self destroying during `GestureTapAt`.
  generator.GestureTapAt(tap_point);
  ASSERT_TRUE(parent_view_->HasFocus());
  ASSERT_TRUE(observer.destroyed());
}
#endif

#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS)
// On Desktop Linux, select boxes need mouse capture in order to work. Test that
// when a select box is opened via a mouse press that it retains mouse capture
// after the mouse is released.
TEST_F(RenderWidgetHostViewAuraTest, PopupRetainsCaptureAfterMouseRelease) {}
#endif

// Test that select boxes close when their parent window loses focus (e.g. due
// to an alert or system modal dialog).
TEST_F(RenderWidgetHostViewAuraTest, PopupClosesWhenParentLosesFocus) {}

// Checks that IME-composition-event state is maintained correctly.
TEST_F(RenderWidgetHostViewAuraTest, SetCompositionText) {}

// Checks that we reset has_composition_text_ to false upon when the focused
// node is changed.
TEST_F(RenderWidgetHostViewAuraTest, FocusedNodeChanged) {}

// Checks that sequence of IME-composition-event and mouse-event when mouse
// clicking to cancel the composition.
TEST_F(RenderWidgetHostViewAuraTest, FinishCompositionByMouse) {}

// Checks that WasOcculded/WasUnOccluded notifies RenderWidgetHostImpl.
TEST_F(RenderWidgetHostViewAuraTest, WasOccluded) {}

// Checks that touch-event state is maintained correctly.
TEST_F(RenderWidgetHostViewAuraTest, TouchEventState) {}

TEST_F(RenderWidgetHostViewAuraTest,
       KeyEventRoutingWithKeyboardLockActiveForOneKey) {}

TEST_F(RenderWidgetHostViewAuraTest,
       KeyEventRoutingWithKeyboardLockActiveForEscKey) {}

TEST_F(RenderWidgetHostViewAuraTest,
       KeyEventRoutingWithKeyboardLockActiveForAllKeys) {}

TEST_F(RenderWidgetHostViewAuraTest,
       KeyEventRoutingKeyboardLockAndChildPopupWithInputGrab) {}

TEST_F(RenderWidgetHostViewAuraTest, TimerBasedWheelEventPhaseInfo) {}

TEST_F(RenderWidgetHostViewAuraTest,
       TimerBasedWheelEventPhaseInfoWithPercentBasedScrolling) {}

void RenderWidgetHostViewAuraTest::RunTimerBasedWheelEventPhaseInfoTest(
    bool percent_based_scrolling_enabled) {}

// Tests that latching breaks when the difference between location of the first
// wheel event in the sequence and the location of the current wheel event is
// larger than some maximum threshold.
TEST_F(RenderWidgetHostViewAuraTest, TimerBasedLatchingBreaksWithMouseMove) {}

// Tests that latching breaks when the current wheel event has different
// modifiers.
TEST_F(RenderWidgetHostViewAuraTest,
       TimerBasedLatchingBreaksWithModifiersChange) {}

// Tests that latching breaks when the new wheel event goes a different
// direction from previous wheel events and the previous GSU events are not
// consumed.
TEST_F(RenderWidgetHostViewAuraTest,
       TimerBasedLatchingBreaksWithDirectionChange) {}

TEST_F(RenderWidgetHostViewAuraTest,
       TimerBasedLatchingBreaksWithAutoscrollStart) {}

// Tests that a gesture fling start with touchpad source resets wheel phase
// state.
TEST_F(RenderWidgetHostViewAuraTest, TouchpadFlingStartResetsWheelPhaseState) {}

// Tests that the touchpad scroll state in mouse wheel phase handler gets reset
// when a mouse wheel event from an external mouse arrives.
TEST_F(RenderWidgetHostViewAuraTest, MouseWheelScrollingAfterGFCWithoutGFS) {}

TEST_F(RenderWidgetHostViewAuraTest,
       ScrollingWithExternalMouseBreaksTouchpadScrollLatching) {}

TEST_F(RenderWidgetHostViewAuraTest,
       GSBWithTouchSourceStopsWheelScrollSequence) {}

TEST_F(RenderWidgetHostViewAuraTest,
       SyntheticFlingCancelAtTouchpadScrollBegin) {}

// Checks that touch-event state is maintained correctly for multiple touch
// points.
TEST_F(RenderWidgetHostViewAuraTest, MultiTouchPointsStates) {}

// Checks that touch-events are queued properly when there is a touch-event
// handler on the page.
TEST_F(RenderWidgetHostViewAuraTest, TouchEventSyncAsync) {}

TEST_F(RenderWidgetHostViewAuraTest, CompositorViewportPixelSizeWithScale) {}

// This test verifies that in AutoResize mode a new
// blink::mojom::Widget::UpdateVisualProperties message is sent when ScreenInfo
// changes and that message contains the latest ScreenInfo.
TEST_F(RenderWidgetHostViewAuraTest, AutoResizeWithScale) {}

// Verifies that when synchronizing the visual properties after disabling the
// auto resize mode, both the view size and the auto resize state get updated at
// the same time (https://crbug.com/1200601).
TEST_F(RenderWidgetHostViewAuraTest,
       VerifyVisualPropertiesWhenDisablingAutoResize) {}

// This test verifies that in AutoResize mode a new
// blink::mojom::Widget::UpdateVisualProperties message is sent when size
// changes.
TEST_F(RenderWidgetHostViewAuraTest, AutoResizeWithBrowserInitiatedResize) {}

// This test verifies that in AutoResize mode a child-allocated
// viz::LocalSurfaceId will be properly routed and stored in the parent.
TEST_F(RenderWidgetHostViewAuraTest, ChildAllocationAcceptedInParent) {}

// This test verifies that if the parent is hidden when the child sends a
// child-allocated viz::LocalSurfaceId, the parent will store it and it will
// not send a blink::mojom::Widget::UpdateVisualProperties back to the child.
TEST_F(RenderWidgetHostViewAuraTest,
       ChildAllocationAcceptedInParentWhileHidden) {}

// This test verifies that when the child and parent both allocate their own
// viz::LocalSurfaceId the resulting conflict is resolved.
TEST_F(RenderWidgetHostViewAuraTest, ConflictingAllocationsResolve) {}

// Checks that WidgetInputHandler::CursorVisibilityChange IPC messages are
// dispatched to the renderer at the correct times.
TEST_F(RenderWidgetHostViewAuraTest, CursorVisibilityChange) {}

TEST_F(RenderWidgetHostViewAuraTest, UpdateCursorIfOverSelf) {}

TEST_F(RenderWidgetHostViewAuraTest, ZeroSizeStillGetsLocalSurfaceId) {}

TEST_F(RenderWidgetHostViewAuraTest, BackgroundColorMatchesCompositorFrame) {}

// Tests background setting priority.
TEST_F(RenderWidgetHostViewAuraTest, BackgroundColorOrder) {}

TEST_F(RenderWidgetHostViewAuraTest, Resize) {}

// This test verifies that the primary SurfaceId is populated on resize.
TEST_F(RenderWidgetHostViewAuraTest, SurfaceChanges) {}

// This test verifies that the primary SurfaceId is updated on device scale
// factor changes.
TEST_F(RenderWidgetHostViewAuraTest, DeviceScaleFactorChanges) {}

// This test verifies that frame eviction plays well with surface
// synchronization.
TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFrames) {}

// Test that changing the memory pressure should delete saved frames. This test
// only applies to ChromeOS.
TEST_F(RenderWidgetHostViewAuraTest, DiscardDelegatedFramesWithMemoryPressure) {}

TEST_F(RenderWidgetHostViewAuraTest, VisibleViewportTest) {}

// Ensures that touch event positions are never truncated to integers.
TEST_F(RenderWidgetHostViewAuraTest, TouchEventPositionsArentRounded) {}

// Tests that non-precise mouse-wheel events do not initiate overscroll.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, WheelNotPreciseScrollEvent) {}
// Tests that precise mouse-wheel events initiate overscroll and a mouse move
// will cancel it.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, WheelScrollEventOverscrolls) {}

// Tests that if some scroll events are consumed towards the start, then
// subsequent scrolls do not overscroll.
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       WheelScrollConsumedDoNotOverscroll) {}

// Tests that wheel-scrolling correctly turns overscroll on and off.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, WheelScrollOverscrollToggle) {}

// Tests that a small fling after overscroll is initiated aborts the overscroll.
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       ScrollEventsOverscrollWithFling) {}

// Same as ScrollEventsOverscrollWithFling, but with zero velocity. Checks that
// the zero-velocity fling does not reach the renderer.
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       ScrollEventsOverscrollWithZeroFling) {}

// Tests that a fling in the opposite direction of the overscroll cancels the
// overscroll instead of completing it.
// Flaky on Fuchsia:  http://crbug.com/810690.
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
#define MAYBE_ReverseFlingCancelsOverscroll
#else
#define MAYBE_ReverseFlingCancelsOverscroll
#endif
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       MAYBE_ReverseFlingCancelsOverscroll) {}

// Tests that touch-scroll events are handled correctly by the overscroll
// controller. This also tests that the overscroll controller and the
// gesture-event filter play nice with each other.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, GestureScrollOverscrolls) {}

// Tests that when a cap is set for overscroll delta, extra overscroll delta is
// ignored.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollDeltaCap) {}

// Tests that if the page is scrolled because of a scroll-gesture, then that
// particular scroll sequence never generates overscroll.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, GestureScrollConsumed) {}

// Tests that the overscroll controller plays nice with touch-scrolls and the
// gesture event filter with debounce filtering turned on.
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       GestureScrollDebounceOverscrolls) {}

// Tests that the gesture debounce timer plays nice with the overscroll
// controller.
// TODO(crbug.com/40545668): Disabled due to flakiness on Linux tsan.
#if BUILDFLAG(USING_SANITIZER)
#define MAYBE_GestureScrollDebounceTimerOverscroll
#else
#define MAYBE_GestureScrollDebounceTimerOverscroll
#endif
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       MAYBE_GestureScrollDebounceTimerOverscroll) {}

// Tests that when touch-events are dispatched to the renderer, the overscroll
// gesture deals with them correctly.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollWithTouchEvents) {}

// Tests that touch-gesture end is dispatched to the renderer at the end of a
// touch-gesture initiated overscroll.
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       TouchGestureEndDispatchedAfterOverscrollComplete) {}

// Tests that after touchscreen overscroll is initiated, scrolling in the
// opposite direction ends the overscroll in the original direction without
// initiating overscroll in the opposite direction. The scroll-update events
// should still be consumed to prevent content scroll.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollDirectionChange) {}

TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       CompleteOverscrollOnGestureScrollEndAck) {}

TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       InterleavedScrollUpdateAckAndScrollEnd) {}

// Tests that after touchpad overscroll is initiated, scrolling in the opposite
// direction ends the overscroll in the original direction without initiating
// overscroll in the opposite direction. The scroll-update events should still
// be consumed to prevent content scroll.
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       OverscrollDirectionChangeMouseWheel) {}

// Tests that mouse-move completes overscoll if it has passed activation
// threshold and aborts it otherwise.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollMouseMoveCompletion) {}

// Tests that if a page scrolled, then the overscroll controller's states are
// reset after the end of the scroll.
TEST_F(RenderWidgetHostViewAuraOverscrollTest,
       OverscrollStateResetsAfterScroll) {}

// Tests that overscroll is reset when window loses focus. It should not affect
// subsequent overscrolls.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollResetsOnBlur) {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
// Check that when accessibility virtual keyboard is enabled, windows are
// shifted up when focused and restored when focus is lost.
TEST_F(RenderWidgetHostViewAuraTest, VirtualKeyboardFocusEnsureCaretInRect) {
  // TODO (oshima): Test that overscroll occurs.

  InitViewForFrame(nullptr);
  aura::Window* root_window = parent_view_->GetNativeView()->GetRootWindow();
  aura::client::ParentWindowWithContext(view_->GetNativeView(), root_window,
                                        gfx::Rect(),
                                        display::kInvalidDisplayId);

  const gfx::Rect orig_view_bounds = gfx::Rect(0, 300, 400, 200);
  const gfx::Rect shifted_view_bounds = gfx::Rect(0, 200, 400, 200);
  const gfx::Rect root_bounds = root_window->bounds();
  const int keyboard_height = 200;
  const gfx::Rect keyboard_view_bounds =
      gfx::Rect(0, root_bounds.height() - keyboard_height, root_bounds.width(),
                keyboard_height);

  ui::InputMethod* input_method = root_window->GetHost()->GetInputMethod();

  // Focus the window.
  view_->SetBounds(orig_view_bounds);
  input_method->SetFocusedTextInputClient(view_);
  EXPECT_EQ(view_->GetNativeView()->bounds(), orig_view_bounds);

  // Simulate virtual keyboard.
  input_method->SetVirtualKeyboardBounds(keyboard_view_bounds);

  // Window should be shifted.
  EXPECT_EQ(view_->GetNativeView()->bounds(), shifted_view_bounds);

  // Detach the RenderWidgetHostViewAura from the IME.
  view_->DetachFromInputMethod(false);

  // Window should be restored.
  EXPECT_EQ(view_->GetNativeView()->bounds(), orig_view_bounds);
}

// Check that the window insets is updated with the bounds changed when the
// virtual keyboard is shown.
TEST_F(RenderWidgetHostViewAuraTest, UpdateInsetsWithVirtualKeyboardEnabled) {
  InitViewForFrame(nullptr);
  aura::Window* root_window = parent_view_->GetNativeView()->GetRootWindow();
  aura::client::ParentWindowWithContext(view_->GetNativeView(), root_window,
                                        gfx::Rect(),
                                        display::kInvalidDisplayId);

  const gfx::Rect orig_view_bounds = gfx::Rect(0, 300, 400, 200);
  const gfx::Rect shifted_view_bounds = gfx::Rect(0, 200, 400, 200);
  const gfx::Rect moved_view_bounds = gfx::Rect(100, 250, 400, 200);
  const gfx::Rect resized_view_bounds = gfx::Rect(100, 250, 300, 175);

  const auto origin_view_insets = gfx::Insets::TLBR(0, 0, 100, 0);
  const auto shifted_view_insets = gfx::Insets();
  const auto moved_view_insets = gfx::Insets::TLBR(0, 0, 50, 0);
  const auto resized_view_insets = gfx::Insets::TLBR(0, 0, 25, 0);

  const gfx::Rect root_bounds = root_window->bounds();
  const int keyboard_height = 200;
  const gfx::Rect keyboard_view_bounds =
      gfx::Rect(0, root_bounds.height() - keyboard_height, root_bounds.width(),
                keyboard_height);

  ui::InputMethod* input_method = root_window->GetHost()->GetInputMethod();

  // Focus the window.
  view_->SetBounds(orig_view_bounds);
  input_method->SetFocusedTextInputClient(view_);
  EXPECT_EQ(view_->GetNativeView()->bounds(), orig_view_bounds);

  // Simulate virtual keyboard. For chrome browser window, the window insets
  // will be changed.
  view_->SetInsets(gfx::Insets::TLBR(
      0, 0,
      gfx::IntersectRects(orig_view_bounds, keyboard_view_bounds).height(), 0));
  EXPECT_EQ(view_->insets_, origin_view_insets);
  input_method->SetVirtualKeyboardBounds(keyboard_view_bounds);

  // Window should be shifted. The insets will be updated.
  EXPECT_EQ(view_->GetNativeView()->bounds(), shifted_view_bounds);
  EXPECT_EQ(view_->insets_, shifted_view_insets);

  // Move the view and the insets will be updated.
  view_->SetBounds(moved_view_bounds);
  EXPECT_EQ(view_->insets_, moved_view_insets);

  // Resize the view and the insets will be updated.
  view_->SetBounds(resized_view_bounds);
  EXPECT_EQ(view_->insets_, resized_view_insets);
}

#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

// Tests that invalid touch events are consumed and handled
// synchronously.
TEST_F(RenderWidgetHostViewAuraTest,
       InvalidEventsHaveSyncHandlingDisabled) {}

// Checks key event codes.
TEST_F(RenderWidgetHostViewAuraTest, KeyEvent) {}

TEST_F(RenderWidgetHostViewAuraTest, KeyEventsHandled) {}

TEST_F(RenderWidgetHostViewAuraTest, SetCanScrollForWebMouseWheelEvent) {}

// Ensures that the mapping from ui::TouchEvent to blink::WebTouchEvent doesn't
// lose track of the number of acks required.
TEST_F(RenderWidgetHostViewAuraTest, CorrectNumberOfAcksAreDispatched) {}

// Tests that the scroll deltas stored within the overscroll controller get
// reset at the end of the overscroll gesture even if the overscroll threshold
// isn't surpassed and the overscroll mode stays OVERSCROLL_NONE.
TEST_F(RenderWidgetHostViewAuraOverscrollTest, ScrollDeltasResetOnEnd) {}

TEST_F(RenderWidgetHostViewAuraTest, ForwardMouseEvent) {}

#if BUILDFLAG(IS_WIN)
class MockWindowEventTarget : public ui::WindowEventTarget {
 public:
  MockWindowEventTarget() = default;

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

  LRESULT HandleMouseMessage(unsigned int message,
                             WPARAM w_param,
                             LPARAM l_param,
                             bool* handled) override {
    return S_OK;
  }

  LRESULT HandlePointerMessage(unsigned int message,
                               WPARAM w_param,
                               LPARAM l_param,
                               bool* handled) override {
    return S_OK;
  }

  LRESULT HandleKeyboardMessage(unsigned int message,
                                WPARAM w_param,
                                LPARAM l_param,
                                bool* handled) override {
    return S_OK;
  }

  LRESULT HandleTouchMessage(unsigned int message,
                             WPARAM w_param,
                             LPARAM l_param,
                             bool* handled) override {
    return S_OK;
  }

  LRESULT HandleInputMessage(unsigned int message,
                             WPARAM w_param,
                             LPARAM l_param,
                             bool* handled) override {
    return S_OK;
  }

  LRESULT HandleScrollMessage(unsigned int message,
                              WPARAM w_param,
                              LPARAM l_param,
                              bool* handled) override {
    return S_OK;
  }

  LRESULT HandleNcHitTestMessage(unsigned int message,
                                 WPARAM w_param,
                                 LPARAM l_param,
                                 bool* handled) override {
    return S_OK;
  }

  void HandleParentChanged() override {}
  void ApplyPinchZoomScale(float scale) override {}
  void ApplyPinchZoomBegin() override {}
  void ApplyPinchZoomEnd() override {}
  void ApplyPanGestureScroll(int scroll_x, int scroll_y) override {}
  void ApplyPanGestureFling(int scroll_x, int scroll_y) override {}
  void ApplyPanGestureScrollBegin(int scroll_x, int scroll_y) override {}
  void ApplyPanGestureFlingBegin() override {}
  void ApplyPanGestureFlingEnd() override {}
  void ApplyPanGestureScrollEnd(bool tranisitioning_to_pinch) override {}
};

// On Windows, a native HWND (Chrome_RenderWidgetHostHWND) forwards mouse events
// to the browser window so that they can reach the tooltip controller. Since we
// reparent this HWND when the view is occluded, some mouse exits might not be
// forwarded, resulting in stuck tooltips. Test that tooltips are cleared.
TEST_F(RenderWidgetHostViewAuraTest, OcclusionHidesTooltip) {
  // Give the host window an event target, which allows the view to create the
  // LegacyRenderWidgetHostHWND Chrome_RenderWidgetHostHWND window.
  MockWindowEventTarget event_target;
  auto prop_window_target = std::make_unique<ui::ViewProp>(
      parent_view_->GetHostWindowHWND(),
      ui::WindowEventTarget::kWin32InputEventTarget,
      static_cast<ui::WindowEventTarget*>(&event_target));

  // Initialize the view.
  InitViewForFrame(nullptr);
  ParentHostView(view_, parent_view_);
  view_->Show();
  EXPECT_TRUE(legacy_render_widget_host_HWND());

  // Simulate a tooltip.
  std::u16string tooltip_text(u"The tooltip!");
  view_->UpdateTooltipUnderCursor(tooltip_text);
  EXPECT_FALSE(widget_host_->is_hidden());
  EXPECT_EQ(tooltip_text, view_->tooltip_);

  // Simulate occlusion, which should clear the tooltip.
  view_->WasOccluded();
  EXPECT_TRUE(widget_host_->is_hidden());
  EXPECT_EQ(std::u16string(), view_->tooltip_);
}

TEST_F(RenderWidgetHostViewAuraTest, LegacyRenderWidgetHostHWNDAuraLookup) {
  // Give the host window an event target, which allows the view to create the
  // LegacyRenderWidgetHostHWND Chrome_RenderWidgetHostHWND window.
  MockWindowEventTarget event_target;
  auto prop_window_target = std::make_unique<ui::ViewProp>(
      parent_view_->GetHostWindowHWND(),
      ui::WindowEventTarget::kWin32InputEventTarget,
      static_cast<ui::WindowEventTarget*>(&event_target));

  // Initialize the view.
  InitViewForFrame(nullptr);
  ParentHostView(view_, parent_view_);
  view_->Show();

  ASSERT_TRUE(legacy_render_widget_host_HWND());
  HWND hwnd = legacy_render_widget_host_HWND()->hwnd();
  EXPECT_TRUE(hwnd);
  auto* window_tree_host = aura::WindowTreeHost::GetForAcceleratedWidget(hwnd);
  EXPECT_TRUE(window_tree_host);
  EXPECT_EQ(view_->GetNativeView()->GetHost(), window_tree_host);
}
#endif

// Test that we elide touchpad pinch gesture steams consisting of only begin
// and end events.
TEST_F(RenderWidgetHostViewAuraTest, ElideEmptyTouchpadPinchSequence) {}

TEST_F(RenderWidgetHostViewAuraTest,
       TouchpadScrollThenPinchFiresImmediateScrollEnd) {}

TEST_F(RenderWidgetHostViewAuraTest, GestureTapFromStylusHasPointerType) {}

// Test that the rendering timeout for newly loaded content fires when enough
// time passes without receiving a new compositor frame.
// TODO(crbug.com/40775652): This test is flaky on "Linux ASan LSan Tests
// (1)"
#if BUILDFLAG(IS_LINUX)
#define MAYBE_NewContentRenderingTimeout
#else
#define MAYBE_NewContentRenderingTimeout
#endif
TEST_F(RenderWidgetHostViewAuraTest, MAYBE_NewContentRenderingTimeout) {}

// If a tab is evicted, allocate a new LocalSurfaceId next time it's shown.
TEST_F(RenderWidgetHostViewAuraTest, AllocateLocalSurfaceIdOnEviction) {}

// If a tab was resized while it's hidden, drop the fallback so next time it's
// visible we show blank.
TEST_F(RenderWidgetHostViewAuraTest, DropFallbackIfResizedWhileHidden) {}

// If a tab is hidden and shown without being resized in the meantime, the
// fallback SurfaceId has to be preserved.
TEST_F(RenderWidgetHostViewAuraTest, DontDropFallbackIfNotResizedWhileHidden) {}

// Check that TakeFallbackContentFrom() copies the fallback SurfaceId and
// background color from the previous view to the new view.
TEST_F(RenderWidgetHostViewAuraTest, TakeFallbackContent) {}

// Check that TakeFallbackContentFrom() copies the fallback SurfaceId and
// background color from the previous view to the new view if the new view is
// for a pre-rendered page which is loaded as hidden.
TEST_F(RenderWidgetHostViewAuraTest, TakeFallbackContentForPrerender) {}

// This class provides functionality to test a RenderWidgetHostViewAura
// instance which has been hooked up to a test RenderViewHost instance and
// a WebContents instance.
class RenderWidgetHostViewAuraWithViewHarnessTest
    : public RenderViewHostImplTestHarness {};

// Provides a mock implementation of the WebContentsViewDelegate class.
// Currently provides functionality to validate the ShowContextMenu
// callback.
class MockWebContentsViewDelegate : public WebContentsViewDelegate {};

// On Windows we don't want the context menu to be displayed in the context of
// a long press gesture. It should be displayed when the touch is released.
// On other platforms we should display the context menu in the long press
// gesture.
// This test validates this behavior.
TEST_F(RenderWidgetHostViewAuraWithViewHarnessTest,
       ContextMenuTest) {}

// ----------------------------------------------------------------------------
// TextInputManager and IME-Related Tests

// The test class for OOPIF IME related unit tests in RenderWidgetHostViewAura.
// In each test, 3 views are created where one is in process with main frame and
// the other two are in distinct processes (this makes a total of 4 RWHVs).
class InputMethodAuraTestBase : public RenderWidgetHostViewAuraTest {};

// A group of tests which verify that the IME method results are routed to the
// right RenderWidget when there are multiple RenderWidgetHostViews on tab. Each
// test will verify the correctness of routing for one of the IME result
// methods. The method is called on ui::TextInputClient (i.e., RWHV for the tab
// in aura) and then the test verifies that the IPC is routed to the
// RenderWidget corresponding to the active view (i.e., the RenderWidget
// with focused <input>).
class InputMethodResultAuraTest : public InputMethodAuraTestBase {};

// This test verifies ui::TextInputClient::SetCompositionText.
TEST_F(InputMethodResultAuraTest, SetCompositionText) {}

// This test is for ui::TextInputClient::ConfirmCompositionText.
TEST_F(InputMethodResultAuraTest, ConfirmCompositionText) {}

// This test is for ui::TextInputClient::ClearCompositionText.
TEST_F(InputMethodResultAuraTest, ClearCompositionText) {}

// This test is for ui::TextInputClient::InsertText with empty text.
TEST_F(InputMethodResultAuraTest, InsertEmptyText) {}

// This test is for ui::TextInputClient::InsertText with non-empty text.
TEST_F(InputMethodResultAuraTest, CommitText) {}

TEST_F(InputMethodResultAuraTest, CommitTextWithEmptyText) {}

TEST_F(InputMethodResultAuraTest, CommitTextBeforeCursor) {}

// This test is for RenderWidgetHostViewAura::FinishImeCompositionSession which
// is in response to a mouse click during an ongoing composition.
TEST_F(InputMethodResultAuraTest, FinishImeCompositionSession) {}

// This test is for ui::TextInputClient::ChangeTextDirectionAndLayoutAlignment.
TEST_F(InputMethodResultAuraTest, ChangeTextDirectionAndLayoutAlignment) {}

// A class of tests which verify the correctness of some tracked IME related
// state at the browser side. Each test verifies the correctness tracking for
// one specific state. To do so, the views are activated in a predetermined
// sequence and each time, the IPC call for the corresponding state is simulated
// through calling the method on the view. Then the test verifies that the value
// returned by the view or ui::TextInputClient is the expected value from IPC.
class InputMethodStateAuraTest : public InputMethodAuraTestBase {};

// This test is for caret bounds which are calculated based on the tracked value
// for selection bounds.
TEST_F(InputMethodStateAuraTest, GetCaretBounds) {}

// This test is for composition character bounds.
TEST_F(InputMethodStateAuraTest, GetCompositionCharacterBounds) {}

// This test is for selected text.
TEST_F(InputMethodStateAuraTest, GetSelectedText) {}

// This test is for text range.
TEST_F(InputMethodStateAuraTest, GetTextRange) {}

TEST_F(InputMethodStateAuraTest, GetCompositionTextRange) {}

// This test is for selection range.
TEST_F(InputMethodStateAuraTest, GetEditableSelectionRange) {}

TEST_F(InputMethodStateAuraTest, GetTextFromRange) {}

// This test will verify that after selection, the selected text is written to
// the clipboard from the focused widget.
TEST_F(InputMethodStateAuraTest, SelectedTextCopiedToClipboard) {}

// This test verifies that when any view on the page cancels an ongoing
// composition, the RenderWidgetHostViewAura will receive the notification and
// the current composition is canceled.
TEST_F(InputMethodStateAuraTest, ImeCancelCompositionForAllViews) {}

// This test verifies that when the focused node is changed,
// RenderWidgetHostViewAura will tell InputMethodAuraLinux to cancel the current
// composition.
TEST_F(InputMethodStateAuraTest, ImeFocusedNodeChanged) {}

TEST_F(RenderWidgetHostViewAuraTest, FocusReasonNotFocused) {}

TEST_F(RenderWidgetHostViewAuraTest, FocusReasonMouse) {}

TEST_F(RenderWidgetHostViewAuraTest, FocusReasonTouch) {}

TEST_F(RenderWidgetHostViewAuraTest, FocusReasonPen) {}

TEST_F(RenderWidgetHostViewAuraTest, FocusReasonMultipleEventsOnSameNode) {}

class RenderWidgetHostViewAuraInputMethodTest
    : public RenderWidgetHostViewAuraTest,
      public ui::InputMethodObserver {};

// This test is for notifying InputMethod for surrounding text changes.
TEST_F(RenderWidgetHostViewAuraInputMethodTest, OnCaretBoundsChanged) {}

// The input method should still receive caret bounds changes even if inputmode
// is NONE. See crbug.com/1114559.
TEST_F(RenderWidgetHostViewAuraInputMethodTest,
       OnCaretBoundsChangedInputModeNone) {}

#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
class MockVirtualKeyboardController final
    : public ui::VirtualKeyboardController {
 public:
  MockVirtualKeyboardController() = default;

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

  bool DisplayVirtualKeyboard() override {
    virtual_keyboard_requested_ = true;
    return virtual_keyboard_requested_;
  }

  void DismissVirtualKeyboard() override {
    virtual_keyboard_requested_ = false;
  }

  void AddObserver(ui::VirtualKeyboardControllerObserver* observer) override {
    observer_count_++;
  }

  void RemoveObserver(
      ui::VirtualKeyboardControllerObserver* observer) override {
    observer_count_--;
  }

  bool IsKeyboardVisible() override { return virtual_keyboard_requested_; }

  size_t observer_count() const { return observer_count_; }

 private:
  size_t observer_count_ = 0;
  bool virtual_keyboard_requested_ = false;
};

class RenderWidgetHostViewAuraKeyboardMockInputMethod
    : public ui::MockInputMethod {
 public:
  RenderWidgetHostViewAuraKeyboardMockInputMethod()
      : MockInputMethod(nullptr) {}

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

  ui::VirtualKeyboardController* GetVirtualKeyboardController() override {
    return &keyboard_controller_;
  }
  size_t keyboard_controller_observer_count() const {
    return keyboard_controller_.observer_count();
  }
  void SetVirtualKeyboardVisibilityIfEnabled(bool should_show) override {
    if (should_show) {
      keyboard_controller_.DisplayVirtualKeyboard();
    } else {
      keyboard_controller_.DismissVirtualKeyboard();
    }
  }
  bool IsKeyboardVisible() { return keyboard_controller_.IsKeyboardVisible(); }

 private:
  MockVirtualKeyboardController keyboard_controller_;
};

class RenderWidgetHostViewAuraKeyboardTest
    : public RenderWidgetHostViewAuraTest {
 public:
  RenderWidgetHostViewAuraKeyboardTest() = default;

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

  ~RenderWidgetHostViewAuraKeyboardTest() override {}
  void SetUp() override {
    // TODO(crbug.com/40275284) Pass as unique_ptr<>.
    ui::SetUpInputMethodForTesting(
        new RenderWidgetHostViewAuraKeyboardMockInputMethod());
    SetUpEnvironment();
  }

  RenderWidgetHostViewAuraKeyboardMockInputMethod* GetMockInputMethod() const {
    return static_cast<RenderWidgetHostViewAuraKeyboardMockInputMethod*>(
        GetInputMethod());
  }

  size_t keyboard_controller_observer_count() const {
    return GetMockInputMethod()->keyboard_controller_observer_count();
  }
  bool IsKeyboardVisible() const {
    return GetMockInputMethod()->IsKeyboardVisible();
  }
};
#endif

#if BUILDFLAG(IS_CHROMEOS)
TEST_F(RenderWidgetHostViewAuraKeyboardTest,
       UpdateTextInputStateUpdatesVirtualKeyboardState) {
  ActivateViewForTextInputManager(parent_view_, ui::TEXT_INPUT_TYPE_TEXT);

  ui::mojom::TextInputState state;
  state.type = ui::TEXT_INPUT_TYPE_TEXT;
  state.mode = ui::TEXT_INPUT_MODE_NONE;
  state.last_vk_visibility_request =
      ui::mojom::VirtualKeyboardVisibilityRequest::SHOW;

  EXPECT_EQ(IsKeyboardVisible(), false);

  GetTextInputManager(parent_view_)->UpdateTextInputState(parent_view_, state);

  EXPECT_EQ(IsKeyboardVisible(), true);

  state.last_vk_visibility_request =
      ui::mojom::VirtualKeyboardVisibilityRequest::HIDE;
  GetTextInputManager(parent_view_)->UpdateTextInputState(parent_view_, state);

  EXPECT_EQ(IsKeyboardVisible(), false);
}
#endif

#if BUILDFLAG(IS_WIN)
TEST_F(RenderWidgetHostViewAuraKeyboardTest, KeyboardObserverDestroyed) {
  parent_view_->SetLastPointerType(ui::EventPointerType::kTouch);
  ActivateViewForTextInputManager(parent_view_, ui::TEXT_INPUT_TYPE_TEXT);
  EXPECT_NE(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 1u);
  EXPECT_EQ(IsKeyboardVisible(), true);
  // Detach the RenderWidgetHostViewAura from the IME.
  parent_view_->DetachFromInputMethod(true);
  EXPECT_EQ(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 0u);
}

TEST_F(RenderWidgetHostViewAuraKeyboardTest, NoKeyboardObserverForMouseInput) {
  // Not checking for both touch and mouse inputs here as the user could use
  // mouse and touch input on a touch device. The keyboard observer shouldn't be
  // removed after it has been registered with a touch input and we received a
  // mouse event.
  // Do not show virtual keyboard for mouse inputs.
  parent_view_->SetLastPointerType(ui::EventPointerType::kMouse);
  ActivateViewForTextInputManager(parent_view_, ui::TEXT_INPUT_TYPE_TEXT);
  EXPECT_EQ(keyboard_controller_observer_count(), 0u);
  EXPECT_EQ(IsKeyboardVisible(), false);
}

TEST_F(RenderWidgetHostViewAuraKeyboardTest,
       KeyboardObserverForOnlyTouchInput) {
  // Show virtual keyboard for touch inputs.
  parent_view_->SetLastPointerType(ui::EventPointerType::kTouch);
  ActivateViewForTextInputManager(parent_view_, ui::TEXT_INPUT_TYPE_TEXT);
  EXPECT_NE(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 1u);
  EXPECT_EQ(IsKeyboardVisible(), true);
}

TEST_F(RenderWidgetHostViewAuraKeyboardTest,
       KeyboardObserverForFocusedNodeChanged) {
  // Show virtual keyboard for touch inputs.
  parent_view_->SetLastPointerType(ui::EventPointerType::kTouch);
  ActivateViewForTextInputManager(parent_view_, ui::TEXT_INPUT_TYPE_TEXT);
  EXPECT_EQ(IsKeyboardVisible(), true);
  EXPECT_NE(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 1u);

  // Change the focused node to a read-only node so the keyboard is dismissed,
  // but the keyboard observer should still be valid.
  parent_view_->FocusedNodeChanged(false, gfx::Rect());
  EXPECT_NE(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 1u);
  EXPECT_EQ(IsKeyboardVisible(), false);
  // Detaching the input method should destroy the keyboard observer.
  parent_view_->DetachFromInputMethod(true);
  EXPECT_EQ(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 0u);
}

TEST_F(RenderWidgetHostViewAuraKeyboardTest, KeyboardObserverForPenInput) {
  // Show virtual keyboard for pen inputs.
  parent_view_->SetLastPointerType(ui::EventPointerType::kPen);
  ActivateViewForTextInputManager(parent_view_, ui::TEXT_INPUT_TYPE_TEXT);
  EXPECT_NE(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 1u);
}

TEST_F(RenderWidgetHostViewAuraKeyboardTest,
       KeyboardObserverDetachDuringWindowDestroy) {
  parent_view_->SetLastPointerType(ui::EventPointerType::kTouch);
  ActivateViewForTextInputManager(parent_view_, ui::TEXT_INPUT_TYPE_TEXT);
  EXPECT_NE(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 1u);
  EXPECT_EQ(IsKeyboardVisible(), true);
  // Detach the RenderWidgetHostViewAura from the IME, but don't destroy the
  // Vk controller as it might need to notify about VK hide so the sites can
  // reflow their content.
  parent_view_->DetachFromInputMethod(false);
  EXPECT_EQ(IsKeyboardVisible(), false);
  EXPECT_NE(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 1u);
  // Detach the keyboard observer as the window is getting destroyed.
  parent_view_->DetachFromInputMethod(true);
  EXPECT_EQ(parent_view_->virtual_keyboard_controller_win_.get(), nullptr);
  EXPECT_EQ(keyboard_controller_observer_count(), 0u);
}

#endif  // BUILDFLAG(IS_WIN)

}  // namespace content