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.
5 #include "content/browser/devtools/protocol/input_handler.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/trace_event/trace_event.h"
11 #include "cc/output/compositor_frame_metadata.h"
12 #include "content/browser/renderer_host/render_widget_host_impl.h"
13 #include "content/common/input/synthetic_pinch_gesture_params.h"
14 #include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
15 #include "content/common/input/synthetic_tap_gesture_params.h"
16 #include "third_party/WebKit/public/web/WebInputEvent.h"
17 #include "ui/events/keycodes/dom/keycode_converter.h"
18 #include "ui/gfx/geometry/point.h"
26 gfx::Point
CssPixelsToPoint(int x
, int y
, float page_scale_factor
) {
27 return gfx::Point(x
* page_scale_factor
, y
* page_scale_factor
);
30 gfx::Vector2d
CssPixelsToVector2d(int x
, int y
, float page_scale_factor
) {
31 return gfx::Vector2d(x
* page_scale_factor
, y
* page_scale_factor
);
34 bool StringToGestureSourceType(const std::string
& in
,
35 SyntheticGestureParams::GestureSourceType
& out
) {
36 if (in
== kGestureSourceTypeDefault
) {
37 out
= SyntheticGestureParams::GestureSourceType::DEFAULT_INPUT
;
39 } else if (in
== kGestureSourceTypeTouch
) {
40 out
= SyntheticGestureParams::GestureSourceType::TOUCH_INPUT
;
42 } else if (in
== kGestureSourceTypeMouse
) {
43 out
= SyntheticGestureParams::GestureSourceType::MOUSE_INPUT
;
52 typedef DevToolsProtocolClient::Response Response
;
56 void SetEventModifiers(blink::WebInputEvent
* event
, const int* modifiers
) {
60 event
->modifiers
|= blink::WebInputEvent::AltKey
;
62 event
->modifiers
|= blink::WebInputEvent::ControlKey
;
64 event
->modifiers
|= blink::WebInputEvent::MetaKey
;
66 event
->modifiers
|= blink::WebInputEvent::ShiftKey
;
69 void SetEventTimestamp(blink::WebInputEvent
* event
, const double* timestamp
) {
70 event
->timeStampSeconds
=
71 timestamp
? *timestamp
: base::Time::Now().ToDoubleT();
74 bool SetKeyboardEventText(blink::WebUChar
* to
, const std::string
* from
) {
78 base::string16 text16
= base::UTF8ToUTF16(*from
);
79 if (text16
.size() > blink::WebKeyboardEvent::textLengthCap
)
82 for (size_t i
= 0; i
< text16
.size(); ++i
)
87 bool SetMouseEventButton(blink::WebMouseEvent
* event
,
88 const std::string
* button
) {
92 if (*button
== dispatch_mouse_event::kButtonNone
) {
93 event
->button
= blink::WebMouseEvent::ButtonNone
;
94 } else if (*button
== dispatch_mouse_event::kButtonLeft
) {
95 event
->button
= blink::WebMouseEvent::ButtonLeft
;
96 event
->modifiers
|= blink::WebInputEvent::LeftButtonDown
;
97 } else if (*button
== dispatch_mouse_event::kButtonMiddle
) {
98 event
->button
= blink::WebMouseEvent::ButtonMiddle
;
99 event
->modifiers
|= blink::WebInputEvent::MiddleButtonDown
;
100 } else if (*button
== dispatch_mouse_event::kButtonRight
) {
101 event
->button
= blink::WebMouseEvent::ButtonRight
;
102 event
->modifiers
|= blink::WebInputEvent::RightButtonDown
;
109 bool SetMouseEventType(blink::WebMouseEvent
* event
, const std::string
& type
) {
110 if (type
== dispatch_mouse_event::kTypeMousePressed
) {
111 event
->type
= blink::WebInputEvent::MouseDown
;
112 } else if (type
== dispatch_mouse_event::kTypeMouseReleased
) {
113 event
->type
= blink::WebInputEvent::MouseUp
;
114 } else if (type
== dispatch_mouse_event::kTypeMouseMoved
) {
115 event
->type
= blink::WebInputEvent::MouseMove
;
124 InputHandler::InputHandler()
126 page_scale_factor_(1.0),
127 weak_factory_(this) {
130 InputHandler::~InputHandler() {
133 void InputHandler::SetRenderWidgetHost(RenderWidgetHostImpl
* host
) {
137 void InputHandler::SetClient(scoped_ptr
<Client
> client
) {
138 client_
.swap(client
);
141 void InputHandler::OnSwapCompositorFrame(
142 const cc::CompositorFrameMetadata
& frame_metadata
) {
143 page_scale_factor_
= frame_metadata
.page_scale_factor
;
144 scrollable_viewport_size_
= frame_metadata
.scrollable_viewport_size
;
147 Response
InputHandler::DispatchKeyEvent(
148 const std::string
& type
,
149 const int* modifiers
,
150 const double* timestamp
,
151 const std::string
* text
,
152 const std::string
* unmodified_text
,
153 const std::string
* key_identifier
,
154 const std::string
* code
,
155 const std::string
* key
,
156 const int* windows_virtual_key_code
,
157 const int* native_virtual_key_code
,
158 const bool* auto_repeat
,
159 const bool* is_keypad
,
160 const bool* is_system_key
) {
161 NativeWebKeyboardEvent event
;
163 if (type
== dispatch_key_event::kTypeKeyDown
) {
164 event
.type
= blink::WebInputEvent::KeyDown
;
165 } else if (type
== dispatch_key_event::kTypeKeyUp
) {
166 event
.type
= blink::WebInputEvent::KeyUp
;
167 } else if (type
== dispatch_key_event::kTypeChar
) {
168 event
.type
= blink::WebInputEvent::Char
;
169 } else if (type
== dispatch_key_event::kTypeRawKeyDown
) {
170 event
.type
= blink::WebInputEvent::RawKeyDown
;
172 return Response::InvalidParams(
173 base::StringPrintf("Unexpected event type '%s'", type
.c_str()));
176 SetEventModifiers(&event
, modifiers
);
177 SetEventTimestamp(&event
, timestamp
);
178 if (!SetKeyboardEventText(event
.text
, text
))
179 return Response::InvalidParams("Invalid 'text' parameter");
180 if (!SetKeyboardEventText(event
.unmodifiedText
, unmodified_text
))
181 return Response::InvalidParams("Invalid 'unmodifiedText' parameter");
183 if (windows_virtual_key_code
)
184 event
.windowsKeyCode
= *windows_virtual_key_code
;
185 if (native_virtual_key_code
)
186 event
.nativeKeyCode
= *native_virtual_key_code
;
187 if (auto_repeat
&& *auto_repeat
)
188 event
.modifiers
|= blink::WebInputEvent::IsAutoRepeat
;
189 if (is_keypad
&& *is_keypad
)
190 event
.modifiers
|= blink::WebInputEvent::IsKeyPad
;
192 event
.isSystemKey
= *is_system_key
;
194 if (key_identifier
) {
195 if (key_identifier
->size() >
196 blink::WebKeyboardEvent::keyIdentifierLengthCap
) {
197 return Response::InvalidParams("Invalid 'keyIdentifier' parameter");
199 for (size_t i
= 0; i
< key_identifier
->size(); ++i
)
200 event
.keyIdentifier
[i
] = (*key_identifier
)[i
];
201 } else if (event
.type
!= blink::WebInputEvent::Char
) {
202 event
.setKeyIdentifierFromWindowsKeyCode();
206 event
.domCode
= static_cast<int>(
207 ui::KeycodeConverter::CodeStringToDomCode(code
->c_str()));
211 event
.domKey
= static_cast<int>(
212 ui::KeycodeConverter::KeyStringToDomKey(key
->c_str()));
216 return Response::ServerError("Could not connect to view");
219 host_
->ForwardKeyboardEvent(event
);
220 return Response::OK();
223 Response
InputHandler::DispatchMouseEvent(
224 const std::string
& type
,
227 const int* modifiers
,
228 const double* timestamp
,
229 const std::string
* button
,
230 const int* click_count
) {
231 blink::WebMouseEvent event
;
233 if (!SetMouseEventType(&event
, type
)) {
234 return Response::InvalidParams(
235 base::StringPrintf("Unexpected event type '%s'", type
.c_str()));
237 SetEventModifiers(&event
, modifiers
);
238 SetEventTimestamp(&event
, timestamp
);
239 if (!SetMouseEventButton(&event
, button
))
240 return Response::InvalidParams("Invalid mouse button");
242 event
.x
= x
* page_scale_factor_
;
243 event
.y
= y
* page_scale_factor_
;
244 event
.windowX
= x
* page_scale_factor_
;
245 event
.windowY
= y
* page_scale_factor_
;
246 event
.globalX
= x
* page_scale_factor_
;
247 event
.globalY
= y
* page_scale_factor_
;
248 event
.clickCount
= click_count
? *click_count
: 0;
251 return Response::ServerError("Could not connect to view");
254 host_
->ForwardMouseEvent(event
);
255 return Response::OK();
258 Response
InputHandler::EmulateTouchFromMouseEvent(const std::string
& type
,
262 const std::string
& button
,
267 blink::WebMouseWheelEvent wheel_event
;
268 blink::WebMouseEvent mouse_event
;
269 blink::WebMouseEvent
* event
= &mouse_event
;
271 if (type
== emulate_touch_from_mouse_event::kTypeMouseWheel
) {
272 if (!delta_x
|| !delta_y
) {
273 return Response::InvalidParams(
274 "'deltaX' and 'deltaY' are expected for mouseWheel event");
276 wheel_event
.deltaX
= static_cast<float>(*delta_x
);
277 wheel_event
.deltaY
= static_cast<float>(*delta_y
);
278 event
= &wheel_event
;
279 event
->type
= blink::WebInputEvent::MouseWheel
;
280 } else if (!SetMouseEventType(event
, type
)) {
281 return Response::InvalidParams(
282 base::StringPrintf("Unexpected event type '%s'", type
.c_str()));
285 SetEventModifiers(event
, modifiers
);
286 SetEventTimestamp(event
, ×tamp
);
287 if (!SetMouseEventButton(event
, &button
))
288 return Response::InvalidParams("Invalid mouse button");
296 event
->clickCount
= click_count
? *click_count
: 0;
299 return Response::ServerError("Could not connect to view");
301 if (event
->type
== blink::WebInputEvent::MouseWheel
)
302 host_
->ForwardWheelEvent(wheel_event
);
304 host_
->ForwardMouseEvent(mouse_event
);
305 return Response::OK();
308 Response
InputHandler::SynthesizePinchGesture(
309 DevToolsCommandId command_id
,
313 const int* relative_speed
,
314 const std::string
* gesture_source_type
) {
316 return Response::ServerError("Could not connect to view");
318 SyntheticPinchGestureParams gesture_params
;
319 const int kDefaultRelativeSpeed
= 800;
321 gesture_params
.scale_factor
= scale_factor
;
322 gesture_params
.anchor
= CssPixelsToPoint(x
, y
, page_scale_factor_
);
323 gesture_params
.relative_pointer_speed_in_pixels_s
=
324 relative_speed
? *relative_speed
: kDefaultRelativeSpeed
;
326 if (!StringToGestureSourceType(
327 gesture_source_type
? *gesture_source_type
: kGestureSourceTypeDefault
,
328 gesture_params
.gesture_source_type
)) {
329 return Response::InvalidParams("gestureSourceType");
332 host_
->QueueSyntheticGesture(
333 SyntheticGesture::Create(gesture_params
),
334 base::Bind(&InputHandler::SendSynthesizePinchGestureResponse
,
335 weak_factory_
.GetWeakPtr(), command_id
));
337 return Response::OK();
340 Response
InputHandler::SynthesizeScrollGesture(
341 DevToolsCommandId command_id
,
344 const int* x_distance
,
345 const int* y_distance
,
346 const int* x_overscroll
,
347 const int* y_overscroll
,
348 const bool* prevent_fling
,
350 const std::string
* gesture_source_type
,
351 const int* repeat_count
,
352 const int* repeat_delay_ms
,
353 const std::string
* interaction_marker_name
) {
355 return Response::ServerError("Could not connect to view");
357 SyntheticSmoothScrollGestureParams gesture_params
;
358 const bool kDefaultPreventFling
= true;
359 const int kDefaultSpeed
= 800;
361 gesture_params
.anchor
= CssPixelsToPoint(x
, y
, page_scale_factor_
);
362 gesture_params
.prevent_fling
=
363 prevent_fling
? *prevent_fling
: kDefaultPreventFling
;
364 gesture_params
.speed_in_pixels_s
= speed
? *speed
: kDefaultSpeed
;
366 if (x_distance
|| y_distance
) {
367 gesture_params
.distances
.push_back(
368 CssPixelsToVector2d(x_distance
? *x_distance
: 0,
369 y_distance
? *y_distance
: 0,
370 page_scale_factor_
));
373 if (x_overscroll
|| y_overscroll
) {
374 gesture_params
.distances
.push_back(
375 CssPixelsToVector2d(x_overscroll
? -*x_overscroll
: 0,
376 y_overscroll
? -*y_overscroll
: 0,
377 page_scale_factor_
));
380 if (!StringToGestureSourceType(
381 gesture_source_type
? *gesture_source_type
: kGestureSourceTypeDefault
,
382 gesture_params
.gesture_source_type
)) {
383 return Response::InvalidParams("gestureSourceType");
386 SynthesizeRepeatingScroll(
387 gesture_params
, repeat_count
? *repeat_count
: 0,
388 base::TimeDelta::FromMilliseconds(repeat_delay_ms
? *repeat_delay_ms
390 interaction_marker_name
? *interaction_marker_name
: "", command_id
);
392 return Response::OK();
395 void InputHandler::SynthesizeRepeatingScroll(
396 SyntheticSmoothScrollGestureParams gesture_params
,
398 base::TimeDelta repeat_delay
,
399 std::string interaction_marker_name
,
400 DevToolsCommandId command_id
) {
401 if (!interaction_marker_name
.empty()) {
402 // TODO(alexclarke): Can we move this elsewhere? It doesn't really fit here.
403 TRACE_EVENT_COPY_ASYNC_BEGIN0("benchmark",
404 interaction_marker_name
.c_str(), command_id
);
407 host_
->QueueSyntheticGesture(
408 SyntheticGesture::Create(gesture_params
),
409 base::Bind(&InputHandler::OnScrollFinished
, weak_factory_
.GetWeakPtr(),
410 gesture_params
, repeat_count
, repeat_delay
,
411 interaction_marker_name
, command_id
));
414 void InputHandler::OnScrollFinished(
415 SyntheticSmoothScrollGestureParams gesture_params
,
417 base::TimeDelta repeat_delay
,
418 std::string interaction_marker_name
,
419 DevToolsCommandId command_id
,
420 SyntheticGesture::Result result
) {
421 if (!interaction_marker_name
.empty()) {
422 TRACE_EVENT_COPY_ASYNC_END0("benchmark",
423 interaction_marker_name
.c_str(), command_id
);
426 if (repeat_count
> 0) {
427 base::MessageLoop::current()->task_runner()->PostDelayedTask(
429 base::Bind(&InputHandler::SynthesizeRepeatingScroll
,
430 weak_factory_
.GetWeakPtr(), gesture_params
, repeat_count
- 1,
431 repeat_delay
, interaction_marker_name
, command_id
),
434 SendSynthesizeScrollGestureResponse(command_id
, result
);
438 Response
InputHandler::SynthesizeTapGesture(
439 DevToolsCommandId command_id
,
443 const int* tap_count
,
444 const std::string
* gesture_source_type
) {
446 return Response::ServerError("Could not connect to view");
448 SyntheticTapGestureParams gesture_params
;
449 const int kDefaultDuration
= 50;
450 const int kDefaultTapCount
= 1;
452 gesture_params
.position
= CssPixelsToPoint(x
, y
, page_scale_factor_
);
453 gesture_params
.duration_ms
= duration
? *duration
: kDefaultDuration
;
455 if (!StringToGestureSourceType(
456 gesture_source_type
? *gesture_source_type
: kGestureSourceTypeDefault
,
457 gesture_params
.gesture_source_type
)) {
458 return Response::InvalidParams("gestureSourceType");
462 tap_count
= &kDefaultTapCount
;
464 for (int i
= 0; i
< *tap_count
; i
++) {
465 // If we're doing more than one tap, don't send the response to the client
466 // until we've completed the last tap.
467 bool is_last_tap
= i
== *tap_count
- 1;
468 host_
->QueueSyntheticGesture(
469 SyntheticGesture::Create(gesture_params
),
470 base::Bind(&InputHandler::SendSynthesizeTapGestureResponse
,
471 weak_factory_
.GetWeakPtr(), command_id
, is_last_tap
));
474 return Response::OK();
477 void InputHandler::SendSynthesizePinchGestureResponse(
478 DevToolsCommandId command_id
,
479 SyntheticGesture::Result result
) {
480 if (result
== SyntheticGesture::Result::GESTURE_FINISHED
) {
481 client_
->SendSynthesizePinchGestureResponse(
482 command_id
, SynthesizePinchGestureResponse::Create());
484 client_
->SendError(command_id
,
485 Response::InternalError(base::StringPrintf(
486 "Synthetic pinch failed, result was %d", result
)));
490 void InputHandler::SendSynthesizeScrollGestureResponse(
491 DevToolsCommandId command_id
,
492 SyntheticGesture::Result result
) {
493 if (result
== SyntheticGesture::Result::GESTURE_FINISHED
) {
494 client_
->SendSynthesizeScrollGestureResponse(
495 command_id
, SynthesizeScrollGestureResponse::Create());
497 client_
->SendError(command_id
,
498 Response::InternalError(base::StringPrintf(
499 "Synthetic scroll failed, result was %d", result
)));
503 void InputHandler::SendSynthesizeTapGestureResponse(
504 DevToolsCommandId command_id
,
506 SyntheticGesture::Result result
) {
507 if (result
== SyntheticGesture::Result::GESTURE_FINISHED
) {
509 client_
->SendSynthesizeTapGestureResponse(
510 command_id
, SynthesizeTapGestureResponse::Create());
513 client_
->SendError(command_id
,
514 Response::InternalError(base::StringPrintf(
515 "Synthetic tap failed, result was %d", result
)));
520 } // namespace devtools
521 } // namespace content