|  | // 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 <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <memory> | 
|  | #include <tuple> | 
|  |  | 
|  | #include "base/command_line.h" | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/location.h" | 
|  | #include "base/memory/raw_ptr.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/task/single_thread_task_runner.h" | 
|  | #include "base/test/metrics/histogram_tester.h" | 
|  | #include "base/test/scoped_feature_list.h" | 
|  | #include "base/test/simple_test_tick_clock.h" | 
|  | #include "base/time/time.h" | 
|  | #include "base/timer/timer.h" | 
|  | #include "build/build_config.h" | 
|  | #include "cc/mojom/render_frame_metadata.mojom.h" | 
|  | #include "cc/trees/render_frame_metadata.h" | 
|  | #include "components/viz/common/surfaces/local_surface_id.h" | 
|  | #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" | 
|  | #include "components/viz/test/begin_frame_args_test.h" | 
|  | #include "content/browser/blob_storage/chrome_blob_storage_context.h" | 
|  | #include "content/browser/gpu/compositor_util.h" | 
|  | #include "content/browser/renderer_host/data_transfer_util.h" | 
|  | #include "content/browser/renderer_host/display_feature.h" | 
|  | #include "content/browser/renderer_host/frame_token_message_queue.h" | 
|  | #include "content/browser/renderer_host/input/mock_input_router.h" | 
|  | #include "content/browser/renderer_host/input/touch_emulator.h" | 
|  | #include "content/browser/renderer_host/mock_render_widget_host.h" | 
|  | #include "content/browser/renderer_host/render_view_host_delegate_view.h" | 
|  | #include "content/browser/renderer_host/render_widget_host_delegate.h" | 
|  | #include "content/browser/renderer_host/render_widget_host_view_base.h" | 
|  | #include "content/browser/renderer_host/visible_time_request_trigger.h" | 
|  | #include "content/browser/site_instance_group.h" | 
|  | #include "content/browser/storage_partition_impl.h" | 
|  | #include "content/common/content_constants_internal.h" | 
|  | #include "content/public/browser/keyboard_event_processing_result.h" | 
|  | #include "content/public/common/content_features.h" | 
|  | #include "content/public/common/content_switches.h" | 
|  | #include "content/public/common/drop_data.h" | 
|  | #include "content/public/test/browser_task_environment.h" | 
|  | #include "content/public/test/mock_render_process_host.h" | 
|  | #include "content/public/test/test_browser_context.h" | 
|  | #include "content/test/mock_widget.h" | 
|  | #include "content/test/mock_widget_input_handler.h" | 
|  | #include "content/test/stub_render_widget_host_owner_delegate.h" | 
|  | #include "content/test/test_render_view_host.h" | 
|  | #include "content/test/test_render_widget_host.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 "skia/ext/skia_utils_base.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/common/page/page_zoom.h" | 
|  | #include "third_party/blink/public/common/widget/visual_properties.h" | 
|  | #include "third_party/blink/public/mojom/drag/drag.mojom.h" | 
|  | #include "third_party/blink/public/mojom/input/input_handler.mojom-shared.h" | 
|  | #include "third_party/blink/public/mojom/input/touch_event.mojom.h" | 
|  | #include "ui/base/cursor/cursor.h" | 
|  | #include "ui/display/display_util.h" | 
|  | #include "ui/display/screen.h" | 
|  | #include "ui/events/base_event_utils.h" | 
|  | #include "ui/events/blink/blink_features.h" | 
|  | #include "ui/events/blink/web_input_event_traits.h" | 
|  | #include "ui/events/gesture_detection/gesture_provider_config_helper.h" | 
|  | #include "ui/events/keycodes/keyboard_codes.h" | 
|  | #include "ui/gfx/canvas.h" | 
|  |  | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | #include "base/strings/utf_string_conversions.h" | 
|  | #include "content/browser/renderer_host/render_widget_host_view_android.h" | 
|  | #include "ui/android/screen_android.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | #include "content/browser/renderer_host/test_render_widget_host_view_mac_factory.h" | 
|  | #include "ui/display/test/test_screen.h" | 
|  | #endif | 
|  |  | 
|  | #if BUILDFLAG(IS_IOS) | 
|  | #include "content/browser/renderer_host/test_render_widget_host_view_ios_factory.h" | 
|  | #endif | 
|  |  | 
|  | #if defined(USE_AURA) || BUILDFLAG(IS_MAC) | 
|  | #include "content/browser/compositor/test/test_image_transport_factory.h" | 
|  | #endif | 
|  |  | 
|  | #if defined(USE_AURA) | 
|  | #include "content/browser/renderer_host/render_widget_host_view_aura.h" | 
|  | #include "content/browser/renderer_host/ui_events_helper.h" | 
|  | #include "ui/aura/test/test_screen.h" | 
|  | #include "ui/events/event.h" | 
|  | #endif | 
|  |  | 
|  | using blink::WebGestureDevice; | 
|  | using blink::WebGestureEvent; | 
|  | using blink::WebInputEvent; | 
|  | using blink::WebKeyboardEvent; | 
|  | using blink::WebMouseEvent; | 
|  | using blink::WebMouseWheelEvent; | 
|  | using blink::WebTouchEvent; | 
|  |  | 
|  | using testing::_; | 
|  | using testing::Return; | 
|  |  | 
|  | namespace content { | 
|  | namespace  { | 
|  |  | 
|  | // RenderWidgetHostProcess ----------------------------------------------------- | 
|  |  | 
|  | class RenderWidgetHostProcess : public MockRenderProcessHost { | 
|  | public: | 
|  | explicit RenderWidgetHostProcess(BrowserContext* browser_context) | 
|  | : MockRenderProcessHost(browser_context) { | 
|  | } | 
|  |  | 
|  | RenderWidgetHostProcess(const RenderWidgetHostProcess&) = delete; | 
|  | RenderWidgetHostProcess& operator=(const RenderWidgetHostProcess&) = delete; | 
|  |  | 
|  | ~RenderWidgetHostProcess() override {} | 
|  |  | 
|  | bool IsInitializedAndNotDead() override { return true; } | 
|  | }; | 
|  |  | 
|  | // TestView -------------------------------------------------------------------- | 
|  |  | 
|  | // This test view allows us to specify the size, and keep track of acked | 
|  | // touch-events. | 
|  | class TestView : public TestRenderWidgetHostView { | 
|  | public: | 
|  | explicit TestView(RenderWidgetHostImpl* rwh) | 
|  | : TestRenderWidgetHostView(rwh), | 
|  | unhandled_wheel_event_count_(0), | 
|  | acked_event_count_(0), | 
|  | gesture_event_type_(WebInputEvent::Type::kUndefined), | 
|  | use_fake_compositor_viewport_pixel_size_(false), | 
|  | ack_result_(blink::mojom::InputEventResultState::kUnknown) { | 
|  | local_surface_id_allocator_.GenerateId(); | 
|  | } | 
|  |  | 
|  | TestView(const TestView&) = delete; | 
|  | TestView& operator=(const TestView&) = delete; | 
|  |  | 
|  | // Sets the bounds returned by GetViewBounds. | 
|  | void SetBounds(const gfx::Rect& bounds) override { | 
|  | if (bounds_ == bounds) | 
|  | return; | 
|  | bounds_ = bounds; | 
|  | local_surface_id_allocator_.GenerateId(); | 
|  | } | 
|  |  | 
|  | void SetScreenInfo(const display::ScreenInfo& screen_info) { | 
|  | if (screen_info_ == screen_info) | 
|  | return; | 
|  | screen_info_ = screen_info; | 
|  | local_surface_id_allocator_.GenerateId(); | 
|  | } | 
|  |  | 
|  | void InvalidateLocalSurfaceId() { local_surface_id_allocator_.Invalidate(); } | 
|  |  | 
|  | display::ScreenInfo GetScreenInfo() const override { return screen_info_; } | 
|  | display::ScreenInfos GetScreenInfos() const override { | 
|  | return display::ScreenInfos(screen_info_); | 
|  | } | 
|  |  | 
|  | const WebTouchEvent& acked_event() const { return acked_event_; } | 
|  | int acked_event_count() const { return acked_event_count_; } | 
|  | void ClearAckedEvent() { | 
|  | acked_event_.SetType(blink::WebInputEvent::Type::kUndefined); | 
|  | acked_event_count_ = 0; | 
|  | } | 
|  |  | 
|  | const WebMouseWheelEvent& unhandled_wheel_event() const { | 
|  | return unhandled_wheel_event_; | 
|  | } | 
|  | int unhandled_wheel_event_count() const { | 
|  | return unhandled_wheel_event_count_; | 
|  | } | 
|  | WebInputEvent::Type gesture_event_type() const { return gesture_event_type_; } | 
|  | blink::mojom::InputEventResultState ack_result() const { return ack_result_; } | 
|  |  | 
|  | void SetMockCompositorViewportPixelSize( | 
|  | const gfx::Size& mock_compositor_viewport_pixel_size) { | 
|  | if (use_fake_compositor_viewport_pixel_size_ && | 
|  | mock_compositor_viewport_pixel_size_ == | 
|  | mock_compositor_viewport_pixel_size) { | 
|  | return; | 
|  | } | 
|  | use_fake_compositor_viewport_pixel_size_ = true; | 
|  | mock_compositor_viewport_pixel_size_ = mock_compositor_viewport_pixel_size; | 
|  | local_surface_id_allocator_.GenerateId(); | 
|  | } | 
|  | void ClearMockCompositorViewportPixelSize() { | 
|  | if (!use_fake_compositor_viewport_pixel_size_) | 
|  | return; | 
|  | use_fake_compositor_viewport_pixel_size_ = false; | 
|  | local_surface_id_allocator_.GenerateId(); | 
|  | } | 
|  |  | 
|  | // RenderWidgetHostView override. | 
|  | gfx::Rect GetViewBounds() override { return bounds_; } | 
|  | const viz::LocalSurfaceId& GetLocalSurfaceId() const override { | 
|  | return local_surface_id_allocator_.GetCurrentLocalSurfaceId(); | 
|  | } | 
|  |  | 
|  | void SetInsets(const gfx::Insets& insets) override { insets_ = insets; } | 
|  | gfx::Size GetVisibleViewportSize() override { | 
|  | gfx::Rect requested_rect(GetRequestedRendererSize()); | 
|  | requested_rect.Inset(insets_); | 
|  | return requested_rect.size(); | 
|  | } | 
|  |  | 
|  | void ProcessAckedTouchEvent( | 
|  | const TouchEventWithLatencyInfo& touch, | 
|  | blink::mojom::InputEventResultState ack_result) override { | 
|  | acked_event_ = touch.event; | 
|  | ++acked_event_count_; | 
|  | } | 
|  | void WheelEventAck(const WebMouseWheelEvent& event, | 
|  | blink::mojom::InputEventResultState ack_result) override { | 
|  | if (ack_result == blink::mojom::InputEventResultState::kConsumed) | 
|  | return; | 
|  | unhandled_wheel_event_count_++; | 
|  | unhandled_wheel_event_ = event; | 
|  | } | 
|  | void GestureEventAck( | 
|  | const WebGestureEvent& event, | 
|  | blink::mojom::InputEventResultState ack_result, | 
|  | blink::mojom::ScrollResultDataPtr scroll_result_data) override { | 
|  | gesture_event_type_ = event.GetType(); | 
|  | ack_result_ = ack_result; | 
|  | } | 
|  | gfx::Size GetCompositorViewportPixelSize() override { | 
|  | if (use_fake_compositor_viewport_pixel_size_) | 
|  | return mock_compositor_viewport_pixel_size_; | 
|  | return TestRenderWidgetHostView::GetCompositorViewportPixelSize(); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | WebMouseWheelEvent unhandled_wheel_event_; | 
|  | int unhandled_wheel_event_count_; | 
|  | WebTouchEvent acked_event_; | 
|  | int acked_event_count_; | 
|  | WebInputEvent::Type gesture_event_type_; | 
|  | gfx::Rect bounds_; | 
|  | bool use_fake_compositor_viewport_pixel_size_; | 
|  | gfx::Size mock_compositor_viewport_pixel_size_; | 
|  | blink::mojom::InputEventResultState ack_result_; | 
|  | viz::ParentLocalSurfaceIdAllocator local_surface_id_allocator_; | 
|  | display::ScreenInfo screen_info_; | 
|  | gfx::Insets insets_; | 
|  | }; | 
|  |  | 
|  | // MockRenderViewHostDelegateView ------------------------------------------ | 
|  | class MockRenderViewHostDelegateView : public RenderViewHostDelegateView { | 
|  | public: | 
|  | MockRenderViewHostDelegateView() = default; | 
|  |  | 
|  | MockRenderViewHostDelegateView(const MockRenderViewHostDelegateView&) = | 
|  | delete; | 
|  | MockRenderViewHostDelegateView& operator=( | 
|  | const MockRenderViewHostDelegateView&) = delete; | 
|  |  | 
|  | ~MockRenderViewHostDelegateView() override = default; | 
|  |  | 
|  | int start_dragging_count() const { return start_dragging_count_; } | 
|  |  | 
|  | // RenderViewHostDelegateView: | 
|  | void StartDragging(const DropData& drop_data, | 
|  | blink::DragOperationsMask allowed_ops, | 
|  | const gfx::ImageSkia& image, | 
|  | const gfx::Vector2d& cursor_offset, | 
|  | const gfx::Rect& drag_obj_rect, | 
|  | const blink::mojom::DragEventSourceInfo& event_info, | 
|  | RenderWidgetHostImpl* source_rwh) override { | 
|  | ++start_dragging_count_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | int start_dragging_count_ = 0; | 
|  | }; | 
|  |  | 
|  | // FakeRenderFrameMetadataObserver ----------------------------------------- | 
|  |  | 
|  | // Fake out the renderer side of mojom::RenderFrameMetadataObserver, allowing | 
|  | // for RenderWidgetHostImpl to be created. | 
|  | // | 
|  | // All methods are no-opts, the provided mojo receiver and remote are held, but | 
|  | // never bound. | 
|  | class FakeRenderFrameMetadataObserver | 
|  | : public cc::mojom::RenderFrameMetadataObserver { | 
|  | public: | 
|  | FakeRenderFrameMetadataObserver( | 
|  | mojo::PendingReceiver<cc::mojom::RenderFrameMetadataObserver> receiver, | 
|  | mojo::PendingRemote<cc::mojom::RenderFrameMetadataObserverClient> | 
|  | client_remote); | 
|  |  | 
|  | FakeRenderFrameMetadataObserver(const FakeRenderFrameMetadataObserver&) = | 
|  | delete; | 
|  | FakeRenderFrameMetadataObserver& operator=( | 
|  | const FakeRenderFrameMetadataObserver&) = delete; | 
|  |  | 
|  | ~FakeRenderFrameMetadataObserver() override {} | 
|  |  | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | void UpdateRootScrollOffsetUpdateFrequency( | 
|  | cc::mojom::RootScrollOffsetUpdateFrequency frequency) override {} | 
|  | #endif | 
|  | void ReportAllFrameSubmissionsForTesting(bool enabled) override {} | 
|  |  | 
|  | private: | 
|  | mojo::PendingReceiver<cc::mojom::RenderFrameMetadataObserver> receiver_; | 
|  | mojo::PendingRemote<cc::mojom::RenderFrameMetadataObserverClient> | 
|  | client_remote_; | 
|  | }; | 
|  |  | 
|  | FakeRenderFrameMetadataObserver::FakeRenderFrameMetadataObserver( | 
|  | mojo::PendingReceiver<cc::mojom::RenderFrameMetadataObserver> receiver, | 
|  | mojo::PendingRemote<cc::mojom::RenderFrameMetadataObserverClient> | 
|  | client_remote) | 
|  | : receiver_(std::move(receiver)), | 
|  | client_remote_(std::move(client_remote)) {} | 
|  |  | 
|  | // MockInputEventObserver ------------------------------------------------- | 
|  | class MockInputEventObserver : public RenderWidgetHost::InputEventObserver { | 
|  | public: | 
|  | MOCK_METHOD1(OnInputEvent, void(const blink::WebInputEvent&)); | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | MOCK_METHOD1(OnImeTextCommittedEvent, void(const std::u16string& text_str)); | 
|  | MOCK_METHOD1(OnImeSetComposingTextEvent, | 
|  | void(const std::u16string& text_str)); | 
|  | MOCK_METHOD0(OnImeFinishComposingTextEvent, void()); | 
|  | #endif | 
|  | }; | 
|  |  | 
|  | // MockRenderWidgetHostDelegate -------------------------------------------- | 
|  |  | 
|  | class MockRenderWidgetHostDelegate : public RenderWidgetHostDelegate { | 
|  | public: | 
|  | MockRenderWidgetHostDelegate() | 
|  | : prehandle_keyboard_event_(false), | 
|  | prehandle_keyboard_event_is_shortcut_(false), | 
|  | prehandle_keyboard_event_called_(false), | 
|  | prehandle_keyboard_event_type_(WebInputEvent::Type::kUndefined), | 
|  | unhandled_keyboard_event_called_(false), | 
|  | unhandled_keyboard_event_type_(WebInputEvent::Type::kUndefined), | 
|  | handle_wheel_event_(false), | 
|  | handle_wheel_event_called_(false), | 
|  | unresponsive_timer_fired_(false), | 
|  | ignore_input_events_(false), | 
|  | render_view_host_delegate_view_(new MockRenderViewHostDelegateView()) {} | 
|  | ~MockRenderWidgetHostDelegate() override {} | 
|  |  | 
|  | // Tests that make sure we ignore keyboard event acknowledgments to events we | 
|  | // didn't send work by making sure we didn't call UnhandledKeyboardEvent(). | 
|  | bool unhandled_keyboard_event_called() const { | 
|  | return unhandled_keyboard_event_called_; | 
|  | } | 
|  |  | 
|  | WebInputEvent::Type unhandled_keyboard_event_type() const { | 
|  | return unhandled_keyboard_event_type_; | 
|  | } | 
|  |  | 
|  | bool prehandle_keyboard_event_called() const { | 
|  | return prehandle_keyboard_event_called_; | 
|  | } | 
|  |  | 
|  | WebInputEvent::Type prehandle_keyboard_event_type() const { | 
|  | return prehandle_keyboard_event_type_; | 
|  | } | 
|  |  | 
|  | void set_prehandle_keyboard_event(bool handle) { | 
|  | prehandle_keyboard_event_ = handle; | 
|  | } | 
|  |  | 
|  | void set_handle_wheel_event(bool handle) { | 
|  | handle_wheel_event_ = handle; | 
|  | } | 
|  |  | 
|  | void set_prehandle_keyboard_event_is_shortcut(bool is_shortcut) { | 
|  | prehandle_keyboard_event_is_shortcut_ = is_shortcut; | 
|  | } | 
|  |  | 
|  | bool handle_wheel_event_called() const { return handle_wheel_event_called_; } | 
|  |  | 
|  | bool unresponsive_timer_fired() const { return unresponsive_timer_fired_; } | 
|  |  | 
|  | MockRenderViewHostDelegateView* mock_delegate_view() { | 
|  | return render_view_host_delegate_view_.get(); | 
|  | } | 
|  |  | 
|  | void SetZoomLevel(double zoom_level) { zoom_level_ = zoom_level; } | 
|  |  | 
|  | double GetPendingPageZoomLevel() override { return zoom_level_; } | 
|  |  | 
|  | void FocusOwningWebContents( | 
|  | RenderWidgetHostImpl* render_widget_host) override { | 
|  | focus_owning_web_contents_call_count++; | 
|  | } | 
|  |  | 
|  | int GetFocusOwningWebContentsCallCount() const { | 
|  | return focus_owning_web_contents_call_count; | 
|  | } | 
|  |  | 
|  | void OnVerticalScrollDirectionChanged( | 
|  | viz::VerticalScrollDirection scroll_direction) override { | 
|  | ++on_vertical_scroll_direction_changed_call_count_; | 
|  | last_vertical_scroll_direction_ = scroll_direction; | 
|  | } | 
|  |  | 
|  | int GetOnVerticalScrollDirectionChangedCallCount() const { | 
|  | return on_vertical_scroll_direction_changed_call_count_; | 
|  | } | 
|  |  | 
|  | viz::VerticalScrollDirection GetLastVerticalScrollDirection() const { | 
|  | return last_vertical_scroll_direction_; | 
|  | } | 
|  |  | 
|  | RenderViewHostDelegateView* GetDelegateView() override { | 
|  | return mock_delegate_view(); | 
|  | } | 
|  |  | 
|  | void SetIgnoreInputEvents(bool ignore_input_events) { | 
|  | ignore_input_events_ = ignore_input_events; | 
|  | } | 
|  |  | 
|  | bool IsFullscreen() override { return is_fullscreen_; } | 
|  |  | 
|  | void set_is_fullscreen(bool enabled) { is_fullscreen_ = enabled; } | 
|  |  | 
|  | protected: | 
|  | KeyboardEventProcessingResult PreHandleKeyboardEvent( | 
|  | const NativeWebKeyboardEvent& event) override { | 
|  | prehandle_keyboard_event_type_ = event.GetType(); | 
|  | prehandle_keyboard_event_called_ = true; | 
|  | if (prehandle_keyboard_event_) | 
|  | return KeyboardEventProcessingResult::HANDLED; | 
|  | return prehandle_keyboard_event_is_shortcut_ | 
|  | ? KeyboardEventProcessingResult::NOT_HANDLED_IS_SHORTCUT | 
|  | : KeyboardEventProcessingResult::NOT_HANDLED; | 
|  | } | 
|  |  | 
|  | bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event) override { | 
|  | unhandled_keyboard_event_type_ = event.GetType(); | 
|  | unhandled_keyboard_event_called_ = true; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool HandleWheelEvent(const blink::WebMouseWheelEvent& event) override { | 
|  | handle_wheel_event_called_ = true; | 
|  | return handle_wheel_event_; | 
|  | } | 
|  |  | 
|  | void RendererUnresponsive( | 
|  | RenderWidgetHostImpl* render_widget_host, | 
|  | base::RepeatingClosure hang_monitor_restarter) override { | 
|  | unresponsive_timer_fired_ = true; | 
|  | } | 
|  |  | 
|  | bool ShouldIgnoreInputEvents() override { return ignore_input_events_; } | 
|  |  | 
|  | void ExecuteEditCommand( | 
|  | const std::string& command, | 
|  | const absl::optional<std::u16string>& value) override {} | 
|  |  | 
|  | void Undo() override {} | 
|  | void Redo() override {} | 
|  | void Cut() override {} | 
|  | void Copy() override {} | 
|  | void Paste() override {} | 
|  | void PasteAndMatchStyle() override {} | 
|  | void SelectAll() override {} | 
|  |  | 
|  | VisibleTimeRequestTrigger& GetVisibleTimeRequestTrigger() override { | 
|  | return visible_time_request_trigger_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool prehandle_keyboard_event_; | 
|  | bool prehandle_keyboard_event_is_shortcut_; | 
|  | bool prehandle_keyboard_event_called_; | 
|  | WebInputEvent::Type prehandle_keyboard_event_type_; | 
|  |  | 
|  | bool unhandled_keyboard_event_called_; | 
|  | WebInputEvent::Type unhandled_keyboard_event_type_; | 
|  |  | 
|  | bool handle_wheel_event_; | 
|  | bool handle_wheel_event_called_; | 
|  |  | 
|  | bool unresponsive_timer_fired_; | 
|  |  | 
|  | bool ignore_input_events_; | 
|  |  | 
|  | std::unique_ptr<MockRenderViewHostDelegateView> | 
|  | render_view_host_delegate_view_; | 
|  |  | 
|  | double zoom_level_ = 0; | 
|  |  | 
|  | int focus_owning_web_contents_call_count = 0; | 
|  |  | 
|  | int on_vertical_scroll_direction_changed_call_count_ = 0; | 
|  | viz::VerticalScrollDirection last_vertical_scroll_direction_ = | 
|  | viz::VerticalScrollDirection::kNull; | 
|  |  | 
|  | bool is_fullscreen_ = false; | 
|  |  | 
|  | VisibleTimeRequestTrigger visible_time_request_trigger_; | 
|  | }; | 
|  |  | 
|  | class MockRenderWidgetHostOwnerDelegate | 
|  | : public StubRenderWidgetHostOwnerDelegate { | 
|  | public: | 
|  | MOCK_METHOD1(SetBackgroundOpaque, void(bool opaque)); | 
|  | MOCK_METHOD0(IsMainFrameActive, bool()); | 
|  | }; | 
|  |  | 
|  | // RenderWidgetHostTest -------------------------------------------------------- | 
|  |  | 
|  | class RenderWidgetHostTest : public testing::Test { | 
|  | public: | 
|  | RenderWidgetHostTest() : last_simulated_event_time_(ui::EventTimeForNow()) {} | 
|  |  | 
|  | RenderWidgetHostTest(const RenderWidgetHostTest&) = delete; | 
|  | RenderWidgetHostTest& operator=(const RenderWidgetHostTest&) = delete; | 
|  |  | 
|  | ~RenderWidgetHostTest() override = default; | 
|  |  | 
|  | bool KeyPressEventCallback(const NativeWebKeyboardEvent& /* event */) { | 
|  | return handle_key_press_event_; | 
|  | } | 
|  | bool MouseEventCallback(const blink::WebMouseEvent& /* event */) { | 
|  | return handle_mouse_event_; | 
|  | } | 
|  |  | 
|  | void ClearVisualProperties() { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | widget_.ClearVisualProperties(); | 
|  | } | 
|  |  | 
|  | void ClearScreenRects() { | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | widget_.ClearScreenRects(); | 
|  | } | 
|  |  | 
|  | bool HasTouchEventHandlers(bool has_handlers) { return has_handlers; } | 
|  | bool HasHitTestableScrollbar(bool has_scrollbar) { return has_scrollbar; } | 
|  |  | 
|  | protected: | 
|  | // testing::Test | 
|  | void SetUp() override { | 
|  | base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | 
|  | command_line->AppendSwitch(switches::kValidateInputEventStream); | 
|  | browser_context_ = std::make_unique<TestBrowserContext>(); | 
|  | delegate_ = std::make_unique<MockRenderWidgetHostDelegate>(); | 
|  | process_ = | 
|  | std::make_unique<RenderWidgetHostProcess>(browser_context_.get()); | 
|  | site_instance_group_ = base::WrapRefCounted(new SiteInstanceGroup( | 
|  | SiteInstanceImpl::NextBrowsingInstanceId(), process_.get())); | 
|  | sink_ = &process_->sink(); | 
|  | #if defined(USE_AURA) || BUILDFLAG(IS_MAC) | 
|  | ImageTransportFactory::SetFactory( | 
|  | std::make_unique<TestImageTransportFactory>()); | 
|  | #endif | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | // calls display::Screen::SetScreenInstance(). | 
|  | ui::SetScreenAndroid(false /* use_display_wide_color_gamut */); | 
|  | #endif | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | screen_ = std::make_unique<display::test::TestScreen>(); | 
|  | display::Screen::SetScreenInstance(screen_.get()); | 
|  | #endif | 
|  | #if defined(USE_AURA) | 
|  | screen_.reset(aura::TestScreen::Create(gfx::Size())); | 
|  | display::Screen::SetScreenInstance(screen_.get()); | 
|  | #endif | 
|  | host_ = MockRenderWidgetHost::Create( | 
|  | /* frame_tree= */ nullptr, delegate_.get(), | 
|  | site_instance_group_->GetSafeRef(), process_->GetNextRoutingID(), | 
|  | widget_.GetNewRemote()); | 
|  | // Set up the RenderWidgetHost as being for a main frame. | 
|  | host_->set_owner_delegate(&mock_owner_delegate_); | 
|  | // Act like there is no RenderWidget present in the renderer yet. | 
|  | EXPECT_CALL(mock_owner_delegate_, IsMainFrameActive()) | 
|  | .WillRepeatedly(Return(false)); | 
|  |  | 
|  | view_ = std::make_unique<TestView>(host_.get()); | 
|  | ConfigureView(view_.get()); | 
|  | host_->SetView(view_.get()); | 
|  | host_->DisableGestureDebounce(); | 
|  | // Act like we've created a RenderWidget. | 
|  | host_->GetInitialVisualProperties(); | 
|  | EXPECT_CALL(mock_owner_delegate_, IsMainFrameActive()) | 
|  | .WillRepeatedly(Return(true)); | 
|  |  | 
|  | mojo::PendingRemote<cc::mojom::RenderFrameMetadataObserver> | 
|  | renderer_render_frame_metadata_observer_remote; | 
|  | mojo::PendingRemote<cc::mojom::RenderFrameMetadataObserverClient> | 
|  | render_frame_metadata_observer_remote; | 
|  | mojo::PendingReceiver<cc::mojom::RenderFrameMetadataObserverClient> | 
|  | render_frame_metadata_observer_client_receiver = | 
|  | render_frame_metadata_observer_remote | 
|  | .InitWithNewPipeAndPassReceiver(); | 
|  | renderer_render_frame_metadata_observer_ = | 
|  | std::make_unique<FakeRenderFrameMetadataObserver>( | 
|  | renderer_render_frame_metadata_observer_remote | 
|  | .InitWithNewPipeAndPassReceiver(), | 
|  | std::move(render_frame_metadata_observer_remote)); | 
|  |  | 
|  | host_->RegisterRenderFrameMetadataObserver( | 
|  | std::move(render_frame_metadata_observer_client_receiver), | 
|  | std::move(renderer_render_frame_metadata_observer_remote)); | 
|  |  | 
|  | // The blink::mojom::Widget is already set during MockRenderWidgetHost | 
|  | // construction. | 
|  | host_->BindFrameWidgetInterfaces( | 
|  | mojo::PendingAssociatedRemote<blink::mojom::FrameWidgetHost>() | 
|  | .InitWithNewEndpointAndPassReceiver(), | 
|  | TestRenderWidgetHost::CreateStubFrameWidgetRemote()); | 
|  |  | 
|  | host_->RendererWidgetCreated(/*for_frame_widget=*/true); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | view_.reset(); | 
|  | host_.reset(); | 
|  | delegate_.reset(); | 
|  | process_->Cleanup(); | 
|  | site_instance_group_.reset(); | 
|  | process_.reset(); | 
|  | browser_context_.reset(); | 
|  | #if defined(USE_AURA) || BUILDFLAG(IS_MAC) | 
|  | ImageTransportFactory::Terminate(); | 
|  | display::Screen::SetScreenInstance(nullptr); | 
|  | screen_.reset(); | 
|  | #endif | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | display::Screen::SetScreenInstance(nullptr); | 
|  | #endif | 
|  |  | 
|  | // Process all pending tasks to avoid leaks. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | virtual void ConfigureView(TestView* view) {} | 
|  |  | 
|  | void ReinitalizeHost() { | 
|  | host_->BindWidgetInterfaces( | 
|  | mojo::AssociatedRemote<blink::mojom::WidgetHost>() | 
|  | .BindNewEndpointAndPassDedicatedReceiver(), | 
|  | widget_.GetNewRemote()); | 
|  | host_->BindFrameWidgetInterfaces( | 
|  | mojo::AssociatedRemote<blink::mojom::FrameWidgetHost>() | 
|  | .BindNewEndpointAndPassDedicatedReceiver(), | 
|  | TestRenderWidgetHost::CreateStubFrameWidgetRemote()); | 
|  |  | 
|  | host_->RendererWidgetCreated(/*for_frame_widget=*/true); | 
|  | } | 
|  |  | 
|  | base::TimeTicks GetNextSimulatedEventTime() { | 
|  | last_simulated_event_time_ += simulated_event_time_delta_; | 
|  | return last_simulated_event_time_; | 
|  | } | 
|  |  | 
|  | void SimulateKeyboardEvent(WebInputEvent::Type type) { | 
|  | SimulateKeyboardEvent(type, 0); | 
|  | } | 
|  |  | 
|  | void SimulateKeyboardEvent(WebInputEvent::Type type, int modifiers) { | 
|  | NativeWebKeyboardEvent native_event(type, modifiers, | 
|  | GetNextSimulatedEventTime()); | 
|  | host_->ForwardKeyboardEvent(native_event); | 
|  | } | 
|  |  | 
|  | void SimulateKeyboardEventWithCommands(WebInputEvent::Type type) { | 
|  | NativeWebKeyboardEvent native_event(type, 0, GetNextSimulatedEventTime()); | 
|  | std::vector<blink::mojom::EditCommandPtr> edit_commands; | 
|  | edit_commands.push_back(blink::mojom::EditCommand::New("name", "value")); | 
|  | host_->ForwardKeyboardEventWithCommands(native_event, ui::LatencyInfo(), | 
|  | std::move(edit_commands), nullptr); | 
|  | } | 
|  |  | 
|  | void SimulateMouseEvent(WebInputEvent::Type type) { | 
|  | host_->ForwardMouseEvent(blink::SyntheticWebMouseEventBuilder::Build(type)); | 
|  | } | 
|  |  | 
|  | void SimulateMouseEventWithLatencyInfo(WebInputEvent::Type type, | 
|  | const ui::LatencyInfo& ui_latency) { | 
|  | host_->ForwardMouseEventWithLatencyInfo( | 
|  | blink::SyntheticWebMouseEventBuilder::Build(type), ui_latency); | 
|  | } | 
|  |  | 
|  | void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) { | 
|  | host_->ForwardWheelEvent(blink::SyntheticWebMouseWheelEventBuilder::Build( | 
|  | 0, 0, dX, dY, modifiers, | 
|  | precise ? ui::ScrollGranularity::kScrollByPrecisePixel | 
|  | : ui::ScrollGranularity::kScrollByPixel)); | 
|  | } | 
|  |  | 
|  | void SimulateWheelEvent(float dX, | 
|  | float dY, | 
|  | int modifiers, | 
|  | bool precise, | 
|  | WebMouseWheelEvent::Phase phase) { | 
|  | WebMouseWheelEvent wheel_event = | 
|  | blink::SyntheticWebMouseWheelEventBuilder::Build( | 
|  | 0, 0, dX, dY, modifiers, | 
|  | precise ? ui::ScrollGranularity::kScrollByPrecisePixel | 
|  | : ui::ScrollGranularity::kScrollByPixel); | 
|  | wheel_event.phase = phase; | 
|  | host_->ForwardWheelEvent(wheel_event); | 
|  | } | 
|  |  | 
|  | void SimulateWheelEventWithLatencyInfo(float dX, | 
|  | float dY, | 
|  | int modifiers, | 
|  | bool precise, | 
|  | const ui::LatencyInfo& ui_latency) { | 
|  | host_->ForwardWheelEventWithLatencyInfo( | 
|  | blink::SyntheticWebMouseWheelEventBuilder::Build( | 
|  | 0, 0, dX, dY, modifiers, | 
|  | precise ? ui::ScrollGranularity::kScrollByPrecisePixel | 
|  | : ui::ScrollGranularity::kScrollByPixel), | 
|  | ui_latency); | 
|  | } | 
|  |  | 
|  | void SimulateWheelEventWithLatencyInfo(float dX, | 
|  | float dY, | 
|  | int modifiers, | 
|  | bool precise, | 
|  | const ui::LatencyInfo& ui_latency, | 
|  | WebMouseWheelEvent::Phase phase) { | 
|  | WebMouseWheelEvent wheel_event = | 
|  | blink::SyntheticWebMouseWheelEventBuilder::Build( | 
|  | 0, 0, dX, dY, modifiers, | 
|  | precise ? ui::ScrollGranularity::kScrollByPrecisePixel | 
|  | : ui::ScrollGranularity::kScrollByPixel); | 
|  | wheel_event.phase = phase; | 
|  | host_->ForwardWheelEventWithLatencyInfo(wheel_event, ui_latency); | 
|  | } | 
|  |  | 
|  | void SimulateMouseMove(int x, int y, int modifiers) { | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseMove, x, y, modifiers, false); | 
|  | } | 
|  |  | 
|  | void SimulateMouseEvent( | 
|  | WebInputEvent::Type type, int x, int y, int modifiers, bool pressed) { | 
|  | WebMouseEvent event = | 
|  | blink::SyntheticWebMouseEventBuilder::Build(type, x, y, modifiers); | 
|  | if (pressed) | 
|  | event.button = WebMouseEvent::Button::kLeft; | 
|  | event.SetTimeStamp(GetNextSimulatedEventTime()); | 
|  | host_->ForwardMouseEvent(event); | 
|  | } | 
|  |  | 
|  | // Inject simple synthetic WebGestureEvent instances. | 
|  | void SimulateGestureEvent(WebInputEvent::Type type, | 
|  | WebGestureDevice sourceDevice) { | 
|  | host_->ForwardGestureEvent( | 
|  | blink::SyntheticWebGestureEventBuilder::Build(type, sourceDevice)); | 
|  | } | 
|  |  | 
|  | void SimulateGestureEventWithLatencyInfo(WebInputEvent::Type type, | 
|  | WebGestureDevice sourceDevice, | 
|  | const ui::LatencyInfo& ui_latency) { | 
|  | host_->ForwardGestureEventWithLatencyInfo( | 
|  | blink::SyntheticWebGestureEventBuilder::Build(type, sourceDevice), | 
|  | ui_latency); | 
|  | } | 
|  |  | 
|  | // Set the timestamp for the touch-event. | 
|  | void SetTouchTimestamp(base::TimeTicks timestamp) { | 
|  | touch_event_.SetTimestamp(timestamp); | 
|  | } | 
|  |  | 
|  | // Sends a touch event (irrespective of whether the page has a touch-event | 
|  | // handler or not). | 
|  | uint32_t SendTouchEvent() { | 
|  | uint32_t touch_event_id = touch_event_.unique_touch_event_id; | 
|  | host_->ForwardTouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo()); | 
|  |  | 
|  | touch_event_.ResetPoints(); | 
|  | return touch_event_id; | 
|  | } | 
|  |  | 
|  | int PressTouchPoint(int x, int y) { | 
|  | return touch_event_.PressPoint(x, y); | 
|  | } | 
|  |  | 
|  | void MoveTouchPoint(int index, int x, int y) { | 
|  | touch_event_.MovePoint(index, x, y); | 
|  | } | 
|  |  | 
|  | void ReleaseTouchPoint(int index) { | 
|  | touch_event_.ReleasePoint(index); | 
|  | } | 
|  |  | 
|  | const WebInputEvent* GetInputEventFromMessage(const IPC::Message& message) { | 
|  | base::PickleIterator iter(message); | 
|  | const char* data; | 
|  | size_t data_length; | 
|  | if (!iter.ReadData(&data, &data_length)) | 
|  | return nullptr; | 
|  | return reinterpret_cast<const WebInputEvent*>(data); | 
|  | } | 
|  |  | 
|  | BrowserTaskEnvironment task_environment_{ | 
|  | base::test::TaskEnvironment::TimeSource::MOCK_TIME}; | 
|  | std::unique_ptr<TestBrowserContext> browser_context_; | 
|  | std::unique_ptr<RenderWidgetHostProcess> process_; | 
|  | scoped_refptr<SiteInstanceGroup> site_instance_group_; | 
|  | std::unique_ptr<MockRenderWidgetHostDelegate> delegate_; | 
|  | testing::NiceMock<MockRenderWidgetHostOwnerDelegate> mock_owner_delegate_; | 
|  | std::unique_ptr<MockRenderWidgetHost> host_; | 
|  | std::unique_ptr<TestView> view_; | 
|  | std::unique_ptr<display::Screen> screen_; | 
|  | bool handle_key_press_event_ = false; | 
|  | bool handle_mouse_event_ = false; | 
|  | base::TimeTicks last_simulated_event_time_; | 
|  | base::TimeDelta simulated_event_time_delta_; | 
|  | raw_ptr<IPC::TestSink> sink_; | 
|  | std::unique_ptr<FakeRenderFrameMetadataObserver> | 
|  | renderer_render_frame_metadata_observer_; | 
|  | MockWidget widget_; | 
|  |  | 
|  | private: | 
|  | blink::SyntheticWebTouchEvent touch_event_; | 
|  | }; | 
|  |  | 
|  | // RenderWidgetHostWithSourceTest ---------------------------------------------- | 
|  |  | 
|  | // This is for tests that are to be run for all source devices. | 
|  | class RenderWidgetHostWithSourceTest | 
|  | : public RenderWidgetHostTest, | 
|  | public testing::WithParamInterface<WebGestureDevice> {}; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // ----------------------------------------------------------------------------- | 
|  |  | 
|  | // Tests that renderer doesn't change bounds while browser has its | 
|  | // bounds changed (and until bounds are acked), which might be a result | 
|  | // of a system's display compositor/server changing bounds of an | 
|  | // application. | 
|  | TEST_F(RenderWidgetHostTest, DoNotAcceptPopupBoundsUntilScreenRectsAcked) { | 
|  | // The host should wait for the screen rects ack now as SendScreenRects were | 
|  | // called during the initialization step. | 
|  | EXPECT_TRUE(host_->waiting_for_screen_rects_ack_); | 
|  |  | 
|  | // Execute pending callbacks and clear screen rects. | 
|  | ClearScreenRects(); | 
|  |  | 
|  | // Lets mojo to pass the message from the renderer to the browser (from widget | 
|  | // to host). | 
|  | base::RunLoop().RunUntilIdle(); | 
|  |  | 
|  | // The host shouldn't wait for ack now as it has received it. | 
|  | EXPECT_FALSE(host_->waiting_for_screen_rects_ack_); | 
|  |  | 
|  | // Change the bounds of the view and send screen rects. | 
|  | view_->SetBounds({10, 20, 300, 200}); | 
|  | // Pass updated bounds from the browser to the renderer. | 
|  | host_->SendScreenRects(); | 
|  |  | 
|  | // The host should wait for the screen rects ack now. | 
|  | EXPECT_TRUE(host_->waiting_for_screen_rects_ack_); | 
|  |  | 
|  | // Store the current view's bounds and pretend popup bounds are | 
|  | // being changed. However, they mustn't be changed as the host is still | 
|  | // waiting for the screen rects ack. This ensures that the renderer | 
|  | // doesn't clobber browser's bounds. | 
|  | auto old_view_bounds = view_->GetViewBounds(); | 
|  | auto new_popup_view_bounds = gfx::Rect(5, 5, 20, 20); | 
|  | // Act like a renderer sending new bounds to the browser. | 
|  | static_cast<blink::mojom::PopupWidgetHost*>(host_.get()) | 
|  | ->SetPopupBounds(new_popup_view_bounds, base::DoNothing()); | 
|  | // The view still has the old bounds... | 
|  | EXPECT_EQ(old_view_bounds, view_->GetViewBounds()); | 
|  | // which are not the same as the new bounds that were tried to be | 
|  | // set. | 
|  | EXPECT_NE(view_->GetViewBounds(), new_popup_view_bounds); | 
|  |  | 
|  | // Clear the screen rects and send the ack callback back to the host. | 
|  | ClearScreenRects(); | 
|  |  | 
|  | // Allows mojo to pass the message from the renderer to the browser | 
|  | // (ClearScreenRects executed a callback via mojo that notifies the browser | 
|  | // that the renderer completed processing the new rects). | 
|  | base::RunLoop().RunUntilIdle(); | 
|  |  | 
|  | // The change must have been acked by now. | 
|  | EXPECT_FALSE(host_->waiting_for_screen_rects_ack_); | 
|  |  | 
|  | // Pretend that the renderer changes the popup bounds again... | 
|  | static_cast<blink::mojom::PopupWidgetHost*>(host_.get()) | 
|  | ->SetPopupBounds(new_popup_view_bounds, base::DoNothing()); | 
|  | // And the host must accept them now as the screen rects have been | 
|  | // acked. | 
|  | EXPECT_EQ(new_popup_view_bounds, view_->GetViewBounds()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, SynchronizeVisualProperties) { | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // The initial zoom is 0 so host should not send a sync message | 
|  | delegate_->SetZoomLevel(0); | 
|  | EXPECT_FALSE(host_->SynchronizeVisualProperties()); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // The zoom has changed so host should send out a sync message. | 
|  | double new_zoom_level = blink::PageZoomFactorToZoomLevel(0.25); | 
|  | delegate_->SetZoomLevel(new_zoom_level); | 
|  | EXPECT_TRUE(host_->SynchronizeVisualProperties()); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_NEAR(new_zoom_level, host_->old_visual_properties_->zoom_level, 0.01); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // The initial bounds is the empty rect, so setting it to the same thing | 
|  | // shouldn't send the resize message. | 
|  | view_->SetBounds(gfx::Rect()); | 
|  | host_->SynchronizeVisualProperties(); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // No visual properties ACK if the physical backing gets set, but the view | 
|  | // bounds are zero. | 
|  | view_->SetMockCompositorViewportPixelSize(gfx::Size(200, 200)); | 
|  | host_->SynchronizeVisualProperties(); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Setting the view bounds to nonzero should send out the notification. | 
|  | // but should not expect ack for empty physical backing size. | 
|  | gfx::Rect original_size(0, 0, 100, 100); | 
|  | view_->SetBounds(original_size); | 
|  | view_->SetMockCompositorViewportPixelSize(gfx::Size()); | 
|  | host_->SynchronizeVisualProperties(); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_EQ(original_size.size(), host_->old_visual_properties_->new_size); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Setting the bounds and physical backing size to nonzero should send out | 
|  | // the notification and expect an ack. | 
|  | view_->ClearMockCompositorViewportPixelSize(); | 
|  | host_->SynchronizeVisualProperties(); | 
|  | EXPECT_TRUE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_EQ(original_size.size(), host_->old_visual_properties_->new_size); | 
|  | cc::RenderFrameMetadata metadata; | 
|  | metadata.viewport_size_in_pixels = original_size.size(); | 
|  | metadata.local_surface_id = absl::nullopt; | 
|  | static_cast<RenderFrameMetadataProvider::Observer&>(*host_) | 
|  | .OnLocalSurfaceIdChanged(metadata); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | gfx::Rect second_size(0, 0, 110, 110); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | view_->SetBounds(second_size); | 
|  | EXPECT_TRUE(host_->SynchronizeVisualProperties()); | 
|  | EXPECT_TRUE(host_->visual_properties_ack_pending_); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Sending out a new notification should NOT send out a new IPC message since | 
|  | // a visual properties ACK is pending. | 
|  | gfx::Rect third_size(0, 0, 120, 120); | 
|  | process_->sink().ClearMessages(); | 
|  | view_->SetBounds(third_size); | 
|  | EXPECT_FALSE(host_->SynchronizeVisualProperties()); | 
|  | EXPECT_TRUE(host_->visual_properties_ack_pending_); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Send a update that's a visual properties ACK, but for the original_size we | 
|  | // sent. Since this isn't the second_size, the message handler should | 
|  | // immediately send a new resize message for the new size to the renderer. | 
|  | metadata.viewport_size_in_pixels = original_size.size(); | 
|  | metadata.local_surface_id = absl::nullopt; | 
|  | static_cast<RenderFrameMetadataProvider::Observer&>(*host_) | 
|  | .OnLocalSurfaceIdChanged(metadata); | 
|  | EXPECT_TRUE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_EQ(third_size.size(), host_->old_visual_properties_->new_size); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Send the visual properties ACK for the latest size. | 
|  | metadata.viewport_size_in_pixels = third_size.size(); | 
|  | metadata.local_surface_id = absl::nullopt; | 
|  | static_cast<RenderFrameMetadataProvider::Observer&>(*host_) | 
|  | .OnLocalSurfaceIdChanged(metadata); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_EQ(third_size.size(), host_->old_visual_properties_->new_size); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Now clearing the bounds should send out a notification but we shouldn't | 
|  | // expect a visual properties ACK (since the renderer won't ack empty sizes). | 
|  | // The message should contain the new size (0x0) and not the previous one that | 
|  | // we skipped. | 
|  | view_->SetBounds(gfx::Rect()); | 
|  | host_->SynchronizeVisualProperties(); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_EQ(gfx::Size(), host_->old_visual_properties_->new_size); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Send a rect that has no area but has either width or height set. | 
|  | view_->SetBounds(gfx::Rect(0, 0, 0, 30)); | 
|  | host_->SynchronizeVisualProperties(); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_EQ(gfx::Size(0, 30), host_->old_visual_properties_->new_size); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Set the same size again. It should not be sent again. | 
|  | host_->SynchronizeVisualProperties(); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_EQ(gfx::Size(0, 30), host_->old_visual_properties_->new_size); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // A different size should be sent again, however. | 
|  | view_->SetBounds(gfx::Rect(0, 0, 0, 31)); | 
|  | host_->SynchronizeVisualProperties(); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_EQ(gfx::Size(0, 31), host_->old_visual_properties_->new_size); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // An invalid LocalSurfaceId should result in no change to the | 
|  | // |visual_properties_ack_pending_| bit. | 
|  | view_->SetBounds(gfx::Rect(25, 25)); | 
|  | view_->InvalidateLocalSurfaceId(); | 
|  | host_->SynchronizeVisualProperties(); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | EXPECT_EQ(gfx::Size(25, 25), host_->old_visual_properties_->new_size); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | } | 
|  |  | 
|  | // Test that a resize event is sent if SynchronizeVisualProperties() is called | 
|  | // after a ScreenInfo change. | 
|  | TEST_F(RenderWidgetHostTest, ResizeScreenInfo) { | 
|  | display::ScreenInfo screen_info; | 
|  | screen_info.rect = gfx::Rect(0, 0, 800, 600); | 
|  | screen_info.available_rect = gfx::Rect(0, 0, 800, 600); | 
|  | screen_info.orientation_type = | 
|  | display::mojom::ScreenOrientation::kPortraitPrimary; | 
|  |  | 
|  | ClearVisualProperties(); | 
|  | view_->SetScreenInfo(screen_info); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  | EXPECT_TRUE(host_->SynchronizeVisualProperties()); | 
|  | // blink::mojom::Widget::UpdateVisualProperties sent to the renderer. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | ASSERT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  |  | 
|  | screen_info.orientation_angle = 180; | 
|  | screen_info.orientation_type = | 
|  | display::mojom::ScreenOrientation::kLandscapePrimary; | 
|  |  | 
|  | ClearVisualProperties(); | 
|  | view_->SetScreenInfo(screen_info); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  | EXPECT_TRUE(host_->SynchronizeVisualProperties()); | 
|  | // blink::mojom::Widget::UpdateVisualProperties sent to the renderer. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | ASSERT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  |  | 
|  | screen_info.device_scale_factor = 2.f; | 
|  |  | 
|  | ClearVisualProperties(); | 
|  | view_->SetScreenInfo(screen_info); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  | EXPECT_TRUE(host_->SynchronizeVisualProperties()); | 
|  | // blink::mojom::Widget::UpdateVisualProperties sent to the renderer. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | ASSERT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  |  | 
|  | // No screen change. | 
|  | ClearVisualProperties(); | 
|  | view_->SetScreenInfo(screen_info); | 
|  | EXPECT_FALSE(host_->SynchronizeVisualProperties()); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | } | 
|  |  | 
|  | class RenderWidgetHostFullscreenScreenSizeTest | 
|  | : public RenderWidgetHostTest, | 
|  | public testing::WithParamInterface<bool> { | 
|  | public: | 
|  | RenderWidgetHostFullscreenScreenSizeTest() { | 
|  | scoped_feature_list_.InitWithFeatureState( | 
|  | blink::features::kFullscreenScreenSizeMatchesDisplay, | 
|  | FullscreenScreenSizeMatchesDisplayEnabled()); | 
|  | } | 
|  | bool FullscreenScreenSizeMatchesDisplayEnabled() { return GetParam(); } | 
|  |  | 
|  | private: | 
|  | base::test::ScopedFeatureList scoped_feature_list_; | 
|  | }; | 
|  |  | 
|  | INSTANTIATE_TEST_SUITE_P(All, | 
|  | RenderWidgetHostFullscreenScreenSizeTest, | 
|  | testing::Bool()); | 
|  |  | 
|  | // Test that VisualProperties conveys a ScreenInfo size override with the view's | 
|  | // size for the current screen, when the frame is fullscreen. | 
|  | // This lets `window.screen` provide viewport dimensions while the frame is | 
|  | // fullscreen as a speculative site compatibility measure, because web authors | 
|  | // may assume that screen dimensions match window.innerWidth/innerHeight while | 
|  | // a page is fullscreen, but that is not always true. crbug.com/1367416 | 
|  | TEST_P(RenderWidgetHostFullscreenScreenSizeTest, ScreenSizeInFullscreen) { | 
|  | const gfx::Rect kScreenBounds(0, 0, 800, 600); | 
|  | const gfx::Rect kViewBounds(55, 66, 600, 500); | 
|  |  | 
|  | display::ScreenInfo screen_info; | 
|  | screen_info.rect = kScreenBounds; | 
|  | screen_info.available_rect = kScreenBounds; | 
|  | screen_info.orientation_type = | 
|  | display::mojom::ScreenOrientation::kPortraitPrimary; | 
|  | view_->SetScreenInfo(screen_info); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Do initial VisualProperties sync while not fullscreened. | 
|  | view_->SetBounds(kViewBounds); | 
|  | ASSERT_FALSE(delegate_->IsFullscreen()); | 
|  | host_->SynchronizeVisualPropertiesIgnoringPendingAck(); | 
|  | // blink::mojom::Widget::UpdateVisualProperties sent to the renderer. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | blink::VisualProperties props = widget_.ReceivedVisualProperties().at(0); | 
|  | EXPECT_EQ(absl::nullopt, props.screen_infos.current().size_override); | 
|  | EXPECT_EQ(kScreenBounds, props.screen_infos.current().rect); | 
|  | EXPECT_EQ(kScreenBounds, props.screen_infos.current().available_rect); | 
|  | EXPECT_EQ(kViewBounds.size(), props.new_size); | 
|  |  | 
|  | // Enter fullscreen and do another VisualProperties sync. | 
|  | delegate_->set_is_fullscreen(true); | 
|  | host_->SynchronizeVisualPropertiesIgnoringPendingAck(); | 
|  | // blink::mojom::Widget::UpdateVisualProperties sent to the renderer. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(2u, widget_.ReceivedVisualProperties().size()); | 
|  | props = widget_.ReceivedVisualProperties().at(1); | 
|  | if (FullscreenScreenSizeMatchesDisplayEnabled()) | 
|  | EXPECT_EQ(absl::nullopt, props.screen_infos.current().size_override); | 
|  | else | 
|  | EXPECT_EQ(kViewBounds.size(), props.screen_infos.current().size_override); | 
|  | EXPECT_EQ(kScreenBounds, props.screen_infos.current().rect); | 
|  | EXPECT_EQ(kScreenBounds, props.screen_infos.current().available_rect); | 
|  | EXPECT_EQ(kViewBounds.size(), props.new_size); | 
|  |  | 
|  | // Exit fullscreen and do another VisualProperties sync. | 
|  | delegate_->set_is_fullscreen(false); | 
|  | host_->SynchronizeVisualPropertiesIgnoringPendingAck(); | 
|  | // blink::mojom::Widget::UpdateVisualProperties sent to the renderer. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(3u, widget_.ReceivedVisualProperties().size()); | 
|  | props = widget_.ReceivedVisualProperties().at(2); | 
|  | EXPECT_EQ(absl::nullopt, props.screen_infos.current().size_override); | 
|  | EXPECT_EQ(kScreenBounds, props.screen_infos.current().rect); | 
|  | EXPECT_EQ(kScreenBounds, props.screen_infos.current().available_rect); | 
|  | EXPECT_EQ(kViewBounds.size(), props.new_size); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, RootWindowSegments) { | 
|  | gfx::Rect screen_rect(0, 0, 800, 600); | 
|  | display::ScreenInfo screen_info; | 
|  | screen_info.rect = screen_rect; | 
|  | screen_info.available_rect = screen_rect; | 
|  | screen_info.orientation_type = | 
|  | display::mojom::ScreenOrientation::kPortraitPrimary; | 
|  | view_->SetScreenInfo(screen_info); | 
|  |  | 
|  | // Set a vertical display feature which must result in two window segments, | 
|  | // side-by-side. | 
|  | const int kDisplayFeatureLength = 20; | 
|  | DisplayFeature emulated_display_feature{ | 
|  | DisplayFeature::Orientation::kVertical, | 
|  | /* offset */ screen_rect.width() / 2 - kDisplayFeatureLength / 2, | 
|  | /* mask_length */ kDisplayFeatureLength}; | 
|  | RenderWidgetHostViewBase* render_widget_host_view = view_.get(); | 
|  | render_widget_host_view->SetDisplayFeatureForTesting( | 
|  | &emulated_display_feature); | 
|  |  | 
|  | ClearScreenRects(); | 
|  |  | 
|  | view_->SetBounds(screen_rect); | 
|  | host_->SendScreenRects(); | 
|  |  | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Run SynchronizeVisualProperties and validate the window segments sent to | 
|  | // the renderer are correct. | 
|  | EXPECT_TRUE(host_->SynchronizeVisualProperties()); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | auto window_segments = | 
|  | widget_.ReceivedVisualProperties().at(0).root_widget_window_segments; | 
|  | EXPECT_EQ(window_segments.size(), 2u); | 
|  | gfx::Rect expected_first_rect(0, 0, 390, 600); | 
|  | EXPECT_EQ(window_segments[0], expected_first_rect); | 
|  | gfx::Rect expected_second_rect(410, 0, 390, 600); | 
|  | EXPECT_EQ(window_segments[1], expected_second_rect); | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Setting a bottom inset (simulating virtual keyboard displaying on Aura) | 
|  | // should result in 'shorter' segments. | 
|  | auto insets = gfx::Insets::TLBR(0, 0, 100, 0); | 
|  | view_->SetInsets(insets); | 
|  | expected_first_rect.Inset(insets); | 
|  | expected_second_rect.Inset(insets); | 
|  | host_->SynchronizeVisualPropertiesIgnoringPendingAck(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | auto inset_window_segments = | 
|  | widget_.ReceivedVisualProperties().at(0).root_widget_window_segments; | 
|  | EXPECT_EQ(inset_window_segments.size(), 2u); | 
|  | EXPECT_EQ(inset_window_segments[0], expected_first_rect); | 
|  | EXPECT_EQ(inset_window_segments[1], expected_second_rect); | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | view_->SetInsets(gfx::Insets(0)); | 
|  |  | 
|  | // Setting back to empty should result in a single rect. The previous call | 
|  | // resized the widget and causes a pending ack. This is unrelated to what | 
|  | // we're testing here so ignore the pending ack by using | 
|  | // |SynchronizeVisualPropertiesIgnoringPendingAck()|. | 
|  | render_widget_host_view->SetDisplayFeatureForTesting(nullptr); | 
|  | host_->SynchronizeVisualPropertiesIgnoringPendingAck(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | auto single_window_segments = | 
|  | widget_.ReceivedVisualProperties().at(0).root_widget_window_segments; | 
|  | EXPECT_EQ(single_window_segments.size(), 1u); | 
|  | EXPECT_EQ(single_window_segments[0], gfx::Rect(0, 0, 800, 600)); | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // Set a horizontal display feature which results in two window segments | 
|  | // stacked on top of each other. | 
|  | emulated_display_feature = { | 
|  | DisplayFeature::Orientation::kHorizontal, | 
|  | /* offset */ screen_rect.height() / 2 - kDisplayFeatureLength / 2, | 
|  | /* mask_length */ kDisplayFeatureLength}; | 
|  | render_widget_host_view->SetDisplayFeatureForTesting( | 
|  | &emulated_display_feature); | 
|  | host_->SynchronizeVisualPropertiesIgnoringPendingAck(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | auto vertical_window_segments = | 
|  | widget_.ReceivedVisualProperties().at(0).root_widget_window_segments; | 
|  | EXPECT_EQ(vertical_window_segments.size(), 2u); | 
|  | expected_first_rect = gfx::Rect(0, 0, 800, 290); | 
|  | EXPECT_EQ(vertical_window_segments[0], expected_first_rect); | 
|  | expected_second_rect = gfx::Rect(0, 310, 800, 290); | 
|  | EXPECT_EQ(vertical_window_segments[1], expected_second_rect); | 
|  | ClearVisualProperties(); | 
|  |  | 
|  | // If the segments don't change, there should be no IPC message sent. | 
|  | host_->SynchronizeVisualPropertiesIgnoringPendingAck(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedVisualProperties().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, ReceiveFrameTokenFromCrashedRenderer) { | 
|  | // The Renderer sends a monotonically increasing frame token. | 
|  | host_->DidProcessFrame(2, base::TimeTicks::Now()); | 
|  |  | 
|  | // Simulate a renderer crash. | 
|  | host_->SetView(nullptr); | 
|  | host_->RendererExited(); | 
|  |  | 
|  | // Receive an in-flight frame token (it needs to monotonically increase) | 
|  | // while the RenderWidget is gone. | 
|  | host_->DidProcessFrame(3, base::TimeTicks::Now()); | 
|  |  | 
|  | // The renderer is recreated. | 
|  | host_->SetView(view_.get()); | 
|  | // Make a new RenderWidget when the renderer is recreated and inform that a | 
|  | // RenderWidget is being created. | 
|  | blink::VisualProperties props = host_->GetInitialVisualProperties(); | 
|  | // The RenderWidget is recreated with the initial VisualProperties. | 
|  | ReinitalizeHost(); | 
|  |  | 
|  | // The new RenderWidget sends a frame token, which is lower than what the | 
|  | // previous RenderWidget sent. This should be okay, as the expected token has | 
|  | // been reset. | 
|  | host_->DidProcessFrame(1, base::TimeTicks::Now()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, ReceiveFrameTokenFromDeletedRenderWidget) { | 
|  | // The RenderWidget sends a monotonically increasing frame token. | 
|  | host_->DidProcessFrame(2, base::TimeTicks::Now()); | 
|  |  | 
|  | // The RenderWidget is destroyed in the renderer process as the main frame | 
|  | // is removed from this RenderWidgetHost's RenderWidgetView, but the | 
|  | // RenderWidgetView is still kept around for another RenderFrame. | 
|  | EXPECT_CALL(mock_owner_delegate_, IsMainFrameActive()) | 
|  | .WillRepeatedly(Return(false)); | 
|  |  | 
|  | // Receive an in-flight frame token (it needs to monotonically increase) | 
|  | // while the RenderWidget is gone. | 
|  | host_->DidProcessFrame(3, base::TimeTicks::Now()); | 
|  |  | 
|  | // Make a new RenderWidget when the renderer is recreated and inform that a | 
|  | // RenderWidget is being created. | 
|  | blink::VisualProperties props = host_->GetInitialVisualProperties(); | 
|  | // The RenderWidget is recreated with the initial VisualProperties. | 
|  | EXPECT_CALL(mock_owner_delegate_, IsMainFrameActive()) | 
|  | .WillRepeatedly(Return(true)); | 
|  |  | 
|  | // The new RenderWidget sends a frame token, which is lower than what the | 
|  | // previous RenderWidget sent. This should be okay, as the expected token has | 
|  | // been reset. | 
|  | host_->DidProcessFrame(1, base::TimeTicks::Now()); | 
|  | } | 
|  |  | 
|  | // Tests setting background transparency. | 
|  | TEST_F(RenderWidgetHostTest, Background) { | 
|  | RenderWidgetHostViewBase* view; | 
|  | #if defined(USE_AURA) | 
|  | view = new RenderWidgetHostViewAura(host_.get()); | 
|  | #elif BUILDFLAG(IS_ANDROID) | 
|  | view = new RenderWidgetHostViewAndroid(host_.get(), nullptr); | 
|  | #elif BUILDFLAG(IS_MAC) | 
|  | view = CreateRenderWidgetHostViewMacForTesting(host_.get()); | 
|  | #elif BUILDFLAG(IS_IOS) | 
|  | view = CreateRenderWidgetHostViewIOSForTesting(host_.get()); | 
|  | #endif | 
|  |  | 
|  | #if !BUILDFLAG(IS_ANDROID) | 
|  | // TODO(derat): Call this on all platforms: http://crbug.com/102450. | 
|  | view->InitAsChild(nullptr); | 
|  | #endif | 
|  | host_->SetView(view); | 
|  |  | 
|  | ASSERT_FALSE(view->GetBackgroundColor()); | 
|  |  | 
|  | { | 
|  | // The background is assumed opaque by default, so choosing opaque won't | 
|  | // do anything if it's not set to transparent first. | 
|  | EXPECT_CALL(mock_owner_delegate_, SetBackgroundOpaque(_)).Times(0); | 
|  | view->SetBackgroundColor(SK_ColorRED); | 
|  | EXPECT_EQ(unsigned{SK_ColorRED}, *view->GetBackgroundColor()); | 
|  | } | 
|  | { | 
|  | // Another opaque color doesn't inform the view of any change. | 
|  | EXPECT_CALL(mock_owner_delegate_, SetBackgroundOpaque(_)).Times(0); | 
|  | view->SetBackgroundColor(SK_ColorBLUE); | 
|  | EXPECT_EQ(unsigned{SK_ColorBLUE}, *view->GetBackgroundColor()); | 
|  | } | 
|  | { | 
|  | // The owner delegate will be called to pass it over IPC to the | 
|  | // `blink::WebView`. | 
|  | EXPECT_CALL(mock_owner_delegate_, SetBackgroundOpaque(false)); | 
|  | view->SetBackgroundColor(SK_ColorTRANSPARENT); | 
|  | #if BUILDFLAG(IS_MAC) | 
|  | // Mac replaces transparent background colors with white. See the comment in | 
|  | // RenderWidgetHostViewMac::GetBackgroundColor. (https://crbug.com/735407) | 
|  | EXPECT_EQ(unsigned{SK_ColorWHITE}, *view->GetBackgroundColor()); | 
|  | #else | 
|  | // The browser side will represent the background color as transparent | 
|  | // immediately. | 
|  | EXPECT_EQ(unsigned{SK_ColorTRANSPARENT}, *view->GetBackgroundColor()); | 
|  | #endif | 
|  | } | 
|  | { | 
|  | // Setting back an opaque color informs the view. | 
|  | EXPECT_CALL(mock_owner_delegate_, SetBackgroundOpaque(true)); | 
|  | view->SetBackgroundColor(SK_ColorBLUE); | 
|  | EXPECT_EQ(unsigned{SK_ColorBLUE}, *view->GetBackgroundColor()); | 
|  | } | 
|  |  | 
|  | host_->SetView(nullptr); | 
|  | view->Destroy(); | 
|  | } | 
|  |  | 
|  | // Test that the RenderWidgetHost tells the renderer when it is hidden and | 
|  | // shown, and can accept a racey update from the renderer after hiding. | 
|  | TEST_F(RenderWidgetHostTest, HideShowMessages) { | 
|  | // Hide the widget, it should have sent out a message to the renderer. | 
|  | EXPECT_FALSE(host_->is_hidden_); | 
|  | { | 
|  | base::RunLoop run_loop; | 
|  | widget_.SetShownHiddenCallback(run_loop.QuitClosure()); | 
|  | host_->WasHidden(); | 
|  | run_loop.Run(); | 
|  | } | 
|  | EXPECT_TRUE(host_->is_hidden_); | 
|  | ASSERT_TRUE(widget_.IsHidden().has_value()); | 
|  | EXPECT_TRUE(widget_.IsHidden().value()); | 
|  |  | 
|  | // Send it an update as from the renderer. | 
|  | process_->sink().ClearMessages(); | 
|  | cc::RenderFrameMetadata metadata; | 
|  | metadata.viewport_size_in_pixels = gfx::Size(100, 100); | 
|  | metadata.local_surface_id = absl::nullopt; | 
|  | static_cast<RenderFrameMetadataProvider::Observer&>(*host_) | 
|  | .OnLocalSurfaceIdChanged(metadata); | 
|  |  | 
|  | // Now unhide. | 
|  | widget_.ClearHidden(); | 
|  | ASSERT_FALSE(widget_.IsHidden().has_value()); | 
|  | { | 
|  | base::RunLoop run_loop; | 
|  | widget_.SetShownHiddenCallback(run_loop.QuitClosure()); | 
|  |  | 
|  | host_->WasShown({} /* record_tab_switch_time_request */); | 
|  | run_loop.Run(); | 
|  | } | 
|  | EXPECT_FALSE(host_->is_hidden_); | 
|  |  | 
|  | // It should have sent out a mojo message. | 
|  | ASSERT_TRUE(widget_.IsHidden().has_value()); | 
|  | EXPECT_FALSE(widget_.IsHidden().value()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, IgnoreKeyEventsHandledByRenderer) { | 
|  | // Simulate a keyboard event. | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  |  | 
|  | // Make sure we sent the input event to the renderer. | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  |  | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kConsumed); | 
|  | EXPECT_FALSE(delegate_->unhandled_keyboard_event_called()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, SendEditCommandsBeforeKeyEvent) { | 
|  | // Simulate a keyboard event. | 
|  | SimulateKeyboardEventWithCommands(WebInputEvent::Type::kRawKeyDown); | 
|  |  | 
|  | // Make sure we sent commands and key event to the renderer. | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(2u, dispatched_events.size()); | 
|  |  | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEditCommand()); | 
|  | ASSERT_TRUE(dispatched_events[1]->ToEvent()); | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[1]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kConsumed); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, PreHandleRawKeyDownEvent) { | 
|  | // Simulate the situation that the browser handled the key down event during | 
|  | // pre-handle phrase. | 
|  | delegate_->set_prehandle_keyboard_event(true); | 
|  |  | 
|  | // Simulate a keyboard event. | 
|  | SimulateKeyboardEventWithCommands(WebInputEvent::Type::kRawKeyDown); | 
|  |  | 
|  | EXPECT_TRUE(delegate_->prehandle_keyboard_event_called()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kRawKeyDown, | 
|  | delegate_->prehandle_keyboard_event_type()); | 
|  |  | 
|  | // Make sure the commands and key event are not sent to the renderer. | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | EXPECT_EQ(0u, dispatched_events.size()); | 
|  |  | 
|  | // The browser won't pre-handle a Char event. | 
|  | delegate_->set_prehandle_keyboard_event(false); | 
|  |  | 
|  | // Forward the Char event. | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kChar); | 
|  |  | 
|  | // Make sure the Char event is suppressed. | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | EXPECT_EQ(0u, dispatched_events.size()); | 
|  |  | 
|  | // Forward the KeyUp event. | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kKeyUp); | 
|  |  | 
|  | // Make sure the KeyUp event is suppressed. | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | EXPECT_EQ(0u, dispatched_events.size()); | 
|  |  | 
|  | // Simulate a new RawKeyDown event. | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kRawKeyDown, | 
|  | dispatched_events[0]->ToEvent()->Event()->Event().GetType()); | 
|  |  | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kNotConsumed); | 
|  |  | 
|  | EXPECT_TRUE(delegate_->unhandled_keyboard_event_called()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kRawKeyDown, | 
|  | delegate_->unhandled_keyboard_event_type()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, RawKeyDownShortcutEvent) { | 
|  | // Simulate the situation that the browser marks the key down as a keyboard | 
|  | // shortcut, but doesn't consume it in the pre-handle phase. | 
|  | delegate_->set_prehandle_keyboard_event_is_shortcut(true); | 
|  |  | 
|  | // Simulate a keyboard event. | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  |  | 
|  | EXPECT_TRUE(delegate_->prehandle_keyboard_event_called()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kRawKeyDown, | 
|  | delegate_->prehandle_keyboard_event_type()); | 
|  |  | 
|  | // Make sure the RawKeyDown event is sent to the renderer. | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kRawKeyDown, | 
|  | dispatched_events[0]->ToEvent()->Event()->Event().GetType()); | 
|  |  | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kNotConsumed); | 
|  | EXPECT_EQ(WebInputEvent::Type::kRawKeyDown, | 
|  | delegate_->unhandled_keyboard_event_type()); | 
|  |  | 
|  | // The browser won't pre-handle a Char event. | 
|  | delegate_->set_prehandle_keyboard_event_is_shortcut(false); | 
|  |  | 
|  | // Forward the Char event. | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kChar); | 
|  |  | 
|  | // The Char event is not suppressed; the renderer will ignore it | 
|  | // if the preceding RawKeyDown shortcut goes unhandled. | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kChar, | 
|  | dispatched_events[0]->ToEvent()->Event()->Event().GetType()); | 
|  |  | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kNotConsumed); | 
|  | EXPECT_EQ(WebInputEvent::Type::kChar, | 
|  | delegate_->unhandled_keyboard_event_type()); | 
|  |  | 
|  | // Forward the KeyUp event. | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kKeyUp); | 
|  |  | 
|  | // Make sure only KeyUp was sent to the renderer. | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kKeyUp, | 
|  | dispatched_events[0]->ToEvent()->Event()->Event().GetType()); | 
|  |  | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kNotConsumed); | 
|  | EXPECT_EQ(WebInputEvent::Type::kKeyUp, | 
|  | delegate_->unhandled_keyboard_event_type()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, UnhandledWheelEvent) { | 
|  | SimulateWheelEvent(-5, 0, 0, true, WebMouseWheelEvent::kPhaseBegan); | 
|  |  | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kMouseWheel, | 
|  | dispatched_events[0]->ToEvent()->Event()->Event().GetType()); | 
|  |  | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kNotConsumed); | 
|  |  | 
|  | EXPECT_TRUE(delegate_->handle_wheel_event_called()); | 
|  | EXPECT_EQ(1, view_->unhandled_wheel_event_count()); | 
|  | EXPECT_EQ(-5, view_->unhandled_wheel_event().delta_x); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, HandleWheelEvent) { | 
|  | // Indicate that we're going to handle this wheel event | 
|  | delegate_->set_handle_wheel_event(true); | 
|  |  | 
|  | SimulateWheelEvent(-5, 0, 0, true, WebMouseWheelEvent::kPhaseBegan); | 
|  |  | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kMouseWheel, | 
|  | dispatched_events[0]->ToEvent()->Event()->Event().GetType()); | 
|  |  | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kNotConsumed); | 
|  |  | 
|  | // ensure the wheel event handler was invoked | 
|  | EXPECT_TRUE(delegate_->handle_wheel_event_called()); | 
|  |  | 
|  | // and that it suppressed the unhandled wheel event handler. | 
|  | EXPECT_EQ(0, view_->unhandled_wheel_event_count()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, EventsCausingFocus) { | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseDown); | 
|  | EXPECT_EQ(1, delegate_->GetFocusOwningWebContentsCallCount()); | 
|  |  | 
|  | PressTouchPoint(0, 1); | 
|  | SendTouchEvent(); | 
|  | EXPECT_EQ(2, delegate_->GetFocusOwningWebContentsCallCount()); | 
|  |  | 
|  | ReleaseTouchPoint(0); | 
|  | SendTouchEvent(); | 
|  | EXPECT_EQ(2, delegate_->GetFocusOwningWebContentsCallCount()); | 
|  |  | 
|  | SimulateGestureEvent(WebInputEvent::Type::kGestureTapDown, | 
|  | blink::WebGestureDevice::kTouchscreen); | 
|  | EXPECT_EQ(2, delegate_->GetFocusOwningWebContentsCallCount()); | 
|  |  | 
|  | SimulateGestureEvent(WebInputEvent::Type::kGestureTap, | 
|  | blink::WebGestureDevice::kTouchscreen); | 
|  | EXPECT_EQ(3, delegate_->GetFocusOwningWebContentsCallCount()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, UnhandledGestureEvent) { | 
|  | SimulateGestureEvent(WebInputEvent::Type::kGestureTwoFingerTap, | 
|  | blink::WebGestureDevice::kTouchscreen); | 
|  |  | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kGestureTwoFingerTap, | 
|  | dispatched_events[0]->ToEvent()->Event()->Event().GetType()); | 
|  |  | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kNotConsumed); | 
|  |  | 
|  | EXPECT_EQ(WebInputEvent::Type::kGestureTwoFingerTap, | 
|  | view_->gesture_event_type()); | 
|  | EXPECT_EQ(blink::mojom::InputEventResultState::kNotConsumed, | 
|  | view_->ack_result()); | 
|  | } | 
|  |  | 
|  | // Test that the hang monitor timer expires properly if a new timer is started | 
|  | // while one is in progress (see crbug.com/11007). | 
|  | TEST_F(RenderWidgetHostTest, DontPostponeInputEventAckTimeout) { | 
|  | base::TimeDelta delay = kHungRendererDelay; | 
|  |  | 
|  | // Start a timeout. | 
|  | host_->StartInputEventAckTimeout(); | 
|  |  | 
|  | task_environment_.FastForwardBy(delay / 2); | 
|  |  | 
|  | // Add another timeout. | 
|  | EXPECT_FALSE(delegate_->unresponsive_timer_fired()); | 
|  | host_->StartInputEventAckTimeout(); | 
|  |  | 
|  | // Wait long enough for first timeout and see if it fired. | 
|  | task_environment_.FastForwardBy(delay); | 
|  | EXPECT_TRUE(delegate_->unresponsive_timer_fired()); | 
|  | } | 
|  |  | 
|  | // Test that the hang monitor timer expires properly if it is started, stopped, | 
|  | // and then started again. | 
|  | TEST_F(RenderWidgetHostTest, StopAndStartInputEventAckTimeout) { | 
|  | // Start a timeout, then stop it. | 
|  | host_->StartInputEventAckTimeout(); | 
|  | host_->StopInputEventAckTimeout(); | 
|  |  | 
|  | // Start it again to ensure it still works. | 
|  | EXPECT_FALSE(delegate_->unresponsive_timer_fired()); | 
|  | host_->StartInputEventAckTimeout(); | 
|  |  | 
|  | // Wait long enough for first timeout and see if it fired. | 
|  | task_environment_.FastForwardBy(kHungRendererDelay + base::Milliseconds(10)); | 
|  | EXPECT_TRUE(delegate_->unresponsive_timer_fired()); | 
|  | } | 
|  |  | 
|  | // Test that the hang monitor timer is effectively disabled when the widget is | 
|  | // hidden. | 
|  | TEST_F(RenderWidgetHostTest, InputEventAckTimeoutDisabledForInputWhenHidden) { | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseMove, 10, 10, 0, false); | 
|  |  | 
|  | // Hiding the widget should deactivate the timeout. | 
|  | host_->WasHidden(); | 
|  |  | 
|  | // The timeout should not fire. | 
|  | EXPECT_FALSE(delegate_->unresponsive_timer_fired()); | 
|  | task_environment_.FastForwardBy(kHungRendererDelay + base::Milliseconds(10)); | 
|  | EXPECT_FALSE(delegate_->unresponsive_timer_fired()); | 
|  |  | 
|  | // The timeout should never reactivate while hidden. | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseMove, 10, 10, 0, false); | 
|  | task_environment_.FastForwardBy(kHungRendererDelay + base::Milliseconds(10)); | 
|  | EXPECT_FALSE(delegate_->unresponsive_timer_fired()); | 
|  |  | 
|  | // Showing the widget should restore the timeout, as the events have | 
|  | // not yet been ack'ed. | 
|  | host_->WasShown({} /* record_tab_switch_time_request */); | 
|  | task_environment_.FastForwardBy(kHungRendererDelay + base::Milliseconds(10)); | 
|  | EXPECT_TRUE(delegate_->unresponsive_timer_fired()); | 
|  | } | 
|  |  | 
|  | // Test that the hang monitor catches two input events but only one ack. | 
|  | // This can happen if the second input event causes the renderer to hang. | 
|  | // This test will catch a regression of crbug.com/111185. | 
|  | TEST_F(RenderWidgetHostTest, MultipleInputEvents) { | 
|  | // Send two events but only one ack. | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  | task_environment_.FastForwardBy(kHungRendererDelay / 2); | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  |  | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(2u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  |  | 
|  | // Send the simulated response from the renderer back. | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kConsumed); | 
|  |  | 
|  | // Wait long enough for second timeout and see if it fired. | 
|  | task_environment_.FastForwardBy(kHungRendererDelay + base::Milliseconds(10)); | 
|  | EXPECT_TRUE(delegate_->unresponsive_timer_fired()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, IgnoreInputEvent) { | 
|  | host_->SetupForInputRouterTest(); | 
|  |  | 
|  | delegate_->SetIgnoreInputEvents(true); | 
|  |  | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  | EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); | 
|  |  | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseMove); | 
|  | EXPECT_FALSE(host_->mock_input_router()->sent_mouse_event_); | 
|  |  | 
|  | SimulateWheelEvent(0, 100, 0, true); | 
|  | EXPECT_FALSE(host_->mock_input_router()->sent_wheel_event_); | 
|  |  | 
|  | SimulateGestureEvent(WebInputEvent::Type::kGestureScrollBegin, | 
|  | blink::WebGestureDevice::kTouchpad); | 
|  | EXPECT_FALSE(host_->mock_input_router()->sent_gesture_event_); | 
|  |  | 
|  | PressTouchPoint(100, 100); | 
|  | SendTouchEvent(); | 
|  | EXPECT_FALSE(host_->mock_input_router()->send_touch_event_not_cancelled_); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, KeyboardListenerIgnoresEvent) { | 
|  | host_->SetupForInputRouterTest(); | 
|  | host_->AddKeyPressEventCallback(base::BindRepeating( | 
|  | &RenderWidgetHostTest::KeyPressEventCallback, base::Unretained(this))); | 
|  | handle_key_press_event_ = false; | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  |  | 
|  | EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, KeyboardListenerSuppressFollowingEvents) { | 
|  | host_->SetupForInputRouterTest(); | 
|  |  | 
|  | host_->AddKeyPressEventCallback(base::BindRepeating( | 
|  | &RenderWidgetHostTest::KeyPressEventCallback, base::Unretained(this))); | 
|  |  | 
|  | // The callback handles the first event | 
|  | handle_key_press_event_ = true; | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  |  | 
|  | EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); | 
|  |  | 
|  | // Following Char events should be suppressed | 
|  | handle_key_press_event_ = false; | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kChar); | 
|  | EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kChar); | 
|  | EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); | 
|  |  | 
|  | // Sending RawKeyDown event should stop suppression | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  | EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); | 
|  |  | 
|  | host_->mock_input_router()->sent_keyboard_event_ = false; | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kChar); | 
|  | EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, MouseEventCallbackCanHandleEvent) { | 
|  | host_->SetupForInputRouterTest(); | 
|  |  | 
|  | host_->AddMouseEventCallback(base::BindRepeating( | 
|  | &RenderWidgetHostTest::MouseEventCallback, base::Unretained(this))); | 
|  |  | 
|  | handle_mouse_event_ = true; | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseDown); | 
|  |  | 
|  | EXPECT_FALSE(host_->mock_input_router()->sent_mouse_event_); | 
|  |  | 
|  | handle_mouse_event_ = false; | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseDown); | 
|  |  | 
|  | EXPECT_TRUE(host_->mock_input_router()->sent_mouse_event_); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, InputRouterReceivesHasTouchEventHandlers) { | 
|  | host_->SetupForInputRouterTest(); | 
|  |  | 
|  | ASSERT_FALSE(host_->mock_input_router()->has_handlers_); | 
|  |  | 
|  | auto touch_event_consumers = blink::mojom::TouchEventConsumers::New( | 
|  | HasTouchEventHandlers(true), HasHitTestableScrollbar(false)); | 
|  | host_->SetHasTouchEventConsumers(std::move(touch_event_consumers)); | 
|  | EXPECT_TRUE(host_->mock_input_router()->has_handlers_); | 
|  | } | 
|  |  | 
|  | void CheckLatencyInfoComponentInMessage( | 
|  | MockWidgetInputHandler::MessageVector& dispatched_events, | 
|  | WebInputEvent::Type expected_type) { | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  |  | 
|  | EXPECT_TRUE(dispatched_events[0]->ToEvent()->Event()->Event().GetType() == | 
|  | expected_type); | 
|  | EXPECT_TRUE( | 
|  | dispatched_events[0]->ToEvent()->Event()->latency_info().FindLatency( | 
|  | ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kConsumed); | 
|  | } | 
|  |  | 
|  | void CheckLatencyInfoComponentInGestureScrollUpdate( | 
|  | MockWidgetInputHandler::MessageVector& dispatched_events) { | 
|  | ASSERT_EQ(2u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | ASSERT_TRUE(dispatched_events[1]->ToEvent()); | 
|  | EXPECT_EQ(WebInputEvent::Type::kTouchScrollStarted, | 
|  | dispatched_events[0]->ToEvent()->Event()->Event().GetType()); | 
|  |  | 
|  | EXPECT_EQ(WebInputEvent::Type::kGestureScrollUpdate, | 
|  | dispatched_events[1]->ToEvent()->Event()->Event().GetType()); | 
|  | EXPECT_TRUE( | 
|  | dispatched_events[1]->ToEvent()->Event()->latency_info().FindLatency( | 
|  | ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); | 
|  | dispatched_events[1]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kConsumed); | 
|  | } | 
|  |  | 
|  | // Tests that after input event passes through RWHI through ForwardXXXEvent() | 
|  | // or ForwardXXXEventWithLatencyInfo(), LatencyInfo component | 
|  | // ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT will always present in the | 
|  | // event's LatencyInfo. | 
|  | TEST_F(RenderWidgetHostTest, InputEventRWHLatencyComponent) { | 
|  | auto touch_event_consumers = blink::mojom::TouchEventConsumers::New( | 
|  | HasTouchEventHandlers(true), HasHitTestableScrollbar(false)); | 
|  | host_->SetHasTouchEventConsumers(std::move(touch_event_consumers)); | 
|  |  | 
|  | // Tests RWHI::ForwardWheelEvent(). | 
|  | SimulateWheelEvent(-5, 0, 0, true, WebMouseWheelEvent::kPhaseBegan); | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | CheckLatencyInfoComponentInMessage(dispatched_events, | 
|  | WebInputEvent::Type::kMouseWheel); | 
|  |  | 
|  | // Tests RWHI::ForwardWheelEventWithLatencyInfo(). | 
|  | SimulateWheelEventWithLatencyInfo(-5, 0, 0, true, ui::LatencyInfo(), | 
|  | WebMouseWheelEvent::kPhaseChanged); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | CheckLatencyInfoComponentInMessage(dispatched_events, | 
|  | WebInputEvent::Type::kMouseWheel); | 
|  |  | 
|  | // Tests RWHI::ForwardMouseEvent(). | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseMove); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | CheckLatencyInfoComponentInMessage(dispatched_events, | 
|  | WebInputEvent::Type::kMouseMove); | 
|  |  | 
|  | // Tests RWHI::ForwardMouseEventWithLatencyInfo(). | 
|  | SimulateMouseEventWithLatencyInfo(WebInputEvent::Type::kMouseMove, | 
|  | ui::LatencyInfo()); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | CheckLatencyInfoComponentInMessage(dispatched_events, | 
|  | WebInputEvent::Type::kMouseMove); | 
|  |  | 
|  | // Tests RWHI::ForwardGestureEvent(). | 
|  | PressTouchPoint(0, 1); | 
|  | SendTouchEvent(); | 
|  | widget_.SetTouchActionFromMain(cc::TouchAction::kAuto); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | CheckLatencyInfoComponentInMessage(dispatched_events, | 
|  | WebInputEvent::Type::kTouchStart); | 
|  |  | 
|  | SimulateGestureEvent(WebInputEvent::Type::kGestureScrollBegin, | 
|  | blink::WebGestureDevice::kTouchscreen); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | CheckLatencyInfoComponentInMessage(dispatched_events, | 
|  | WebInputEvent::Type::kGestureScrollBegin); | 
|  |  | 
|  | // Tests RWHI::ForwardGestureEventWithLatencyInfo(). | 
|  | SimulateGestureEventWithLatencyInfo(WebInputEvent::Type::kGestureScrollUpdate, | 
|  | blink::WebGestureDevice::kTouchscreen, | 
|  | ui::LatencyInfo()); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | CheckLatencyInfoComponentInGestureScrollUpdate(dispatched_events); | 
|  |  | 
|  | ReleaseTouchPoint(0); | 
|  | SendTouchEvent(); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  |  | 
|  | // Tests RWHI::ForwardTouchEventWithLatencyInfo(). | 
|  | PressTouchPoint(0, 1); | 
|  | SendTouchEvent(); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | CheckLatencyInfoComponentInMessage(dispatched_events, | 
|  | WebInputEvent::Type::kTouchStart); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, RendererExitedResetsInputRouter) { | 
|  | EXPECT_EQ(0u, host_->in_flight_event_count()); | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  | EXPECT_EQ(1u, host_->in_flight_event_count()); | 
|  |  | 
|  | EXPECT_FALSE(host_->input_router()->HasPendingEvents()); | 
|  | blink::WebMouseWheelEvent event; | 
|  | event.phase = blink::WebMouseWheelEvent::kPhaseBegan; | 
|  | host_->input_router()->SendWheelEvent(MouseWheelEventWithLatencyInfo(event)); | 
|  | EXPECT_TRUE(host_->input_router()->HasPendingEvents()); | 
|  |  | 
|  | // RendererExited will delete the view. | 
|  | host_->SetView(new TestView(host_.get())); | 
|  | host_->RendererExited(); | 
|  |  | 
|  | // The renderer is recreated. | 
|  | host_->SetView(view_.get()); | 
|  | // Make a new RenderWidget when the renderer is recreated and inform that a | 
|  | // RenderWidget is being created. | 
|  | blink::VisualProperties props = host_->GetInitialVisualProperties(); | 
|  | // The RenderWidget is recreated with the initial VisualProperties. | 
|  | ReinitalizeHost(); | 
|  |  | 
|  | // Make sure the input router is in a fresh state. | 
|  | ASSERT_FALSE(host_->input_router()->HasPendingEvents()); | 
|  | // There should be no in flight events. https://crbug.com/615090#152. | 
|  | EXPECT_EQ(0u, host_->in_flight_event_count()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, DestroyingRenderWidgetResetsInputRouter) { | 
|  | EXPECT_EQ(0u, host_->in_flight_event_count()); | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); | 
|  | EXPECT_EQ(1u, host_->in_flight_event_count()); | 
|  |  | 
|  | EXPECT_FALSE(host_->input_router()->HasPendingEvents()); | 
|  | blink::WebMouseWheelEvent event; | 
|  | event.phase = blink::WebMouseWheelEvent::kPhaseBegan; | 
|  | host_->input_router()->SendWheelEvent(MouseWheelEventWithLatencyInfo(event)); | 
|  | EXPECT_TRUE(host_->input_router()->HasPendingEvents()); | 
|  |  | 
|  | // The RenderWidget is destroyed in the renderer process as the main frame | 
|  | // is removed from this RenderWidgetHost's RenderWidgetView, but the | 
|  | // RenderWidgetView is still kept around for another RenderFrame. | 
|  | EXPECT_CALL(mock_owner_delegate_, IsMainFrameActive()) | 
|  | .WillRepeatedly(Return(false)); | 
|  |  | 
|  | // Make a new RenderWidget when the renderer is recreated and inform that a | 
|  | // RenderWidget is being created. | 
|  | blink::VisualProperties props = host_->GetInitialVisualProperties(); | 
|  | // The RenderWidget is recreated with the initial VisualProperties. | 
|  | EXPECT_CALL(mock_owner_delegate_, IsMainFrameActive()) | 
|  | .WillRepeatedly(Return(true)); | 
|  |  | 
|  | // Make sure the input router is in a fresh state. | 
|  | EXPECT_FALSE(host_->input_router()->HasPendingEvents()); | 
|  | // There should be no in flight events. https://crbug.com/615090#152. | 
|  | EXPECT_EQ(0u, host_->in_flight_event_count()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, RendererExitedResetsScreenRectsAck) { | 
|  | // Screen rects are sent during initialization, but we are waiting for an ack. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedScreenRects().size()); | 
|  | // Waiting for the ack prevents further sending. | 
|  | host_->SendScreenRects(); | 
|  | host_->SendScreenRects(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedScreenRects().size()); | 
|  |  | 
|  | // RendererExited will delete the view. | 
|  | host_->SetView(new TestView(host_.get())); | 
|  | host_->RendererExited(); | 
|  |  | 
|  | // Still can't send until the RenderWidget is replaced. | 
|  | host_->SendScreenRects(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedScreenRects().size()); | 
|  |  | 
|  | // The renderer is recreated. | 
|  | host_->SetView(view_.get()); | 
|  | // Make a new RenderWidget when the renderer is recreated and inform that a | 
|  | // RenderWidget is being created. | 
|  | blink::VisualProperties props = host_->GetInitialVisualProperties(); | 
|  | // The RenderWidget is recreated with the initial VisualProperties. | 
|  | ReinitalizeHost(); | 
|  |  | 
|  | // The RenderWidget is shown when navigation completes. This sends screen | 
|  | // rects again. The IPC is sent as it's not waiting for an ack. | 
|  | host_->WasShown({}); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(2u, widget_.ReceivedScreenRects().size()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, DestroyingRenderWidgetResetsScreenRectsAck) { | 
|  | // Screen rects are sent during initialization, but we are waiting for an ack. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedScreenRects().size()); | 
|  | // Waiting for the ack prevents further sending. | 
|  | host_->SendScreenRects(); | 
|  | host_->SendScreenRects(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedScreenRects().size()); | 
|  |  | 
|  | // If screen rects haven't changed, don't send them to the widget. | 
|  | ClearScreenRects(); | 
|  | host_->SendScreenRects(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedScreenRects().size()); | 
|  |  | 
|  | // The RenderWidget has been destroyed in the renderer. | 
|  | EXPECT_CALL(mock_owner_delegate_, IsMainFrameActive()) | 
|  | .WillRepeatedly(Return(false)); | 
|  |  | 
|  | // Still can't send until the RenderWidget is replaced. | 
|  | host_->SendScreenRects(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(0u, widget_.ReceivedScreenRects().size()); | 
|  |  | 
|  | // Make a new RenderWidget when the renderer is recreated and inform that a | 
|  | // RenderWidget is being created. | 
|  | blink::VisualProperties props = host_->GetInitialVisualProperties(); | 
|  | // The RenderWidget is recreated with the initial VisualProperties. | 
|  | EXPECT_CALL(mock_owner_delegate_, IsMainFrameActive()) | 
|  | .WillRepeatedly(Return(true)); | 
|  |  | 
|  | // We are able to send screen rects again. The IPC is sent as it's not waiting | 
|  | // for an ack. | 
|  | host_->SendScreenRects(); | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedScreenRects().size()); | 
|  | } | 
|  |  | 
|  | // Regression test for http://crbug.com/401859 and http://crbug.com/522795. | 
|  | TEST_F(RenderWidgetHostTest, RendererExitedResetsIsHidden) { | 
|  | // RendererExited will delete the view. | 
|  | host_->SetView(new TestView(host_.get())); | 
|  | host_->WasShown({} /* record_tab_switch_time_request */); | 
|  |  | 
|  | ASSERT_FALSE(host_->is_hidden()); | 
|  | host_->RendererExited(); | 
|  | ASSERT_TRUE(host_->is_hidden()); | 
|  |  | 
|  | // Make sure the input router is in a fresh state. | 
|  | ASSERT_FALSE(host_->input_router()->HasPendingEvents()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, VisualProperties) { | 
|  | gfx::Rect bounds(0, 0, 100, 100); | 
|  | gfx::Rect compositor_viewport_pixel_rect(40, 50); | 
|  | view_->SetBounds(bounds); | 
|  | view_->SetMockCompositorViewportPixelSize( | 
|  | compositor_viewport_pixel_rect.size()); | 
|  |  | 
|  | blink::VisualProperties visual_properties = host_->GetVisualProperties(); | 
|  | EXPECT_EQ(bounds.size(), visual_properties.new_size); | 
|  | EXPECT_EQ(compositor_viewport_pixel_rect, | 
|  | visual_properties.compositor_viewport_pixel_rect); | 
|  | } | 
|  |  | 
|  | // Make sure no dragging occurs after renderer exited. See crbug.com/704832. | 
|  | TEST_F(RenderWidgetHostTest, RendererExitedNoDrag) { | 
|  | host_->SetView(new TestView(host_.get())); | 
|  |  | 
|  | EXPECT_EQ(delegate_->mock_delegate_view()->start_dragging_count(), 0); | 
|  |  | 
|  | GURL http_url = GURL("http://www.domain.com/index.html"); | 
|  | DropData drop_data; | 
|  | drop_data.url = http_url; | 
|  | drop_data.html_base_url = http_url; | 
|  | FileSystemAccessManagerImpl* file_system_manager = | 
|  | static_cast<StoragePartitionImpl*>(process_->GetStoragePartition()) | 
|  | ->GetFileSystemAccessManager(); | 
|  | blink::DragOperationsMask drag_operation = blink::kDragOperationEvery; | 
|  | host_->StartDragging( | 
|  | DropDataToDragData( | 
|  | drop_data, file_system_manager, process_->GetID(), | 
|  | ChromeBlobStorageContext::GetFor(process_->GetBrowserContext())), | 
|  | drag_operation, SkBitmap(), gfx::Vector2d(), gfx::Rect(), | 
|  | blink::mojom::DragEventSourceInfo::New()); | 
|  | EXPECT_EQ(delegate_->mock_delegate_view()->start_dragging_count(), 1); | 
|  |  | 
|  | // Simulate that renderer exited due navigation to the next page. | 
|  | host_->RendererExited(); | 
|  | EXPECT_FALSE(host_->GetView()); | 
|  | host_->StartDragging( | 
|  | DropDataToDragData( | 
|  | drop_data, file_system_manager, process_->GetID(), | 
|  | ChromeBlobStorageContext::GetFor(process_->GetBrowserContext())), | 
|  | drag_operation, SkBitmap(), gfx::Vector2d(), gfx::Rect(), | 
|  | blink::mojom::DragEventSourceInfo::New()); | 
|  | EXPECT_EQ(delegate_->mock_delegate_view()->start_dragging_count(), 1); | 
|  | } | 
|  |  | 
|  | class RenderWidgetHostInitialSizeTest : public RenderWidgetHostTest { | 
|  | public: | 
|  | RenderWidgetHostInitialSizeTest() | 
|  | : RenderWidgetHostTest(), initial_size_(200, 100) {} | 
|  |  | 
|  | void ConfigureView(TestView* view) override { | 
|  | view->SetBounds(gfx::Rect(initial_size_)); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | gfx::Size initial_size_; | 
|  | }; | 
|  |  | 
|  | TEST_F(RenderWidgetHostInitialSizeTest, InitialSize) { | 
|  | // Having an initial size set means that the size information had been sent | 
|  | // with the request to new up the `blink::WebView` and so subsequent | 
|  | // SynchronizeVisualProperties calls should not result in new IPC (unless the | 
|  | // size has actually changed). | 
|  | EXPECT_FALSE(host_->SynchronizeVisualProperties()); | 
|  | EXPECT_EQ(initial_size_, host_->old_visual_properties_->new_size); | 
|  | EXPECT_TRUE(host_->visual_properties_ack_pending_); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, HideUnthrottlesResize) { | 
|  | ClearVisualProperties(); | 
|  | view_->SetBounds(gfx::Rect(100, 100)); | 
|  | EXPECT_TRUE(host_->SynchronizeVisualProperties()); | 
|  | // blink::mojom::Widget::UpdateVisualProperties sent to the renderer. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | EXPECT_EQ(1u, widget_.ReceivedVisualProperties().size()); | 
|  | { | 
|  | // Size sent to the renderer. | 
|  | EXPECT_EQ(gfx::Size(100, 100), | 
|  | widget_.ReceivedVisualProperties().at(0).new_size); | 
|  | } | 
|  | // An ack is pending, throttling further updates. | 
|  | EXPECT_TRUE(host_->visual_properties_ack_pending_); | 
|  |  | 
|  | // Hiding the widget should unthrottle resize. | 
|  | host_->WasHidden(); | 
|  | EXPECT_FALSE(host_->visual_properties_ack_pending_); | 
|  | } | 
|  |  | 
|  | // Tests that event dispatch after the delegate has been detached doesn't cause | 
|  | // a crash. See crbug.com/563237. | 
|  | TEST_F(RenderWidgetHostTest, EventDispatchPostDetach) { | 
|  | auto touch_event_consumers = blink::mojom::TouchEventConsumers::New( | 
|  | HasTouchEventHandlers(true), HasHitTestableScrollbar(false)); | 
|  | host_->SetHasTouchEventConsumers(std::move(touch_event_consumers)); | 
|  | process_->sink().ClearMessages(); | 
|  |  | 
|  | host_->DetachDelegate(); | 
|  |  | 
|  | // Tests RWHI::ForwardGestureEventWithLatencyInfo(). | 
|  | SimulateGestureEventWithLatencyInfo(WebInputEvent::Type::kGestureScrollUpdate, | 
|  | blink::WebGestureDevice::kTouchscreen, | 
|  | ui::LatencyInfo()); | 
|  |  | 
|  | // Tests RWHI::ForwardWheelEventWithLatencyInfo(). | 
|  | SimulateWheelEventWithLatencyInfo(-5, 0, 0, true, ui::LatencyInfo()); | 
|  |  | 
|  | ASSERT_FALSE(host_->input_router()->HasPendingEvents()); | 
|  | } | 
|  |  | 
|  | // If a navigation happens while the widget is hidden, we shouldn't show | 
|  | // contents of the previous page when we become visible. | 
|  | TEST_F(RenderWidgetHostTest, NavigateInBackgroundShowsBlank) { | 
|  | // When visible, navigation does not immediately call into | 
|  | // ClearDisplayedGraphics. | 
|  | host_->WasShown({} /* record_tab_switch_time_request */); | 
|  | host_->DidNavigate(); | 
|  | EXPECT_FALSE(host_->new_content_rendering_timeout_fired()); | 
|  |  | 
|  | // Hide then show. ClearDisplayedGraphics must be called. | 
|  | host_->WasHidden(); | 
|  | host_->WasShown({} /* record_tab_switch_time_request */); | 
|  | EXPECT_TRUE(host_->new_content_rendering_timeout_fired()); | 
|  | host_->reset_new_content_rendering_timeout_fired(); | 
|  |  | 
|  | // Hide, navigate, then show. ClearDisplayedGraphics must be called. | 
|  | host_->WasHidden(); | 
|  | host_->DidNavigate(); | 
|  | host_->WasShown({} /* record_tab_switch_time_request */); | 
|  | EXPECT_TRUE(host_->new_content_rendering_timeout_fired()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, PendingUserActivationTimeout) { | 
|  | base::test::ScopedFeatureList scoped_feature_list_; | 
|  | scoped_feature_list_.InitWithFeatures( | 
|  | {features::kBrowserVerifiedUserActivationMouse, | 
|  | features::kBrowserVerifiedUserActivationKeyboard}, | 
|  | {}); | 
|  |  | 
|  | // One event allows one activation notification. | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseDown); | 
|  | EXPECT_TRUE(host_->RemovePendingUserActivationIfAvailable()); | 
|  | EXPECT_FALSE(host_->RemovePendingUserActivationIfAvailable()); | 
|  |  | 
|  | // Mouse move and up does not increase pending user activation counter. | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseMove); | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseUp); | 
|  | EXPECT_FALSE(host_->RemovePendingUserActivationIfAvailable()); | 
|  |  | 
|  | // 2 events allow 2 activation notifications. | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseDown); | 
|  | SimulateKeyboardEvent(WebInputEvent::Type::kKeyDown); | 
|  | EXPECT_TRUE(host_->RemovePendingUserActivationIfAvailable()); | 
|  | EXPECT_TRUE(host_->RemovePendingUserActivationIfAvailable()); | 
|  | EXPECT_FALSE(host_->RemovePendingUserActivationIfAvailable()); | 
|  |  | 
|  | // Timer reset the pending activation. | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseDown); | 
|  | SimulateMouseEvent(WebInputEvent::Type::kMouseDown); | 
|  | task_environment_.FastForwardBy( | 
|  | RenderWidgetHostImpl::kActivationNotificationExpireTime); | 
|  | EXPECT_FALSE(host_->RemovePendingUserActivationIfAvailable()); | 
|  | } | 
|  |  | 
|  | // Tests that fling events are not dispatched when the wheel event is consumed. | 
|  | TEST_F(RenderWidgetHostTest, NoFlingEventsWhenLastScrollEventConsumed) { | 
|  | // Simulate a consumed wheel event. | 
|  | SimulateWheelEvent(10, 0, 0, true, WebMouseWheelEvent::kPhaseBegan); | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kConsumed); | 
|  |  | 
|  | // A GestureFlingStart event following a consumed scroll event should not be | 
|  | // dispatched. | 
|  | SimulateGestureEvent(blink::WebInputEvent::Type::kGestureFlingStart, | 
|  | blink::WebGestureDevice::kTouchpad); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | EXPECT_EQ(0u, dispatched_events.size()); | 
|  | } | 
|  |  | 
|  | // Tests that fling events are dispatched when some, but not all, scroll events | 
|  | // were consumed. | 
|  | TEST_F(RenderWidgetHostTest, FlingEventsWhenSomeScrollEventsConsumed) { | 
|  | // Simulate a consumed wheel event. | 
|  | SimulateWheelEvent(10, 0, 0, true, WebMouseWheelEvent::kPhaseBegan); | 
|  | MockWidgetInputHandler::MessageVector dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kConsumed); | 
|  |  | 
|  | // Followed by a not consumed wheel event. | 
|  | SimulateWheelEvent(10, 0, 0, true, WebMouseWheelEvent::kPhaseChanged); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | ASSERT_EQ(1u, dispatched_events.size()); | 
|  | ASSERT_TRUE(dispatched_events[0]->ToEvent()); | 
|  | dispatched_events[0]->ToEvent()->CallCallback( | 
|  | blink::mojom::InputEventResultState::kNotConsumed); | 
|  |  | 
|  | // A GestureFlingStart event following the scroll events should be dispatched. | 
|  | SimulateGestureEvent(blink::WebInputEvent::Type::kGestureFlingStart, | 
|  | blink::WebGestureDevice::kTouchpad); | 
|  | dispatched_events = | 
|  | host_->mock_widget_input_handler_.GetAndResetDispatchedMessages(); | 
|  | EXPECT_NE(0u, dispatched_events.size()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, AddAndRemoveInputEventObserver) { | 
|  | MockInputEventObserver observer; | 
|  |  | 
|  | // Add InputEventObserver. | 
|  | host_->AddInputEventObserver(&observer); | 
|  |  | 
|  | // Confirm OnInputEvent is triggered. | 
|  | NativeWebKeyboardEvent native_event(WebInputEvent::Type::kChar, 0, | 
|  | GetNextSimulatedEventTime()); | 
|  | ui::LatencyInfo latency_info = ui::LatencyInfo(); | 
|  | EXPECT_CALL(observer, OnInputEvent(_)).Times(1); | 
|  | host_->DispatchInputEventWithLatencyInfo(native_event, &latency_info); | 
|  |  | 
|  | // Remove InputEventObserver. | 
|  | host_->RemoveInputEventObserver(&observer); | 
|  |  | 
|  | // Confirm InputEventObserver is removed. | 
|  | EXPECT_CALL(observer, OnInputEvent(_)).Times(0); | 
|  | latency_info = ui::LatencyInfo(); | 
|  | host_->DispatchInputEventWithLatencyInfo(native_event, &latency_info); | 
|  | } | 
|  |  | 
|  | #if BUILDFLAG(IS_ANDROID) | 
|  | TEST_F(RenderWidgetHostTest, AddAndRemoveImeInputEventObserver) { | 
|  | MockInputEventObserver observer; | 
|  |  | 
|  | // Add ImeInputEventObserver. | 
|  | host_->AddImeInputEventObserver(&observer); | 
|  |  | 
|  | // Confirm ImeFinishComposingTextEvent is triggered. | 
|  | EXPECT_CALL(observer, OnImeFinishComposingTextEvent()).Times(1); | 
|  | host_->ImeFinishComposingText(true); | 
|  |  | 
|  | // Remove ImeInputEventObserver. | 
|  | host_->RemoveImeInputEventObserver(&observer); | 
|  |  | 
|  | // Confirm ImeInputEventObserver is removed. | 
|  | EXPECT_CALL(observer, OnImeFinishComposingTextEvent()).Times(0); | 
|  | host_->ImeFinishComposingText(true); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // Tests that vertical scroll direction changes are propagated to the delegate. | 
|  | TEST_F(RenderWidgetHostTest, OnVerticalScrollDirectionChanged) { | 
|  | const auto NotifyVerticalScrollDirectionChanged = | 
|  | [this](viz::VerticalScrollDirection scroll_direction) { | 
|  | static uint32_t frame_token = 1u; | 
|  | host_->frame_token_message_queue_->DidProcessFrame( | 
|  | frame_token, base::TimeTicks::Now()); | 
|  |  | 
|  | cc::RenderFrameMetadata metadata; | 
|  | metadata.new_vertical_scroll_direction = scroll_direction; | 
|  | static_cast<cc::mojom::RenderFrameMetadataObserverClient*>( | 
|  | host_->render_frame_metadata_provider()) | 
|  | ->OnRenderFrameMetadataChanged(frame_token++, metadata); | 
|  | }; | 
|  |  | 
|  | // Verify initial state. | 
|  | EXPECT_EQ(0, delegate_->GetOnVerticalScrollDirectionChangedCallCount()); | 
|  | EXPECT_EQ(viz::VerticalScrollDirection::kNull, | 
|  | delegate_->GetLastVerticalScrollDirection()); | 
|  |  | 
|  | // Verify that we will *not* propagate a vertical scroll of |kNull| which is | 
|  | // only used to indicate the absence of a change in vertical scroll direction. | 
|  | NotifyVerticalScrollDirectionChanged(viz::VerticalScrollDirection::kNull); | 
|  | EXPECT_EQ(0, delegate_->GetOnVerticalScrollDirectionChangedCallCount()); | 
|  | EXPECT_EQ(viz::VerticalScrollDirection::kNull, | 
|  | delegate_->GetLastVerticalScrollDirection()); | 
|  |  | 
|  | // Verify that we will propagate a vertical scroll |kUp|. | 
|  | NotifyVerticalScrollDirectionChanged(viz::VerticalScrollDirection::kUp); | 
|  | EXPECT_EQ(1, delegate_->GetOnVerticalScrollDirectionChangedCallCount()); | 
|  | EXPECT_EQ(viz::VerticalScrollDirection::kUp, | 
|  | delegate_->GetLastVerticalScrollDirection()); | 
|  |  | 
|  | // Verify that we will propagate a vertical scroll |kDown|. | 
|  | NotifyVerticalScrollDirectionChanged(viz::VerticalScrollDirection::kDown); | 
|  | EXPECT_EQ(2, delegate_->GetOnVerticalScrollDirectionChangedCallCount()); | 
|  | EXPECT_EQ(viz::VerticalScrollDirection::kDown, | 
|  | delegate_->GetLastVerticalScrollDirection()); | 
|  | } | 
|  |  | 
|  | TEST_F(RenderWidgetHostTest, SetCursorWithBitmap) { | 
|  | ui::Cursor cursor(ui::mojom::CursorType::kCustom); | 
|  |  | 
|  | SkBitmap bitmap; | 
|  | bitmap.allocN32Pixels(1, 1); | 
|  | bitmap.eraseColor(SK_ColorGREEN); | 
|  | cursor.set_custom_bitmap(bitmap); | 
|  |  | 
|  | host_->SetCursor(cursor); | 
|  | EXPECT_EQ(cursor, view_->last_cursor()); | 
|  | } | 
|  |  | 
|  | }  // namespace content |