OLD | NEW |
| (Empty) |
1 // Copyright (c) 2010 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 // This file contains the definition for EventSendingController. | |
6 // | |
7 // Some notes about drag and drop handling: | |
8 // Windows drag and drop goes through a system call to DoDragDrop. At that | |
9 // point, program control is given to Windows which then periodically makes | |
10 // callbacks into the webview. This won't work for layout tests, so instead, | |
11 // we queue up all the mouse move and mouse up events. When the test tries to | |
12 // start a drag (by calling EvenSendingController::DoDragDrop), we take the | |
13 // events in the queue and replay them. | |
14 // The behavior of queuing events and replaying them can be disabled by a | |
15 // layout test by setting eventSender.dragMode to false. | |
16 | |
17 #include "webkit/tools/test_shell/event_sending_controller.h" | |
18 | |
19 #include <queue> | |
20 #include <vector> | |
21 | |
22 #include "base/compiler_specific.h" | |
23 #include "base/file_path.h" | |
24 #include "base/file_util.h" | |
25 #include "base/logging.h" | |
26 #include "base/message_loop.h" | |
27 #include "base/time.h" | |
28 #include "base/string_number_conversions.h" | |
29 #include "base/string_util.h" | |
30 #include "base/utf_string_conversions.h" | |
31 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDragData.h" | |
32 #include "third_party/WebKit/Source/WebKit/chromium/public/WebDragOperation.h" | |
33 #include "third_party/WebKit/Source/WebKit/chromium/public/WebPoint.h" | |
34 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" | |
35 #include "third_party/WebKit/Source/WebKit/chromium/public/WebTouchPoint.h" | |
36 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
37 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" | |
38 #include "ui/base/keycodes/keyboard_codes.h" | |
39 #include "webkit/glue/webkit_glue.h" | |
40 #include "webkit/tools/test_shell/test_shell.h" | |
41 #include "webkit/tools/test_shell/test_webview_delegate.h" | |
42 | |
43 #if defined(OS_WIN) | |
44 #include "third_party/WebKit/Source/WebKit/chromium/public/win/WebInputEventFact
ory.h" | |
45 using WebKit::WebInputEventFactory; | |
46 #endif | |
47 | |
48 // TODO(mpcomplete): layout before each event? | |
49 | |
50 using base::Time; | |
51 using base::TimeTicks; | |
52 using WebKit::WebDragOperation; | |
53 using WebKit::WebDragOperationsMask; | |
54 using WebKit::WebDragData; | |
55 using WebKit::WebInputEvent; | |
56 using WebKit::WebKeyboardEvent; | |
57 using WebKit::WebMouseEvent; | |
58 using WebKit::WebMouseWheelEvent; | |
59 using WebKit::WebPoint; | |
60 using WebKit::WebString; | |
61 using WebKit::WebTouchEvent; | |
62 using WebKit::WebTouchPoint; | |
63 using WebKit::WebView; | |
64 | |
65 gfx::Point EventSendingController::last_mouse_pos_; | |
66 WebMouseEvent::Button EventSendingController::pressed_button_ = | |
67 WebMouseEvent::ButtonNone; | |
68 | |
69 WebMouseEvent::Button EventSendingController::last_button_type_ = | |
70 WebMouseEvent::ButtonNone; | |
71 | |
72 namespace { | |
73 | |
74 struct SavedEvent { | |
75 enum SavedEventType { | |
76 Unspecified, | |
77 MouseUp, | |
78 MouseMove, | |
79 LeapForward | |
80 }; | |
81 | |
82 SavedEventType type; | |
83 WebMouseEvent::Button button_type; // For MouseUp | |
84 gfx::Point pos; // For MouseMove. | |
85 int milliseconds; // For LeapForward. | |
86 | |
87 SavedEvent() | |
88 : type(Unspecified), | |
89 button_type(WebMouseEvent::ButtonNone), | |
90 milliseconds(0) { | |
91 } | |
92 }; | |
93 | |
94 static WebDragData current_drag_data; | |
95 static WebDragOperation current_drag_effect; | |
96 static WebDragOperationsMask current_drag_effects_allowed; | |
97 static bool replaying_saved_events = false; | |
98 static std::queue<SavedEvent> mouse_event_queue; | |
99 static int touch_modifiers; | |
100 static std::vector<WebTouchPoint> touch_points; | |
101 | |
102 // Time and place of the last mouse up event. | |
103 static double last_click_time_sec = 0; | |
104 static gfx::Point last_click_pos; | |
105 static int click_count = 0; | |
106 | |
107 // maximum distance (in space and time) for a mouse click | |
108 // to register as a double or triple click | |
109 static const double kMultiClickTimeSec = 1; | |
110 static const int kMultiClickRadiusPixels = 5; | |
111 | |
112 // How much we should scroll per event - the value here is chosen to | |
113 // match the WebKit impl and layout test results. | |
114 static const float kScrollbarPixelsPerTick = 40.0f; | |
115 | |
116 inline bool outside_multiclick_radius(const gfx::Point &a, | |
117 const gfx::Point &b) { | |
118 return ((a.x() - b.x()) * (a.x() - b.x()) + | |
119 (a.y() - b.y()) * (a.y() - b.y())) > | |
120 kMultiClickRadiusPixels * kMultiClickRadiusPixels; | |
121 } | |
122 | |
123 // Used to offset the time the event hander things an event happened. This is | |
124 // done so tests can run without a delay, but bypass checks that are time | |
125 // dependent (e.g., dragging has a timeout vs selection). | |
126 static uint32 time_offset_ms = 0; | |
127 | |
128 double GetCurrentEventTimeSec() { | |
129 return (TimeTicks::Now().ToInternalValue() | |
130 / Time::kMicrosecondsPerMillisecond + | |
131 time_offset_ms) / 1000.0; | |
132 } | |
133 | |
134 void AdvanceEventTime(int32 delta_ms) { | |
135 time_offset_ms += delta_ms; | |
136 } | |
137 | |
138 void InitMouseEvent(WebInputEvent::Type t, WebMouseEvent::Button b, | |
139 const gfx::Point& pos, WebMouseEvent* e) { | |
140 e->type = t; | |
141 e->button = b; | |
142 e->modifiers = 0; | |
143 e->x = pos.x(); | |
144 e->y = pos.y(); | |
145 e->globalX = pos.x(); | |
146 e->globalY = pos.y(); | |
147 e->timeStampSeconds = GetCurrentEventTimeSec(); | |
148 e->clickCount = click_count; | |
149 } | |
150 | |
151 // Returns true if the specified key is the system key. | |
152 bool ApplyKeyModifier(const std::string& key, WebInputEvent* event) { | |
153 bool system_key = false; | |
154 if (key == "ctrlKey" | |
155 #if !defined(OS_MACOSX) | |
156 || key == "addSelectionKey" | |
157 #endif | |
158 ) { | |
159 event->modifiers |= WebInputEvent::ControlKey; | |
160 } else if (key == "shiftKey" || key == "rangeSelectionKey") { | |
161 event->modifiers |= WebInputEvent::ShiftKey; | |
162 } else if (key == "altKey") { | |
163 event->modifiers |= WebInputEvent::AltKey; | |
164 #if !defined(OS_MACOSX) | |
165 // On Windows all keys with Alt modifier will be marked as system key. | |
166 // We keep the same behavior on Linux and everywhere non-Mac, see: | |
167 // third_party/WebKit/Source/WebKit/chromium/src/gtk/WebInputEventFactory.cp
p | |
168 // If we want to change this behavior on Linux, this piece of code must be | |
169 // kept in sync with the related code in above file. | |
170 system_key = true; | |
171 #endif | |
172 #if defined(OS_MACOSX) | |
173 } else if (key == "metaKey" || key == "addSelectionKey") { | |
174 event->modifiers |= WebInputEvent::MetaKey; | |
175 // On Mac only command key presses are marked as system key. | |
176 // See the related code in: | |
177 // third_party/WebKit/Source/WebKit/chromium/src/mac/WebInputEventFactory.cp
p | |
178 // It must be kept in sync with the related code in above file. | |
179 system_key = true; | |
180 #else | |
181 } else if (key == "metaKey") { | |
182 event->modifiers |= WebInputEvent::MetaKey; | |
183 #endif | |
184 } | |
185 return system_key; | |
186 } | |
187 | |
188 bool ApplyKeyModifiers(const CppVariant* arg, WebInputEvent* event) { | |
189 bool system_key = false; | |
190 if (arg->isObject()) { | |
191 std::vector<std::string> args = arg->ToStringVector(); | |
192 for (std::vector<std::string>::const_iterator i = args.begin(); | |
193 i != args.end(); ++i) { | |
194 system_key |= ApplyKeyModifier(*i, event); | |
195 } | |
196 } else if (arg->isString()) { | |
197 system_key = ApplyKeyModifier(arg->ToString(), event); | |
198 } | |
199 return system_key; | |
200 } | |
201 | |
202 // Get the edit command corresponding to a keyboard event. | |
203 // Returns true if the specified event corresponds to an edit command, the name | |
204 // of the edit command will be stored in |*name|. | |
205 bool GetEditCommand(const WebKeyboardEvent& event, std::string* name) { | |
206 #if defined(OS_MACOSX) | |
207 // We only cares about Left,Right,Up,Down keys with Command or Command+Shift | |
208 // modifiers. These key events correspond to some special movement and | |
209 // selection editor commands, and was supposed to be handled in | |
210 // third_party/WebKit/Source/WebKit/chromium/src/EditorClientImpl.cpp. But the
se keys | |
211 // will be marked as system key, which prevents them from being handled. | |
212 // Thus they must be handled specially. | |
213 if ((event.modifiers & ~WebKeyboardEvent::ShiftKey) != | |
214 WebKeyboardEvent::MetaKey) | |
215 return false; | |
216 | |
217 switch (event.windowsKeyCode) { | |
218 case ui::VKEY_LEFT: | |
219 *name = "MoveToBeginningOfLine"; | |
220 break; | |
221 case ui::VKEY_RIGHT: | |
222 *name = "MoveToEndOfLine"; | |
223 break; | |
224 case ui::VKEY_UP: | |
225 *name = "MoveToBeginningOfDocument"; | |
226 break; | |
227 case ui::VKEY_DOWN: | |
228 *name = "MoveToEndOfDocument"; | |
229 break; | |
230 default: | |
231 return false; | |
232 } | |
233 | |
234 if (event.modifiers & WebKeyboardEvent::ShiftKey) | |
235 name->append("AndModifySelection"); | |
236 | |
237 return true; | |
238 #else | |
239 return false; | |
240 #endif | |
241 } | |
242 | |
243 // Key event location code introduced in DOM Level 3. | |
244 // See also: http://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents | |
245 enum KeyLocationCode { | |
246 DOM_KEY_LOCATION_STANDARD = 0x00, | |
247 DOM_KEY_LOCATION_LEFT = 0x01, | |
248 DOM_KEY_LOCATION_RIGHT = 0x02, | |
249 DOM_KEY_LOCATION_NUMPAD = 0x03 | |
250 }; | |
251 | |
252 } // anonymous namespace | |
253 | |
254 EventSendingController::EventSendingController(TestShell* shell) | |
255 : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), | |
256 shell_(shell) { | |
257 // Initialize the map that associates methods of this class with the names | |
258 // they will use when called by JavaScript. The actual binding of those | |
259 // names to their methods will be done by calling BindToJavaScript() (defined | |
260 // by CppBoundClass, the parent to EventSendingController). | |
261 BindMethod("mouseDown", &EventSendingController::mouseDown); | |
262 BindMethod("mouseUp", &EventSendingController::mouseUp); | |
263 BindMethod("contextClick", &EventSendingController::contextClick); | |
264 BindMethod("mouseMoveTo", &EventSendingController::mouseMoveTo); | |
265 BindMethod("leapForward", &EventSendingController::leapForward); | |
266 BindMethod("keyDown", &EventSendingController::keyDown); | |
267 BindMethod("dispatchMessage", &EventSendingController::dispatchMessage); | |
268 BindMethod("enableDOMUIEventLogging", | |
269 &EventSendingController::enableDOMUIEventLogging); | |
270 BindMethod("fireKeyboardEventsToElement", | |
271 &EventSendingController::fireKeyboardEventsToElement); | |
272 BindMethod("clearKillRing", &EventSendingController::clearKillRing); | |
273 BindMethod("textZoomIn", &EventSendingController::textZoomIn); | |
274 BindMethod("textZoomOut", &EventSendingController::textZoomOut); | |
275 BindMethod("zoomPageIn", &EventSendingController::zoomPageIn); | |
276 BindMethod("zoomPageOut", &EventSendingController::zoomPageOut); | |
277 BindMethod("mouseScrollBy", &EventSendingController::mouseScrollBy); | |
278 BindMethod("continuousMouseScrollBy", | |
279 &EventSendingController::continuousMouseScrollBy); | |
280 BindMethod("scheduleAsynchronousClick", | |
281 &EventSendingController::scheduleAsynchronousClick); | |
282 BindMethod("beginDragWithFiles", | |
283 &EventSendingController::beginDragWithFiles); | |
284 BindMethod("addTouchPoint", &EventSendingController::addTouchPoint); | |
285 BindMethod("cancelTouchPoint", &EventSendingController::cancelTouchPoint); | |
286 BindMethod("clearTouchPoints", &EventSendingController::clearTouchPoints); | |
287 BindMethod("releaseTouchPoint", &EventSendingController::releaseTouchPoint); | |
288 BindMethod("updateTouchPoint", &EventSendingController::updateTouchPoint); | |
289 BindMethod("setTouchModifier", &EventSendingController::setTouchModifier); | |
290 BindMethod("touchCancel", &EventSendingController::touchCancel); | |
291 BindMethod("touchEnd", &EventSendingController::touchEnd); | |
292 BindMethod("touchMove", &EventSendingController::touchMove); | |
293 BindMethod("touchStart", &EventSendingController::touchStart); | |
294 | |
295 // When set to true (the default value), we batch mouse move and mouse up | |
296 // events so we can simulate drag & drop. | |
297 BindProperty("dragMode", &dragMode); | |
298 #if defined(OS_WIN) | |
299 BindProperty("WM_KEYDOWN", &wmKeyDown); | |
300 BindProperty("WM_KEYUP", &wmKeyUp); | |
301 BindProperty("WM_CHAR", &wmChar); | |
302 BindProperty("WM_DEADCHAR", &wmDeadChar); | |
303 BindProperty("WM_SYSKEYDOWN", &wmSysKeyDown); | |
304 BindProperty("WM_SYSKEYUP", &wmSysKeyUp); | |
305 BindProperty("WM_SYSCHAR", &wmSysChar); | |
306 BindProperty("WM_SYSDEADCHAR", &wmSysDeadChar); | |
307 #endif | |
308 } | |
309 | |
310 EventSendingController::~EventSendingController() { | |
311 } | |
312 | |
313 void EventSendingController::Reset() { | |
314 // The test should have finished a drag and the mouse button state. | |
315 DCHECK(current_drag_data.isNull()); | |
316 current_drag_data.reset(); | |
317 current_drag_effect = WebKit::WebDragOperationNone; | |
318 current_drag_effects_allowed = WebKit::WebDragOperationNone; | |
319 pressed_button_ = WebMouseEvent::ButtonNone; | |
320 dragMode.Set(true); | |
321 #if defined(OS_WIN) | |
322 wmKeyDown.Set(WM_KEYDOWN); | |
323 wmKeyUp.Set(WM_KEYUP); | |
324 wmChar.Set(WM_CHAR); | |
325 wmDeadChar.Set(WM_DEADCHAR); | |
326 wmSysKeyDown.Set(WM_SYSKEYDOWN); | |
327 wmSysKeyUp.Set(WM_SYSKEYUP); | |
328 wmSysChar.Set(WM_SYSCHAR); | |
329 wmSysDeadChar.Set(WM_SYSDEADCHAR); | |
330 #endif | |
331 last_mouse_pos_.SetPoint(0, 0); | |
332 last_click_time_sec = 0; | |
333 last_click_pos.SetPoint(0, 0); | |
334 click_count = 0; | |
335 last_button_type_ = WebMouseEvent::ButtonNone; | |
336 time_offset_ms = 0; | |
337 touch_modifiers = 0; | |
338 touch_points.clear(); | |
339 } | |
340 | |
341 WebView* EventSendingController::webview() { | |
342 return shell_->webView(); | |
343 } | |
344 | |
345 void EventSendingController::DoDragDrop(const WebDragData& drag_data, | |
346 WebDragOperationsMask mask) { | |
347 WebMouseEvent event; | |
348 InitMouseEvent(WebInputEvent::MouseDown, pressed_button_, last_mouse_pos_, | |
349 &event); | |
350 WebPoint client_point(event.x, event.y); | |
351 WebPoint screen_point(event.globalX, event.globalY); | |
352 current_drag_data = drag_data; | |
353 current_drag_effects_allowed = mask; | |
354 current_drag_effect = webview()->dragTargetDragEnter( | |
355 drag_data, 0, client_point, screen_point, current_drag_effects_allowed); | |
356 | |
357 // Finish processing events. | |
358 ReplaySavedEvents(); | |
359 } | |
360 | |
361 WebMouseEvent::Button EventSendingController::GetButtonTypeFromButtonNumber( | |
362 int button_code) { | |
363 if (button_code == 0) | |
364 return WebMouseEvent::ButtonLeft; | |
365 else if (button_code == 2) | |
366 return WebMouseEvent::ButtonRight; | |
367 | |
368 return WebMouseEvent::ButtonMiddle; | |
369 } | |
370 | |
371 // static | |
372 int EventSendingController::GetButtonNumberFromSingleArg( | |
373 const CppArgumentList& args) { | |
374 int button_code = 0; | |
375 | |
376 if (args.size() > 0 && args[0].isNumber()) { | |
377 button_code = args[0].ToInt32(); | |
378 } | |
379 | |
380 return button_code; | |
381 } | |
382 | |
383 void EventSendingController::UpdateClickCountForButton( | |
384 WebMouseEvent::Button button_type) { | |
385 if ((GetCurrentEventTimeSec() - last_click_time_sec < kMultiClickTimeSec) && | |
386 (!outside_multiclick_radius(last_mouse_pos_, last_click_pos)) && | |
387 (button_type == last_button_type_)) { | |
388 ++click_count; | |
389 } else { | |
390 click_count = 1; | |
391 last_button_type_ = button_type; | |
392 } | |
393 } | |
394 | |
395 // | |
396 // Implemented javascript methods. | |
397 // | |
398 | |
399 void EventSendingController::mouseDown( | |
400 const CppArgumentList& args, CppVariant* result) { | |
401 if (result) // Could be NULL if invoked asynchronously. | |
402 result->SetNull(); | |
403 | |
404 webview()->layout(); | |
405 | |
406 int button_number = GetButtonNumberFromSingleArg(args); | |
407 DCHECK(button_number != -1); | |
408 | |
409 WebMouseEvent::Button button_type = GetButtonTypeFromButtonNumber( | |
410 button_number); | |
411 | |
412 UpdateClickCountForButton(button_type); | |
413 | |
414 WebMouseEvent event; | |
415 pressed_button_ = button_type; | |
416 InitMouseEvent(WebInputEvent::MouseDown, button_type, | |
417 last_mouse_pos_, &event); | |
418 if (args.size() >= 2 && (args[1].isObject() || args[1].isString())) | |
419 ApplyKeyModifiers(&(args[1]), &event); | |
420 webview()->handleInputEvent(event); | |
421 } | |
422 | |
423 void EventSendingController::mouseUp( | |
424 const CppArgumentList& args, CppVariant* result) { | |
425 if (result) // Could be NULL if invoked asynchronously. | |
426 result->SetNull(); | |
427 | |
428 webview()->layout(); | |
429 | |
430 int button_number = GetButtonNumberFromSingleArg(args); | |
431 DCHECK(button_number != -1); | |
432 | |
433 WebMouseEvent::Button button_type = GetButtonTypeFromButtonNumber( | |
434 button_number); | |
435 | |
436 if (drag_mode() && !replaying_saved_events) { | |
437 SavedEvent saved_event; | |
438 saved_event.type = SavedEvent::MouseUp; | |
439 saved_event.button_type = button_type; | |
440 mouse_event_queue.push(saved_event); | |
441 ReplaySavedEvents(); | |
442 } else { | |
443 WebMouseEvent event; | |
444 InitMouseEvent(WebInputEvent::MouseUp, button_type, | |
445 last_mouse_pos_, &event); | |
446 if (args.size() >= 2 && (args[1].isObject() || args[1].isString())) | |
447 ApplyKeyModifiers(&(args[1]), &event); | |
448 DoMouseUp(event); | |
449 } | |
450 } | |
451 | |
452 void EventSendingController::DoMouseUp(const WebMouseEvent& e) { | |
453 webview()->handleInputEvent(e); | |
454 | |
455 pressed_button_ = WebMouseEvent::ButtonNone; | |
456 last_click_time_sec = e.timeStampSeconds; | |
457 last_click_pos = last_mouse_pos_; | |
458 | |
459 // If we're in a drag operation, complete it. | |
460 if (!current_drag_data.isNull()) { | |
461 WebPoint client_point(e.x, e.y); | |
462 WebPoint screen_point(e.globalX, e.globalY); | |
463 | |
464 current_drag_effect = webview()->dragTargetDragOver( | |
465 client_point, screen_point, current_drag_effects_allowed); | |
466 if (current_drag_effect) { | |
467 webview()->dragTargetDrop(client_point, screen_point); | |
468 } else { | |
469 webview()->dragTargetDragLeave(); | |
470 } | |
471 webview()->dragSourceEndedAt( | |
472 client_point, screen_point, current_drag_effect); | |
473 webview()->dragSourceSystemDragEnded(); | |
474 | |
475 current_drag_data.reset(); | |
476 } | |
477 } | |
478 | |
479 void EventSendingController::mouseMoveTo( | |
480 const CppArgumentList& args, CppVariant* result) { | |
481 result->SetNull(); | |
482 | |
483 if (args.size() >= 2 && args[0].isNumber() && args[1].isNumber()) { | |
484 webview()->layout(); | |
485 | |
486 gfx::Point mouse_pos; | |
487 mouse_pos.SetPoint(args[0].ToInt32(), args[1].ToInt32()); | |
488 | |
489 if (drag_mode() && pressed_button_ == WebMouseEvent::ButtonLeft && | |
490 !replaying_saved_events) { | |
491 SavedEvent saved_event; | |
492 saved_event.type = SavedEvent::MouseMove; | |
493 saved_event.pos = mouse_pos; | |
494 mouse_event_queue.push(saved_event); | |
495 } else { | |
496 WebMouseEvent event; | |
497 InitMouseEvent(WebInputEvent::MouseMove, pressed_button_, | |
498 mouse_pos, &event); | |
499 DoMouseMove(event); | |
500 } | |
501 } | |
502 } | |
503 | |
504 void EventSendingController::DoMouseMove(const WebMouseEvent& e) { | |
505 last_mouse_pos_.SetPoint(e.x, e.y); | |
506 | |
507 webview()->handleInputEvent(e); | |
508 | |
509 if (pressed_button_ != WebMouseEvent::ButtonNone && | |
510 !current_drag_data.isNull()) { | |
511 WebPoint client_point(e.x, e.y); | |
512 WebPoint screen_point(e.globalX, e.globalY); | |
513 | |
514 current_drag_effect = webview()->dragTargetDragOver( | |
515 client_point, screen_point, current_drag_effects_allowed); | |
516 } | |
517 } | |
518 | |
519 void EventSendingController::keyDown( | |
520 const CppArgumentList& args, CppVariant* result) { | |
521 result->SetNull(); | |
522 | |
523 bool generate_char = false; | |
524 | |
525 if (args.size() >= 1 && args[0].isString()) { | |
526 // TODO(mpcomplete): I'm not exactly sure how we should convert the string | |
527 // to a key event. This seems to work in the cases I tested. | |
528 // TODO(mpcomplete): Should we also generate a KEY_UP? | |
529 std::wstring code_str = UTF8ToWide(args[0].ToString()); | |
530 | |
531 // Convert \n -> VK_RETURN. Some layout tests use \n to mean "Enter", when | |
532 // Windows uses \r for "Enter". | |
533 int code = 0; | |
534 int text = 0; | |
535 bool needs_shift_key_modifier = false; | |
536 if (L"\n" == code_str) { | |
537 generate_char = true; | |
538 text = code = ui::VKEY_RETURN; | |
539 } else if (L"rightArrow" == code_str) { | |
540 code = ui::VKEY_RIGHT; | |
541 } else if (L"downArrow" == code_str) { | |
542 code = ui::VKEY_DOWN; | |
543 } else if (L"leftArrow" == code_str) { | |
544 code = ui::VKEY_LEFT; | |
545 } else if (L"upArrow" == code_str) { | |
546 code = ui::VKEY_UP; | |
547 } else if (L"insert" == code_str) { | |
548 code = ui::VKEY_INSERT; | |
549 } else if (L"delete" == code_str) { | |
550 code = ui::VKEY_DELETE; | |
551 } else if (L"pageUp" == code_str) { | |
552 code = ui::VKEY_PRIOR; | |
553 } else if (L"pageDown" == code_str) { | |
554 code = ui::VKEY_NEXT; | |
555 } else if (L"home" == code_str) { | |
556 code = ui::VKEY_HOME; | |
557 } else if (L"end" == code_str) { | |
558 code = ui::VKEY_END; | |
559 } else if (L"printScreen" == code_str) { | |
560 code = ui::VKEY_SNAPSHOT; | |
561 } else { | |
562 // Compare the input string with the function-key names defined by the | |
563 // DOM spec (i.e. "F1",...,"F24"). If the input string is a function-key | |
564 // name, set its key code. | |
565 for (int i = 1; i <= 24; ++i) { | |
566 std::wstring function_key_name; | |
567 function_key_name += L"F"; | |
568 function_key_name += UTF8ToWide(base::IntToString(i)); | |
569 if (function_key_name == code_str) { | |
570 code = ui::VKEY_F1 + (i - 1); | |
571 break; | |
572 } | |
573 } | |
574 if (!code) { | |
575 DCHECK(code_str.length() == 1); | |
576 text = code = code_str[0]; | |
577 needs_shift_key_modifier = NeedsShiftModifier(code); | |
578 if ((code & 0xFF) >= 'a' && (code & 0xFF) <= 'z') | |
579 code -= 'a' - 'A'; | |
580 generate_char = true; | |
581 } | |
582 } | |
583 | |
584 // For one generated keyboard event, we need to generate a keyDown/keyUp | |
585 // pair; refer to EventSender.cpp in WebKit/Tools/DumpRenderTree/win. | |
586 // On Windows, we might also need to generate a char event to mimic the | |
587 // Windows event flow; on other platforms we create a merged event and test | |
588 // the event flow that that platform provides. | |
589 WebKeyboardEvent event_down, event_char, event_up; | |
590 event_down.type = WebInputEvent::RawKeyDown; | |
591 event_down.modifiers = 0; | |
592 event_down.windowsKeyCode = code; | |
593 if (generate_char) { | |
594 event_down.text[0] = text; | |
595 event_down.unmodifiedText[0] = text; | |
596 } | |
597 event_down.setKeyIdentifierFromWindowsKeyCode(); | |
598 | |
599 if (args.size() >= 2 && (args[1].isObject() || args[1].isString())) | |
600 event_down.isSystemKey = ApplyKeyModifiers(&(args[1]), &event_down); | |
601 | |
602 if (needs_shift_key_modifier) | |
603 event_down.modifiers |= WebInputEvent::ShiftKey; | |
604 | |
605 // See if KeyLocation argument is given. | |
606 if (args.size() >= 3 && args[2].isNumber()) { | |
607 int location = args[2].ToInt32(); | |
608 if (location == DOM_KEY_LOCATION_NUMPAD) { | |
609 event_down.modifiers |= WebInputEvent::IsKeyPad; | |
610 } | |
611 } | |
612 | |
613 event_char = event_up = event_down; | |
614 event_up.type = WebInputEvent::KeyUp; | |
615 // EventSendingController.m forces a layout here, with at least one | |
616 // test (fast\forms\focus-control-to-page.html) relying on this. | |
617 webview()->layout(); | |
618 | |
619 // In the browser, if a keyboard event corresponds to an editor command, | |
620 // the command will be dispatched to the renderer just before dispatching | |
621 // the keyboard event, and then it will be executed in the | |
622 // RenderView::handleCurrentKeyboardEvent() method, which is called from | |
623 // third_party/WebKit/Source/WebKit/chromium/src/EditorClientImpl.cpp. | |
624 // We just simulate the same behavior here. | |
625 std::string edit_command; | |
626 if (GetEditCommand(event_down, &edit_command)) | |
627 shell_->delegate()->SetEditCommand(edit_command, ""); | |
628 | |
629 webview()->handleInputEvent(event_down); | |
630 | |
631 shell_->delegate()->ClearEditCommand(); | |
632 | |
633 if (generate_char) { | |
634 event_char.type = WebInputEvent::Char; | |
635 event_char.keyIdentifier[0] = '\0'; | |
636 webview()->handleInputEvent(event_char); | |
637 } | |
638 | |
639 webview()->handleInputEvent(event_up); | |
640 } | |
641 } | |
642 | |
643 void EventSendingController::dispatchMessage( | |
644 const CppArgumentList& args, CppVariant* result) { | |
645 result->SetNull(); | |
646 | |
647 #if defined(OS_WIN) | |
648 if (args.size() == 3) { | |
649 // Grab the message id to see if we need to dispatch it. | |
650 int msg = args[0].ToInt32(); | |
651 | |
652 // WebKit's version of this function stuffs a MSG struct and uses | |
653 // TranslateMessage and DispatchMessage. We use a WebKeyboardEvent, which | |
654 // doesn't need to receive the DeadChar and SysDeadChar messages. | |
655 if (msg == WM_DEADCHAR || msg == WM_SYSDEADCHAR) | |
656 return; | |
657 | |
658 webview()->layout(); | |
659 | |
660 unsigned long lparam = static_cast<unsigned long>(args[2].ToDouble()); | |
661 webview()->handleInputEvent(WebInputEventFactory::keyboardEvent( | |
662 NULL, msg, args[1].ToInt32(), lparam)); | |
663 } else { | |
664 NOTREACHED() << L"Wrong number of arguments"; | |
665 } | |
666 #endif | |
667 } | |
668 | |
669 bool EventSendingController::NeedsShiftModifier(int key_code) { | |
670 // If code is an uppercase letter, assign a SHIFT key to | |
671 // event_down.modifier, this logic comes from | |
672 // WebKit/Tools/DumpRenderTree/Win/EventSender.cpp | |
673 if ((key_code & 0xFF) >= 'A' && (key_code & 0xFF) <= 'Z') | |
674 return true; | |
675 return false; | |
676 } | |
677 | |
678 void EventSendingController::leapForward( | |
679 const CppArgumentList& args, CppVariant* result) { | |
680 result->SetNull(); | |
681 | |
682 if (args.size() <1 || !args[0].isNumber()) | |
683 return; | |
684 | |
685 int milliseconds = args[0].ToInt32(); | |
686 if (drag_mode() && pressed_button_ == WebMouseEvent::ButtonLeft && | |
687 !replaying_saved_events) { | |
688 SavedEvent saved_event; | |
689 saved_event.type = SavedEvent::LeapForward; | |
690 saved_event.milliseconds = milliseconds; | |
691 mouse_event_queue.push(saved_event); | |
692 } else { | |
693 DoLeapForward(milliseconds); | |
694 } | |
695 } | |
696 | |
697 // static | |
698 void EventSendingController::DoLeapForward(int milliseconds) { | |
699 AdvanceEventTime(milliseconds); | |
700 } | |
701 | |
702 // Apple's port of WebKit zooms by a factor of 1.2 (see | |
703 // WebKit/WebView/WebView.mm) | |
704 void EventSendingController::textZoomIn( | |
705 const CppArgumentList& args, CppVariant* result) { | |
706 webview()->setZoomLevel(true, webview()->zoomLevel() + 1); | |
707 result->SetNull(); | |
708 } | |
709 | |
710 void EventSendingController::textZoomOut( | |
711 const CppArgumentList& args, CppVariant* result) { | |
712 webview()->setZoomLevel(true, webview()->zoomLevel() - 1); | |
713 result->SetNull(); | |
714 } | |
715 | |
716 void EventSendingController::zoomPageIn( | |
717 const CppArgumentList& args, CppVariant* result) { | |
718 webview()->setZoomLevel(false, webview()->zoomLevel() + 1); | |
719 result->SetNull(); | |
720 } | |
721 | |
722 void EventSendingController::zoomPageOut( | |
723 const CppArgumentList& args, CppVariant* result) { | |
724 webview()->setZoomLevel(false, webview()->zoomLevel() - 1); | |
725 result->SetNull(); | |
726 } | |
727 | |
728 void EventSendingController::mouseScrollBy(const CppArgumentList& args, | |
729 CppVariant* result) { | |
730 handleMouseWheel(args, result, false); | |
731 } | |
732 | |
733 void EventSendingController::continuousMouseScrollBy( | |
734 const CppArgumentList& args, | |
735 CppVariant* result) { | |
736 handleMouseWheel(args, result, true); | |
737 } | |
738 | |
739 void EventSendingController::ReplaySavedEvents() { | |
740 replaying_saved_events = true; | |
741 while (!mouse_event_queue.empty()) { | |
742 SavedEvent e = mouse_event_queue.front(); | |
743 mouse_event_queue.pop(); | |
744 | |
745 switch (e.type) { | |
746 case SavedEvent::MouseMove: { | |
747 WebMouseEvent event; | |
748 InitMouseEvent(WebInputEvent::MouseMove, pressed_button_, | |
749 e.pos, &event); | |
750 DoMouseMove(event); | |
751 break; | |
752 } | |
753 case SavedEvent::LeapForward: | |
754 DoLeapForward(e.milliseconds); | |
755 break; | |
756 case SavedEvent::MouseUp: { | |
757 WebMouseEvent event; | |
758 InitMouseEvent(WebInputEvent::MouseUp, e.button_type, | |
759 last_mouse_pos_, &event); | |
760 DoMouseUp(event); | |
761 break; | |
762 } | |
763 default: | |
764 NOTREACHED(); | |
765 } | |
766 } | |
767 | |
768 replaying_saved_events = false; | |
769 } | |
770 | |
771 // Because actual context menu is implemented by the browser side, | |
772 // this function does only what LayoutTests are expecting: | |
773 // - Many test checks the count of items. So returning non-zero value | |
774 // makes sense. | |
775 // - Some test compares the count before and after some action. So | |
776 // changing the count based on flags also makes sense. This function | |
777 // is doing such for some flags. | |
778 // - Some test even checks actual string content. So providing it | |
779 // would be also helpful. | |
780 static std::vector<WebString> | |
781 MakeMenuItemStringsFor(const WebKit::WebContextMenuData* context_menu, | |
782 MockSpellCheck* spellcheck) { | |
783 // These constants are based on Safari's context menu because tests | |
784 // are made for it. | |
785 static const char* kNonEditableMenuStrings[] = { | |
786 "Back", "Reload Page", "Open in Dashbaord", "<separator>", | |
787 "View Source", "Save Page As", "Print Page", "Inspect Element", | |
788 0 }; | |
789 static const char* kEditableMenuStrings[] = { | |
790 "Cut", "Copy", "<separator>", "Paste", "Spelling and Grammar", | |
791 "Substitutions, Transformations", "Font", "Speech", | |
792 "Paragraph Direction", "<separator>", 0 }; | |
793 | |
794 // This is possible because mouse events are cancelleable. | |
795 if (!context_menu) | |
796 return std::vector<WebString>(); | |
797 | |
798 std::vector<WebString> strings; | |
799 | |
800 if (context_menu->isEditable) { | |
801 for (const char** item = kEditableMenuStrings; *item; ++item) | |
802 strings.push_back(WebString::fromUTF8(*item)); | |
803 std::vector<string16> suggestions; | |
804 spellcheck->FillSuggestions(context_menu->misspelledWord, &suggestions); | |
805 for (size_t i = 0; i < suggestions.size(); ++i) | |
806 strings.push_back(WebString(suggestions[i])); | |
807 } else { | |
808 for (const char** item = kNonEditableMenuStrings; *item; ++item) | |
809 strings.push_back(WebString::fromUTF8(*item)); | |
810 } | |
811 | |
812 return strings; | |
813 } | |
814 | |
815 void EventSendingController::contextClick( | |
816 const CppArgumentList& args, CppVariant* result) { | |
817 result->SetNull(); | |
818 | |
819 webview()->layout(); | |
820 | |
821 // Clears last context menu data because we need to know if the | |
822 // context menu be requested after following mouse events. | |
823 shell_->delegate()->ClearContextMenuData(); | |
824 | |
825 UpdateClickCountForButton(WebMouseEvent::ButtonRight); | |
826 | |
827 // Generate right mouse down and up. | |
828 | |
829 WebMouseEvent event; | |
830 pressed_button_ = WebMouseEvent::ButtonRight; | |
831 InitMouseEvent(WebInputEvent::MouseDown, WebMouseEvent::ButtonRight, | |
832 last_mouse_pos_, &event); | |
833 webview()->handleInputEvent(event); | |
834 | |
835 InitMouseEvent(WebInputEvent::MouseUp, WebMouseEvent::ButtonRight, | |
836 last_mouse_pos_, &event); | |
837 webview()->handleInputEvent(event); | |
838 | |
839 pressed_button_ = WebMouseEvent::ButtonNone; | |
840 | |
841 result->Set(WebKit::WebBindings::makeStringArray( | |
842 MakeMenuItemStringsFor( | |
843 shell_->delegate()->last_context_menu_data(), | |
844 shell_->delegate()->mock_spellcheck()))); | |
845 | |
846 } | |
847 | |
848 void EventSendingController::scheduleAsynchronousClick( | |
849 const CppArgumentList& args, CppVariant* result) { | |
850 result->SetNull(); | |
851 | |
852 MessageLoop::current()->PostTask(FROM_HERE, | |
853 method_factory_.NewRunnableMethod(&EventSendingController::mouseDown, | |
854 args, static_cast<CppVariant*>(NULL))); | |
855 MessageLoop::current()->PostTask(FROM_HERE, | |
856 method_factory_.NewRunnableMethod(&EventSendingController::mouseUp, | |
857 args, static_cast<CppVariant*>(NULL))); | |
858 } | |
859 | |
860 void EventSendingController::beginDragWithFiles( | |
861 const CppArgumentList& args, CppVariant* result) { | |
862 current_drag_data.initialize(); | |
863 std::vector<std::string> files = args[0].ToStringVector(); | |
864 for (size_t i = 0; i < files.size(); ++i) { | |
865 std::wstring file = UTF8ToWide(files[i]); | |
866 FilePath file_path = FilePath::FromWStringHack(file); | |
867 file_util::AbsolutePath(&file_path); | |
868 current_drag_data.appendToFilenames( | |
869 webkit_glue::FilePathStringToWebString(file_path.value())); | |
870 } | |
871 current_drag_effects_allowed = WebKit::WebDragOperationCopy; | |
872 | |
873 // Provide a drag source. | |
874 WebPoint client_point(last_mouse_pos_.x(), last_mouse_pos_.y()); | |
875 WebPoint screen_point(last_mouse_pos_.x(), last_mouse_pos_.y()); | |
876 webview()->dragTargetDragEnter( | |
877 current_drag_data, 0, client_point, screen_point, | |
878 current_drag_effects_allowed); | |
879 | |
880 // dragMode saves events and then replays them later. We don't need/want that. | |
881 dragMode.Set(false); | |
882 | |
883 // Make the rest of eventSender think a drag is in progress. | |
884 pressed_button_ = WebMouseEvent::ButtonLeft; | |
885 | |
886 result->SetNull(); | |
887 } | |
888 | |
889 void EventSendingController::addTouchPoint( | |
890 const CppArgumentList& args, CppVariant* result) { | |
891 result->SetNull(); | |
892 | |
893 WebTouchPoint touch_point; | |
894 touch_point.state = WebTouchPoint::StatePressed; | |
895 touch_point.position = WebPoint(args[0].ToInt32(), args[1].ToInt32()); | |
896 touch_point.screenPosition = touch_point.position; | |
897 touch_point.id = touch_points.size(); | |
898 touch_points.push_back(touch_point); | |
899 } | |
900 | |
901 void EventSendingController::clearTouchPoints( | |
902 const CppArgumentList& args, CppVariant* result) { | |
903 result->SetNull(); | |
904 | |
905 touch_points.clear(); | |
906 } | |
907 | |
908 void EventSendingController::releaseTouchPoint( | |
909 const CppArgumentList& args, CppVariant* result) { | |
910 result->SetNull(); | |
911 | |
912 const unsigned int index = args[0].ToInt32(); | |
913 if (index >= touch_points.size()) { | |
914 NOTREACHED() << "Invalid touch point index"; | |
915 } | |
916 | |
917 WebTouchPoint* touch_point = &touch_points[index]; | |
918 touch_point->state = WebTouchPoint::StateReleased; | |
919 } | |
920 | |
921 void EventSendingController::setTouchModifier( | |
922 const CppArgumentList& args, CppVariant* result) { | |
923 result->SetNull(); | |
924 | |
925 int mask = 0; | |
926 const std::string key_name = args[0].ToString(); | |
927 if (key_name == "shift") { | |
928 mask = WebInputEvent::ShiftKey; | |
929 } else if (key_name == "alt") { | |
930 mask = WebInputEvent::AltKey; | |
931 } else if (key_name == "ctrl") { | |
932 mask = WebInputEvent::ControlKey; | |
933 } else if (key_name == "meta") { | |
934 mask = WebInputEvent::MetaKey; | |
935 } | |
936 | |
937 if (args[1].ToBoolean() == true) { | |
938 touch_modifiers |= mask; | |
939 } else { | |
940 touch_modifiers &= ~mask; | |
941 } | |
942 } | |
943 | |
944 void EventSendingController::updateTouchPoint( | |
945 const CppArgumentList& args, CppVariant* result) { | |
946 result->SetNull(); | |
947 | |
948 const unsigned int index = args[0].ToInt32(); | |
949 if (index >= touch_points.size()) { | |
950 NOTREACHED() << "Invalid touch point index"; | |
951 } | |
952 | |
953 WebPoint position(args[1].ToInt32(), args[2].ToInt32()); | |
954 | |
955 WebTouchPoint* touch_point = &touch_points[index]; | |
956 touch_point->state = WebTouchPoint::StateMoved; | |
957 touch_point->position = position; | |
958 touch_point->screenPosition = position; | |
959 } | |
960 | |
961 void EventSendingController::cancelTouchPoint( | |
962 const CppArgumentList& args, CppVariant* result) { | |
963 result->SetNull(); | |
964 | |
965 const unsigned int index = args[0].ToInt32(); | |
966 if (index >= touch_points.size()) { | |
967 NOTREACHED() << "Invalid touch point index"; | |
968 } | |
969 | |
970 WebTouchPoint* touch_point = &touch_points[index]; | |
971 touch_point->state = WebTouchPoint::StateCancelled; | |
972 } | |
973 | |
974 void EventSendingController::SendCurrentTouchEvent( | |
975 const WebInputEvent::Type type) { | |
976 webview()->layout(); | |
977 | |
978 if (static_cast<unsigned int>(WebTouchEvent::touchPointsLengthCap) <= | |
979 touch_points.size()) { | |
980 NOTREACHED() << "Too many touch points for event"; | |
981 } | |
982 | |
983 WebTouchEvent touch_event; | |
984 touch_event.type = type; | |
985 touch_event.modifiers = touch_modifiers; | |
986 touch_event.timeStampSeconds = GetCurrentEventTimeSec(); | |
987 touch_event.touchPointsLength = touch_points.size(); | |
988 for (unsigned int i = 0; i < touch_points.size(); ++i) { | |
989 touch_event.touchPoints[i] = touch_points[i]; | |
990 } | |
991 webview()->handleInputEvent(touch_event); | |
992 | |
993 std::vector<WebTouchPoint>::iterator i = touch_points.begin(); | |
994 while (i != touch_points.end()) { | |
995 WebTouchPoint* touch_point = &(*i); | |
996 if (touch_point->state == WebTouchPoint::StateReleased) { | |
997 i = touch_points.erase(i); | |
998 } else { | |
999 touch_point->state = WebTouchPoint::StateStationary; | |
1000 ++i; | |
1001 } | |
1002 } | |
1003 } | |
1004 | |
1005 void EventSendingController::handleMouseWheel(const CppArgumentList& args, | |
1006 CppVariant* result, | |
1007 bool continuous) { | |
1008 result->SetNull(); | |
1009 | |
1010 if (args.size() < 2 || !args[0].isNumber() || !args[1].isNumber()) | |
1011 return; | |
1012 | |
1013 // Force a layout here just to make sure every position has been | |
1014 // determined before we send events (as well as all the other methods | |
1015 // that send an event do). | |
1016 webview()->layout(); | |
1017 | |
1018 int horizontal = args[0].ToInt32(); | |
1019 int vertical = args[1].ToInt32(); | |
1020 | |
1021 WebMouseWheelEvent event; | |
1022 InitMouseEvent(WebInputEvent::MouseWheel, pressed_button_, last_mouse_pos_, | |
1023 &event); | |
1024 event.wheelTicksX = static_cast<float>(horizontal); | |
1025 event.wheelTicksY = static_cast<float>(vertical); | |
1026 event.deltaX = event.wheelTicksX; | |
1027 event.deltaY = event.wheelTicksY; | |
1028 if (continuous) { | |
1029 event.wheelTicksX /= kScrollbarPixelsPerTick; | |
1030 event.wheelTicksY /= kScrollbarPixelsPerTick; | |
1031 } else { | |
1032 event.deltaX *= kScrollbarPixelsPerTick; | |
1033 event.deltaY *= kScrollbarPixelsPerTick; | |
1034 } | |
1035 webview()->handleInputEvent(event); | |
1036 } | |
1037 | |
1038 void EventSendingController::touchEnd( | |
1039 const CppArgumentList& args, CppVariant* result) { | |
1040 result->SetNull(); | |
1041 SendCurrentTouchEvent(WebInputEvent::TouchEnd); | |
1042 } | |
1043 | |
1044 void EventSendingController::touchMove( | |
1045 const CppArgumentList& args, CppVariant* result) { | |
1046 result->SetNull(); | |
1047 SendCurrentTouchEvent(WebInputEvent::TouchMove); | |
1048 } | |
1049 | |
1050 void EventSendingController::touchStart( | |
1051 const CppArgumentList& args, CppVariant* result) { | |
1052 result->SetNull(); | |
1053 SendCurrentTouchEvent(WebInputEvent::TouchStart); | |
1054 } | |
1055 | |
1056 void EventSendingController::touchCancel( | |
1057 const CppArgumentList& args, CppVariant* result) { | |
1058 result->SetNull(); | |
1059 SendCurrentTouchEvent(WebInputEvent::TouchCancel); | |
1060 } | |
1061 | |
1062 // | |
1063 // Unimplemented stubs | |
1064 // | |
1065 | |
1066 void EventSendingController::enableDOMUIEventLogging( | |
1067 const CppArgumentList& args, CppVariant* result) { | |
1068 result->SetNull(); | |
1069 } | |
1070 | |
1071 void EventSendingController::fireKeyboardEventsToElement( | |
1072 const CppArgumentList& args, CppVariant* result) { | |
1073 result->SetNull(); | |
1074 } | |
1075 | |
1076 void EventSendingController::clearKillRing( | |
1077 const CppArgumentList& args, CppVariant* result) { | |
1078 result->SetNull(); | |
1079 } | |
OLD | NEW |