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