1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/input/touch_emulator.h"
6
7 #include "base/containers/queue.h"
8 #include "base/time/time.h"
9 #include "build/build_config.h"
10 #include "content/browser/renderer_host/input/motion_event_web.h"
11 #include "content/browser/renderer_host/render_widget_host_view_base.h"
12 #include "content/browser/renderer_host/ui_events_helper.h"
13 #include "content/common/input/web_touch_event_traits.h"
14 #include "content/grit/content_resources.h"
15 #include "content/public/common/content_client.h"
16 #include "content/public/common/content_switches.h"
17 #include "third_party/blink/public/common/input/web_keyboard_event.h"
18 #include "third_party/blink/public/common/input/web_mouse_event.h"
19 #include "ui/base/cursor/cursor.h"
20 #include "ui/base/cursor/mojom/cursor_type.mojom-shared.h"
21 #include "ui/base/ui_base_types.h"
22 #include "ui/events/base_event_utils.h"
23 #include "ui/events/blink/blink_event_util.h"
24 #include "ui/events/gesture_detection/gesture_provider_config_helper.h"
25 #include "ui/gfx/image/image.h"
26
27 using blink::WebGestureEvent;
28 using blink::WebInputEvent;
29 using blink::WebKeyboardEvent;
30 using blink::WebMouseEvent;
31 using blink::WebMouseWheelEvent;
32 using blink::WebTouchEvent;
33 using blink::WebTouchPoint;
34
35 namespace content {
36
37 namespace {
38
GetEmulatorGestureProviderConfig(ui::GestureProviderConfigType config_type,TouchEmulator::Mode mode)39 ui::GestureProvider::Config GetEmulatorGestureProviderConfig(
40 ui::GestureProviderConfigType config_type,
41 TouchEmulator::Mode mode) {
42 ui::GestureProvider::Config config =
43 ui::GetGestureProviderConfig(config_type);
44 config.gesture_begin_end_types_enabled = false;
45 config.gesture_detector_config.swipe_enabled = false;
46 config.gesture_detector_config.two_finger_tap_enabled = false;
47 if (mode == TouchEmulator::Mode::kInjectingTouchEvents) {
48 config.gesture_detector_config.longpress_timeout = base::TimeDelta::Max();
49 config.gesture_detector_config.showpress_timeout = base::TimeDelta::Max();
50 }
51 return config;
52 }
53
ModifiersWithoutMouseButtons(const WebInputEvent & event)54 int ModifiersWithoutMouseButtons(const WebInputEvent& event) {
55 const int all_buttons = WebInputEvent::kLeftButtonDown |
56 WebInputEvent::kMiddleButtonDown |
57 WebInputEvent::kRightButtonDown;
58 return event.GetModifiers() & ~all_buttons;
59 }
60
61 // Time between two consecutive mouse moves, during which second mouse move
62 // is not converted to touch.
63 constexpr base::TimeDelta kMouseMoveDropInterval =
64 base::TimeDelta::FromMilliseconds(5);
65
66 } // namespace
67
TouchEmulator(TouchEmulatorClient * client,float device_scale_factor)68 TouchEmulator::TouchEmulator(TouchEmulatorClient* client,
69 float device_scale_factor)
70 : client_(client),
71 gesture_provider_config_type_(
72 ui::GestureProviderConfigType::CURRENT_PLATFORM),
73 double_tap_enabled_(true),
74 use_2x_cursors_(false),
75 pinch_gesture_mode_for_testing_(false),
76 emulated_stream_active_sequence_count_(0),
77 native_stream_active_sequence_count_(0),
78 last_emulated_start_target_(nullptr),
79 pending_taps_count_(0) {
80 DCHECK(client_);
81 ResetState();
82 InitCursors(device_scale_factor, true);
83 }
84
~TouchEmulator()85 TouchEmulator::~TouchEmulator() {
86 // We cannot cleanup properly in destructor, as we need roundtrip to the
87 // renderer for ack. Instead, the owner should call Disable, and only
88 // destroy this object when renderer is dead.
89 }
90
ResetState()91 void TouchEmulator::ResetState() {
92 last_mouse_event_was_move_ = false;
93 last_mouse_move_timestamp_ = base::TimeTicks();
94 mouse_pressed_ = false;
95 shift_pressed_ = false;
96 suppress_next_fling_cancel_ = false;
97 pinch_scale_ = 1.f;
98 pinch_gesture_active_ = false;
99 }
100
Enable(Mode mode,ui::GestureProviderConfigType config_type)101 void TouchEmulator::Enable(Mode mode,
102 ui::GestureProviderConfigType config_type) {
103 if (gesture_provider_ && mode_ != mode)
104 client_->SetCursor(pointer_cursor_);
105
106 if (!gesture_provider_ || gesture_provider_config_type_ != config_type ||
107 mode_ != mode) {
108 mode_ = mode;
109 gesture_provider_config_type_ = config_type;
110 gesture_provider_.reset(new ui::FilteredGestureProvider(
111 GetEmulatorGestureProviderConfig(config_type, mode), this));
112 gesture_provider_->SetDoubleTapSupportForPageEnabled(double_tap_enabled_);
113 // TODO(dgozman): Use synthetic secondary touch to support multi-touch.
114 gesture_provider_->SetMultiTouchZoomSupportEnabled(
115 mode != Mode::kEmulatingTouchFromMouse);
116 }
117
118 UpdateCursor();
119 }
120
Disable()121 void TouchEmulator::Disable() {
122 if (!enabled())
123 return;
124
125 mode_ = Mode::kEmulatingTouchFromMouse;
126 CancelTouch();
127 gesture_provider_.reset();
128 base::queue<base::OnceClosure> empty;
129 injected_touch_completion_callbacks_.swap(empty);
130 client_->SetCursor(pointer_cursor_);
131 ResetState();
132 }
133
SetDeviceScaleFactor(float device_scale_factor)134 void TouchEmulator::SetDeviceScaleFactor(float device_scale_factor) {
135 if (!InitCursors(device_scale_factor, false))
136 return;
137 if (enabled())
138 UpdateCursor();
139 }
140
SetDoubleTapSupportForPageEnabled(bool enabled)141 void TouchEmulator::SetDoubleTapSupportForPageEnabled(bool enabled) {
142 double_tap_enabled_ = enabled;
143 if (gesture_provider_)
144 gesture_provider_->SetDoubleTapSupportForPageEnabled(enabled);
145 }
146
InitCursors(float device_scale_factor,bool force)147 bool TouchEmulator::InitCursors(float device_scale_factor, bool force) {
148 bool use_2x = device_scale_factor > 1.5f;
149 if (use_2x == use_2x_cursors_ && !force)
150 return false;
151 use_2x_cursors_ = use_2x;
152 float cursor_scale_factor = use_2x ? 2.f : 1.f;
153 cursor_size_ = InitCursorFromResource(&touch_cursor_,
154 cursor_scale_factor,
155 use_2x ? IDR_DEVTOOLS_TOUCH_CURSOR_ICON_2X :
156 IDR_DEVTOOLS_TOUCH_CURSOR_ICON);
157 InitCursorFromResource(&pinch_cursor_,
158 cursor_scale_factor,
159 use_2x ? IDR_DEVTOOLS_PINCH_CURSOR_ICON_2X :
160 IDR_DEVTOOLS_PINCH_CURSOR_ICON);
161
162 pointer_cursor_ = WebCursor(ui::mojom::CursorType::kPointer);
163 return true;
164 }
165
InitCursorFromResource(WebCursor * cursor,float scale,int resource_id)166 gfx::SizeF TouchEmulator::InitCursorFromResource(
167 WebCursor* cursor, float scale, int resource_id) {
168 gfx::Image& cursor_image =
169 content::GetContentClient()->GetNativeImageNamed(resource_id);
170 ui::Cursor cursor_info(ui::mojom::CursorType::kCustom);
171 cursor_info.set_image_scale_factor(scale);
172 cursor_info.set_custom_bitmap(cursor_image.AsBitmap());
173 cursor_info.set_custom_hotspot(
174 gfx::Point(cursor_image.Width() / 2, cursor_image.Height() / 2));
175
176 *cursor = WebCursor(cursor_info);
177 return gfx::ScaleSize(gfx::SizeF(cursor_image.Size()), 1.f / scale);
178 }
179
HandleMouseEvent(const WebMouseEvent & mouse_event,RenderWidgetHostViewBase * target_view)180 bool TouchEmulator::HandleMouseEvent(const WebMouseEvent& mouse_event,
181 RenderWidgetHostViewBase* target_view) {
182 if (!enabled() || mode_ != Mode::kEmulatingTouchFromMouse)
183 return false;
184
185 UpdateCursor();
186
187 if (mouse_event.button == WebMouseEvent::Button::kRight &&
188 mouse_event.GetType() == WebInputEvent::Type::kMouseDown) {
189 client_->ShowContextMenuAtPoint(
190 gfx::Point(mouse_event.PositionInWidget().x(),
191 mouse_event.PositionInWidget().y()),
192 ui::MENU_SOURCE_MOUSE, target_view);
193 }
194
195 if (mouse_event.button != WebMouseEvent::Button::kLeft)
196 return true;
197
198 if (mouse_event.GetType() == WebInputEvent::Type::kMouseMove) {
199 if (last_mouse_event_was_move_ &&
200 mouse_event.TimeStamp() <
201 last_mouse_move_timestamp_ + kMouseMoveDropInterval)
202 return true;
203
204 last_mouse_event_was_move_ = true;
205 last_mouse_move_timestamp_ = mouse_event.TimeStamp();
206 } else {
207 last_mouse_event_was_move_ = false;
208 }
209
210 if (mouse_event.GetType() == WebInputEvent::Type::kMouseDown)
211 mouse_pressed_ = true;
212 else if (mouse_event.GetType() == WebInputEvent::Type::kMouseUp)
213 mouse_pressed_ = false;
214
215 UpdateShiftPressed((mouse_event.GetModifiers() & WebInputEvent::kShiftKey) !=
216 0);
217
218 if (mouse_event.GetType() != WebInputEvent::Type::kMouseDown &&
219 mouse_event.GetType() != WebInputEvent::Type::kMouseMove &&
220 mouse_event.GetType() != WebInputEvent::Type::kMouseUp) {
221 return true;
222 }
223
224 gfx::PointF pos_in_root = mouse_event.PositionInWidget();
225 if (target_view)
226 pos_in_root = target_view->TransformPointToRootCoordSpaceF(pos_in_root);
227 FillTouchEventAndPoint(mouse_event, pos_in_root);
228 HandleEmulatedTouchEvent(touch_event_, target_view);
229
230 // Do not pass mouse events to the renderer.
231 return true;
232 }
233
HandleMouseWheelEvent(const WebMouseWheelEvent & event)234 bool TouchEmulator::HandleMouseWheelEvent(const WebMouseWheelEvent& event) {
235 if (!enabled() || mode_ != Mode::kEmulatingTouchFromMouse)
236 return false;
237
238 // Send mouse wheel for easy scrolling when there is no active touch.
239 return emulated_stream_active_sequence_count_ > 0;
240 }
241
HandleKeyboardEvent(const WebKeyboardEvent & event)242 bool TouchEmulator::HandleKeyboardEvent(const WebKeyboardEvent& event) {
243 if (!enabled() || mode_ != Mode::kEmulatingTouchFromMouse)
244 return false;
245
246 if (!UpdateShiftPressed((event.GetModifiers() & WebInputEvent::kShiftKey) !=
247 0))
248 return false;
249
250 if (!mouse_pressed_)
251 return false;
252
253 // Note: The necessary pinch events will be lazily inserted by
254 // |OnGestureEvent| depending on the state of |shift_pressed_|, using the
255 // scroll stream as the event driver.
256 if (shift_pressed_) {
257 // TODO(dgozman): Add secondary touch point and set anchor.
258 } else {
259 // TODO(dgozman): Remove secondary touch point and anchor.
260 }
261
262 // Never block keyboard events.
263 return false;
264 }
265
HandleTouchEvent(const blink::WebTouchEvent & event)266 bool TouchEmulator::HandleTouchEvent(const blink::WebTouchEvent& event) {
267 // Block native event when emulated touch stream is active.
268 if (emulated_stream_active_sequence_count_)
269 return true;
270
271 bool is_sequence_start = WebTouchEventTraits::IsTouchSequenceStart(event);
272 // Do not allow middle-sequence event to pass through, if start was blocked.
273 if (!native_stream_active_sequence_count_ && !is_sequence_start)
274 return true;
275
276 if (is_sequence_start)
277 native_stream_active_sequence_count_++;
278 return false;
279 }
280
HandleEmulatedTouchEvent(blink::WebTouchEvent event,RenderWidgetHostViewBase * target_view)281 bool TouchEmulator::HandleEmulatedTouchEvent(
282 blink::WebTouchEvent event,
283 RenderWidgetHostViewBase* target_view) {
284 DCHECK(gesture_provider_);
285 event.unique_touch_event_id = ui::GetNextTouchEventId();
286 auto result = gesture_provider_->OnTouchEvent(MotionEventWeb(event));
287 if (!result.succeeded)
288 return true;
289
290 const bool event_consumed = true;
291 const bool is_source_touch_event_set_blocking = false;
292 // Block emulated event when emulated native stream is active.
293 if (native_stream_active_sequence_count_) {
294 gesture_provider_->OnTouchEventAck(event.unique_touch_event_id,
295 event_consumed,
296 is_source_touch_event_set_blocking);
297 return true;
298 }
299
300 bool is_sequence_start = WebTouchEventTraits::IsTouchSequenceStart(event);
301 // Do not allow middle-sequence event to pass through, if start was blocked.
302 if (!emulated_stream_active_sequence_count_ && !is_sequence_start) {
303 gesture_provider_->OnTouchEventAck(event.unique_touch_event_id,
304 event_consumed,
305 is_source_touch_event_set_blocking);
306 return true;
307 }
308
309 if (is_sequence_start) {
310 emulated_stream_active_sequence_count_++;
311 last_emulated_start_target_ = target_view;
312 }
313
314 event.moved_beyond_slop_region = result.moved_beyond_slop_region;
315 client_->ForwardEmulatedTouchEvent(event, target_view);
316 return false;
317 }
318
HandleTouchEventAck(const blink::WebTouchEvent & event,blink::mojom::InputEventResultState ack_result)319 bool TouchEmulator::HandleTouchEventAck(
320 const blink::WebTouchEvent& event,
321 blink::mojom::InputEventResultState ack_result) {
322 bool is_sequence_end = WebTouchEventTraits::IsTouchSequenceEnd(event);
323 if (emulated_stream_active_sequence_count_) {
324 if (is_sequence_end)
325 emulated_stream_active_sequence_count_--;
326
327 int taps_count_before = pending_taps_count_;
328 const bool event_consumed =
329 ack_result == blink::mojom::InputEventResultState::kConsumed;
330 if (gesture_provider_) {
331 gesture_provider_->OnTouchEventAck(
332 event.unique_touch_event_id, event_consumed,
333 InputEventResultStateIsSetNonBlocking(ack_result));
334 }
335 if (pending_taps_count_ == taps_count_before)
336 OnInjectedTouchCompleted();
337 return true;
338 }
339
340 // We may have not seen native touch sequence start (when created in the
341 // middle of a sequence), so don't decrement sequence count below zero.
342 if (is_sequence_end && native_stream_active_sequence_count_)
343 native_stream_active_sequence_count_--;
344 return false;
345 }
346
OnGestureEventAck(const WebGestureEvent & event,RenderWidgetHostViewBase *)347 void TouchEmulator::OnGestureEventAck(const WebGestureEvent& event,
348 RenderWidgetHostViewBase*) {
349 if (event.GetType() != WebInputEvent::Type::kGestureTap)
350 return;
351 if (pending_taps_count_) {
352 pending_taps_count_--;
353 OnInjectedTouchCompleted();
354 }
355 }
356
OnViewDestroyed(RenderWidgetHostViewBase * destroyed_view)357 void TouchEmulator::OnViewDestroyed(RenderWidgetHostViewBase* destroyed_view) {
358 if (destroyed_view != last_emulated_start_target_)
359 return;
360
361 emulated_stream_active_sequence_count_ = 0;
362 // If we aren't enabled, just reset out target.
363 if (!gesture_provider_) {
364 last_emulated_start_target_ = nullptr;
365 return;
366 }
367
368 // TouchEmulator is still enabled. To reset the state go through a
369 // Disable/Enable sequence to destroy the GestureRecognizer otherwise it will
370 // be left in an unknown state because the associated view was destroyed.
371 ui::GestureProviderConfigType config_type = gesture_provider_config_type_;
372 Mode mode = mode_;
373 Disable();
374 Enable(mode, config_type);
375 }
376
OnGestureEvent(const ui::GestureEventData & gesture)377 void TouchEmulator::OnGestureEvent(const ui::GestureEventData& gesture) {
378 WebGestureEvent gesture_event =
379 ui::CreateWebGestureEventFromGestureEventData(gesture);
380
381 DCHECK(gesture_event.unique_touch_event_id);
382
383 switch (gesture_event.GetType()) {
384 case WebInputEvent::Type::kUndefined:
385 NOTREACHED() << "Undefined WebInputEvent type";
386 // Bail without sending the junk event to the client.
387 return;
388
389 case WebInputEvent::Type::kGestureScrollBegin:
390 client_->ForwardEmulatedGestureEvent(gesture_event);
391 // PinchBegin must always follow ScrollBegin.
392 if (InPinchGestureMode())
393 PinchBegin(gesture_event);
394 break;
395
396 case WebInputEvent::Type::kGestureScrollUpdate:
397 if (InPinchGestureMode()) {
398 // Convert scrolls to pinches while shift is pressed.
399 if (!pinch_gesture_active_)
400 PinchBegin(gesture_event);
401 else
402 PinchUpdate(gesture_event);
403 } else {
404 // Pass scroll update further. If shift was released, end the pinch.
405 if (pinch_gesture_active_)
406 PinchEnd(gesture_event);
407 client_->ForwardEmulatedGestureEvent(gesture_event);
408 }
409 break;
410
411 case WebInputEvent::Type::kGestureScrollEnd:
412 // PinchEnd must precede ScrollEnd.
413 if (pinch_gesture_active_)
414 PinchEnd(gesture_event);
415 client_->ForwardEmulatedGestureEvent(gesture_event);
416 break;
417
418 case WebInputEvent::Type::kGestureFlingStart:
419 // PinchEnd must precede FlingStart.
420 if (pinch_gesture_active_)
421 PinchEnd(gesture_event);
422 if (InPinchGestureMode()) {
423 // No fling in pinch mode. Forward scroll end instead of fling start.
424 suppress_next_fling_cancel_ = true;
425 ScrollEnd(gesture_event);
426 } else {
427 suppress_next_fling_cancel_ = false;
428 client_->ForwardEmulatedGestureEvent(gesture_event);
429 }
430 break;
431
432 case WebInputEvent::Type::kGestureFlingCancel:
433 // If fling start was suppressed, we should not send fling cancel either.
434 if (!suppress_next_fling_cancel_)
435 client_->ForwardEmulatedGestureEvent(gesture_event);
436 suppress_next_fling_cancel_ = false;
437 break;
438
439 case WebInputEvent::Type::kGestureTap:
440 pending_taps_count_++;
441 client_->ForwardEmulatedGestureEvent(gesture_event);
442 break;
443
444 default:
445 // Everything else goes through.
446 client_->ForwardEmulatedGestureEvent(gesture_event);
447 }
448 }
449
RequiresDoubleTapGestureEvents() const450 bool TouchEmulator::RequiresDoubleTapGestureEvents() const {
451 return true;
452 }
453
InjectTouchEvent(const blink::WebTouchEvent & event,RenderWidgetHostViewBase * target_view,base::OnceClosure callback)454 void TouchEmulator::InjectTouchEvent(const blink::WebTouchEvent& event,
455 RenderWidgetHostViewBase* target_view,
456 base::OnceClosure callback) {
457 DCHECK(enabled() && mode_ == Mode::kInjectingTouchEvents);
458 touch_event_ = event;
459 injected_touch_completion_callbacks_.push(std::move(callback));
460 if (HandleEmulatedTouchEvent(touch_event_, target_view))
461 OnInjectedTouchCompleted();
462 }
463
OnInjectedTouchCompleted()464 void TouchEmulator::OnInjectedTouchCompleted() {
465 if (injected_touch_completion_callbacks_.empty())
466 return;
467 if (!injected_touch_completion_callbacks_.front().is_null())
468 std::move(injected_touch_completion_callbacks_.front()).Run();
469 injected_touch_completion_callbacks_.pop();
470 }
471
CancelTouch()472 void TouchEmulator::CancelTouch() {
473 if (!emulated_stream_active_sequence_count_ || !enabled() ||
474 mode_ != Mode::kEmulatingTouchFromMouse) {
475 return;
476 }
477
478 WebTouchEventTraits::ResetTypeAndTouchStates(
479 WebInputEvent::Type::kTouchCancel, ui::EventTimeForNow(), &touch_event_);
480 DCHECK(gesture_provider_);
481 if (gesture_provider_->GetCurrentDownEvent())
482 HandleEmulatedTouchEvent(touch_event_, last_emulated_start_target_);
483 }
484
UpdateCursor()485 void TouchEmulator::UpdateCursor() {
486 DCHECK(enabled());
487 if (mode_ == Mode::kEmulatingTouchFromMouse)
488 client_->SetCursor(InPinchGestureMode() ? pinch_cursor_ : touch_cursor_);
489 }
490
UpdateShiftPressed(bool shift_pressed)491 bool TouchEmulator::UpdateShiftPressed(bool shift_pressed) {
492 DCHECK(enabled() && mode_ == Mode::kEmulatingTouchFromMouse);
493 if (shift_pressed_ == shift_pressed)
494 return false;
495 shift_pressed_ = shift_pressed;
496 UpdateCursor();
497 return true;
498 }
499
PinchBegin(const WebGestureEvent & event)500 void TouchEmulator::PinchBegin(const WebGestureEvent& event) {
501 DCHECK(InPinchGestureMode());
502 DCHECK(!pinch_gesture_active_);
503 pinch_gesture_active_ = true;
504 pinch_anchor_ = event.PositionInWidget();
505 pinch_scale_ = 1.f;
506 WebGestureEvent pinch_event =
507 GetPinchGestureEvent(WebInputEvent::Type::kGesturePinchBegin, event);
508 client_->ForwardEmulatedGestureEvent(pinch_event);
509 }
510
PinchUpdate(const WebGestureEvent & event)511 void TouchEmulator::PinchUpdate(const WebGestureEvent& event) {
512 DCHECK(pinch_gesture_active_);
513 float dy = pinch_anchor_.y() - event.PositionInWidget().y();
514 float scale = exp(dy * 0.002f);
515 WebGestureEvent pinch_event =
516 GetPinchGestureEvent(WebInputEvent::Type::kGesturePinchUpdate, event);
517 pinch_event.data.pinch_update.scale = scale / pinch_scale_;
518 client_->ForwardEmulatedGestureEvent(pinch_event);
519 pinch_scale_ = scale;
520 }
521
PinchEnd(const WebGestureEvent & event)522 void TouchEmulator::PinchEnd(const WebGestureEvent& event) {
523 DCHECK(pinch_gesture_active_);
524 pinch_gesture_active_ = false;
525 WebGestureEvent pinch_event =
526 GetPinchGestureEvent(WebInputEvent::Type::kGesturePinchEnd, event);
527 client_->ForwardEmulatedGestureEvent(pinch_event);
528 }
529
ScrollEnd(const WebGestureEvent & event)530 void TouchEmulator::ScrollEnd(const WebGestureEvent& event) {
531 WebGestureEvent scroll_event(WebInputEvent::Type::kGestureScrollEnd,
532 ModifiersWithoutMouseButtons(event),
533 event.TimeStamp(),
534 blink::WebGestureDevice::kTouchscreen);
535 scroll_event.unique_touch_event_id = event.unique_touch_event_id;
536 client_->ForwardEmulatedGestureEvent(scroll_event);
537 }
538
GetPinchGestureEvent(WebInputEvent::Type type,const WebGestureEvent & original_event)539 WebGestureEvent TouchEmulator::GetPinchGestureEvent(
540 WebInputEvent::Type type,
541 const WebGestureEvent& original_event) {
542 WebGestureEvent event(type, ModifiersWithoutMouseButtons(original_event),
543 original_event.TimeStamp(),
544 blink::WebGestureDevice::kTouchscreen);
545 event.SetPositionInWidget(pinch_anchor_);
546 event.unique_touch_event_id = original_event.unique_touch_event_id;
547 return event;
548 }
549
FillTouchEventAndPoint(const WebMouseEvent & mouse_event,const gfx::PointF & pos_in_root)550 void TouchEmulator::FillTouchEventAndPoint(const WebMouseEvent& mouse_event,
551 const gfx::PointF& pos_in_root) {
552 WebInputEvent::Type eventType;
553 switch (mouse_event.GetType()) {
554 case WebInputEvent::Type::kMouseDown:
555 eventType = WebInputEvent::Type::kTouchStart;
556 break;
557 case WebInputEvent::Type::kMouseMove:
558 eventType = WebInputEvent::Type::kTouchMove;
559 break;
560 case WebInputEvent::Type::kMouseUp:
561 eventType = WebInputEvent::Type::kTouchEnd;
562 break;
563 default:
564 eventType = WebInputEvent::Type::kUndefined;
565 NOTREACHED() << "Invalid event for touch emulation: "
566 << mouse_event.GetType();
567 }
568 touch_event_.touches_length = 1;
569 touch_event_.SetModifiers(ModifiersWithoutMouseButtons(mouse_event));
570 WebTouchEventTraits::ResetTypeAndTouchStates(
571 eventType, mouse_event.TimeStamp(), &touch_event_);
572 WebTouchPoint& point = touch_event_.touches[0];
573 point.id = 0;
574 point.radius_x = 0.5f * cursor_size_.width();
575 point.radius_y = 0.5f * cursor_size_.height();
576 point.force = eventType == WebInputEvent::Type::kTouchEnd ? 0.f : 1.f;
577 point.rotation_angle = 0.f;
578 // We need to convert this to the root-view's coord space, otherwise the
579 // GestureRecognizer will potentially receive events for a moving widget,
580 // for example when scroll bubbling is taking place. The GestureRecognizer
581 // isn't designed to handle that.
582 point.SetPositionInWidget(pos_in_root);
583 point.SetPositionInScreen(mouse_event.PositionInScreen().x(),
584 mouse_event.PositionInScreen().y());
585 point.tilt_x = 0;
586 point.tilt_y = 0;
587 point.pointer_type = blink::WebPointerProperties::PointerType::kTouch;
588 }
589
InPinchGestureMode() const590 bool TouchEmulator::InPinchGestureMode() const {
591 return shift_pressed_ || pinch_gesture_mode_for_testing_;
592 }
593
SetPinchGestureModeForTesting(bool pinch_gesture_mode)594 void TouchEmulator::SetPinchGestureModeForTesting(bool pinch_gesture_mode) {
595 pinch_gesture_mode_for_testing_ = pinch_gesture_mode;
596 }
597
598 } // namespace content
599