Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(539)

Side by Side Diff: content/browser/renderer_host/input/touch_emulator.cc

Issue 138163016: [DevTools] Touch emulation in content. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Missing files added Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "content/browser/renderer_host/input/motion_event_web.h"
8 #include "content/browser/renderer_host/input/web_input_event_util.h"
9 #include "content/public/common/content_client.h"
10 #include "content/public/common/content_switches.h"
11 #include "grit/content_resources.h"
12 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
13 #include "ui/events/gesture_detection/gesture_config_helper.h"
14 #include "ui/gfx/image/image.h"
15
16 using blink::WebGestureEvent;
17 using blink::WebInputEvent;
18 using blink::WebKeyboardEvent;
19 using blink::WebMouseEvent;
20 using blink::WebMouseWheelEvent;
21 using blink::WebTouchEvent;
22 using blink::WebTouchPoint;
23
24 namespace content {
25
26 TouchEmulator::TouchEmulator(TouchEmulatorClient* client)
27 : client_(client),
28 gesture_provider_(ui::DefaultGestureProviderConfig(), this) {
jdduke (slow) 2014/04/07 18:07:26 We'll need to modify the default config slightly,
dgozman 2014/04/08 16:56:49 Done.
29 Init();
30 }
31
32 TouchEmulator::TouchEmulator(TouchEmulatorClient* client,
33 ui::GestureProvider::Config gesture_config)
34 : client_(client),
35 gesture_provider_(gesture_config, this) {
36 Init();
37 }
38
39 void TouchEmulator::Init() {
40 DCHECK(client_);
41 mouse_pressed_ = false;
42 shift_pressed_ = false;
43 touch_active_ = false;
44 scroll_gesture_active_ = false;
45 fling_start_suppressed_ = false;
46 pinch_scale_ = 1.f;
47 pinch_gesture_active_ = false;
48 InitCursorFromResource(&touch_cursor_, IDR_DEVTOOLS_TOUCH_CURSOR_ICON);
49 InitCursorFromResource(&pinch_cursor_, IDR_DEVTOOLS_PINCH_CURSOR_ICON);
50 UpdateCursor();
51 // TODO(dgozman): Use synthetic secondary touch to support multi-touch.
52 gesture_provider_.SetMultiTouchSupportEnabled(false);
53 // TODO(dgozman): Enable double tap if requested by the renderer.
54 // TODO(dgozman): Don't break double-tap-based pinch with shift handling.
55 gesture_provider_.SetDoubleTapSupportForPlatformEnabled(false);
56 }
57
58 TouchEmulator::~TouchEmulator() {
59 WebCursor::CursorInfo cursor_info;
60 cursor_info.type = blink::WebCursorInfo::TypePointer;
61 WebCursor cursor;
62 cursor.InitFromCursorInfo(cursor_info);
63 client_->SetCursor(cursor);
64
65 if (pinch_gesture_active_) {
66 pinch_event_.timeStampSeconds = base::Time::Now().ToDoubleT();
67 PinchEnd(pinch_event_);
68 }
69 if (scroll_gesture_active_) {
70 pinch_event_.timeStampSeconds = base::Time::Now().ToDoubleT();
71 ScrollEnd(pinch_event_);
72 }
73 // We can also cancel touch, but that only has an effect on gesture provider,
74 // which will be deleted along with emulator anyway.
75 }
76
77 void TouchEmulator::InitCursorFromResource(WebCursor* cursor, int resource_id) {
pfeldman 2014/04/07 17:30:09 Is there anything you could reuse here?
dgozman 2014/04/08 16:56:49 Didn't find anything.
78 gfx::Image& cursor_image =
79 content::GetContentClient()->GetNativeImageNamed(resource_id);
80 WebCursor::CursorInfo cursor_info;
81 cursor_info.type = blink::WebCursorInfo::TypeCustom;
82 // TODO(dgozman): Add HiDPI cursors.
83 cursor_info.image_scale_factor = 1.f;
84 cursor_info.custom_image = cursor_image.AsBitmap();
85 cursor_info.hotspot =
86 gfx::Point(cursor_image.Width() / 2, cursor_image.Height() / 2);
87 #if defined(OS_WIN)
88 cursor_info.external_handle = 0;
89 #endif
90
91 cursor->InitFromCursorInfo(cursor_info);
92 }
93
94 bool TouchEmulator::HandleMouseEvent(const WebMouseEvent& mouse_event) {
95 if (mouse_event.button != WebMouseEvent::ButtonLeft)
96 return true;
97
98 if (mouse_event.type == WebInputEvent::MouseDown)
99 mouse_pressed_ = true;
100 if (mouse_event.type == WebInputEvent::MouseUp)
101 mouse_pressed_ = false;
102
103 UpdateShiftPressed((mouse_event.modifiers & WebInputEvent::ShiftKey) != 0);
104
105 if (FillTouchEventAndPoint(mouse_event) &&
106 gesture_provider_.OnTouchEvent(MotionEventWeb(touch_event_))) {
107 client_->ForwardTouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo());
108 }
109
110 // Do not pass mouse events to the renderer.
111 return true;
112 }
113
114 bool TouchEmulator::HandleMouseWheelEvent(const WebMouseWheelEvent& event) {
115 // No mouse wheel events for the renderer.
116 return true;
117 }
118
119 bool TouchEmulator::HandleKeyboardEvent(const WebKeyboardEvent& event) {
120 if (!UpdateShiftPressed((event.modifiers & WebInputEvent::ShiftKey) != 0))
121 return false;
122
123 if (!mouse_pressed_)
124 return false;
125
126 // Note: The necessary pinch events will be lazily inserted by
127 // |OnGestureEvent| depending on the state of |shift_pressed_|, using the
128 // scroll stream as the event driver.
129 if (shift_pressed_) {
130 // TODO(dgozman): Add secondary touch point and set anchor.
131 } else {
132 // TODO(dgozman): Remove secondary touch point and anchor.
133 }
134
135 // Never block keyboard events.
136 return false;
137 }
138
139 bool TouchEmulator::HandleTouchEventAck(InputEventAckState ack_result) {
140 const bool event_consumed = ack_result == INPUT_EVENT_ACK_STATE_CONSUMED;
141 gesture_provider_.OnTouchEventAck(event_consumed);
142 // TODO(dgozman): Disable emulation when real touch events are available.
143 return true;
144 }
145
146 void TouchEmulator::OnGestureEvent(const ui::GestureEventData& gesture) {
147 WebGestureEvent gesture_event =
148 CreateWebGestureEventFromGestureEventData(gesture, 1);
149
150 // Convert scrolls to pinches while shift is pressed.
151 if (gesture_event.type == WebInputEvent::GestureScrollUpdate) {
152 if (InPinchGestureMode()) {
153 if (!pinch_gesture_active_)
154 PinchBegin(gesture_event);
155 else
156 PinchUpdate(gesture_event);
157 return;
158 } else {
159 if (pinch_gesture_active_)
160 PinchEnd(gesture_event);
161 }
162 }
163
164 if ((gesture_event.type == WebInputEvent::GestureScrollEnd ||
165 gesture_event.type == WebInputEvent::GestureFlingStart)) {
166 scroll_gesture_active_ = false;
167 // PinchEnd must always precede ScrollEnd/FlingStart.
168 if (pinch_gesture_active_)
169 PinchEnd(gesture_event);
170 }
171
172 if (gesture_event.type == WebInputEvent::GestureFlingStart &&
173 InPinchGestureMode()) {
174 // No fling in pinch mode. Forward scroll end instead of fling start.
175 fling_start_suppressed_ = true;
176 ScrollEnd(gesture_event);
177 } else {
178 bool suppress_fling_cancel =
179 gesture_event.type == WebInputEvent::GestureFlingCancel &&
180 fling_start_suppressed_;
181 if (!suppress_fling_cancel)
182 client_->ForwardGestureEvent(gesture_event);
183 if (gesture_event.type == WebInputEvent::GestureFlingStart ||
184 gesture_event.type == WebInputEvent::GestureFlingCancel)
185 fling_start_suppressed_ = false;
186 // TODO(dgozman): sometimes we hit a race, where keyboard event cancels
187 // fling in browser process, but does not in renderer process.
188 }
189
190 if (gesture_event.type == WebInputEvent::GestureScrollBegin) {
191 scroll_gesture_active_ = true;
192 // PinchBegin must always follow ScrollBegin.
193 if (InPinchGestureMode())
194 PinchBegin(gesture_event);
195 }
196 }
197
198 void TouchEmulator::CancelTouch() {
199 if (!touch_active_)
200 return;
201
202 touch_event_.timeStampSeconds = base::Time::Now().ToDoubleT();
203 touch_event_.type = WebInputEvent::TouchCancel;
204 touch_event_.touches[0].state = WebTouchPoint::StateCancelled;
205 touch_active_ = false;
206 if (gesture_provider_.OnTouchEvent(MotionEventWeb(touch_event_)))
207 client_->ForwardTouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo());
208 }
209
210 void TouchEmulator::UpdateCursor() {
211 client_->SetCursor(InPinchGestureMode() ? pinch_cursor_ : touch_cursor_);
212 }
213
214 bool TouchEmulator::UpdateShiftPressed(bool shift_pressed) {
215 if (shift_pressed_ == shift_pressed)
216 return false;
217 shift_pressed_ = shift_pressed;
218 UpdateCursor();
219 return true;
220 }
221
222 void TouchEmulator::PinchBegin(const WebGestureEvent& event) {
223 DCHECK(InPinchGestureMode());
224 DCHECK(!pinch_gesture_active_);
225 pinch_gesture_active_ = true;
226 pinch_anchor_ = gfx::Point(event.x, event.y);
227 pinch_scale_ = 1.f;
228 FillPinchEvent(event);
229 pinch_event_.type = WebInputEvent::GesturePinchBegin;
230 client_->ForwardGestureEvent(pinch_event_);
231 }
232
233 void TouchEmulator::PinchUpdate(const WebGestureEvent& event) {
234 DCHECK(pinch_gesture_active_);
235 int dy = pinch_anchor_.y() - event.y;
236 float scale = exp(dy * 0.002f);
237 FillPinchEvent(event);
238 pinch_event_.type = WebInputEvent::GesturePinchUpdate;
239 pinch_event_.data.pinchUpdate.scale = scale / pinch_scale_;
240 client_->ForwardGestureEvent(pinch_event_);
241 pinch_scale_ = scale;
242 }
243
244 void TouchEmulator::PinchEnd(const WebGestureEvent& event) {
245 DCHECK(pinch_gesture_active_);
246 pinch_gesture_active_ = false;
247 FillPinchEvent(event);
248 pinch_event_.type = WebInputEvent::GesturePinchEnd;
249 client_->ForwardGestureEvent(pinch_event_);
250 }
251
252 void TouchEmulator::FillPinchEvent(const WebInputEvent& event) {
253 pinch_event_.timeStampSeconds = event.timeStampSeconds;
254 pinch_event_.modifiers = event.modifiers;
255 pinch_event_.sourceDevice = blink::WebGestureEvent::Touchscreen;
256 pinch_event_.x = pinch_anchor_.x();
257 pinch_event_.y = pinch_anchor_.y();
258 }
259
260 void TouchEmulator::ScrollEnd(const WebGestureEvent& event) {
261 WebGestureEvent scroll_event;
262 scroll_event.timeStampSeconds = event.timeStampSeconds;
263 scroll_event.modifiers = event.modifiers;
264 scroll_event.sourceDevice = blink::WebGestureEvent::Touchscreen;
265 scroll_event.type = WebInputEvent::GestureScrollEnd;
266 client_->ForwardGestureEvent(scroll_event);
267 }
268
269 bool TouchEmulator::FillTouchEventAndPoint(const WebMouseEvent& mouse_event) {
270 if (mouse_event.type != WebInputEvent::MouseDown &&
271 mouse_event.type != WebInputEvent::MouseMove &&
272 mouse_event.type != WebInputEvent::MouseUp) {
273 return false;
274 }
275
276 touch_event_.touchesLength = 1;
277 touch_event_.timeStampSeconds = mouse_event.timeStampSeconds;
278 touch_event_.modifiers = mouse_event.modifiers;
279
280 WebTouchPoint& point = touch_event_.touches[0];
281 point.id = 0;
282 point.radiusX = point.radiusY = 1.f;
283 point.force = 1.f;
284 point.rotationAngle = 0.f;
285 point.position.x = mouse_event.x;
286 point.screenPosition.x = mouse_event.globalX;
287 point.position.y = mouse_event.y;
288 point.screenPosition.y = mouse_event.globalY;
289
290 switch (mouse_event.type) {
291 case WebInputEvent::MouseDown:
292 touch_event_.type = WebInputEvent::TouchStart;
293 touch_active_ = true;
294 point.state = WebTouchPoint::StatePressed;
295 break;
296 case WebInputEvent::MouseMove:
297 touch_event_.type = WebInputEvent::TouchMove;
298 point.state = WebTouchPoint::StateMoved;
299 break;
300 case WebInputEvent::MouseUp:
301 touch_event_.type = WebInputEvent::TouchEnd;
302 touch_active_ = false;
303 point.state = WebTouchPoint::StateReleased;
304 break;
305 default:
306 NOTREACHED();
307 }
308 return true;
309 }
310
311 bool TouchEmulator::InPinchGestureMode() const {
312 return shift_pressed_;
313 }
314
315 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698