| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // 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/bind.h" |
| #include "base/command_line.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/timer/timer.h" |
| #include "build/build_config.h" |
| #include "content/browser/gpu/compositor_util.h" |
| #include "content/browser/renderer_host/input/legacy_input_router_impl.h" |
| #include "content/browser/renderer_host/input/mock_widget_input_handler.h" |
| #include "content/browser/renderer_host/input/touch_emulator.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/common/edit_command.h" |
| #include "content/common/input/synthetic_web_input_event_builders.h" |
| #include "content/common/input_messages.h" |
| #include "content/common/resize_params.h" |
| #include "content/common/view_messages.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/test/mock_render_process_host.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "content/test/fake_renderer_compositor_frame_sink.h" |
| #include "content/test/mock_widget_impl.h" |
| #include "content/test/test_render_view_host.h" |
| #include "mojo/public/cpp/bindings/interface_request.h" |
| #include "testing/gtest/include/gtest/gtest.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 defined(OS_ANDROID) |
| #include "content/browser/renderer_host/render_widget_host_view_android.h" |
| #include "ui/android/screen_android.h" |
| #endif |
| |
| #if defined(USE_AURA) || defined(OS_MACOSX) |
| #include "content/browser/compositor/test/no_transport_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 base::TimeDelta; |
| using blink::WebGestureDevice; |
| using blink::WebGestureEvent; |
| using blink::WebInputEvent; |
| using blink::WebKeyboardEvent; |
| using blink::WebMouseEvent; |
| using blink::WebMouseWheelEvent; |
| using blink::WebTouchEvent; |
| using blink::WebTouchPoint; |
| |
| namespace content { |
| |
| // MockInputRouter ------------------------------------------------------------- |
| |
| class MockInputRouter : public InputRouter { |
| public: |
| explicit MockInputRouter(InputRouterClient* client) |
| : sent_mouse_event_(false), |
| sent_wheel_event_(false), |
| sent_keyboard_event_(false), |
| sent_gesture_event_(false), |
| send_touch_event_not_cancelled_(false), |
| message_received_(false), |
| client_(client) {} |
| ~MockInputRouter() override {} |
| |
| // InputRouter |
| void SendMouseEvent(const MouseEventWithLatencyInfo& mouse_event) override { |
| sent_mouse_event_ = true; |
| } |
| void SendWheelEvent( |
| const MouseWheelEventWithLatencyInfo& wheel_event) override { |
| sent_wheel_event_ = true; |
| } |
| void SendKeyboardEvent( |
| const NativeWebKeyboardEventWithLatencyInfo& key_event) override { |
| sent_keyboard_event_ = true; |
| } |
| void SendGestureEvent( |
| const GestureEventWithLatencyInfo& gesture_event) override { |
| sent_gesture_event_ = true; |
| } |
| void SendTouchEvent(const TouchEventWithLatencyInfo& touch_event) override { |
| send_touch_event_not_cancelled_ = |
| client_->FilterInputEvent(touch_event.event, touch_event.latency) == |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED; |
| } |
| void NotifySiteIsMobileOptimized(bool is_mobile_optimized) override {} |
| bool HasPendingEvents() const override { return false; } |
| void SetDeviceScaleFactor(float device_scale_factor) override {} |
| void SetFrameTreeNodeId(int frameTreeNodeId) override {} |
| cc::TouchAction AllowedTouchAction() override { return cc::kTouchActionAuto; } |
| void SetForceEnableZoom(bool enabled) override {} |
| void BindHost(mojom::WidgetInputHandlerHostRequest request) override {} |
| |
| // IPC::Listener |
| bool OnMessageReceived(const IPC::Message& message) override { |
| message_received_ = true; |
| return false; |
| } |
| |
| bool sent_mouse_event_; |
| bool sent_wheel_event_; |
| bool sent_keyboard_event_; |
| bool sent_gesture_event_; |
| bool send_touch_event_not_cancelled_; |
| bool message_received_; |
| |
| private: |
| InputRouterClient* client_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockInputRouter); |
| }; |
| |
| // MockRenderWidgetHost ---------------------------------------------------- |
| |
| class MockRenderWidgetHost : public RenderWidgetHostImpl { |
| public: |
| |
| // Allow poking at a few private members. |
| using RenderWidgetHostImpl::GetResizeParams; |
| using RenderWidgetHostImpl::OnUpdateRect; |
| using RenderWidgetHostImpl::RendererExited; |
| using RenderWidgetHostImpl::SetInitialRenderSizeParams; |
| using RenderWidgetHostImpl::old_resize_params_; |
| using RenderWidgetHostImpl::is_hidden_; |
| using RenderWidgetHostImpl::resize_ack_pending_; |
| using RenderWidgetHostImpl::input_router_; |
| using RenderWidgetHostImpl::queued_messages_; |
| |
| void OnTouchEventAck(const TouchEventWithLatencyInfo& event, |
| InputEventAckState ack_result) override { |
| // Sniff touch acks. |
| acked_touch_event_type_ = event.event.GetType(); |
| RenderWidgetHostImpl::OnTouchEventAck(event, ack_result); |
| } |
| |
| void reset_new_content_rendering_timeout_fired() { |
| new_content_rendering_timeout_fired_ = false; |
| } |
| |
| bool new_content_rendering_timeout_fired() const { |
| return new_content_rendering_timeout_fired_; |
| } |
| |
| void DisableGestureDebounce() { |
| if (base::FeatureList::IsEnabled(features::kMojoInputMessages)) { |
| input_router_.reset( |
| new InputRouterImpl(this, this, InputRouter::Config())); |
| legacy_widget_input_handler_ = nullptr; |
| } else { |
| input_router_.reset(new LegacyInputRouterImpl( |
| process_, this, this, routing_id_, InputRouter::Config())); |
| legacy_widget_input_handler_ = |
| base::MakeUnique<LegacyIPCWidgetInputHandler>( |
| static_cast<LegacyInputRouterImpl*>(input_router_.get())); |
| } |
| } |
| |
| WebInputEvent::Type acked_touch_event_type() const { |
| return acked_touch_event_type_; |
| } |
| |
| void SetupForInputRouterTest() { |
| input_router_.reset(new MockInputRouter(this)); |
| legacy_widget_input_handler_ = nullptr; |
| } |
| |
| MockInputRouter* mock_input_router() { |
| return static_cast<MockInputRouter*>(input_router_.get()); |
| } |
| |
| uint32_t processed_frame_messages_count() { |
| return processed_frame_messages_count_; |
| } |
| |
| static MockRenderWidgetHost* Create(RenderWidgetHostDelegate* delegate, |
| RenderProcessHost* process, |
| int32_t routing_id) { |
| mojom::WidgetPtr widget; |
| std::unique_ptr<MockWidgetImpl> widget_impl = |
| base::MakeUnique<MockWidgetImpl>(mojo::MakeRequest(&widget)); |
| |
| return new MockRenderWidgetHost(delegate, process, routing_id, |
| std::move(widget_impl), std::move(widget)); |
| } |
| |
| mojom::WidgetInputHandler* GetWidgetInputHandler() override { |
| if (base::FeatureList::IsEnabled(features::kMojoInputMessages)) { |
| return &mock_widget_input_handler_; |
| } |
| return RenderWidgetHostImpl::GetWidgetInputHandler(); |
| } |
| |
| MockWidgetInputHandler mock_widget_input_handler_; |
| |
| protected: |
| void NotifyNewContentRenderingTimeoutForTesting() override { |
| new_content_rendering_timeout_fired_ = true; |
| } |
| |
| bool new_content_rendering_timeout_fired_; |
| WebInputEvent::Type acked_touch_event_type_; |
| |
| private: |
| MockRenderWidgetHost(RenderWidgetHostDelegate* delegate, |
| RenderProcessHost* process, |
| int routing_id, |
| std::unique_ptr<MockWidgetImpl> widget_impl, |
| mojom::WidgetPtr widget) |
| : RenderWidgetHostImpl(delegate, |
| process, |
| routing_id, |
| std::move(widget), |
| false), |
| new_content_rendering_timeout_fired_(false), |
| widget_impl_(std::move(widget_impl)) { |
| acked_touch_event_type_ = blink::WebInputEvent::kUndefined; |
| } |
| |
| void ProcessSwapMessages(std::vector<IPC::Message> messages) override { |
| processed_frame_messages_count_++; |
| } |
| uint32_t processed_frame_messages_count_ = 0; |
| std::unique_ptr<MockWidgetImpl> widget_impl_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockRenderWidgetHost); |
| }; |
| |
| namespace { |
| |
| cc::CompositorFrame MakeCompositorFrame(float scale_factor, gfx::Size size) { |
| cc::CompositorFrame frame; |
| frame.metadata.device_scale_factor = scale_factor; |
| frame.metadata.begin_frame_ack = viz::BeginFrameAck(0, 1, true); |
| |
| std::unique_ptr<cc::RenderPass> pass = cc::RenderPass::Create(); |
| pass->SetNew(1, gfx::Rect(size), gfx::Rect(), gfx::Transform()); |
| frame.render_pass_list.push_back(std::move(pass)); |
| if (!size.IsEmpty()) { |
| viz::TransferableResource resource; |
| resource.id = 1; |
| frame.resource_list.push_back(std::move(resource)); |
| } |
| return frame; |
| } |
| |
| // RenderWidgetHostProcess ----------------------------------------------------- |
| |
| class RenderWidgetHostProcess : public MockRenderProcessHost { |
| public: |
| explicit RenderWidgetHostProcess(BrowserContext* browser_context) |
| : MockRenderProcessHost(browser_context) { |
| } |
| ~RenderWidgetHostProcess() override {} |
| |
| bool HasConnection() const override { return true; } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostProcess); |
| }; |
| |
| // 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_(-1), |
| use_fake_physical_backing_size_(false), |
| ack_result_(INPUT_EVENT_ACK_STATE_UNKNOWN) { |
| } |
| |
| // Sets the bounds returned by GetViewBounds. |
| void set_bounds(const gfx::Rect& bounds) { |
| bounds_ = bounds; |
| } |
| |
| void set_top_controls_height(float top_controls_height) { |
| top_controls_height_ = top_controls_height; |
| } |
| |
| void set_bottom_controls_height(float bottom_controls_height) { |
| bottom_controls_height_ = bottom_controls_height; |
| } |
| |
| const WebTouchEvent& acked_event() const { return acked_event_; } |
| int acked_event_count() const { return acked_event_count_; } |
| void ClearAckedEvent() { |
| acked_event_.SetType(blink::WebInputEvent::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_; |
| } |
| int gesture_event_type() const { return gesture_event_type_; } |
| InputEventAckState ack_result() const { return ack_result_; } |
| |
| void SetMockPhysicalBackingSize(const gfx::Size& mock_physical_backing_size) { |
| use_fake_physical_backing_size_ = true; |
| mock_physical_backing_size_ = mock_physical_backing_size; |
| } |
| void ClearMockPhysicalBackingSize() { |
| use_fake_physical_backing_size_ = false; |
| } |
| |
| // RenderWidgetHostView override. |
| gfx::Rect GetViewBounds() const override { return bounds_; } |
| float GetTopControlsHeight() const override { return top_controls_height_; } |
| float GetBottomControlsHeight() const override { |
| return bottom_controls_height_; |
| } |
| void ProcessAckedTouchEvent(const TouchEventWithLatencyInfo& touch, |
| InputEventAckState ack_result) override { |
| acked_event_ = touch.event; |
| ++acked_event_count_; |
| } |
| void WheelEventAck(const WebMouseWheelEvent& event, |
| InputEventAckState ack_result) override { |
| if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) |
| return; |
| unhandled_wheel_event_count_++; |
| unhandled_wheel_event_ = event; |
| } |
| void GestureEventAck(const WebGestureEvent& event, |
| InputEventAckState ack_result) override { |
| gesture_event_type_ = event.GetType(); |
| ack_result_ = ack_result; |
| } |
| gfx::Size GetPhysicalBackingSize() const override { |
| if (use_fake_physical_backing_size_) |
| return mock_physical_backing_size_; |
| return TestRenderWidgetHostView::GetPhysicalBackingSize(); |
| } |
| |
| protected: |
| WebMouseWheelEvent unhandled_wheel_event_; |
| int unhandled_wheel_event_count_; |
| WebTouchEvent acked_event_; |
| int acked_event_count_; |
| int gesture_event_type_; |
| gfx::Rect bounds_; |
| bool use_fake_physical_backing_size_; |
| gfx::Size mock_physical_backing_size_; |
| InputEventAckState ack_result_; |
| float top_controls_height_; |
| float bottom_controls_height_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestView); |
| }; |
| |
| // MockRenderViewHostDelegateView ------------------------------------------ |
| class MockRenderViewHostDelegateView : public RenderViewHostDelegateView { |
| public: |
| MockRenderViewHostDelegateView() = default; |
| ~MockRenderViewHostDelegateView() override = default; |
| |
| int start_dragging_count() const { return start_dragging_count_; } |
| |
| // RenderViewHostDelegateView: |
| void StartDragging(const DropData& drop_data, |
| blink::WebDragOperationsMask allowed_ops, |
| const gfx::ImageSkia& image, |
| const gfx::Vector2d& image_offset, |
| const DragEventSourceInfo& event_info, |
| RenderWidgetHostImpl* source_rwh) override { |
| ++start_dragging_count_; |
| } |
| |
| private: |
| int start_dragging_count_ = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockRenderViewHostDelegateView); |
| }; |
| |
| // 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::kUndefined), |
| unhandled_keyboard_event_called_(false), |
| unhandled_keyboard_event_type_(WebInputEvent::kUndefined), |
| handle_wheel_event_(false), |
| handle_wheel_event_called_(false), |
| unresponsive_timer_fired_(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 SetScreenInfo(const ScreenInfo& screen_info) { |
| screen_info_ = screen_info; |
| } |
| |
| // RenderWidgetHostDelegate overrides. |
| void GetScreenInfo(ScreenInfo* screen_info) override { |
| *screen_info = screen_info_; |
| } |
| |
| RenderViewHostDelegateView* GetDelegateView() override { |
| return mock_delegate_view(); |
| } |
| |
| 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; |
| } |
| |
| void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) override { |
| unhandled_keyboard_event_type_ = event.GetType(); |
| unhandled_keyboard_event_called_ = true; |
| } |
| |
| bool HandleWheelEvent(const blink::WebMouseWheelEvent& event) override { |
| handle_wheel_event_called_ = true; |
| return handle_wheel_event_; |
| } |
| |
| void RendererUnresponsive(RenderWidgetHostImpl* render_widget_host) override { |
| unresponsive_timer_fired_ = true; |
| } |
| |
| void ExecuteEditCommand( |
| const std::string& command, |
| const base::Optional<base::string16>& value) override {} |
| |
| void Cut() override {} |
| void Copy() override {} |
| void Paste() override {} |
| void SelectAll() override {} |
| |
| 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_; |
| |
| ScreenInfo screen_info_; |
| |
| std::unique_ptr<MockRenderViewHostDelegateView> |
| render_view_host_delegate_view_; |
| }; |
| |
| enum WheelScrollingMode { |
| kWheelScrollingModeNone, |
| kWheelScrollLatching, |
| kAsyncWheelEvents, |
| }; |
| |
| enum class UseMojoInputMessages { kEnabled, kDisabled }; |
| |
| // RenderWidgetHostTest -------------------------------------------------------- |
| |
| class RenderWidgetHostTest : public testing::Test { |
| public: |
| RenderWidgetHostTest( |
| UseMojoInputMessages input_messages_mode = UseMojoInputMessages::kEnabled, |
| WheelScrollingMode wheel_scrolling_mode = kWheelScrollLatching) |
| : process_(NULL), |
| handle_key_press_event_(false), |
| handle_mouse_event_(false), |
| simulated_event_time_delta_seconds_(0), |
| wheel_scroll_latching_enabled_(wheel_scrolling_mode != |
| kWheelScrollingModeNone) { |
| std::vector<base::StringPiece> features; |
| std::vector<base::StringPiece> disabled_features; |
| if (input_messages_mode == UseMojoInputMessages::kEnabled) { |
| features.push_back(features::kMojoInputMessages.name); |
| } else { |
| disabled_features.push_back(features::kMojoInputMessages.name); |
| } |
| |
| switch (wheel_scrolling_mode) { |
| case kWheelScrollingModeNone: |
| features.push_back(features::kRafAlignedTouchInputEvents.name); |
| disabled_features.push_back( |
| features::kTouchpadAndWheelScrollLatching.name); |
| disabled_features.push_back(features::kAsyncWheelEvents.name); |
| break; |
| case kWheelScrollLatching: |
| features.push_back(features::kRafAlignedTouchInputEvents.name); |
| features.push_back(features::kTouchpadAndWheelScrollLatching.name); |
| disabled_features.push_back(features::kAsyncWheelEvents.name); |
| break; |
| case kAsyncWheelEvents: |
| features.push_back(features::kRafAlignedTouchInputEvents.name); |
| features.push_back(features::kTouchpadAndWheelScrollLatching.name); |
| features.push_back(features::kAsyncWheelEvents.name); |
| break; |
| } |
| |
| features.push_back(features::kVsyncAlignedInputEvents.name); |
| |
| feature_list_.InitFromCommandLine(base::JoinString(features, ","), |
| base::JoinString(disabled_features, ",")); |
| |
| last_simulated_event_time_seconds_ = |
| ui::EventTimeStampToSeconds(ui::EventTimeForNow()); |
| } |
| ~RenderWidgetHostTest() override {} |
| |
| bool KeyPressEventCallback(const NativeWebKeyboardEvent& /* event */) { |
| return handle_key_press_event_; |
| } |
| bool MouseEventCallback(const blink::WebMouseEvent& /* event */) { |
| return handle_mouse_event_; |
| } |
| |
| protected: |
| // testing::Test |
| void SetUp() override { |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| command_line->AppendSwitch(switches::kValidateInputEventStream); |
| browser_context_.reset(new TestBrowserContext()); |
| delegate_.reset(new MockRenderWidgetHostDelegate()); |
| process_ = new RenderWidgetHostProcess(browser_context_.get()); |
| sink_ = &process_->sink(); |
| #if defined(USE_AURA) || defined(OS_MACOSX) |
| ImageTransportFactory::InitializeForUnitTests( |
| std::unique_ptr<ImageTransportFactory>( |
| new NoTransportImageTransportFactory)); |
| #endif |
| #if defined(OS_ANDROID) |
| ui::SetScreenAndroid(); // calls display::Screen::SetScreenInstance(). |
| #endif |
| #if defined(USE_AURA) |
| screen_.reset(aura::TestScreen::Create(gfx::Size())); |
| display::Screen::SetScreenInstance(screen_.get()); |
| #endif |
| host_.reset(MockRenderWidgetHost::Create(delegate_.get(), process_, |
| process_->GetNextRoutingID())); |
| view_.reset(new TestView(host_.get())); |
| ConfigureView(view_.get()); |
| host_->SetView(view_.get()); |
| SetInitialRenderSizeParams(); |
| host_->Init(); |
| host_->DisableGestureDebounce(); |
| |
| viz::mojom::CompositorFrameSinkPtr sink; |
| viz::mojom::CompositorFrameSinkRequest sink_request = |
| mojo::MakeRequest(&sink); |
| viz::mojom::CompositorFrameSinkClientRequest client_request = |
| mojo::MakeRequest(&renderer_compositor_frame_sink_ptr_); |
| renderer_compositor_frame_sink_ = |
| base::MakeUnique<FakeRendererCompositorFrameSink>( |
| std::move(sink), std::move(client_request)); |
| host_->RequestCompositorFrameSink( |
| std::move(sink_request), |
| std::move(renderer_compositor_frame_sink_ptr_)); |
| } |
| |
| void TearDown() override { |
| view_.reset(); |
| host_.reset(); |
| delegate_.reset(); |
| process_ = NULL; |
| browser_context_.reset(); |
| |
| #if defined(USE_AURA) |
| display::Screen::SetScreenInstance(nullptr); |
| screen_.reset(); |
| #endif |
| #if defined(USE_AURA) || defined(OS_MACOSX) |
| ImageTransportFactory::Terminate(); |
| #endif |
| #if defined(OS_ANDROID) |
| display::Screen::SetScreenInstance(nullptr); |
| #endif |
| |
| // Process all pending tasks to avoid leaks. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void SetInitialRenderSizeParams() { |
| ResizeParams render_size_params; |
| host_->GetResizeParams(&render_size_params); |
| host_->SetInitialRenderSizeParams(render_size_params); |
| } |
| |
| virtual void ConfigureView(TestView* view) { |
| } |
| |
| int64_t GetLatencyComponentId() { return host_->GetLatencyComponentId(); } |
| |
| void SendInputEventACK(WebInputEvent::Type type, |
| InputEventAckState ack_result) { |
| DCHECK(!WebInputEvent::IsTouchEventType(type)); |
| InputEventAck ack(InputEventAckSource::COMPOSITOR_THREAD, type, ack_result); |
| host_->OnMessageReceived(InputHostMsg_HandleInputEvent_ACK(0, ack)); |
| } |
| |
| void SendScrollBeginAckIfneeded(InputEventAckState ack_result) { |
| if (wheel_scroll_latching_enabled_) { |
| // GSB events are blocking, send the ack. |
| SendInputEventACK(WebInputEvent::kGestureScrollBegin, ack_result); |
| } |
| } |
| |
| double GetNextSimulatedEventTimeSeconds() { |
| last_simulated_event_time_seconds_ += simulated_event_time_delta_seconds_; |
| return last_simulated_event_time_seconds_; |
| } |
| |
| void SimulateKeyboardEvent(WebInputEvent::Type type) { |
| SimulateKeyboardEvent(type, 0); |
| } |
| |
| void SimulateKeyboardEvent(WebInputEvent::Type type, int modifiers) { |
| NativeWebKeyboardEvent native_event(type, modifiers, |
| GetNextSimulatedEventTimeSeconds()); |
| host_->ForwardKeyboardEvent(native_event); |
| } |
| |
| void SimulateKeyboardEventWithCommands(WebInputEvent::Type type) { |
| NativeWebKeyboardEvent native_event(type, 0, |
| GetNextSimulatedEventTimeSeconds()); |
| EditCommands commands; |
| commands.emplace_back("name", "value"); |
| host_->ForwardKeyboardEventWithCommands(native_event, ui::LatencyInfo(), |
| &commands, nullptr); |
| } |
| |
| void SimulateMouseEvent(WebInputEvent::Type type) { |
| host_->ForwardMouseEvent(SyntheticWebMouseEventBuilder::Build(type)); |
| } |
| |
| void SimulateMouseEventWithLatencyInfo(WebInputEvent::Type type, |
| const ui::LatencyInfo& ui_latency) { |
| host_->ForwardMouseEventWithLatencyInfo( |
| SyntheticWebMouseEventBuilder::Build(type), |
| ui_latency); |
| } |
| |
| void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) { |
| host_->ForwardWheelEvent(SyntheticWebMouseWheelEventBuilder::Build( |
| 0, 0, dX, dY, modifiers, precise)); |
| } |
| |
| void SimulateWheelEventPossiblyIncludingPhase( |
| float dX, |
| float dY, |
| int modifiers, |
| bool precise, |
| WebMouseWheelEvent::Phase phase) { |
| WebMouseWheelEvent wheel_event = SyntheticWebMouseWheelEventBuilder::Build( |
| 0, 0, dX, dY, modifiers, precise); |
| if (wheel_scroll_latching_enabled_) |
| 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( |
| SyntheticWebMouseWheelEventBuilder::Build(0, 0, dX, dY, modifiers, |
| precise), |
| ui_latency); |
| } |
| |
| void SimulateWheelEventWithLatencyInfoAndPossiblyPhase( |
| float dX, |
| float dY, |
| int modifiers, |
| bool precise, |
| const ui::LatencyInfo& ui_latency, |
| WebMouseWheelEvent::Phase phase) { |
| WebMouseWheelEvent wheel_event = SyntheticWebMouseWheelEventBuilder::Build( |
| 0, 0, dX, dY, modifiers, precise); |
| if (wheel_scroll_latching_enabled_) |
| wheel_event.phase = phase; |
| host_->ForwardWheelEventWithLatencyInfo(wheel_event, ui_latency); |
| } |
| |
| void SimulateMouseMove(int x, int y, int modifiers) { |
| SimulateMouseEvent(WebInputEvent::kMouseMove, x, y, modifiers, false); |
| } |
| |
| void SimulateMouseEvent( |
| WebInputEvent::Type type, int x, int y, int modifiers, bool pressed) { |
| WebMouseEvent event = |
| SyntheticWebMouseEventBuilder::Build(type, x, y, modifiers); |
| if (pressed) |
| event.button = WebMouseEvent::Button::kLeft; |
| event.SetTimeStampSeconds(GetNextSimulatedEventTimeSeconds()); |
| host_->ForwardMouseEvent(event); |
| } |
| |
| // Inject simple synthetic WebGestureEvent instances. |
| void SimulateGestureEvent(WebInputEvent::Type type, |
| WebGestureDevice sourceDevice) { |
| host_->ForwardGestureEvent( |
| SyntheticWebGestureEventBuilder::Build(type, sourceDevice)); |
| } |
| |
| void SimulateGestureEventWithLatencyInfo(WebInputEvent::Type type, |
| WebGestureDevice sourceDevice, |
| const ui::LatencyInfo& ui_latency) { |
| host_->ForwardGestureEventWithLatencyInfo( |
| 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; |
| int data_length; |
| if (!iter.ReadData(&data, &data_length)) |
| return NULL; |
| return reinterpret_cast<const WebInputEvent*>(data); |
| } |
| |
| void UnhandledWheelEvent(); |
| void UnhandledWheelEventMojoInputDisabled(); |
| void HandleWheelEvent(); |
| void HandleWheelEventMojoInputDisabled(); |
| void InputEventRWHLatencyComponent(); |
| void InputEventRWHLatencyComponentMojoInputDisabled(); |
| |
| std::unique_ptr<TestBrowserContext> browser_context_; |
| RenderWidgetHostProcess* process_; // Deleted automatically by the widget. |
| std::unique_ptr<MockRenderWidgetHostDelegate> delegate_; |
| std::unique_ptr<MockRenderWidgetHost> host_; |
| std::unique_ptr<TestView> view_; |
| std::unique_ptr<display::Screen> screen_; |
| bool handle_key_press_event_; |
| bool handle_mouse_event_; |
| double last_simulated_event_time_seconds_; |
| double simulated_event_time_delta_seconds_; |
| IPC::TestSink* sink_; |
| std::unique_ptr<FakeRendererCompositorFrameSink> |
| renderer_compositor_frame_sink_; |
| bool wheel_scroll_latching_enabled_; |
| |
| private: |
| SyntheticWebTouchEvent touch_event_; |
| |
| TestBrowserThreadBundle thread_bundle_; |
| base::test::ScopedFeatureList feature_list_; |
| viz::mojom::CompositorFrameSinkClientPtr renderer_compositor_frame_sink_ptr_; |
| |
| DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostTest); |
| }; |
| |
| class RenderWidgetHostMojoInputDisabledTest : public RenderWidgetHostTest { |
| public: |
| RenderWidgetHostMojoInputDisabledTest() |
| : RenderWidgetHostTest(UseMojoInputMessages::kDisabled) {} |
| }; |
| |
| class RenderWidgetHostWheelScrollLatchingDisabledTest |
| : public RenderWidgetHostTest { |
| public: |
| RenderWidgetHostWheelScrollLatchingDisabledTest() |
| : RenderWidgetHostTest(UseMojoInputMessages::kEnabled, |
| kWheelScrollingModeNone) {} |
| }; |
| |
| class RenderWidgetHostAsyncWheelEventsEnabledTest |
| : public RenderWidgetHostTest { |
| public: |
| RenderWidgetHostAsyncWheelEventsEnabledTest() |
| : RenderWidgetHostTest(UseMojoInputMessages::kEnabled, |
| kAsyncWheelEvents) {} |
| }; |
| |
| class RenderWidgetHostWheelScrollLatchingMojoInputDisabledTest |
| : public RenderWidgetHostTest { |
| public: |
| RenderWidgetHostWheelScrollLatchingMojoInputDisabledTest() |
| : RenderWidgetHostTest(UseMojoInputMessages::kDisabled, |
| kWheelScrollingModeNone) {} |
| }; |
| |
| class RenderWidgetHostAsyncWheelEventsEnabledMojoInputDisabledTest |
| : public RenderWidgetHostTest { |
| public: |
| RenderWidgetHostAsyncWheelEventsEnabledMojoInputDisabledTest() |
| : RenderWidgetHostTest(UseMojoInputMessages::kDisabled, |
| kAsyncWheelEvents) {} |
| }; |
| |
| #if GTEST_HAS_PARAM_TEST |
| // RenderWidgetHostWithSourceTest ---------------------------------------------- |
| |
| // This is for tests that are to be run for all source devices. |
| class RenderWidgetHostWithSourceTest |
| : public RenderWidgetHostTest, |
| public testing::WithParamInterface<WebGestureDevice> {}; |
| #endif // GTEST_HAS_PARAM_TEST |
| |
| void CallCallback(mojom::WidgetInputHandler::DispatchEventCallback callback, |
| InputEventAckState state) { |
| std::move(callback).Run(InputEventAckSource::COMPOSITOR_THREAD, |
| ui::LatencyInfo(), state, base::nullopt, |
| base::nullopt); |
| } |
| |
| } // namespace |
| |
| // ----------------------------------------------------------------------------- |
| |
| TEST_F(RenderWidgetHostTest, Resize) { |
| // The initial bounds is the empty rect, so setting it to the same thing |
| // shouldn't send the resize message. |
| view_->set_bounds(gfx::Rect()); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| |
| // No resize ack if the physical backing gets set, but the view bounds are |
| // zero. |
| view_->SetMockPhysicalBackingSize(gfx::Size(200, 200)); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| |
| // 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); |
| process_->sink().ClearMessages(); |
| view_->set_bounds(original_size); |
| view_->SetMockPhysicalBackingSize(gfx::Size()); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_EQ(original_size.size(), host_->old_resize_params_->new_size); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| |
| // Setting the bounds and physical backing size to nonzero should send out |
| // the notification and expect an ack. |
| process_->sink().ClearMessages(); |
| view_->ClearMockPhysicalBackingSize(); |
| host_->WasResized(); |
| EXPECT_TRUE(host_->resize_ack_pending_); |
| EXPECT_EQ(original_size.size(), host_->old_resize_params_->new_size); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| ViewHostMsg_UpdateRect_Params params; |
| params.flags = ViewHostMsg_UpdateRect_Flags::IS_RESIZE_ACK; |
| params.view_size = original_size.size(); |
| host_->OnUpdateRect(params); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| |
| // Send out a update that's not a resize ack after setting resize ack pending |
| // flag. This should not clean the resize ack pending flag. |
| process_->sink().ClearMessages(); |
| gfx::Rect second_size(0, 0, 110, 110); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| view_->set_bounds(second_size); |
| host_->WasResized(); |
| EXPECT_TRUE(host_->resize_ack_pending_); |
| params.flags = 0; |
| params.view_size = gfx::Size(100, 100); |
| host_->OnUpdateRect(params); |
| EXPECT_TRUE(host_->resize_ack_pending_); |
| EXPECT_EQ(second_size.size(), host_->old_resize_params_->new_size); |
| |
| // Sending out a new notification should NOT send out a new IPC message since |
| // a resize ACK is pending. |
| gfx::Rect third_size(0, 0, 120, 120); |
| process_->sink().ClearMessages(); |
| view_->set_bounds(third_size); |
| host_->WasResized(); |
| EXPECT_TRUE(host_->resize_ack_pending_); |
| EXPECT_EQ(second_size.size(), host_->old_resize_params_->new_size); |
| EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| |
| // Send a update that's a resize 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. |
| process_->sink().ClearMessages(); |
| params.flags = ViewHostMsg_UpdateRect_Flags::IS_RESIZE_ACK; |
| params.view_size = original_size.size(); |
| host_->OnUpdateRect(params); |
| EXPECT_TRUE(host_->resize_ack_pending_); |
| EXPECT_EQ(third_size.size(), host_->old_resize_params_->new_size); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| |
| // Send the resize ack for the latest size. |
| process_->sink().ClearMessages(); |
| params.flags = ViewHostMsg_UpdateRect_Flags::IS_RESIZE_ACK; |
| params.view_size = third_size.size(); |
| host_->OnUpdateRect(params); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_EQ(third_size.size(), host_->old_resize_params_->new_size); |
| EXPECT_FALSE(process_->sink().GetFirstMessageMatching(ViewMsg_Resize::ID)); |
| |
| // Now clearing the bounds should send out a notification but we shouldn't |
| // expect a resize 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 |
| process_->sink().ClearMessages(); |
| view_->set_bounds(gfx::Rect()); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_EQ(gfx::Size(), host_->old_resize_params_->new_size); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| |
| // Send a rect that has no area but has either width or height set. |
| process_->sink().ClearMessages(); |
| view_->set_bounds(gfx::Rect(0, 0, 0, 30)); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_EQ(gfx::Size(0, 30), host_->old_resize_params_->new_size); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| |
| // Set the same size again. It should not be sent again. |
| process_->sink().ClearMessages(); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_EQ(gfx::Size(0, 30), host_->old_resize_params_->new_size); |
| EXPECT_FALSE(process_->sink().GetFirstMessageMatching(ViewMsg_Resize::ID)); |
| |
| // A different size should be sent again, however. |
| view_->set_bounds(gfx::Rect(0, 0, 0, 31)); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_EQ(gfx::Size(0, 31), host_->old_resize_params_->new_size); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| } |
| |
| // Test that a resize event is sent if WasResized() is called after a |
| // ScreenInfo change. |
| TEST_F(RenderWidgetHostTest, ResizeScreenInfo) { |
| ScreenInfo screen_info; |
| screen_info.device_scale_factor = 1.f; |
| screen_info.rect = blink::WebRect(0, 0, 800, 600); |
| screen_info.available_rect = blink::WebRect(0, 0, 800, 600); |
| screen_info.orientation_angle = 0; |
| screen_info.orientation_type = SCREEN_ORIENTATION_VALUES_PORTRAIT_PRIMARY; |
| |
| auto* host_delegate = |
| static_cast<MockRenderWidgetHostDelegate*>(host_->delegate()); |
| |
| host_delegate->SetScreenInfo(screen_info); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| process_->sink().ClearMessages(); |
| |
| screen_info.orientation_angle = 180; |
| screen_info.orientation_type = SCREEN_ORIENTATION_VALUES_LANDSCAPE_PRIMARY; |
| |
| host_delegate->SetScreenInfo(screen_info); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| process_->sink().ClearMessages(); |
| |
| screen_info.device_scale_factor = 2.f; |
| |
| host_delegate->SetScreenInfo(screen_info); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| process_->sink().ClearMessages(); |
| |
| // No screen change. |
| host_delegate->SetScreenInfo(screen_info); |
| host_->WasResized(); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| } |
| |
| // Test for crbug.com/25097. If a renderer crashes between a resize and the |
| // corresponding update message, we must be sure to clear the resize ack logic. |
| TEST_F(RenderWidgetHostTest, ResizeThenCrash) { |
| // Clear the first Resize message that carried screen info. |
| process_->sink().ClearMessages(); |
| |
| // Setting the bounds to a "real" rect should send out the notification. |
| gfx::Rect original_size(0, 0, 100, 100); |
| view_->set_bounds(original_size); |
| host_->WasResized(); |
| EXPECT_TRUE(host_->resize_ack_pending_); |
| EXPECT_EQ(original_size.size(), host_->old_resize_params_->new_size); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| |
| // Simulate a renderer crash before the update message. Ensure all the |
| // resize ack logic is cleared. Must clear the view first so it doesn't get |
| // deleted. |
| host_->SetView(NULL); |
| host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1); |
| EXPECT_FALSE(host_->resize_ack_pending_); |
| EXPECT_EQ(gfx::Size(), host_->old_resize_params_->new_size); |
| |
| // Reset the view so we can exit the test cleanly. |
| host_->SetView(view_.get()); |
| } |
| |
| // Unable to include render_widget_host_view_mac.h and compile. |
| #if !defined(OS_MACOSX) |
| // Tests setting background transparency. |
| TEST_F(RenderWidgetHostTest, Background) { |
| std::unique_ptr<RenderWidgetHostViewBase> view; |
| #if defined(USE_AURA) |
| view.reset(new RenderWidgetHostViewAura(host_.get(), false)); |
| // TODO(derat): Call this on all platforms: http://crbug.com/102450. |
| view->InitAsChild(NULL); |
| #elif defined(OS_ANDROID) |
| view.reset(new RenderWidgetHostViewAndroid(host_.get(), NULL)); |
| #endif |
| host_->SetView(view.get()); |
| |
| EXPECT_NE(static_cast<unsigned>(SK_ColorTRANSPARENT), |
| view->background_color()); |
| view->SetBackgroundColor(SK_ColorTRANSPARENT); |
| EXPECT_EQ(static_cast<unsigned>(SK_ColorTRANSPARENT), |
| view->background_color()); |
| |
| const IPC::Message* set_background = |
| process_->sink().GetUniqueMessageMatching( |
| ViewMsg_SetBackgroundOpaque::ID); |
| ASSERT_TRUE(set_background); |
| std::tuple<bool> sent_background; |
| ViewMsg_SetBackgroundOpaque::Read(set_background, &sent_background); |
| EXPECT_FALSE(std::get<0>(sent_background)); |
| |
| host_->SetView(NULL); |
| static_cast<RenderWidgetHostViewBase*>(view.release())->Destroy(); |
| } |
| #endif |
| |
| // Test that we don't paint when we're hidden, but we still send the ACK. Most |
| // of the rest of the painting is tested in the GetBackingStore* ones. |
| TEST_F(RenderWidgetHostTest, HiddenPaint) { |
| // Hide the widget, it should have sent out a message to the renderer. |
| EXPECT_FALSE(host_->is_hidden_); |
| host_->WasHidden(); |
| EXPECT_TRUE(host_->is_hidden_); |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_WasHidden::ID)); |
| |
| // Send it an update as from the renderer. |
| process_->sink().ClearMessages(); |
| ViewHostMsg_UpdateRect_Params params; |
| params.view_size = gfx::Size(100, 100); |
| host_->OnUpdateRect(params); |
| |
| // Now unhide. |
| process_->sink().ClearMessages(); |
| host_->WasShown(ui::LatencyInfo()); |
| EXPECT_FALSE(host_->is_hidden_); |
| |
| // It should have sent out a restored message with a request to paint. |
| const IPC::Message* restored = process_->sink().GetUniqueMessageMatching( |
| ViewMsg_WasShown::ID); |
| ASSERT_TRUE(restored); |
| std::tuple<bool, ui::LatencyInfo> needs_repaint; |
| ViewMsg_WasShown::Read(restored, &needs_repaint); |
| EXPECT_TRUE(std::get<0>(needs_repaint)); |
| } |
| |
| TEST_F(RenderWidgetHostMojoInputDisabledTest, |
| IgnoreKeyEventsHandledByRenderer) { |
| // Simulate a keyboard event. |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| |
| // Make sure we sent the input event to the renderer. |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( |
| InputMsg_HandleInputEvent::ID)); |
| process_->sink().ClearMessages(); |
| |
| // Send the simulated response from the renderer back. |
| SendInputEventACK(WebInputEvent::kRawKeyDown, INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_FALSE(delegate_->unhandled_keyboard_event_called()); |
| } |
| |
| TEST_F(RenderWidgetHostTest, IgnoreKeyEventsHandledByRenderer) { |
| // Simulate a keyboard event. |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| |
| // Make sure we sent the input event to the renderer. |
| std::vector<MockWidgetInputHandler::DispatchedEvent> dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| |
| // Send the simulated response from the renderer back. |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_FALSE(delegate_->unhandled_keyboard_event_called()); |
| } |
| |
| TEST_F(RenderWidgetHostMojoInputDisabledTest, SendEditCommandsBeforeKeyEvent) { |
| // Clear any messages unrelated to this test. |
| process_->sink().ClearMessages(); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Simulate a keyboard event. |
| SimulateKeyboardEventWithCommands(WebInputEvent::kRawKeyDown); |
| |
| // Make sure we sent commands and key event to the renderer. |
| EXPECT_EQ(2U, process_->sink().message_count()); |
| EXPECT_EQ(InputMsg_SetEditCommandsForNextKeyEvent::ID, |
| process_->sink().GetMessageAt(0)->type()); |
| EXPECT_EQ(InputMsg_HandleInputEvent::ID, |
| process_->sink().GetMessageAt(1)->type()); |
| process_->sink().ClearMessages(); |
| |
| // Send the simulated response from the renderer back. |
| SendInputEventACK(WebInputEvent::kRawKeyDown, INPUT_EVENT_ACK_STATE_CONSUMED); |
| } |
| |
| TEST_F(RenderWidgetHostTest, SendEditCommandsBeforeKeyEvent) { |
| // Simulate a keyboard event. |
| SimulateKeyboardEventWithCommands(WebInputEvent::kRawKeyDown); |
| |
| // Make sure we sent commands and key event to the renderer. |
| std::vector<content::EditCommand> edit_commands = |
| host_->mock_widget_input_handler_.GetAndResetEditCommands(); |
| EXPECT_EQ(1u, edit_commands.size()); |
| |
| std::vector<MockWidgetInputHandler::DispatchedEvent> dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| |
| // Send the simulated response from the renderer back. |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| } |
| |
| TEST_F(RenderWidgetHostMojoInputDisabledTest, PreHandleRawKeyDownEvent) { |
| // Simulate the situation that the browser handled the key down event during |
| // pre-handle phrase. |
| delegate_->set_prehandle_keyboard_event(true); |
| process_->sink().ClearMessages(); |
| |
| // Simulate a keyboard event. |
| SimulateKeyboardEventWithCommands(WebInputEvent::kRawKeyDown); |
| |
| EXPECT_TRUE(delegate_->prehandle_keyboard_event_called()); |
| EXPECT_EQ(WebInputEvent::kRawKeyDown, |
| delegate_->prehandle_keyboard_event_type()); |
| |
| // Make sure the commands and key event are not sent to the renderer. |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // The browser won't pre-handle a Char event. |
| delegate_->set_prehandle_keyboard_event(false); |
| |
| // Forward the Char event. |
| SimulateKeyboardEvent(WebInputEvent::kChar); |
| |
| // Make sure the Char event is suppressed. |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Forward the KeyUp event. |
| SimulateKeyboardEvent(WebInputEvent::kKeyUp); |
| |
| // Make sure the KeyUp event is suppressed. |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Simulate a new RawKeyDown event. |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| EXPECT_EQ(1U, process_->sink().message_count()); |
| EXPECT_EQ(InputMsg_HandleInputEvent::ID, |
| process_->sink().GetMessageAt(0)->type()); |
| process_->sink().ClearMessages(); |
| |
| // Send the simulated response from the renderer back. |
| SendInputEventACK(WebInputEvent::kRawKeyDown, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| EXPECT_TRUE(delegate_->unhandled_keyboard_event_called()); |
| EXPECT_EQ(WebInputEvent::kRawKeyDown, |
| delegate_->unhandled_keyboard_event_type()); |
| } |
| |
| 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::kRawKeyDown); |
| |
| EXPECT_TRUE(delegate_->prehandle_keyboard_event_called()); |
| EXPECT_EQ(WebInputEvent::kRawKeyDown, |
| delegate_->prehandle_keyboard_event_type()); |
| |
| // Make sure the commands and key event are not sent to the renderer. |
| std::vector<MockWidgetInputHandler::DispatchedEvent> dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| 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::kChar); |
| |
| // Make sure the Char event is suppressed. |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(0u, dispatched_events.size()); |
| |
| // Forward the KeyUp event. |
| SimulateKeyboardEvent(WebInputEvent::kKeyUp); |
| |
| // Make sure the KeyUp event is suppressed. |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(0u, dispatched_events.size()); |
| |
| // Simulate a new RawKeyDown event. |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kRawKeyDown, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Send the simulated response from the renderer back. |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| EXPECT_TRUE(delegate_->unhandled_keyboard_event_called()); |
| EXPECT_EQ(WebInputEvent::kRawKeyDown, |
| delegate_->unhandled_keyboard_event_type()); |
| } |
| |
| TEST_F(RenderWidgetHostMojoInputDisabledTest, 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); |
| process_->sink().ClearMessages(); |
| |
| // Simulate a keyboard event. |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| |
| EXPECT_TRUE(delegate_->prehandle_keyboard_event_called()); |
| EXPECT_EQ(WebInputEvent::kRawKeyDown, |
| delegate_->prehandle_keyboard_event_type()); |
| |
| // Make sure the RawKeyDown event is sent to the renderer. |
| EXPECT_EQ(1U, process_->sink().message_count()); |
| EXPECT_EQ("RawKeyDown", GetInputMessageTypes(process_)); |
| process_->sink().ClearMessages(); |
| |
| // Send the simulated response from the renderer back. |
| SendInputEventACK(WebInputEvent::kRawKeyDown, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(WebInputEvent::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::kChar); |
| |
| // The Char event is not suppressed; the renderer will ignore it |
| // if the preceding RawKeyDown shortcut goes unhandled. |
| EXPECT_EQ(1U, process_->sink().message_count()); |
| EXPECT_EQ("Char", GetInputMessageTypes(process_)); |
| process_->sink().ClearMessages(); |
| |
| // Send the simulated response from the renderer back. |
| SendInputEventACK(WebInputEvent::kChar, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(WebInputEvent::kChar, delegate_->unhandled_keyboard_event_type()); |
| |
| // Forward the KeyUp event. |
| SimulateKeyboardEvent(WebInputEvent::kKeyUp); |
| |
| // Make sure only KeyUp was sent to the renderer. |
| EXPECT_EQ(1U, process_->sink().message_count()); |
| EXPECT_EQ("KeyUp", GetInputMessageTypes(process_)); |
| process_->sink().ClearMessages(); |
| |
| // Send the simulated response from the renderer back. |
| SendInputEventACK(WebInputEvent::kKeyUp, INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(WebInputEvent::kKeyUp, 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::kRawKeyDown); |
| |
| EXPECT_TRUE(delegate_->prehandle_keyboard_event_called()); |
| EXPECT_EQ(WebInputEvent::kRawKeyDown, |
| delegate_->prehandle_keyboard_event_type()); |
| |
| // Make sure the RawKeyDown event is sent to the renderer. |
| std::vector<MockWidgetInputHandler::DispatchedEvent> dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kRawKeyDown, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Send the simulated response from the renderer back. |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(WebInputEvent::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::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_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kChar, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Send the simulated response from the renderer back. |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(WebInputEvent::kChar, delegate_->unhandled_keyboard_event_type()); |
| |
| // Forward the KeyUp event. |
| SimulateKeyboardEvent(WebInputEvent::kKeyUp); |
| |
| // Make sure only KeyUp was sent to the renderer. |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kKeyUp, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Send the simulated response from the renderer back. |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(WebInputEvent::kKeyUp, delegate_->unhandled_keyboard_event_type()); |
| } |
| |
| void RenderWidgetHostTest::UnhandledWheelEventMojoInputDisabled() { |
| SimulateWheelEventPossiblyIncludingPhase(-5, 0, 0, true, |
| WebMouseWheelEvent::kPhaseBegan); |
| |
| // Make sure we sent the input event to the renderer. |
| EXPECT_TRUE( |
| process_->sink().GetUniqueMessageMatching(InputMsg_HandleInputEvent::ID)); |
| process_->sink().ClearMessages(); |
| |
| // Send the simulated response from the renderer back. |
| SendInputEventACK(WebInputEvent::kMouseWheel, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| 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(RenderWidgetHostMojoInputDisabledTest, UnhandledWheelEvent) { |
| UnhandledWheelEventMojoInputDisabled(); |
| } |
| TEST_F(RenderWidgetHostWheelScrollLatchingMojoInputDisabledTest, |
| UnhandledWheelEvent) { |
| UnhandledWheelEventMojoInputDisabled(); |
| } |
| TEST_F(RenderWidgetHostAsyncWheelEventsEnabledMojoInputDisabledTest, |
| UnhandledWheelEvent) { |
| UnhandledWheelEventMojoInputDisabled(); |
| } |
| |
| void RenderWidgetHostTest::UnhandledWheelEvent() { |
| SimulateWheelEventPossiblyIncludingPhase(-5, 0, 0, true, |
| WebMouseWheelEvent::kPhaseBegan); |
| |
| std::vector<MockWidgetInputHandler::DispatchedEvent> dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kMouseWheel, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Send the simulated response from the renderer back. |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| 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, UnhandledWheelEvent) { |
| UnhandledWheelEvent(); |
| } |
| TEST_F(RenderWidgetHostWheelScrollLatchingDisabledTest, UnhandledWheelEvent) { |
| UnhandledWheelEvent(); |
| } |
| TEST_F(RenderWidgetHostAsyncWheelEventsEnabledTest, UnhandledWheelEvent) { |
| UnhandledWheelEvent(); |
| } |
| |
| void RenderWidgetHostTest::HandleWheelEventMojoInputDisabled() { |
| // Indicate that we're going to handle this wheel event |
| delegate_->set_handle_wheel_event(true); |
| |
| SimulateWheelEventPossiblyIncludingPhase(-5, 0, 0, true, |
| WebMouseWheelEvent::kPhaseBegan); |
| |
| // Make sure we sent the input event to the renderer. |
| EXPECT_TRUE( |
| process_->sink().GetUniqueMessageMatching(InputMsg_HandleInputEvent::ID)); |
| process_->sink().ClearMessages(); |
| |
| // Send the simulated response from the renderer back. |
| SendInputEventACK(WebInputEvent::kMouseWheel, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| // 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(RenderWidgetHostMojoInputDisabledTest, HandleWheelEvent) { |
| HandleWheelEventMojoInputDisabled(); |
| } |
| TEST_F(RenderWidgetHostWheelScrollLatchingMojoInputDisabledTest, |
| HandleWheelEvent) { |
| HandleWheelEventMojoInputDisabled(); |
| } |
| TEST_F(RenderWidgetHostAsyncWheelEventsEnabledMojoInputDisabledTest, |
| HandleWheelEvent) { |
| HandleWheelEventMojoInputDisabled(); |
| } |
| |
| void RenderWidgetHostTest::HandleWheelEvent() { |
| // Indicate that we're going to handle this wheel event |
| delegate_->set_handle_wheel_event(true); |
| |
| SimulateWheelEventPossiblyIncludingPhase(-5, 0, 0, true, |
| WebMouseWheelEvent::kPhaseBegan); |
| |
| std::vector<MockWidgetInputHandler::DispatchedEvent> dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kMouseWheel, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Send the simulated response from the renderer back. |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| // 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, HandleWheelEvent) { |
| HandleWheelEvent(); |
| } |
| TEST_F(RenderWidgetHostWheelScrollLatchingDisabledTest, HandleWheelEvent) { |
| HandleWheelEvent(); |
| } |
| TEST_F(RenderWidgetHostAsyncWheelEventsEnabledTest, HandleWheelEvent) { |
| HandleWheelEvent(); |
| } |
| |
| TEST_F(RenderWidgetHostMojoInputDisabledTest, UnhandledGestureEvent) { |
| SimulateGestureEvent(WebInputEvent::kGestureTwoFingerTap, |
| blink::kWebGestureDeviceTouchscreen); |
| |
| // Make sure we sent the input event to the renderer. |
| EXPECT_TRUE(process_->sink().GetUniqueMessageMatching( |
| InputMsg_HandleInputEvent::ID)); |
| process_->sink().ClearMessages(); |
| |
| // Send the simulated response from the renderer back. |
| SendInputEventACK(WebInputEvent::kGestureTwoFingerTap, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(WebInputEvent::kGestureTwoFingerTap, view_->gesture_event_type()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, view_->ack_result()); |
| } |
| |
| TEST_F(RenderWidgetHostTest, UnhandledGestureEvent) { |
| SimulateGestureEvent(WebInputEvent::kGestureTwoFingerTap, |
| blink::kWebGestureDeviceTouchscreen); |
| |
| std::vector<MockWidgetInputHandler::DispatchedEvent> dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureTwoFingerTap, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Send the simulated response from the renderer back. |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| EXPECT_EQ(WebInputEvent::kGestureTwoFingerTap, view_->gesture_event_type()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, 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, DontPostponeHangMonitorTimeout) { |
| // Start with a short timeout. |
| host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10), |
| WebInputEvent::kUndefined); |
| |
| // Immediately try to add a long 30 second timeout. |
| EXPECT_FALSE(delegate_->unresponsive_timer_fired()); |
| host_->StartHangMonitorTimeout(TimeDelta::FromSeconds(30), |
| WebInputEvent::kUndefined); |
| |
| // Wait long enough for first timeout and see if it fired. |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMilliseconds(10)); |
| base::RunLoop().Run(); |
| 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, StopAndStartHangMonitorTimeout) { |
| // Start with a short timeout, then stop it. |
| host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10), |
| WebInputEvent::kUndefined); |
| host_->StopHangMonitorTimeout(); |
| |
| // Start it again to ensure it still works. |
| EXPECT_FALSE(delegate_->unresponsive_timer_fired()); |
| host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10), |
| WebInputEvent::kUndefined); |
| |
| // Wait long enough for first timeout and see if it fired. |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMilliseconds(40)); |
| base::RunLoop().Run(); |
| EXPECT_TRUE(delegate_->unresponsive_timer_fired()); |
| } |
| |
| // Test that the hang monitor timer expires properly if it is started, then |
| // updated to a shorter duration. |
| TEST_F(RenderWidgetHostTest, ShorterDelayHangMonitorTimeout) { |
| // Start with a timeout. |
| host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(100), |
| WebInputEvent::kUndefined); |
| |
| // Start it again with shorter delay. |
| EXPECT_FALSE(delegate_->unresponsive_timer_fired()); |
| host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(20), |
| WebInputEvent::kUndefined); |
| |
| // Wait long enough for the second timeout and see if it fired. |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMilliseconds(25)); |
| base::RunLoop().Run(); |
| EXPECT_TRUE(delegate_->unresponsive_timer_fired()); |
| } |
| |
| // Test that the hang monitor timer is effectively disabled when the widget is |
| // hidden. |
| TEST_F(RenderWidgetHostTest, HangMonitorTimeoutDisabledForInputWhenHidden) { |
| host_->set_hung_renderer_delay(base::TimeDelta::FromMicroseconds(1)); |
| SimulateMouseEvent(WebInputEvent::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()); |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMicroseconds(2)); |
| base::RunLoop().Run(); |
| EXPECT_FALSE(delegate_->unresponsive_timer_fired()); |
| |
| // The timeout should never reactivate while hidden. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 10, 0, false); |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMicroseconds(2)); |
| base::RunLoop().Run(); |
| EXPECT_FALSE(delegate_->unresponsive_timer_fired()); |
| |
| // Showing the widget should restore the timeout, as the events have |
| // not yet been ack'ed. |
| host_->WasShown(ui::LatencyInfo()); |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMicroseconds(2)); |
| base::RunLoop().Run(); |
| 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) { |
| // Configure the host to wait 10ms before considering |
| // the renderer hung. |
| host_->set_hung_renderer_delay(base::TimeDelta::FromMicroseconds(10)); |
| |
| // Send two events but only one ack. |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| SendInputEventACK(WebInputEvent::kRawKeyDown, INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Wait long enough for first timeout and see if it fired. |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMicroseconds(20)); |
| base::RunLoop().Run(); |
| EXPECT_TRUE(delegate_->unresponsive_timer_fired()); |
| } |
| |
| // Test that the rendering timeout for newly loaded content fires |
| // when enough time passes without receiving a new compositor frame. |
| TEST_F(RenderWidgetHostTest, NewContentRenderingTimeout) { |
| const gfx::Size frame_size(50, 50); |
| const viz::LocalSurfaceId local_surface_id(1, |
| base::UnguessableToken::Create()); |
| |
| host_->set_new_content_rendering_delay_for_testing( |
| base::TimeDelta::FromMicroseconds(10)); |
| |
| // Start the timer and immediately send a CompositorFrame with the |
| // content_source_id of the new page. The timeout shouldn't fire. |
| host_->StartNewContentRenderingTimeout(5); |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.content_source_id = 5; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMicroseconds(20)); |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(host_->new_content_rendering_timeout_fired()); |
| host_->reset_new_content_rendering_timeout_fired(); |
| |
| // Start the timer but receive frames only from the old page. The timer |
| // should fire. |
| host_->StartNewContentRenderingTimeout(10); |
| frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.content_source_id = 9; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMicroseconds(20)); |
| base::RunLoop().Run(); |
| |
| EXPECT_TRUE(host_->new_content_rendering_timeout_fired()); |
| host_->reset_new_content_rendering_timeout_fired(); |
| |
| // Send a CompositorFrame with content_source_id of the new page before we |
| // attempt to start the timer. The timer shouldn't fire. |
| frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.content_source_id = 7; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| host_->StartNewContentRenderingTimeout(7); |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMicroseconds(20)); |
| base::RunLoop().Run(); |
| |
| EXPECT_FALSE(host_->new_content_rendering_timeout_fired()); |
| host_->reset_new_content_rendering_timeout_fired(); |
| |
| // Don't send any frames after the timer starts. The timer should fire. |
| host_->StartNewContentRenderingTimeout(20); |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(), |
| TimeDelta::FromMicroseconds(20)); |
| base::RunLoop().Run(); |
| EXPECT_TRUE(host_->new_content_rendering_timeout_fired()); |
| host_->reset_new_content_rendering_timeout_fired(); |
| } |
| |
| // This tests that a compositor frame received with a stale content source ID |
| // in its metadata is properly discarded. |
| TEST_F(RenderWidgetHostTest, SwapCompositorFrameWithBadSourceId) { |
| const gfx::Size frame_size(50, 50); |
| const viz::LocalSurfaceId local_surface_id(1, |
| base::UnguessableToken::Create()); |
| |
| host_->StartNewContentRenderingTimeout(100); |
| host_->set_new_content_rendering_delay_for_testing( |
| base::TimeDelta::FromMicroseconds(9999)); |
| |
| { |
| // First swap a frame with an invalid ID. |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.begin_frame_ack = viz::BeginFrameAck(0, 1, true); |
| frame.metadata.content_source_id = 99; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, |
| 0); |
| EXPECT_FALSE( |
| static_cast<TestView*>(host_->GetView())->did_swap_compositor_frame()); |
| static_cast<TestView*>(host_->GetView())->reset_did_swap_compositor_frame(); |
| } |
| |
| { |
| // Test with a valid content ID as a control. |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.content_source_id = 100; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, |
| 0); |
| EXPECT_TRUE( |
| static_cast<TestView*>(host_->GetView())->did_swap_compositor_frame()); |
| static_cast<TestView*>(host_->GetView())->reset_did_swap_compositor_frame(); |
| } |
| |
| { |
| // We also accept frames with higher content IDs, to cover the case where |
| // the browser process receives a compositor frame for a new page before |
| // the corresponding DidCommitProvisionalLoad (it's a race). |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.content_source_id = 101; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, |
| 0); |
| EXPECT_TRUE( |
| static_cast<TestView*>(host_->GetView())->did_swap_compositor_frame()); |
| } |
| } |
| |
| TEST_F(RenderWidgetHostMojoInputDisabledTest, TouchEmulator) { |
| simulated_event_time_delta_seconds_ = 0.1; |
| // Immediately ack all touches instead of sending them to the renderer. |
| host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false)); |
| host_->GetTouchEmulator()->Enable( |
| TouchEmulator::Mode::kEmulatingTouchFromMouse, |
| ui::GestureProviderConfigType::GENERIC_MOBILE); |
| process_->sink().ClearMessages(); |
| view_->set_bounds(gfx::Rect(0, 0, 400, 200)); |
| view_->Show(); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 10, 0, false); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Mouse press becomes touch start which in turn becomes tap. |
| SimulateMouseEvent(WebInputEvent::kMouseDown, 10, 10, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchStart, host_->acked_touch_event_type()); |
| EXPECT_EQ("GestureTapDown", GetInputMessageTypes(process_)); |
| |
| // Mouse drag generates touch move, cancels tap and starts scroll. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 30, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| SendScrollBeginAckIfneeded(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ( |
| "GestureTapCancel GestureScrollBegin TouchScrollStarted " |
| "GestureScrollUpdate", |
| GetInputMessageTypes(process_)); |
| SendInputEventACK(WebInputEvent::kGestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Mouse drag with shift becomes pinch. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 40, |
| WebInputEvent::kShiftKey, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| EXPECT_EQ("GesturePinchBegin", |
| GetInputMessageTypes(process_)); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 50, |
| WebInputEvent::kShiftKey, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| EXPECT_EQ("GesturePinchUpdate", |
| GetInputMessageTypes(process_)); |
| SendInputEventACK(WebInputEvent::kGesturePinchUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Mouse drag without shift becomes scroll again. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 60, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| EXPECT_EQ("GesturePinchEnd GestureScrollUpdate", |
| GetInputMessageTypes(process_)); |
| SendInputEventACK(WebInputEvent::kGestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 70, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| EXPECT_EQ("GestureScrollUpdate", |
| GetInputMessageTypes(process_)); |
| SendInputEventACK(WebInputEvent::kGestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseUp, 10, 70, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchEnd, host_->acked_touch_event_type()); |
| EXPECT_EQ("GestureScrollEnd", GetInputMessageTypes(process_)); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Mouse move does nothing. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 80, 0, false); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Another mouse down continues scroll. |
| SimulateMouseEvent(WebInputEvent::kMouseDown, 10, 80, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchStart, host_->acked_touch_event_type()); |
| EXPECT_EQ("GestureTapDown", GetInputMessageTypes(process_)); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 100, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| SendScrollBeginAckIfneeded(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ( |
| "GestureTapCancel GestureScrollBegin TouchScrollStarted " |
| "GestureScrollUpdate", |
| GetInputMessageTypes(process_)); |
| SendInputEventACK(WebInputEvent::kGestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Another pinch. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 110, |
| WebInputEvent::kShiftKey, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| EXPECT_EQ("GesturePinchBegin", |
| GetInputMessageTypes(process_)); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 120, |
| WebInputEvent::kShiftKey, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| EXPECT_EQ("GesturePinchUpdate", |
| GetInputMessageTypes(process_)); |
| SendInputEventACK(WebInputEvent::kGesturePinchUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Turn off emulation during a pinch. |
| host_->GetTouchEmulator()->Disable(); |
| EXPECT_EQ(WebInputEvent::kTouchCancel, host_->acked_touch_event_type()); |
| EXPECT_EQ("GesturePinchEnd GestureScrollEnd", |
| GetInputMessageTypes(process_)); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Mouse event should pass untouched. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 10, |
| WebInputEvent::kShiftKey, true); |
| EXPECT_EQ("MouseMove", GetInputMessageTypes(process_)); |
| SendInputEventACK(WebInputEvent::kMouseMove, INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Turn on emulation. |
| host_->GetTouchEmulator()->Enable( |
| TouchEmulator::Mode::kEmulatingTouchFromMouse, |
| ui::GestureProviderConfigType::GENERIC_MOBILE); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Another touch. |
| SimulateMouseEvent(WebInputEvent::kMouseDown, 10, 10, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchStart, host_->acked_touch_event_type()); |
| EXPECT_EQ("GestureTapDown", GetInputMessageTypes(process_)); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| |
| // Scroll. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 30, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| SendScrollBeginAckIfneeded(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ( |
| "GestureTapCancel GestureScrollBegin TouchScrollStarted " |
| "GestureScrollUpdate", |
| GetInputMessageTypes(process_)); |
| SendInputEventACK(WebInputEvent::kGestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Turn off emulation during a scroll. |
| host_->GetTouchEmulator()->Disable(); |
| EXPECT_EQ(WebInputEvent::kTouchCancel, host_->acked_touch_event_type()); |
| |
| EXPECT_EQ("GestureScrollEnd", GetInputMessageTypes(process_)); |
| EXPECT_EQ(0U, process_->sink().message_count()); |
| } |
| |
| TEST_F(RenderWidgetHostTest, TouchEmulator) { |
| simulated_event_time_delta_seconds_ = 0.1; |
| // Immediately ack all touches instead of sending them to the renderer. |
| host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false)); |
| host_->GetTouchEmulator()->Enable( |
| TouchEmulator::Mode::kEmulatingTouchFromMouse, |
| ui::GestureProviderConfigType::GENERIC_MOBILE); |
| process_->sink().ClearMessages(); |
| view_->set_bounds(gfx::Rect(0, 0, 400, 200)); |
| view_->Show(); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 10, 0, false); |
| std::vector<MockWidgetInputHandler::DispatchedEvent> dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(0u, dispatched_events.size()); |
| |
| // Mouse press becomes touch start which in turn becomes tap. |
| SimulateMouseEvent(WebInputEvent::kMouseDown, 10, 10, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchStart, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureTapDown, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Mouse drag generates touch move, cancels tap and starts scroll. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 30, 0, true); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(4u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureTapCancel, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollBegin, |
| dispatched_events.at(1).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kTouchScrollStarted, |
| dispatched_events.at(2).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| dispatched_events.at(3).event_->web_event->GetType()); |
| if (dispatched_events.at(1).callback_) { |
| CallCallback(std::move(dispatched_events.at(1).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| } |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| EXPECT_EQ( |
| 0u, |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents().size()); |
| CallCallback(std::move(dispatched_events.at(3).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| // Mouse drag with shift becomes pinch. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 40, |
| WebInputEvent::kShiftKey, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGesturePinchBegin, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 50, |
| WebInputEvent::kShiftKey, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGesturePinchUpdate, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Mouse drag without shift becomes scroll again. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 60, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(2u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGesturePinchEnd, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| dispatched_events.at(1).event_->web_event->GetType()); |
| CallCallback(std::move(dispatched_events.at(1).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 70, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| SimulateMouseEvent(WebInputEvent::kMouseUp, 10, 70, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchEnd, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollEnd, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Mouse move does nothing. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 80, 0, false); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(0u, dispatched_events.size()); |
| |
| // Another mouse down continues scroll. |
| SimulateMouseEvent(WebInputEvent::kMouseDown, 10, 80, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchStart, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureTapDown, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 100, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(4u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureTapCancel, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollBegin, |
| dispatched_events.at(1).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kTouchScrollStarted, |
| dispatched_events.at(2).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| dispatched_events.at(3).event_->web_event->GetType()); |
| if (dispatched_events.at(1).callback_) { |
| CallCallback(std::move(dispatched_events.at(1).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| } |
| EXPECT_EQ( |
| 0u, |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents().size()); |
| CallCallback(std::move(dispatched_events.at(3).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| // Another pinch. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 110, |
| WebInputEvent::kShiftKey, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGesturePinchBegin, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 120, |
| WebInputEvent::kShiftKey, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGesturePinchUpdate, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Turn off emulation during a pinch. |
| host_->GetTouchEmulator()->Disable(); |
| EXPECT_EQ(WebInputEvent::kTouchCancel, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(2u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGesturePinchEnd, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollEnd, |
| dispatched_events.at(1).event_->web_event->GetType()); |
| |
| // Mouse event should pass untouched. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 10, |
| WebInputEvent::kShiftKey, true); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kMouseMove, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Turn on emulation. |
| host_->GetTouchEmulator()->Enable( |
| TouchEmulator::Mode::kEmulatingTouchFromMouse, |
| ui::GestureProviderConfigType::GENERIC_MOBILE); |
| |
| // Another touch. |
| SimulateMouseEvent(WebInputEvent::kMouseDown, 10, 10, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchStart, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureTapDown, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| // Scroll. |
| SimulateMouseEvent(WebInputEvent::kMouseMove, 10, 30, 0, true); |
| EXPECT_EQ(WebInputEvent::kTouchMove, host_->acked_touch_event_type()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(4u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureTapCancel, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollBegin, |
| dispatched_events.at(1).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kTouchScrollStarted, |
| dispatched_events.at(2).event_->web_event->GetType()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| dispatched_events.at(3).event_->web_event->GetType()); |
| if (dispatched_events.at(1).callback_) { |
| CallCallback(std::move(dispatched_events.at(1).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| } |
| EXPECT_EQ( |
| 0u, |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents().size()); |
| CallCallback(std::move(dispatched_events.at(3).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Turn off emulation during a scroll. |
| host_->GetTouchEmulator()->Disable(); |
| EXPECT_EQ(WebInputEvent::kTouchCancel, host_->acked_touch_event_type()); |
| |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kGestureScrollEnd, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| } |
| |
| TEST_F(RenderWidgetHostTest, IgnoreInputEvent) { |
| host_->SetupForInputRouterTest(); |
| |
| host_->SetIgnoreInputEvents(true); |
| |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); |
| |
| SimulateMouseEvent(WebInputEvent::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::kGestureScrollBegin, |
| blink::kWebGestureDeviceTouchpad); |
| 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::Bind(&RenderWidgetHostTest::KeyPressEventCallback, |
| base::Unretained(this))); |
| handle_key_press_event_ = false; |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| |
| EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); |
| } |
| |
| TEST_F(RenderWidgetHostTest, KeyboardListenerSuppressFollowingEvents) { |
| host_->SetupForInputRouterTest(); |
| |
| host_->AddKeyPressEventCallback( |
| base::Bind(&RenderWidgetHostTest::KeyPressEventCallback, |
| base::Unretained(this))); |
| |
| // The callback handles the first event |
| handle_key_press_event_ = true; |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| |
| EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); |
| |
| // Following Char events should be suppressed |
| handle_key_press_event_ = false; |
| SimulateKeyboardEvent(WebInputEvent::kChar); |
| EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); |
| SimulateKeyboardEvent(WebInputEvent::kChar); |
| EXPECT_FALSE(host_->mock_input_router()->sent_keyboard_event_); |
| |
| // Sending RawKeyDown event should stop suppression |
| SimulateKeyboardEvent(WebInputEvent::kRawKeyDown); |
| EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); |
| |
| host_->mock_input_router()->sent_keyboard_event_ = false; |
| SimulateKeyboardEvent(WebInputEvent::kChar); |
| EXPECT_TRUE(host_->mock_input_router()->sent_keyboard_event_); |
| } |
| |
| TEST_F(RenderWidgetHostTest, MouseEventCallbackCanHandleEvent) { |
| host_->SetupForInputRouterTest(); |
| |
| host_->AddMouseEventCallback( |
| base::Bind(&RenderWidgetHostTest::MouseEventCallback, |
| base::Unretained(this))); |
| |
| handle_mouse_event_ = true; |
| SimulateMouseEvent(WebInputEvent::kMouseDown); |
| |
| EXPECT_FALSE(host_->mock_input_router()->sent_mouse_event_); |
| |
| handle_mouse_event_ = false; |
| SimulateMouseEvent(WebInputEvent::kMouseDown); |
| |
| EXPECT_TRUE(host_->mock_input_router()->sent_mouse_event_); |
| } |
| |
| TEST_F(RenderWidgetHostTest, InputRouterReceivesHandleInputEvent_ACK) { |
| host_->SetupForInputRouterTest(); |
| |
| SendInputEventACK(WebInputEvent::kRawKeyDown, INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| EXPECT_TRUE(host_->mock_input_router()->message_received_); |
| } |
| |
| TEST_F(RenderWidgetHostTest, InputRouterReceivesMoveCaret_ACK) { |
| host_->SetupForInputRouterTest(); |
| |
| host_->OnMessageReceived(InputHostMsg_MoveCaret_ACK(0)); |
| |
| EXPECT_TRUE(host_->mock_input_router()->message_received_); |
| } |
| |
| TEST_F(RenderWidgetHostTest, InputRouterReceivesSelectRange_ACK) { |
| host_->SetupForInputRouterTest(); |
| |
| host_->OnMessageReceived(InputHostMsg_SelectRange_ACK(0)); |
| |
| EXPECT_TRUE(host_->mock_input_router()->message_received_); |
| } |
| |
| TEST_F(RenderWidgetHostTest, InputRouterReceivesHasTouchEventHandlers) { |
| host_->SetupForInputRouterTest(); |
| |
| host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); |
| |
| EXPECT_TRUE(host_->mock_input_router()->message_received_); |
| } |
| |
| void CheckLatencyInfoComponentInMessage(RenderWidgetHostProcess* process, |
| int64_t component_id, |
| WebInputEvent::Type expected_type) { |
| EXPECT_EQ(process->sink().message_count(), 1U); |
| |
| const IPC::Message* message = process->sink().GetMessageAt(0); |
| EXPECT_EQ(InputMsg_HandleInputEvent::ID, message->type()); |
| InputMsg_HandleInputEvent::Param params; |
| EXPECT_TRUE(InputMsg_HandleInputEvent::Read(message, ¶ms)); |
| |
| const WebInputEvent* event = std::get<0>(params); |
| ui::LatencyInfo latency_info = std::get<2>(params); |
| |
| EXPECT_TRUE(event->GetType() == expected_type); |
| EXPECT_TRUE(latency_info.FindLatency( |
| ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, component_id, NULL)); |
| |
| process->sink().ClearMessages(); |
| } |
| |
| void CheckLatencyInfoComponentInGestureScrollUpdate( |
| RenderWidgetHostProcess* process, |
| int64_t component_id) { |
| EXPECT_EQ(process->sink().message_count(), 2U); |
| const IPC::Message* message = process->sink().GetMessageAt(0); |
| EXPECT_EQ(InputMsg_HandleInputEvent::ID, message->type()); |
| InputMsg_HandleInputEvent::Param params; |
| EXPECT_TRUE(InputMsg_HandleInputEvent::Read(message, ¶ms)); |
| |
| const WebInputEvent* event = std::get<0>(params); |
| ui::LatencyInfo latency_info = std::get<2>(params); |
| |
| EXPECT_TRUE(event->GetType() == WebInputEvent::kTouchScrollStarted); |
| |
| message = process->sink().GetMessageAt(1); |
| EXPECT_EQ(InputMsg_HandleInputEvent::ID, message->type()); |
| EXPECT_TRUE(InputMsg_HandleInputEvent::Read(message, ¶ms)); |
| |
| event = std::get<0>(params); |
| latency_info = std::get<2>(params); |
| |
| EXPECT_TRUE(event->GetType() == WebInputEvent::kGestureScrollUpdate); |
| EXPECT_TRUE(latency_info.FindLatency( |
| ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, component_id, NULL)); |
| |
| process->sink().ClearMessages(); |
| } |
| |
| // 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. |
| void RenderWidgetHostTest::InputEventRWHLatencyComponentMojoInputDisabled() { |
| host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); |
| process_->sink().ClearMessages(); |
| |
| // Tests RWHI::ForwardWheelEvent(). |
| SimulateWheelEventPossiblyIncludingPhase(-5, 0, 0, true, |
| WebMouseWheelEvent::kPhaseBegan); |
| CheckLatencyInfoComponentInMessage(process_, GetLatencyComponentId(), |
| WebInputEvent::kMouseWheel); |
| SendInputEventACK(WebInputEvent::kMouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Tests RWHI::ForwardWheelEventWithLatencyInfo(). |
| SimulateWheelEventWithLatencyInfoAndPossiblyPhase( |
| -5, 0, 0, true, ui::LatencyInfo(), WebMouseWheelEvent::kPhaseChanged); |
| CheckLatencyInfoComponentInMessage(process_, GetLatencyComponentId(), |
| WebInputEvent::kMouseWheel); |
| SendInputEventACK(WebInputEvent::kMouseWheel, INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Tests RWHI::ForwardMouseEvent(). |
| SimulateMouseEvent(WebInputEvent::kMouseMove); |
| CheckLatencyInfoComponentInMessage(process_, GetLatencyComponentId(), |
| WebInputEvent::kMouseMove); |
| SendInputEventACK(WebInputEvent::kMouseMove, INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Tests RWHI::ForwardMouseEventWithLatencyInfo(). |
| SimulateMouseEventWithLatencyInfo(WebInputEvent::kMouseMove, |
| ui::LatencyInfo()); |
| CheckLatencyInfoComponentInMessage(process_, GetLatencyComponentId(), |
| WebInputEvent::kMouseMove); |
| SendInputEventACK(WebInputEvent::kMouseMove, INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Tests RWHI::ForwardGestureEvent(). |
| SimulateGestureEvent(WebInputEvent::kGestureScrollBegin, |
| blink::kWebGestureDeviceTouchscreen); |
| SendScrollBeginAckIfneeded(INPUT_EVENT_ACK_STATE_CONSUMED); |
| CheckLatencyInfoComponentInMessage(process_, GetLatencyComponentId(), |
| WebInputEvent::kGestureScrollBegin); |
| |
| // Tests RWHI::ForwardGestureEventWithLatencyInfo(). |
| SimulateGestureEventWithLatencyInfo(WebInputEvent::kGestureScrollUpdate, |
| blink::kWebGestureDeviceTouchscreen, |
| ui::LatencyInfo()); |
| CheckLatencyInfoComponentInGestureScrollUpdate(process_, |
| GetLatencyComponentId()); |
| SendInputEventACK(WebInputEvent::kGestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // Tests RWHI::ForwardTouchEventWithLatencyInfo(). |
| PressTouchPoint(0, 1); |
| uint32_t touch_event_id = SendTouchEvent(); |
| InputEventAck ack(InputEventAckSource::COMPOSITOR_THREAD, |
| WebInputEvent::kTouchStart, INPUT_EVENT_ACK_STATE_CONSUMED, |
| touch_event_id); |
| host_->OnMessageReceived(InputHostMsg_HandleInputEvent_ACK(0, ack)); |
| CheckLatencyInfoComponentInMessage(process_, GetLatencyComponentId(), |
| WebInputEvent::kTouchStart); |
| } |
| TEST_F(RenderWidgetHostMojoInputDisabledTest, InputEventRWHLatencyComponent) { |
| InputEventRWHLatencyComponentMojoInputDisabled(); |
| } |
| TEST_F(RenderWidgetHostWheelScrollLatchingMojoInputDisabledTest, |
| InputEventRWHLatencyComponent) { |
| InputEventRWHLatencyComponentMojoInputDisabled(); |
| } |
| TEST_F(RenderWidgetHostAsyncWheelEventsEnabledMojoInputDisabledTest, |
| InputEventRWHLatencyComponent) { |
| InputEventRWHLatencyComponentMojoInputDisabled(); |
| } |
| |
| void CheckLatencyInfoComponentInMessage( |
| std::vector<MockWidgetInputHandler::DispatchedEvent>& dispatched_events, |
| int64_t component_id, |
| WebInputEvent::Type expected_type) { |
| EXPECT_EQ(1u, dispatched_events.size()); |
| EXPECT_TRUE(dispatched_events.at(0).event_->web_event->GetType() == |
| expected_type); |
| EXPECT_TRUE(dispatched_events.at(0).event_->latency_info.FindLatency( |
| ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, component_id, NULL)); |
| if (dispatched_events.at(0).callback_) { |
| CallCallback(std::move(dispatched_events.at(0).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| } |
| } |
| |
| void CheckLatencyInfoComponentInGestureScrollUpdate( |
| std::vector<MockWidgetInputHandler::DispatchedEvent>& dispatched_events, |
| int64_t component_id) { |
| EXPECT_EQ(2u, dispatched_events.size()); |
| EXPECT_EQ(WebInputEvent::kTouchScrollStarted, |
| dispatched_events.at(0).event_->web_event->GetType()); |
| |
| EXPECT_EQ(WebInputEvent::kGestureScrollUpdate, |
| dispatched_events.at(1).event_->web_event->GetType()); |
| EXPECT_TRUE(dispatched_events.at(1).event_->latency_info.FindLatency( |
| ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, component_id, NULL)); |
| CallCallback(std::move(dispatched_events.at(1).callback_), |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| } |
| |
| // 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. |
| void RenderWidgetHostTest::InputEventRWHLatencyComponent() { |
| host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); |
| |
| // Tests RWHI::ForwardWheelEvent(). |
| SimulateWheelEventPossiblyIncludingPhase(-5, 0, 0, true, |
| WebMouseWheelEvent::kPhaseBegan); |
| std::vector<MockWidgetInputHandler::DispatchedEvent> dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| CheckLatencyInfoComponentInMessage(dispatched_events, GetLatencyComponentId(), |
| WebInputEvent::kMouseWheel); |
| |
| // Tests RWHI::ForwardWheelEventWithLatencyInfo(). |
| SimulateWheelEventWithLatencyInfoAndPossiblyPhase( |
| -5, 0, 0, true, ui::LatencyInfo(), WebMouseWheelEvent::kPhaseChanged); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| CheckLatencyInfoComponentInMessage(dispatched_events, GetLatencyComponentId(), |
| WebInputEvent::kMouseWheel); |
| |
| // Tests RWHI::ForwardMouseEvent(). |
| SimulateMouseEvent(WebInputEvent::kMouseMove); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| CheckLatencyInfoComponentInMessage(dispatched_events, GetLatencyComponentId(), |
| WebInputEvent::kMouseMove); |
| |
| // Tests RWHI::ForwardMouseEventWithLatencyInfo(). |
| SimulateMouseEventWithLatencyInfo(WebInputEvent::kMouseMove, |
| ui::LatencyInfo()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| CheckLatencyInfoComponentInMessage(dispatched_events, GetLatencyComponentId(), |
| WebInputEvent::kMouseMove); |
| |
| // Tests RWHI::ForwardGestureEvent(). |
| SimulateGestureEvent(WebInputEvent::kGestureScrollBegin, |
| blink::kWebGestureDeviceTouchscreen); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| CheckLatencyInfoComponentInMessage(dispatched_events, GetLatencyComponentId(), |
| WebInputEvent::kGestureScrollBegin); |
| |
| // Tests RWHI::ForwardGestureEventWithLatencyInfo(). |
| SimulateGestureEventWithLatencyInfo(WebInputEvent::kGestureScrollUpdate, |
| blink::kWebGestureDeviceTouchscreen, |
| ui::LatencyInfo()); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| CheckLatencyInfoComponentInGestureScrollUpdate(dispatched_events, |
| GetLatencyComponentId()); |
| |
| // Tests RWHI::ForwardTouchEventWithLatencyInfo(). |
| PressTouchPoint(0, 1); |
| SendTouchEvent(); |
| dispatched_events = |
| host_->mock_widget_input_handler_.GetAndResetDispatchedEvents(); |
| CheckLatencyInfoComponentInMessage(dispatched_events, GetLatencyComponentId(), |
| WebInputEvent::kTouchStart); |
| } |
| TEST_F(RenderWidgetHostTest, InputEventRWHLatencyComponent) { |
| InputEventRWHLatencyComponent(); |
| } |
| TEST_F(RenderWidgetHostWheelScrollLatchingDisabledTest, |
| InputEventRWHLatencyComponent) { |
| InputEventRWHLatencyComponent(); |
| } |
| TEST_F(RenderWidgetHostAsyncWheelEventsEnabledTest, |
| InputEventRWHLatencyComponent) { |
| InputEventRWHLatencyComponent(); |
| } |
| |
| TEST_F(RenderWidgetHostTest, RendererExitedResetsInputRouter) { |
| // RendererExited will delete the view. |
| host_->SetView(new TestView(host_.get())); |
| host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1); |
| |
| // Make sure the input router is in a fresh state. |
| ASSERT_FALSE(host_->input_router()->HasPendingEvents()); |
| } |
| |
| // 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(ui::LatencyInfo()); |
| |
| ASSERT_FALSE(host_->is_hidden()); |
| host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1); |
| ASSERT_TRUE(host_->is_hidden()); |
| |
| // Make sure the input router is in a fresh state. |
| ASSERT_FALSE(host_->input_router()->HasPendingEvents()); |
| } |
| |
| TEST_F(RenderWidgetHostTest, ResizeParams) { |
| gfx::Rect bounds(0, 0, 100, 100); |
| gfx::Size physical_backing_size(40, 50); |
| view_->set_bounds(bounds); |
| view_->SetMockPhysicalBackingSize(physical_backing_size); |
| |
| ResizeParams resize_params; |
| host_->GetResizeParams(&resize_params); |
| EXPECT_EQ(bounds.size(), resize_params.new_size); |
| EXPECT_EQ(physical_backing_size, resize_params.physical_backing_size); |
| } |
| |
| TEST_F(RenderWidgetHostTest, ResizeParamsDeviceScale) { |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| command_line->AppendSwitchASCII(switches::kEnableUseZoomForDSF, "true"); |
| |
| DCHECK(display::Screen::GetScreen()); |
| const display::Display& display = |
| display::Screen::GetScreen()->GetPrimaryDisplay(); |
| DCHECK(display.id() != display::kInvalidDisplayId); |
| |
| float device_scale = display.device_scale_factor(); |
| |
| float top_controls_height = 10.0f; |
| float bottom_controls_height = 20.0f; |
| view_->set_top_controls_height(top_controls_height); |
| view_->set_bottom_controls_height(bottom_controls_height); |
| |
| ResizeParams resize_params; |
| host_->GetResizeParams(&resize_params); |
| EXPECT_EQ(top_controls_height * device_scale, |
| resize_params.top_controls_height); |
| EXPECT_EQ(bottom_controls_height * device_scale, |
| resize_params.bottom_controls_height); |
| } |
| |
| // 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; |
| blink::WebDragOperationsMask drag_operation = blink::kWebDragOperationEvery; |
| DragEventSourceInfo event_info; |
| host_->OnStartDragging(drop_data, drag_operation, SkBitmap(), gfx::Vector2d(), |
| event_info); |
| EXPECT_EQ(delegate_->mock_delegate_view()->start_dragging_count(), 1); |
| |
| // Simulate that renderer exited due navigation to the next page. |
| host_->RendererExited(base::TERMINATION_STATUS_NORMAL_TERMINATION, 0); |
| EXPECT_FALSE(host_->GetView()); |
| host_->OnStartDragging(drop_data, drag_operation, SkBitmap(), gfx::Vector2d(), |
| event_info); |
| 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->set_bounds(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 reqiest to new up the RenderView and so subsequent WasResized |
| // calls should not result in new IPC (unless the size has actually changed). |
| host_->WasResized(); |
| EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID)); |
| EXPECT_EQ(initial_size_, host_->old_resize_params_->new_size); |
| EXPECT_TRUE(host_->resize_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) { |
| host_->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true)); |
| process_->sink().ClearMessages(); |
| |
| host_->DetachDelegate(); |
| |
| // Tests RWHI::ForwardGestureEventWithLatencyInfo(). |
| SimulateGestureEventWithLatencyInfo(WebInputEvent::kGestureScrollUpdate, |
| blink::kWebGestureDeviceTouchscreen, |
| ui::LatencyInfo()); |
| |
| // Tests RWHI::ForwardWheelEventWithLatencyInfo(). |
| SimulateWheelEventWithLatencyInfo(-5, 0, 0, true, ui::LatencyInfo()); |
| |
| ASSERT_FALSE(host_->input_router()->HasPendingEvents()); |
| } |
| |
| // Check that if messages of a frame arrive earlier than the frame itself, we |
| // queue the messages until the frame arrives and then process them. |
| TEST_F(RenderWidgetHostTest, FrameToken_MessageThenFrame) { |
| const uint32_t frame_token = 99; |
| const gfx::Size frame_size(50, 50); |
| const viz::LocalSurfaceId local_surface_id(1, |
| base::UnguessableToken::Create()); |
| std::vector<IPC::Message> messages; |
| messages.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(5)); |
| |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token, messages)); |
| EXPECT_EQ(1u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.frame_token = frame_token; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(1u, host_->processed_frame_messages_count()); |
| } |
| |
| // Check that if a frame arrives earlier than its messages, we process the |
| // messages immedtiately. |
| TEST_F(RenderWidgetHostTest, FrameToken_FrameThenMessage) { |
| const uint32_t frame_token = 99; |
| const gfx::Size frame_size(50, 50); |
| const viz::LocalSurfaceId local_surface_id(1, |
| base::UnguessableToken::Create()); |
| std::vector<IPC::Message> messages; |
| messages.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(5)); |
| |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.frame_token = frame_token; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token, messages)); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(1u, host_->processed_frame_messages_count()); |
| } |
| |
| // Check that if messages of multiple frames arrive before the frames, we |
| // process each message once it frame arrives. |
| TEST_F(RenderWidgetHostTest, FrameToken_MultipleMessagesThenTokens) { |
| const uint32_t frame_token1 = 99; |
| const uint32_t frame_token2 = 100; |
| const gfx::Size frame_size(50, 50); |
| const viz::LocalSurfaceId local_surface_id(1, |
| base::UnguessableToken::Create()); |
| std::vector<IPC::Message> messages1; |
| std::vector<IPC::Message> messages2; |
| messages1.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(5)); |
| messages2.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(6)); |
| |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token1, messages1)); |
| EXPECT_EQ(1u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token2, messages2)); |
| EXPECT_EQ(2u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.frame_token = frame_token1; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| EXPECT_EQ(1u, host_->queued_messages_.size()); |
| EXPECT_EQ(1u, host_->processed_frame_messages_count()); |
| |
| frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.frame_token = frame_token2; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(2u, host_->processed_frame_messages_count()); |
| } |
| |
| // Check that if multiple frames arrive before their messages, each message is |
| // processed immediately as soon as it arrives. |
| TEST_F(RenderWidgetHostTest, FrameToken_MultipleTokensThenMessages) { |
| const uint32_t frame_token1 = 99; |
| const uint32_t frame_token2 = 100; |
| const gfx::Size frame_size(50, 50); |
| const viz::LocalSurfaceId local_surface_id(1, |
| base::UnguessableToken::Create()); |
| std::vector<IPC::Message> messages1; |
| std::vector<IPC::Message> messages2; |
| messages1.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(5)); |
| messages2.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(6)); |
| |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.frame_token = frame_token1; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.frame_token = frame_token2; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token1, messages1)); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(1u, host_->processed_frame_messages_count()); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token2, messages2)); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(2u, host_->processed_frame_messages_count()); |
| } |
| |
| // Check that if one frame is lost but its messages arrive, we process the |
| // messages on the arrival of the next frame. |
| TEST_F(RenderWidgetHostTest, FrameToken_DroppedFrame) { |
| const uint32_t frame_token1 = 99; |
| const uint32_t frame_token2 = 100; |
| const gfx::Size frame_size(50, 50); |
| const viz::LocalSurfaceId local_surface_id(1, |
| base::UnguessableToken::Create()); |
| std::vector<IPC::Message> messages1; |
| std::vector<IPC::Message> messages2; |
| messages1.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(5)); |
| messages2.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(6)); |
| |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token1, messages1)); |
| EXPECT_EQ(1u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token2, messages2)); |
| EXPECT_EQ(2u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.frame_token = frame_token2; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(2u, host_->processed_frame_messages_count()); |
| } |
| |
| // Check that if the renderer crashes, we drop all queued messages and allow |
| // smaller frame tokens to be sent by the renderer. |
| TEST_F(RenderWidgetHostTest, FrameToken_RendererCrash) { |
| const uint32_t frame_token1 = 99; |
| const uint32_t frame_token2 = 50; |
| const uint32_t frame_token3 = 30; |
| const gfx::Size frame_size(50, 50); |
| const viz::LocalSurfaceId local_surface_id(1, |
| base::UnguessableToken::Create()); |
| std::vector<IPC::Message> messages1; |
| std::vector<IPC::Message> messages3; |
| messages1.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(5)); |
| messages3.push_back(ViewHostMsg_DidFirstVisuallyNonEmptyPaint(6)); |
| |
| // If we don't do this, then RWHI destroys the view in RendererExited and |
| // then a crash occurs when we attempt to destroy it again in TearDown(). |
| host_->SetView(nullptr); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token1, messages1)); |
| EXPECT_EQ(1u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| host_->Init(); |
| |
| cc::CompositorFrame frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.frame_token = frame_token2; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| host_->Init(); |
| |
| host_->OnMessageReceived( |
| ViewHostMsg_FrameSwapMessages(0, frame_token3, messages3)); |
| EXPECT_EQ(1u, host_->queued_messages_.size()); |
| EXPECT_EQ(0u, host_->processed_frame_messages_count()); |
| |
| frame = MakeCompositorFrame(1.f, frame_size); |
| frame.metadata.frame_token = frame_token3; |
| host_->SubmitCompositorFrame(local_surface_id, std::move(frame), nullptr, 0); |
| EXPECT_EQ(0u, host_->queued_messages_.size()); |
| EXPECT_EQ(1u, host_->processed_frame_messages_count()); |
| } |
| |
| } // namespace content |