diff --git a/components/input/fling_controller.cc b/components/input/fling_controller.cc index c3cc5d057071e6..01c721f12f68f9 100644 --- a/components/input/fling_controller.cc +++ b/components/input/fling_controller.cc @@ -378,7 +378,11 @@ void FlingController::GenerateAndSendFlingEndEvents( void FlingController::EndCurrentFling(base::TimeTicks current_time) { last_progress_time_ = base::TimeTicks(); + auto weak_this = weak_ptr_factory_.GetWeakPtr(); GenerateAndSendFlingEndEvents(current_time); + if (!weak_this) { + return; + } current_fling_parameters_ = ActiveFlingParameters(); if (fling_curve_) { diff --git a/components/input/gesture_event_queue.cc b/components/input/gesture_event_queue.cc index 9444a4dc55a5cd..b0752d82cfaccb 100644 --- a/components/input/gesture_event_queue.cc +++ b/components/input/gesture_event_queue.cc @@ -4,7 +4,6 @@ #include "components/input/gesture_event_queue.h" -#include "base/auto_reset.h" #include "base/logging.h" #include "base/trace_event/trace_event.h" #include "components/input/touchpad_tap_suppression_controller.h" @@ -223,7 +222,8 @@ void GestureEventQueue::AckCompletedEvents() { if (processing_acks_) { return; } - base::AutoReset process_acks(&processing_acks_, true); + processing_acks_ = true; + auto weak_this = weak_ptr_factory_.GetWeakPtr(); while (!sent_events_awaiting_ack_.empty()) { auto iter = sent_events_awaiting_ack_.begin(); if (iter->ack_state() == blink::mojom::InputEventResultState::kUnknown) { @@ -233,7 +233,11 @@ void GestureEventQueue::AckCompletedEvents() { sent_events_awaiting_ack_.erase(iter); AckGestureEventToClient(event, event.ack_source(), event.ack_state()); + if (!weak_this) { + return; + } } + processing_acks_ = false; } void GestureEventQueue::AckGestureEventToClient( diff --git a/components/input/gesture_event_queue.h b/components/input/gesture_event_queue.h index 7a610d0f767e48..db528c98430746 100644 --- a/components/input/gesture_event_queue.h +++ b/components/input/gesture_event_queue.h @@ -10,6 +10,7 @@ #include "base/component_export.h" #include "base/containers/circular_deque.h" #include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "components/input/dispatch_to_renderer_callback.h" @@ -246,6 +247,8 @@ class COMPONENT_EXPORT(INPUT) GestureEventQueue { // True when the last GSE event is either in the debouncing_deferral_queue_ or // pushed to the queue and dropped from it later on. bool scroll_end_filtered_by_deboucing_deferral_queue_ = false; + + base::WeakPtrFactory weak_ptr_factory_{this}; }; } // namespace input diff --git a/components/input/input_router_impl.cc b/components/input/input_router_impl.cc index 2d3de8fbd63216..ffe041f602c3a2 100644 --- a/components/input/input_router_impl.cc +++ b/components/input/input_router_impl.cc @@ -151,7 +151,13 @@ void InputRouterImpl::SendKeyboardEvent( const NativeWebKeyboardEventWithLatencyInfo& key_event, KeyboardEventCallback event_result_callback, DispatchToRendererCallback& dispatch_callback) { + auto weak_this = weak_ptr_factory_.GetWeakPtr(); gesture_event_queue_.StopFling(); + if (!weak_this) { + std::move(dispatch_callback) + .Run(key_event.event, DispatchToRendererResult::kNotDispatched); + return; + } blink::mojom::WidgetInputHandler::DispatchEventCallback callback = base::BindOnce(&InputRouterImpl::KeyboardEventHandled, weak_this_, key_event, std::move(event_result_callback)); diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc index c9bb9def7d55e2..7a6028033d537e 100644 --- a/content/browser/renderer_host/input/input_router_impl_unittest.cc +++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc @@ -2962,4 +2962,41 @@ TEST_F(InputRouterImplTest, SynchronousDestructionDuringAck) { EXPECT_EQ(nullptr, input_router_); } +TEST_F(InputRouterImplTest, SynchronousDestructionDuringSendKeyboardEvent) { + // Begin a touchscreen scroll and start a fling so that the fling controller + // is active when the keyboard event arrives. + PressAndSetTouchActionAuto(); + SimulateGestureEvent(WebInputEvent::Type::kGestureScrollBegin, + blink::WebGestureDevice::kTouchscreen); + DispatchedMessages dispatched_messages = GetAndResetDispatchedMessages(); + ASSERT_EQ(1U, dispatched_messages.size()); + ASSERT_TRUE(dispatched_messages[0]->ToEvent()); + dispatched_messages[0]->ToEvent()->CallCallback( + blink::mojom::InputEventResultState::kConsumed); + + SimulateGestureEvent(WebInputEvent::Type::kGestureFlingStart, + blink::WebGestureDevice::kTouchscreen); + for (auto& message : GetAndResetDispatchedMessages()) { + if (message->ToEvent() && message->ToEvent()->HasCallback()) { + message->ToEvent()->CallCallback( + blink::mojom::InputEventResultState::kConsumed); + } + } + disposition_handler_->GetAndResetAckCount(); + + // Set the disposition handler to destroy the input router synchronously when + // it receives the ack for the gesture-scroll-end generated while stopping + // the fling. + disposition_handler_->set_on_gesture_event_ack_closure(base::BindOnce( + [](std::unique_ptr* router) { router->reset(); }, + &input_router_)); + + // SendKeyboardEvent stops the active fling, which synchronously emits and + // acks a gesture-scroll-end. The ack runs the closure above and destroys the + // input router before SendKeyboardEvent returns. + SimulateKeyboardEvent(WebInputEvent::Type::kRawKeyDown); + + EXPECT_EQ(nullptr, input_router_); +} + } // namespace content diff --git a/content/browser/renderer_host/input/mock_input_disposition_handler.cc b/content/browser/renderer_host/input/mock_input_disposition_handler.cc index 56c258891ccd4d..6b75fb0b041b91 100644 --- a/content/browser/renderer_host/input/mock_input_disposition_handler.cc +++ b/content/browser/renderer_host/input/mock_input_disposition_handler.cc @@ -73,6 +73,9 @@ void MockInputDispositionHandler::OnGestureEventAck( VLOG(1) << __FUNCTION__ << " called!"; acked_gesture_event_ = event.event; RecordAckCalled(event.event.GetType(), ack_result); + if (on_gesture_event_ack_closure_) { + std::move(on_gesture_event_ack_closure_).Run(); + } } size_t MockInputDispositionHandler::GetAndResetAckCount() { diff --git a/content/browser/renderer_host/input/mock_input_disposition_handler.h b/content/browser/renderer_host/input/mock_input_disposition_handler.h index c2e55287d63953..a8c72f523b3599 100644 --- a/content/browser/renderer_host/input/mock_input_disposition_handler.h +++ b/content/browser/renderer_host/input/mock_input_disposition_handler.h @@ -57,6 +57,10 @@ class MockInputDispositionHandler : public input::InputDispositionHandler { on_touch_event_ack_closure_ = std::move(closure); } + void set_on_gesture_event_ack_closure(base::OnceClosure closure) { + on_gesture_event_ack_closure_ = std::move(closure); + } + blink::mojom::InputEventResultState ack_state() const { return ack_state_; } blink::mojom::InputEventResultState acked_wheel_event_state() const { @@ -105,6 +109,7 @@ class MockInputDispositionHandler : public input::InputDispositionHandler { std::unique_ptr gesture_followup_event_; std::unique_ptr touch_followup_event_; base::OnceClosure on_touch_event_ack_closure_; + base::OnceClosure on_gesture_event_ack_closure_; }; } // namespace content