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/strings/stringprintf.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "cc/output/compositor_frame_metadata.h"
10 #include "content/browser/renderer_host/render_widget_host_impl.h"
11 #include "content/common/input/synthetic_pinch_gesture_params.h"
12 #include "content/common/input/synthetic_smooth_scroll_gesture_params.h"
13 #include "content/common/input/synthetic_tap_gesture_params.h"
14 #include "third_party/WebKit/public/web/WebInputEvent.h"
15 #include "ui/events/keycodes/dom/keycode_converter.h"
16 #include "ui/gfx/geometry/point.h"
24 gfx::Point
CssPixelsToPoint(int x
, int y
, float page_scale_factor
) {
25 return gfx::Point(x
* page_scale_factor
, y
* page_scale_factor
);
28 gfx::Vector2d
CssPixelsToVector2d(int x
, int y
, float page_scale_factor
) {
29 return gfx::Vector2d(x
* page_scale_factor
, y
* page_scale_factor
);
32 bool StringToGestureSourceType(const std::string
& in
,
33 SyntheticGestureParams::GestureSourceType
& out
) {
34 if (in
== kGestureSourceTypeDefault
) {
35 out
= SyntheticGestureParams::GestureSourceType::DEFAULT_INPUT
;
37 } else if (in
== kGestureSourceTypeTouch
) {
38 out
= SyntheticGestureParams::GestureSourceType::TOUCH_INPUT
;
40 } else if (in
== kGestureSourceTypeMouse
) {
41 out
= SyntheticGestureParams::GestureSourceType::MOUSE_INPUT
;
50 typedef DevToolsProtocolClient::Response Response
;
54 void SetEventModifiers(blink::WebInputEvent
* event
, const int* modifiers
) {
58 event
->modifiers
|= blink::WebInputEvent::AltKey
;
60 event
->modifiers
|= blink::WebInputEvent::ControlKey
;
62 event
->modifiers
|= blink::WebInputEvent::MetaKey
;
64 event
->modifiers
|= blink::WebInputEvent::ShiftKey
;
67 void SetEventTimestamp(blink::WebInputEvent
* event
, const double* timestamp
) {
68 event
->timeStampSeconds
=
69 timestamp
? *timestamp
: base::Time::Now().ToDoubleT();
72 bool SetKeyboardEventText(blink::WebUChar
* to
, const std::string
* from
) {
76 base::string16 text16
= base::UTF8ToUTF16(*from
);
77 if (text16
.size() > blink::WebKeyboardEvent::textLengthCap
)
80 for (size_t i
= 0; i
< text16
.size(); ++i
)
85 bool SetMouseEventButton(blink::WebMouseEvent
* event
,
86 const std::string
* button
) {
90 if (*button
== dispatch_mouse_event::kButtonNone
) {
91 event
->button
= blink::WebMouseEvent::ButtonNone
;
92 } else if (*button
== dispatch_mouse_event::kButtonLeft
) {
93 event
->button
= blink::WebMouseEvent::ButtonLeft
;
94 event
->modifiers
|= blink::WebInputEvent::LeftButtonDown
;
95 } else if (*button
== dispatch_mouse_event::kButtonMiddle
) {
96 event
->button
= blink::WebMouseEvent::ButtonMiddle
;
97 event
->modifiers
|= blink::WebInputEvent::MiddleButtonDown
;
98 } else if (*button
== dispatch_mouse_event::kButtonRight
) {
99 event
->button
= blink::WebMouseEvent::ButtonRight
;
100 event
->modifiers
|= blink::WebInputEvent::RightButtonDown
;
107 bool SetMouseEventType(blink::WebMouseEvent
* event
, const std::string
& type
) {
108 if (type
== dispatch_mouse_event::kTypeMousePressed
) {
109 event
->type
= blink::WebInputEvent::MouseDown
;
110 } else if (type
== dispatch_mouse_event::kTypeMouseReleased
) {
111 event
->type
= blink::WebInputEvent::MouseUp
;
112 } else if (type
== dispatch_mouse_event::kTypeMouseMoved
) {
113 event
->type
= blink::WebInputEvent::MouseMove
;
122 InputHandler::InputHandler()
124 page_scale_factor_(1.0),
125 weak_factory_(this) {
128 InputHandler::~InputHandler() {
131 void InputHandler::SetRenderWidgetHost(RenderWidgetHostImpl
* host
) {
135 void InputHandler::SetClient(scoped_ptr
<Client
> client
) {
136 client_
.swap(client
);
139 void InputHandler::OnSwapCompositorFrame(
140 const cc::CompositorFrameMetadata
& frame_metadata
) {
141 page_scale_factor_
= frame_metadata
.page_scale_factor
;
142 scrollable_viewport_size_
= frame_metadata
.scrollable_viewport_size
;
145 Response
InputHandler::DispatchKeyEvent(
146 const std::string
& type
,
147 const int* modifiers
,
148 const double* timestamp
,
149 const std::string
* text
,
150 const std::string
* unmodified_text
,
151 const std::string
* key_identifier
,
152 const std::string
* code
,
153 const std::string
* key
,
154 const int* windows_virtual_key_code
,
155 const int* native_virtual_key_code
,
156 const bool* auto_repeat
,
157 const bool* is_keypad
,
158 const bool* is_system_key
) {
159 NativeWebKeyboardEvent event
;
161 if (type
== dispatch_key_event::kTypeKeyDown
) {
162 event
.type
= blink::WebInputEvent::KeyDown
;
163 } else if (type
== dispatch_key_event::kTypeKeyUp
) {
164 event
.type
= blink::WebInputEvent::KeyUp
;
165 } else if (type
== dispatch_key_event::kTypeChar
) {
166 event
.type
= blink::WebInputEvent::Char
;
167 } else if (type
== dispatch_key_event::kTypeRawKeyDown
) {
168 event
.type
= blink::WebInputEvent::RawKeyDown
;
170 return Response::InvalidParams(
171 base::StringPrintf("Unexpected event type '%s'", type
.c_str()));
174 SetEventModifiers(&event
, modifiers
);
175 SetEventTimestamp(&event
, timestamp
);
176 if (!SetKeyboardEventText(event
.text
, text
))
177 return Response::InvalidParams("Invalid 'text' parameter");
178 if (!SetKeyboardEventText(event
.unmodifiedText
, unmodified_text
))
179 return Response::InvalidParams("Invalid 'unmodifiedText' parameter");
181 if (windows_virtual_key_code
)
182 event
.windowsKeyCode
= *windows_virtual_key_code
;
183 if (native_virtual_key_code
)
184 event
.nativeKeyCode
= *native_virtual_key_code
;
185 if (auto_repeat
&& *auto_repeat
)
186 event
.modifiers
|= blink::WebInputEvent::IsAutoRepeat
;
187 if (is_keypad
&& *is_keypad
)
188 event
.modifiers
|= blink::WebInputEvent::IsKeyPad
;
190 event
.isSystemKey
= *is_system_key
;
192 if (key_identifier
) {
193 if (key_identifier
->size() >
194 blink::WebKeyboardEvent::keyIdentifierLengthCap
) {
195 return Response::InvalidParams("Invalid 'keyIdentifier' parameter");
197 for (size_t i
= 0; i
< key_identifier
->size(); ++i
)
198 event
.keyIdentifier
[i
] = (*key_identifier
)[i
];
199 } else if (event
.type
!= blink::WebInputEvent::Char
) {
200 event
.setKeyIdentifierFromWindowsKeyCode();
204 event
.domCode
= static_cast<int>(
205 ui::KeycodeConverter::CodeStringToDomCode(code
->c_str()));
209 event
.domKey
= static_cast<int>(
210 ui::KeycodeConverter::KeyStringToDomKey(key
->c_str()));
214 return Response::ServerError("Could not connect to view");
217 host_
->ForwardKeyboardEvent(event
);
218 return Response::OK();
221 Response
InputHandler::DispatchMouseEvent(
222 const std::string
& type
,
225 const int* modifiers
,
226 const double* timestamp
,
227 const std::string
* button
,
228 const int* click_count
) {
229 blink::WebMouseEvent event
;
231 if (!SetMouseEventType(&event
, type
)) {
232 return Response::InvalidParams(
233 base::StringPrintf("Unexpected event type '%s'", type
.c_str()));
235 SetEventModifiers(&event
, modifiers
);
236 SetEventTimestamp(&event
, timestamp
);
237 if (!SetMouseEventButton(&event
, button
))
238 return Response::InvalidParams("Invalid mouse button");
240 event
.x
= x
* page_scale_factor_
;
241 event
.y
= y
* page_scale_factor_
;
242 event
.windowX
= x
* page_scale_factor_
;
243 event
.windowY
= y
* page_scale_factor_
;
244 event
.globalX
= x
* page_scale_factor_
;
245 event
.globalY
= y
* page_scale_factor_
;
246 event
.clickCount
= click_count
? *click_count
: 0;
249 return Response::ServerError("Could not connect to view");
252 host_
->ForwardMouseEvent(event
);
253 return Response::OK();
256 Response
InputHandler::EmulateTouchFromMouseEvent(const std::string
& type
,
260 const std::string
& button
,
265 blink::WebMouseWheelEvent wheel_event
;
266 blink::WebMouseEvent mouse_event
;
267 blink::WebMouseEvent
* event
= &mouse_event
;
269 if (type
== emulate_touch_from_mouse_event::kTypeMouseWheel
) {
270 if (!delta_x
|| !delta_y
) {
271 return Response::InvalidParams(
272 "'deltaX' and 'deltaY' are expected for mouseWheel event");
274 wheel_event
.deltaX
= static_cast<float>(*delta_x
);
275 wheel_event
.deltaY
= static_cast<float>(*delta_y
);
276 event
= &wheel_event
;
277 event
->type
= blink::WebInputEvent::MouseWheel
;
278 } else if (!SetMouseEventType(event
, type
)) {
279 return Response::InvalidParams(
280 base::StringPrintf("Unexpected event type '%s'", type
.c_str()));
283 SetEventModifiers(event
, modifiers
);
284 SetEventTimestamp(event
, ×tamp
);
285 if (!SetMouseEventButton(event
, &button
))
286 return Response::InvalidParams("Invalid mouse button");
294 event
->clickCount
= click_count
? *click_count
: 0;
297 return Response::ServerError("Could not connect to view");
299 if (event
->type
== blink::WebInputEvent::MouseWheel
)
300 host_
->ForwardWheelEvent(wheel_event
);
302 host_
->ForwardMouseEvent(mouse_event
);
303 return Response::OK();
306 Response
InputHandler::SynthesizePinchGesture(
307 DevToolsCommandId command_id
,
311 const int* relative_speed
,
312 const std::string
* gesture_source_type
) {
314 return Response::ServerError("Could not connect to view");
316 SyntheticPinchGestureParams gesture_params
;
317 const int kDefaultRelativeSpeed
= 800;
319 gesture_params
.scale_factor
= scale_factor
;
320 gesture_params
.anchor
= CssPixelsToPoint(x
, y
, page_scale_factor_
);
321 gesture_params
.relative_pointer_speed_in_pixels_s
=
322 relative_speed
? *relative_speed
: kDefaultRelativeSpeed
;
324 if (!StringToGestureSourceType(
325 gesture_source_type
? *gesture_source_type
: kGestureSourceTypeDefault
,
326 gesture_params
.gesture_source_type
)) {
327 return Response::InvalidParams("gestureSourceType");
330 host_
->QueueSyntheticGesture(
331 SyntheticGesture::Create(gesture_params
),
332 base::Bind(&InputHandler::SendSynthesizePinchGestureResponse
,
333 weak_factory_
.GetWeakPtr(), command_id
));
335 return Response::OK();
338 Response
InputHandler::SynthesizeScrollGesture(
339 DevToolsCommandId command_id
,
342 const int* x_distance
,
343 const int* y_distance
,
344 const int* x_overscroll
,
345 const int* y_overscroll
,
346 const bool* prevent_fling
,
348 const std::string
* gesture_source_type
) {
350 return Response::ServerError("Could not connect to view");
352 SyntheticSmoothScrollGestureParams gesture_params
;
353 const bool kDefaultPreventFling
= true;
354 const int kDefaultSpeed
= 800;
356 gesture_params
.anchor
= CssPixelsToPoint(x
, y
, page_scale_factor_
);
357 gesture_params
.prevent_fling
=
358 prevent_fling
? *prevent_fling
: kDefaultPreventFling
;
359 gesture_params
.speed_in_pixels_s
= speed
? *speed
: kDefaultSpeed
;
361 if (x_distance
|| y_distance
) {
362 gesture_params
.distances
.push_back(
363 CssPixelsToVector2d(x_distance
? *x_distance
: 0,
364 y_distance
? *y_distance
: 0,
365 page_scale_factor_
));
368 if (x_overscroll
|| y_overscroll
) {
369 gesture_params
.distances
.push_back(
370 CssPixelsToVector2d(x_overscroll
? -*x_overscroll
: 0,
371 y_overscroll
? -*y_overscroll
: 0,
372 page_scale_factor_
));
375 if (!StringToGestureSourceType(
376 gesture_source_type
? *gesture_source_type
: kGestureSourceTypeDefault
,
377 gesture_params
.gesture_source_type
)) {
378 return Response::InvalidParams("gestureSourceType");
381 host_
->QueueSyntheticGesture(
382 SyntheticGesture::Create(gesture_params
),
383 base::Bind(&InputHandler::SendSynthesizeScrollGestureResponse
,
384 weak_factory_
.GetWeakPtr(), command_id
));
386 return Response::OK();
389 Response
InputHandler::SynthesizeTapGesture(
390 DevToolsCommandId command_id
,
394 const int* tap_count
,
395 const std::string
* gesture_source_type
) {
397 return Response::ServerError("Could not connect to view");
399 SyntheticTapGestureParams gesture_params
;
400 const int kDefaultDuration
= 50;
401 const int kDefaultTapCount
= 1;
403 gesture_params
.position
= CssPixelsToPoint(x
, y
, page_scale_factor_
);
404 gesture_params
.duration_ms
= duration
? *duration
: kDefaultDuration
;
406 if (!StringToGestureSourceType(
407 gesture_source_type
? *gesture_source_type
: kGestureSourceTypeDefault
,
408 gesture_params
.gesture_source_type
)) {
409 return Response::InvalidParams("gestureSourceType");
413 tap_count
= &kDefaultTapCount
;
415 for (int i
= 0; i
< *tap_count
; i
++) {
416 // If we're doing more than one tap, don't send the response to the client
417 // until we've completed the last tap.
418 bool is_last_tap
= i
== *tap_count
- 1;
419 host_
->QueueSyntheticGesture(
420 SyntheticGesture::Create(gesture_params
),
421 base::Bind(&InputHandler::SendSynthesizeTapGestureResponse
,
422 weak_factory_
.GetWeakPtr(), command_id
, is_last_tap
));
425 return Response::OK();
428 void InputHandler::SendSynthesizePinchGestureResponse(
429 DevToolsCommandId command_id
,
430 SyntheticGesture::Result result
) {
431 if (result
== SyntheticGesture::Result::GESTURE_FINISHED
) {
432 client_
->SendSynthesizePinchGestureResponse(
433 command_id
, SynthesizePinchGestureResponse::Create());
435 client_
->SendError(command_id
,
436 Response::InternalError(base::StringPrintf(
437 "Synthetic pinch failed, result was %d", result
)));
441 void InputHandler::SendSynthesizeScrollGestureResponse(
442 DevToolsCommandId command_id
,
443 SyntheticGesture::Result result
) {
444 if (result
== SyntheticGesture::Result::GESTURE_FINISHED
) {
445 client_
->SendSynthesizeScrollGestureResponse(
446 command_id
, SynthesizeScrollGestureResponse::Create());
448 client_
->SendError(command_id
,
449 Response::InternalError(base::StringPrintf(
450 "Synthetic scroll failed, result was %d", result
)));
454 void InputHandler::SendSynthesizeTapGestureResponse(
455 DevToolsCommandId command_id
,
457 SyntheticGesture::Result result
) {
458 if (result
== SyntheticGesture::Result::GESTURE_FINISHED
) {
460 client_
->SendSynthesizeTapGestureResponse(
461 command_id
, SynthesizeTapGestureResponse::Create());
464 client_
->SendError(command_id
,
465 Response::InternalError(base::StringPrintf(
466 "Synthetic tap failed, result was %d", result
)));
471 } // namespace devtools
472 } // namespace content