Commit 90383190 authored by W. James MacLean's avatar W. James MacLean Committed by Commit Bot

TouchEmulator must put a touch event id on ScrollEnd.

Because of the unusual way that the TouchEmulator creates pinch events,
it's possible for a GestureFlingStart (created by the GestureRecognizer)
to terminate a pinch sequence. In that event a synthetic
GestureScrollEnd is inserted.

Prior to this CL, that GestureScrollEnd had a zero value
for unique_touch_event_id, which in turn caused problems when it was
sent to DispatchTouchscreenGestureEvent, along with a null target.

Bug: 900496
Change-Id: I290757980def3f55a0111a2e31f8424ce06e96f1
Reviewed-on: https://chromium-review.googlesource.com/c/1312619
Commit-Queue: James MacLean <wjmaclean@chromium.org>
Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605432}
parent 3adb3051
...@@ -71,6 +71,7 @@ TouchEmulator::TouchEmulator(TouchEmulatorClient* client, ...@@ -71,6 +71,7 @@ TouchEmulator::TouchEmulator(TouchEmulatorClient* client,
ui::GestureProviderConfigType::CURRENT_PLATFORM), ui::GestureProviderConfigType::CURRENT_PLATFORM),
double_tap_enabled_(true), double_tap_enabled_(true),
use_2x_cursors_(false), use_2x_cursors_(false),
pinch_gesture_mode_for_testing_(false),
emulated_stream_active_sequence_count_(0), emulated_stream_active_sequence_count_(0),
native_stream_active_sequence_count_(0), native_stream_active_sequence_count_(0),
last_emulated_start_target_(nullptr), last_emulated_start_target_(nullptr),
...@@ -516,6 +517,7 @@ void TouchEmulator::ScrollEnd(const WebGestureEvent& event) { ...@@ -516,6 +517,7 @@ void TouchEmulator::ScrollEnd(const WebGestureEvent& event) {
WebGestureEvent scroll_event( WebGestureEvent scroll_event(
WebInputEvent::kGestureScrollEnd, ModifiersWithoutMouseButtons(event), WebInputEvent::kGestureScrollEnd, ModifiersWithoutMouseButtons(event),
event.TimeStamp(), blink::kWebGestureDeviceTouchscreen); event.TimeStamp(), blink::kWebGestureDeviceTouchscreen);
scroll_event.unique_touch_event_id = event.unique_touch_event_id;
client_->ForwardEmulatedGestureEvent(scroll_event); client_->ForwardEmulatedGestureEvent(scroll_event);
} }
...@@ -571,7 +573,11 @@ void TouchEmulator::FillTouchEventAndPoint(const WebMouseEvent& mouse_event, ...@@ -571,7 +573,11 @@ void TouchEmulator::FillTouchEventAndPoint(const WebMouseEvent& mouse_event,
} }
bool TouchEmulator::InPinchGestureMode() const { bool TouchEmulator::InPinchGestureMode() const {
return shift_pressed_; return shift_pressed_ || pinch_gesture_mode_for_testing_;
}
void TouchEmulator::SetPinchGestureModeForTesting(bool pinch_gesture_mode) {
pinch_gesture_mode_for_testing_ = pinch_gesture_mode;
} }
} // namespace content } // namespace content
...@@ -85,6 +85,14 @@ class CONTENT_EXPORT TouchEmulator : public ui::GestureProviderClient { ...@@ -85,6 +85,14 @@ class CONTENT_EXPORT TouchEmulator : public ui::GestureProviderClient {
// Cancel any touches, for example, when focus is lost. // Cancel any touches, for example, when focus is lost.
void CancelTouch(); void CancelTouch();
// This is needed because SyntheticGestureSmoothDrag doesn't support setting
// key-modifiers on the drag sequence.
// https://crbug.com/901374.
void SetPinchGestureModeForTesting(bool pinch_gesture_mode);
bool suppress_next_fling_cancel_for_testing() const {
return suppress_next_fling_cancel_;
}
private: private:
// ui::GestureProviderClient implementation. // ui::GestureProviderClient implementation.
void OnGestureEvent(const ui::GestureEventData& gesture) override; void OnGestureEvent(const ui::GestureEventData& gesture) override;
...@@ -147,6 +155,7 @@ class CONTENT_EXPORT TouchEmulator : public ui::GestureProviderClient { ...@@ -147,6 +155,7 @@ class CONTENT_EXPORT TouchEmulator : public ui::GestureProviderClient {
bool mouse_pressed_; bool mouse_pressed_;
bool shift_pressed_; bool shift_pressed_;
bool pinch_gesture_mode_for_testing_;
blink::WebTouchEvent touch_event_; blink::WebTouchEvent touch_event_;
int emulated_stream_active_sequence_count_; int emulated_stream_active_sequence_count_;
......
...@@ -5,8 +5,10 @@ ...@@ -5,8 +5,10 @@
#include <vector> #include <vector>
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "content/browser/renderer_host/input/input_router_impl.h" #include "content/browser/renderer_host/input/input_router_impl.h"
#include "content/browser/renderer_host/input/synthetic_smooth_drag_gesture.h"
#include "content/browser/renderer_host/input/touch_action_filter.h" #include "content/browser/renderer_host/input/touch_action_filter.h"
#include "content/browser/renderer_host/input/touch_emulator.h" #include "content/browser/renderer_host/input/touch_emulator.h"
#include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/renderer_host/render_widget_host_impl.h"
...@@ -141,6 +143,58 @@ class RenderWidgetHostTouchEmulatorBrowserTest : public ContentBrowserTest { ...@@ -141,6 +143,58 @@ class RenderWidgetHostTouchEmulatorBrowserTest : public ContentBrowserTest {
base::TimeDelta simulated_event_time_delta_; base::TimeDelta simulated_event_time_delta_;
}; };
// Synthetic mouse events not allowed on Android.
#if !defined(OS_ANDROID)
// This test makes sure that TouchEmulator doesn't emit a GestureScrollEnd without a valid
// unique_touch_event_id when it sees a GestureFlingStart terminating the underlying mouse
// scroll sequence. If the GestureScrollEnd is given a unique_touch_event_id of 0, then a
// crash will occur.
IN_PROC_BROWSER_TEST_F(RenderWidgetHostTouchEmulatorBrowserTest,
TouchEmulatorPinchWithGestureFling) {
auto* touch_emulator = host()->GetTouchEmulator();
touch_emulator->Enable(TouchEmulator::Mode::kEmulatingTouchFromMouse,
ui::GestureProviderConfigType::GENERIC_MOBILE);
touch_emulator->SetPinchGestureModeForTesting(true);
TestInputEventObserver observer;
host()->AddInputEventObserver(&observer);
SyntheticSmoothDragGestureParams params;
params.start_point = gfx::PointF(10.f, 110.f);
params.gesture_source_type = SyntheticGestureParams::MOUSE_INPUT;
params.distances.push_back(gfx::Vector2d(0, -10));
params.distances.push_back(gfx::Vector2d(0, -10));
params.distances.push_back(gfx::Vector2d(0, -10));
params.distances.push_back(gfx::Vector2d(0, -10));
params.speed_in_pixels_s = 1200;
std::unique_ptr<SyntheticSmoothDragGesture> gesture(
new SyntheticSmoothDragGesture(params));
InputEventAckWaiter scroll_end_ack_waiter(
host(), blink::WebInputEvent::kGestureScrollEnd);
base::RunLoop run_loop;
host()->QueueSyntheticGesture(
std::move(gesture),
base::BindOnce(
base::BindLambdaForTesting([&](SyntheticGesture::Result result) {
EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
run_loop.Quit();
})));
run_loop.Run();
scroll_end_ack_waiter.Wait();
// Verify that a GestureFlingStart was suppressed by the TouchEmulator, and
// that we generated a GestureScrollEnd and routed it without crashing.
TestInputEventObserver::EventTypeVector dispatched_events =
observer.GetAndResetDispatchedEventTypes();
auto it_gse = std::find(dispatched_events.begin(), dispatched_events.end(),
blink::WebInputEvent::kGestureScrollEnd);
EXPECT_NE(dispatched_events.end(), it_gse);
EXPECT_TRUE(touch_emulator->suppress_next_fling_cancel_for_testing());
}
#endif // !defined(OS_ANDROID)
IN_PROC_BROWSER_TEST_F(RenderWidgetHostTouchEmulatorBrowserTest, IN_PROC_BROWSER_TEST_F(RenderWidgetHostTouchEmulatorBrowserTest,
TouchEmulator) { TouchEmulator) {
// All touches will be immediately acked instead of sending them to the // All touches will be immediately acked instead of sending them to the
......
...@@ -1361,7 +1361,9 @@ void RenderWidgetHostInputEventRouter::DispatchTouchscreenGestureEvent( ...@@ -1361,7 +1361,9 @@ void RenderWidgetHostInputEventRouter::DispatchTouchscreenGestureEvent(
// are not associated with touch events, because non-synthetic events can be // are not associated with touch events, because non-synthetic events can be
// created by ContentView. These will use the target found by the // created by ContentView. These will use the target found by the
// RenderWidgetTargeter. These gesture events should always have a // RenderWidgetTargeter. These gesture events should always have a
// unique_touch_event_id of 0. // unique_touch_event_id of 0. They must have a non-null target in order
// to get the coordinate transform.
DCHECK(target);
touchscreen_gesture_target_.target = target; touchscreen_gesture_target_.target = target;
touchscreen_gesture_target_in_map_ = IsViewInMap(target); touchscreen_gesture_target_in_map_ = IsViewInMap(target);
if (!root_view->GetTransformToViewCoordSpace( if (!root_view->GetTransformToViewCoordSpace(
......
...@@ -409,7 +409,7 @@ class CONTENT_EXPORT RenderWidgetHostViewBase ...@@ -409,7 +409,7 @@ class CONTENT_EXPORT RenderWidgetHostViewBase
// this view or the target view has no current FrameSinkId. The latter may // this view or the target view has no current FrameSinkId. The latter may
// happen if either view is not currently visible in the viewport. // happen if either view is not currently visible in the viewport.
// This function is useful if there are multiple points to transform between // This function is useful if there are multiple points to transform between
// the same two views. // the same two views. |target_view| must be non-null.
bool GetTransformToViewCoordSpace(RenderWidgetHostViewBase* target_view, bool GetTransformToViewCoordSpace(RenderWidgetHostViewBase* target_view,
gfx::Transform* transform); gfx::Transform* transform);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment