1 /* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 // This file contains the definition for EventSender. 32 // 33 // Some notes about drag and drop handling: 34 // Windows drag and drop goes through a system call to doDragDrop. At that 35 // point, program control is given to Windows which then periodically makes 36 // callbacks into the webview. This won't work for layout tests, so instead, 37 // we queue up all the mouse move and mouse up events. When the test tries to 38 // start a drag (by calling EvenSendingController::doDragDrop), we take the 39 // events in the queue and replay them. 40 // The behavior of queuing events and replaying them can be disabled by a 41 // layout test by setting eventSender.dragMode to false. 42 43 #include "EventSender.h" 44 45 #include "KeyCodeMapping.h" 46 #include "MockSpellCheck.h" 47 #include "TestCommon.h" 48 #include "TestInterfaces.h" 49 #include "public/platform/WebDragData.h" 50 #include "public/platform/WebString.h" 51 #include "public/platform/WebVector.h" 52 #include "public/testing/WebTestDelegate.h" 53 #include "public/testing/WebTestProxy.h" 54 #include "public/web/WebContextMenuData.h" 55 #include "public/web/WebTouchPoint.h" 56 #include "public/web/WebView.h" 57 #include <deque> 58 59 #ifdef WIN32 60 #include "public/web/win/WebInputEventFactory.h" 61 #elif __APPLE__ 62 #include "public/web/mac/WebInputEventFactory.h" 63 #elif defined(ANDROID) 64 #include "public/web/android/WebInputEventFactory.h" 65 #elif defined(TOOLKIT_GTK) 66 #include "public/web/gtk/WebInputEventFactory.h" 67 #endif 68 69 // FIXME: layout before each event? 70 71 using namespace std; 72 using namespace blink; 73 74 namespace WebTestRunner { 75 76 WebPoint EventSender::lastMousePos; 77 WebMouseEvent::Button EventSender::pressedButton = WebMouseEvent::ButtonNone; 78 WebMouseEvent::Button EventSender::lastButtonType = WebMouseEvent::ButtonNone; 79 80 namespace { 81 82 struct SavedEvent { 83 enum SavedEventType { 84 Unspecified, 85 MouseUp, 86 MouseMove, 87 LeapForward 88 }; 89 90 SavedEventType type; 91 WebMouseEvent::Button buttonType; // For MouseUp. 92 WebPoint pos; // For MouseMove. 93 int milliseconds; // For LeapForward. 94 95 SavedEvent() 96 : type(Unspecified) 97 , buttonType(WebMouseEvent::ButtonNone) 98 , milliseconds(0) { } 99 }; 100 101 WebDragData currentDragData; 102 WebDragOperation currentDragEffect; 103 WebDragOperationsMask currentDragEffectsAllowed; 104 bool replayingSavedEvents = false; 105 deque<SavedEvent> mouseEventQueue; 106 int touchModifiers; 107 vector<WebTouchPoint> touchPoints; 108 109 // Time and place of the last mouse up event. 110 double lastClickTimeSec = 0; 111 WebPoint lastClickPos; 112 int clickCount = 0; 113 114 // maximum distance (in space and time) for a mouse click 115 // to register as a double or triple click 116 const double multipleClickTimeSec = 1; 117 const int multipleClickRadiusPixels = 5; 118 119 // How much we should scroll per event - the value here is chosen to 120 // match the WebKit impl and layout test results. 121 const float scrollbarPixelsPerTick = 40.0f; 122 123 inline bool outsideMultiClickRadius(const WebPoint& a, const WebPoint& b) 124 { 125 return ((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)) > 126 multipleClickRadiusPixels * multipleClickRadiusPixels; 127 } 128 129 // Used to offset the time the event hander things an event happened. This is 130 // done so tests can run without a delay, but bypass checks that are time 131 // dependent (e.g., dragging has a timeout vs selection). 132 uint32 timeOffsetMs = 0; 133 134 double getCurrentEventTimeSec(WebTestDelegate* delegate) 135 { 136 return (delegate->getCurrentTimeInMillisecond() + timeOffsetMs) / 1000.0; 137 } 138 139 void advanceEventTime(int32_t deltaMs) 140 { 141 timeOffsetMs += deltaMs; 142 } 143 144 void initMouseEvent(WebInputEvent::Type t, WebMouseEvent::Button b, const WebPoint& pos, WebMouseEvent* e, double ts) 145 { 146 e->type = t; 147 e->button = b; 148 e->modifiers = 0; 149 e->x = pos.x; 150 e->y = pos.y; 151 e->globalX = pos.x; 152 e->globalY = pos.y; 153 e->timeStampSeconds = ts; 154 e->clickCount = clickCount; 155 } 156 157 void applyKeyModifier(const string& modifierName, WebInputEvent* event) 158 { 159 const char* characters = modifierName.c_str(); 160 if (!strcmp(characters, "ctrlKey") 161 #ifndef __APPLE__ 162 || !strcmp(characters, "addSelectionKey") 163 #endif 164 ) { 165 event->modifiers |= WebInputEvent::ControlKey; 166 } else if (!strcmp(characters, "shiftKey") || !strcmp(characters, "rangeSelectionKey")) 167 event->modifiers |= WebInputEvent::ShiftKey; 168 else if (!strcmp(characters, "altKey")) { 169 event->modifiers |= WebInputEvent::AltKey; 170 #ifdef __APPLE__ 171 } else if (!strcmp(characters, "metaKey") || !strcmp(characters, "addSelectionKey")) { 172 event->modifiers |= WebInputEvent::MetaKey; 173 #else 174 } else if (!strcmp(characters, "metaKey")) { 175 event->modifiers |= WebInputEvent::MetaKey; 176 #endif 177 } else if (!strcmp(characters, "autoRepeat")) { 178 event->modifiers |= WebInputEvent::IsAutoRepeat; 179 } 180 } 181 182 void applyKeyModifiers(const CppVariant* argument, WebInputEvent* event) 183 { 184 if (argument->isObject()) { 185 vector<string> modifiers = argument->toStringVector(); 186 for (vector<string>::const_iterator i = modifiers.begin(); i != modifiers.end(); ++i) 187 applyKeyModifier(*i, event); 188 } else if (argument->isString()) { 189 applyKeyModifier(argument->toString(), event); 190 } 191 } 192 193 // Get the edit command corresponding to a keyboard event. 194 // Returns true if the specified event corresponds to an edit command, the name 195 // of the edit command will be stored in |*name|. 196 bool getEditCommand(const WebKeyboardEvent& event, string* name) 197 { 198 #ifdef __APPLE__ 199 // We only cares about Left,Right,Up,Down keys with Command or Command+Shift 200 // modifiers. These key events correspond to some special movement and 201 // selection editor commands, and was supposed to be handled in 202 // WebKit/chromium/src/EditorClientImpl.cpp. But these keys will be marked 203 // as system key, which prevents them from being handled. Thus they must be 204 // handled specially. 205 if ((event.modifiers & ~WebKeyboardEvent::ShiftKey) != WebKeyboardEvent::MetaKey) 206 return false; 207 208 switch (event.windowsKeyCode) { 209 case VKEY_LEFT: 210 *name = "MoveToBeginningOfLine"; 211 break; 212 case VKEY_RIGHT: 213 *name = "MoveToEndOfLine"; 214 break; 215 case VKEY_UP: 216 *name = "MoveToBeginningOfDocument"; 217 break; 218 case VKEY_DOWN: 219 *name = "MoveToEndOfDocument"; 220 break; 221 default: 222 return false; 223 } 224 225 if (event.modifiers & WebKeyboardEvent::ShiftKey) 226 name->append("AndModifySelection"); 227 228 return true; 229 #else 230 return false; 231 #endif 232 } 233 234 // Key event location code introduced in DOM Level 3. 235 // See also: http://www.w3.org/TR/DOM-Level-3-Events/#events-keyboardevents 236 enum KeyLocationCode { 237 DOMKeyLocationStandard = 0x00, 238 DOMKeyLocationLeft = 0x01, 239 DOMKeyLocationRight = 0x02, 240 DOMKeyLocationNumpad = 0x03 241 }; 242 243 } 244 245 EventSender::EventSender(TestInterfaces* interfaces) 246 : m_testInterfaces(interfaces) 247 , m_delegate(0) 248 { 249 // Initialize the map that associates methods of this class with the names 250 // they will use when called by JavaScript. The actual binding of those 251 // names to their methods will be done by calling bindToJavaScript() (defined 252 // by CppBoundClass, the parent to EventSender). 253 bindMethod("addTouchPoint", &EventSender::addTouchPoint); 254 bindMethod("beginDragWithFiles", &EventSender::beginDragWithFiles); 255 bindMethod("cancelTouchPoint", &EventSender::cancelTouchPoint); 256 bindMethod("clearKillRing", &EventSender::clearKillRing); 257 bindMethod("clearTouchPoints", &EventSender::clearTouchPoints); 258 bindMethod("contextClick", &EventSender::contextClick); 259 bindMethod("continuousMouseScrollBy", &EventSender::continuousMouseScrollBy); 260 bindMethod("dispatchMessage", &EventSender::dispatchMessage); 261 bindMethod("dumpFilenameBeingDragged", &EventSender::dumpFilenameBeingDragged); 262 bindMethod("enableDOMUIEventLogging", &EventSender::enableDOMUIEventLogging); 263 bindMethod("fireKeyboardEventsToElement", &EventSender::fireKeyboardEventsToElement); 264 bindMethod("keyDown", &EventSender::keyDown); 265 bindMethod("leapForward", &EventSender::leapForward); 266 bindMethod("mouseDown", &EventSender::mouseDown); 267 bindMethod("mouseMoveTo", &EventSender::mouseMoveTo); 268 bindMethod("mouseScrollBy", &EventSender::mouseScrollBy); 269 bindMethod("mouseUp", &EventSender::mouseUp); 270 bindMethod("mouseDragBegin", &EventSender::mouseDragBegin); 271 bindMethod("mouseDragEnd", &EventSender::mouseDragEnd); 272 bindMethod("mouseMomentumBegin", &EventSender::mouseMomentumBegin); 273 bindMethod("mouseMomentumScrollBy", &EventSender::mouseMomentumScrollBy); 274 bindMethod("mouseMomentumEnd", &EventSender::mouseMomentumEnd); 275 bindMethod("releaseTouchPoint", &EventSender::releaseTouchPoint); 276 bindMethod("scheduleAsynchronousClick", &EventSender::scheduleAsynchronousClick); 277 bindMethod("scheduleAsynchronousKeyDown", &EventSender::scheduleAsynchronousKeyDown); 278 bindMethod("setTouchModifier", &EventSender::setTouchModifier); 279 bindMethod("textZoomIn", &EventSender::textZoomIn); 280 bindMethod("textZoomOut", &EventSender::textZoomOut); 281 bindMethod("touchCancel", &EventSender::touchCancel); 282 bindMethod("touchEnd", &EventSender::touchEnd); 283 bindMethod("touchMove", &EventSender::touchMove); 284 bindMethod("touchStart", &EventSender::touchStart); 285 bindMethod("updateTouchPoint", &EventSender::updateTouchPoint); 286 bindMethod("gestureFlingCancel", &EventSender::gestureFlingCancel); 287 bindMethod("gestureFlingStart", &EventSender::gestureFlingStart); 288 bindMethod("gestureScrollBegin", &EventSender::gestureScrollBegin); 289 bindMethod("gestureScrollEnd", &EventSender::gestureScrollEnd); 290 bindMethod("gestureScrollFirstPoint", &EventSender::gestureScrollFirstPoint); 291 bindMethod("gestureScrollUpdate", &EventSender::gestureScrollUpdate); 292 bindMethod("gestureScrollUpdateWithoutPropagation", &EventSender::gestureScrollUpdateWithoutPropagation); 293 bindMethod("gestureTap", &EventSender::gestureTap); 294 bindMethod("gestureTapDown", &EventSender::gestureTapDown); 295 bindMethod("gestureShowPress", &EventSender::gestureShowPress); 296 bindMethod("gestureTapCancel", &EventSender::gestureTapCancel); 297 bindMethod("gestureLongPress", &EventSender::gestureLongPress); 298 bindMethod("gestureLongTap", &EventSender::gestureLongTap); 299 bindMethod("gestureTwoFingerTap", &EventSender::gestureTwoFingerTap); 300 bindMethod("zoomPageIn", &EventSender::zoomPageIn); 301 bindMethod("zoomPageOut", &EventSender::zoomPageOut); 302 bindMethod("setPageScaleFactor", &EventSender::setPageScaleFactor); 303 304 bindProperty("forceLayoutOnEvents", &forceLayoutOnEvents); 305 306 // When set to true (the default value), we batch mouse move and mouse up 307 // events so we can simulate drag & drop. 308 bindProperty("dragMode", &dragMode); 309 #ifdef WIN32 310 bindProperty("WM_KEYDOWN", &wmKeyDown); 311 bindProperty("WM_KEYUP", &wmKeyUp); 312 bindProperty("WM_CHAR", &wmChar); 313 bindProperty("WM_DEADCHAR", &wmDeadChar); 314 bindProperty("WM_SYSKEYDOWN", &wmSysKeyDown); 315 bindProperty("WM_SYSKEYUP", &wmSysKeyUp); 316 bindProperty("WM_SYSCHAR", &wmSysChar); 317 bindProperty("WM_SYSDEADCHAR", &wmSysDeadChar); 318 #endif 319 } 320 321 EventSender::~EventSender() 322 { 323 } 324 325 void EventSender::setContextMenuData(const WebContextMenuData& contextMenuData) 326 { 327 m_lastContextMenuData = WebScopedPtr<WebContextMenuData>(new WebContextMenuData(contextMenuData)); 328 } 329 330 void EventSender::reset() 331 { 332 // The test should have finished a drag and the mouse button state. 333 BLINK_ASSERT(currentDragData.isNull()); 334 currentDragData.reset(); 335 currentDragEffect = blink::WebDragOperationNone; 336 currentDragEffectsAllowed = blink::WebDragOperationNone; 337 if (webview() && pressedButton != WebMouseEvent::ButtonNone) 338 webview()->mouseCaptureLost(); 339 pressedButton = WebMouseEvent::ButtonNone; 340 dragMode.set(true); 341 forceLayoutOnEvents.set(true); 342 #ifdef WIN32 343 wmKeyDown.set(WM_KEYDOWN); 344 wmKeyUp.set(WM_KEYUP); 345 wmChar.set(WM_CHAR); 346 wmDeadChar.set(WM_DEADCHAR); 347 wmSysKeyDown.set(WM_SYSKEYDOWN); 348 wmSysKeyUp.set(WM_SYSKEYUP); 349 wmSysChar.set(WM_SYSCHAR); 350 wmSysDeadChar.set(WM_SYSDEADCHAR); 351 #endif 352 lastMousePos = WebPoint(0, 0); 353 lastClickTimeSec = 0; 354 lastClickPos = WebPoint(0, 0); 355 clickCount = 0; 356 lastButtonType = WebMouseEvent::ButtonNone; 357 timeOffsetMs = 0; 358 touchModifiers = 0; 359 touchPoints.clear(); 360 m_taskList.revokeAll(); 361 m_currentGestureLocation = WebPoint(0, 0); 362 mouseEventQueue.clear(); 363 } 364 365 void EventSender::doDragDrop(const WebDragData& dragData, WebDragOperationsMask mask) 366 { 367 WebMouseEvent event; 368 initMouseEvent(WebInputEvent::MouseDown, pressedButton, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 369 WebPoint clientPoint(event.x, event.y); 370 WebPoint screenPoint(event.globalX, event.globalY); 371 currentDragData = dragData; 372 currentDragEffectsAllowed = mask; 373 currentDragEffect = webview()->dragTargetDragEnter(dragData, clientPoint, screenPoint, currentDragEffectsAllowed, 0); 374 375 // Finish processing events. 376 replaySavedEvents(); 377 } 378 379 void EventSender::dumpFilenameBeingDragged(const CppArgumentList&, CppVariant*) 380 { 381 WebString filename; 382 WebVector<WebDragData::Item> items = currentDragData.items(); 383 for (size_t i = 0; i < items.size(); ++i) { 384 if (items[i].storageType == WebDragData::Item::StorageTypeBinaryData) { 385 filename = items[i].title; 386 break; 387 } 388 } 389 m_delegate->printMessage(std::string("Filename being dragged: ") + filename.utf8().data() + "\n"); 390 } 391 392 WebMouseEvent::Button EventSender::getButtonTypeFromButtonNumber(int buttonCode) 393 { 394 if (!buttonCode) 395 return WebMouseEvent::ButtonLeft; 396 if (buttonCode == 2) 397 return WebMouseEvent::ButtonRight; 398 return WebMouseEvent::ButtonMiddle; 399 } 400 401 int EventSender::getButtonNumberFromSingleArg(const CppArgumentList& arguments) 402 { 403 int buttonCode = 0; 404 if (arguments.size() > 0 && arguments[0].isNumber()) 405 buttonCode = arguments[0].toInt32(); 406 return buttonCode; 407 } 408 409 void EventSender::updateClickCountForButton(WebMouseEvent::Button buttonType) 410 { 411 if ((getCurrentEventTimeSec(m_delegate) - lastClickTimeSec < multipleClickTimeSec) 412 && (!outsideMultiClickRadius(lastMousePos, lastClickPos)) 413 && (buttonType == lastButtonType)) 414 ++clickCount; 415 else { 416 clickCount = 1; 417 lastButtonType = buttonType; 418 } 419 } 420 421 // 422 // Implemented javascript methods. 423 // 424 425 void EventSender::mouseDown(const CppArgumentList& arguments, CppVariant* result) 426 { 427 if (result) // Could be 0 if invoked asynchronously. 428 result->setNull(); 429 430 if (shouldForceLayoutOnEvents()) 431 webview()->layout(); 432 433 int buttonNumber = getButtonNumberFromSingleArg(arguments); 434 BLINK_ASSERT(buttonNumber != -1); 435 436 WebMouseEvent::Button buttonType = getButtonTypeFromButtonNumber(buttonNumber); 437 438 updateClickCountForButton(buttonType); 439 440 WebMouseEvent event; 441 pressedButton = buttonType; 442 initMouseEvent(WebInputEvent::MouseDown, buttonType, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 443 if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) 444 applyKeyModifiers(&(arguments[1]), &event); 445 webview()->handleInputEvent(event); 446 } 447 448 void EventSender::mouseUp(const CppArgumentList& arguments, CppVariant* result) 449 { 450 if (result) // Could be 0 if invoked asynchronously. 451 result->setNull(); 452 453 if (shouldForceLayoutOnEvents()) 454 webview()->layout(); 455 456 int buttonNumber = getButtonNumberFromSingleArg(arguments); 457 BLINK_ASSERT(buttonNumber != -1); 458 459 WebMouseEvent::Button buttonType = getButtonTypeFromButtonNumber(buttonNumber); 460 461 if (isDragMode() && !replayingSavedEvents) { 462 SavedEvent savedEvent; 463 savedEvent.type = SavedEvent::MouseUp; 464 savedEvent.buttonType = buttonType; 465 mouseEventQueue.push_back(savedEvent); 466 replaySavedEvents(); 467 } else { 468 WebMouseEvent event; 469 initMouseEvent(WebInputEvent::MouseUp, buttonType, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 470 if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) 471 applyKeyModifiers(&(arguments[1]), &event); 472 doMouseUp(event); 473 } 474 } 475 476 void EventSender::doMouseUp(const WebMouseEvent& e) 477 { 478 webview()->handleInputEvent(e); 479 480 pressedButton = WebMouseEvent::ButtonNone; 481 lastClickTimeSec = e.timeStampSeconds; 482 lastClickPos = lastMousePos; 483 484 // If we're in a drag operation, complete it. 485 if (currentDragData.isNull()) 486 return; 487 488 WebPoint clientPoint(e.x, e.y); 489 WebPoint screenPoint(e.globalX, e.globalY); 490 finishDragAndDrop(e, webview()->dragTargetDragOver(clientPoint, screenPoint, currentDragEffectsAllowed, 0)); 491 } 492 493 void EventSender::finishDragAndDrop(const WebMouseEvent& e, blink::WebDragOperation dragEffect) 494 { 495 WebPoint clientPoint(e.x, e.y); 496 WebPoint screenPoint(e.globalX, e.globalY); 497 currentDragEffect = dragEffect; 498 if (currentDragEffect) 499 webview()->dragTargetDrop(clientPoint, screenPoint, 0); 500 else 501 webview()->dragTargetDragLeave(); 502 webview()->dragSourceEndedAt(clientPoint, screenPoint, currentDragEffect); 503 webview()->dragSourceSystemDragEnded(); 504 505 currentDragData.reset(); 506 } 507 508 void EventSender::mouseMoveTo(const CppArgumentList& arguments, CppVariant* result) 509 { 510 result->setNull(); 511 512 if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) 513 return; 514 if (shouldForceLayoutOnEvents()) 515 webview()->layout(); 516 517 WebPoint mousePos(arguments[0].toInt32(), arguments[1].toInt32()); 518 519 if (isDragMode() && pressedButton == WebMouseEvent::ButtonLeft && !replayingSavedEvents) { 520 SavedEvent savedEvent; 521 savedEvent.type = SavedEvent::MouseMove; 522 savedEvent.pos = mousePos; 523 mouseEventQueue.push_back(savedEvent); 524 } else { 525 WebMouseEvent event; 526 initMouseEvent(WebInputEvent::MouseMove, pressedButton, mousePos, &event, getCurrentEventTimeSec(m_delegate)); 527 if (arguments.size() >= 3 && (arguments[2].isObject() || arguments[2].isString())) 528 applyKeyModifiers(&(arguments[2]), &event); 529 doMouseMove(event); 530 } 531 } 532 533 void EventSender::doMouseMove(const WebMouseEvent& e) 534 { 535 lastMousePos = WebPoint(e.x, e.y); 536 537 webview()->handleInputEvent(e); 538 539 if (pressedButton == WebMouseEvent::ButtonNone || currentDragData.isNull()) 540 return; 541 WebPoint clientPoint(e.x, e.y); 542 WebPoint screenPoint(e.globalX, e.globalY); 543 currentDragEffect = webview()->dragTargetDragOver(clientPoint, screenPoint, currentDragEffectsAllowed, 0); 544 } 545 546 void EventSender::keyDown(const CppArgumentList& arguments, CppVariant* result) 547 { 548 if (result) 549 result->setNull(); 550 if (arguments.size() < 1 || !arguments[0].isString()) 551 return; 552 bool generateChar = false; 553 554 // FIXME: I'm not exactly sure how we should convert the string to a key 555 // event. This seems to work in the cases I tested. 556 // FIXME: Should we also generate a KEY_UP? 557 string codeStr = arguments[0].toString(); 558 559 // Convert \n -> VK_RETURN. Some layout tests use \n to mean "Enter", when 560 // Windows uses \r for "Enter". 561 int code = 0; 562 int text = 0; 563 bool needsShiftKeyModifier = false; 564 if ("\n" == codeStr) { 565 generateChar = true; 566 text = code = VKEY_RETURN; 567 } else if ("rightArrow" == codeStr) 568 code = VKEY_RIGHT; 569 else if ("downArrow" == codeStr) 570 code = VKEY_DOWN; 571 else if ("leftArrow" == codeStr) 572 code = VKEY_LEFT; 573 else if ("upArrow" == codeStr) 574 code = VKEY_UP; 575 else if ("insert" == codeStr) 576 code = VKEY_INSERT; 577 else if ("delete" == codeStr) 578 code = VKEY_DELETE; 579 else if ("pageUp" == codeStr) 580 code = VKEY_PRIOR; 581 else if ("pageDown" == codeStr) 582 code = VKEY_NEXT; 583 else if ("home" == codeStr) 584 code = VKEY_HOME; 585 else if ("end" == codeStr) 586 code = VKEY_END; 587 else if ("printScreen" == codeStr) 588 code = VKEY_SNAPSHOT; 589 else if ("menu" == codeStr) 590 code = VKEY_APPS; 591 else if ("leftControl" == codeStr) 592 code = VKEY_LCONTROL; 593 else if ("rightControl" == codeStr) 594 code = VKEY_RCONTROL; 595 else if ("leftShift" == codeStr) 596 code = VKEY_LSHIFT; 597 else if ("rightShift" == codeStr) 598 code = VKEY_RSHIFT; 599 else if ("leftAlt" == codeStr) 600 code = VKEY_LMENU; 601 else if ("rightAlt" == codeStr) 602 code = VKEY_RMENU; 603 else if ("numLock" == codeStr) 604 code = VKEY_NUMLOCK; 605 else { 606 // Compare the input string with the function-key names defined by the 607 // DOM spec (i.e. "F1",...,"F24"). If the input string is a function-key 608 // name, set its key code. 609 for (int i = 1; i <= 24; ++i) { 610 char functionChars[10]; 611 snprintf(functionChars, 10, "F%d", i); 612 string functionKeyName(functionChars); 613 if (functionKeyName == codeStr) { 614 code = VKEY_F1 + (i - 1); 615 break; 616 } 617 } 618 if (!code) { 619 WebString webCodeStr = WebString::fromUTF8(codeStr.data(), codeStr.size()); 620 BLINK_ASSERT(webCodeStr.length() == 1); 621 text = code = webCodeStr.at(0); 622 needsShiftKeyModifier = needsShiftModifier(code); 623 if ((code & 0xFF) >= 'a' && (code & 0xFF) <= 'z') 624 code -= 'a' - 'A'; 625 generateChar = true; 626 } 627 628 if ("(" == codeStr) { 629 code = '9'; 630 needsShiftKeyModifier = true; 631 } 632 } 633 634 // For one generated keyboard event, we need to generate a keyDown/keyUp 635 // pair; refer to EventSender.cpp in Tools/DumpRenderTree/win. 636 // On Windows, we might also need to generate a char event to mimic the 637 // Windows event flow; on other platforms we create a merged event and test 638 // the event flow that that platform provides. 639 WebKeyboardEvent eventDown, eventChar, eventUp; 640 eventDown.type = WebInputEvent::RawKeyDown; 641 eventDown.modifiers = 0; 642 eventDown.windowsKeyCode = code; 643 #if defined(__linux__) && defined(TOOLKIT_GTK) 644 eventDown.nativeKeyCode = NativeKeyCodeForWindowsKeyCode(code); 645 #endif 646 647 if (generateChar) { 648 eventDown.text[0] = text; 649 eventDown.unmodifiedText[0] = text; 650 } 651 eventDown.setKeyIdentifierFromWindowsKeyCode(); 652 653 if (arguments.size() >= 2 && (arguments[1].isObject() || arguments[1].isString())) { 654 applyKeyModifiers(&(arguments[1]), &eventDown); 655 #if WIN32 || __APPLE__ || defined(ANDROID) || defined(TOOLKIT_GTK) 656 eventDown.isSystemKey = WebInputEventFactory::isSystemKeyEvent(eventDown); 657 #endif 658 } 659 660 if (needsShiftKeyModifier) 661 eventDown.modifiers |= WebInputEvent::ShiftKey; 662 663 // See if KeyLocation argument is given. 664 if (arguments.size() >= 3 && arguments[2].isNumber()) { 665 int location = arguments[2].toInt32(); 666 if (location == DOMKeyLocationNumpad) 667 eventDown.modifiers |= WebInputEvent::IsKeyPad; 668 } 669 670 eventChar = eventUp = eventDown; 671 eventUp.type = WebInputEvent::KeyUp; 672 // EventSender.m forces a layout here, with at least one 673 // test (fast/forms/focus-control-to-page.html) relying on this. 674 if (shouldForceLayoutOnEvents()) 675 webview()->layout(); 676 677 // In the browser, if a keyboard event corresponds to an editor command, 678 // the command will be dispatched to the renderer just before dispatching 679 // the keyboard event, and then it will be executed in the 680 // RenderView::handleCurrentKeyboardEvent() method, which is called from 681 // third_party/WebKit/Source/WebKit/chromium/src/EditorClientImpl.cpp. 682 // We just simulate the same behavior here. 683 string editCommand; 684 if (getEditCommand(eventDown, &editCommand)) 685 m_delegate->setEditCommand(editCommand, ""); 686 687 webview()->handleInputEvent(eventDown); 688 689 if (code == VKEY_ESCAPE && !currentDragData.isNull()) { 690 WebMouseEvent event; 691 initMouseEvent(WebInputEvent::MouseDown, pressedButton, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 692 finishDragAndDrop(event, blink::WebDragOperationNone); 693 } 694 695 m_delegate->clearEditCommand(); 696 697 if (generateChar) { 698 eventChar.type = WebInputEvent::Char; 699 eventChar.keyIdentifier[0] = '\0'; 700 webview()->handleInputEvent(eventChar); 701 } 702 703 webview()->handleInputEvent(eventUp); 704 } 705 706 void EventSender::dispatchMessage(const CppArgumentList& arguments, CppVariant* result) 707 { 708 result->setNull(); 709 710 #ifdef WIN32 711 if (arguments.size() == 3) { 712 // Grab the message id to see if we need to dispatch it. 713 int msg = arguments[0].toInt32(); 714 715 // WebKit's version of this function stuffs a MSG struct and uses 716 // TranslateMessage and DispatchMessage. We use a WebKeyboardEvent, which 717 // doesn't need to receive the DeadChar and SysDeadChar messages. 718 if (msg == WM_DEADCHAR || msg == WM_SYSDEADCHAR) 719 return; 720 721 if (shouldForceLayoutOnEvents()) 722 webview()->layout(); 723 724 unsigned long lparam = static_cast<unsigned long>(arguments[2].toDouble()); 725 webview()->handleInputEvent(WebInputEventFactory::keyboardEvent(0, msg, arguments[1].toInt32(), lparam)); 726 } else 727 BLINK_ASSERT_NOT_REACHED(); 728 #endif 729 } 730 731 bool EventSender::needsShiftModifier(int keyCode) 732 { 733 // If code is an uppercase letter, assign a SHIFT key to 734 // eventDown.modifier, this logic comes from 735 // Tools/DumpRenderTree/win/EventSender.cpp 736 return (keyCode & 0xFF) >= 'A' && (keyCode & 0xFF) <= 'Z'; 737 } 738 739 void EventSender::leapForward(const CppArgumentList& arguments, CppVariant* result) 740 { 741 result->setNull(); 742 743 if (arguments.size() < 1 || !arguments[0].isNumber()) 744 return; 745 746 int milliseconds = arguments[0].toInt32(); 747 if (isDragMode() && pressedButton == WebMouseEvent::ButtonLeft && !replayingSavedEvents) { 748 SavedEvent savedEvent; 749 savedEvent.type = SavedEvent::LeapForward; 750 savedEvent.milliseconds = milliseconds; 751 mouseEventQueue.push_back(savedEvent); 752 } else 753 doLeapForward(milliseconds); 754 } 755 756 void EventSender::doLeapForward(int milliseconds) 757 { 758 advanceEventTime(milliseconds); 759 } 760 761 // Apple's port of WebKit zooms by a factor of 1.2 (see 762 // WebKit/WebView/WebView.mm) 763 void EventSender::textZoomIn(const CppArgumentList&, CppVariant* result) 764 { 765 webview()->setTextZoomFactor(webview()->textZoomFactor() * 1.2f); 766 result->setNull(); 767 } 768 769 void EventSender::textZoomOut(const CppArgumentList&, CppVariant* result) 770 { 771 webview()->setTextZoomFactor(webview()->textZoomFactor() / 1.2f); 772 result->setNull(); 773 } 774 775 void EventSender::zoomPageIn(const CppArgumentList&, CppVariant* result) 776 { 777 const vector<WebTestProxyBase*>& windowList = m_testInterfaces->windowList(); 778 779 for (size_t i = 0; i < windowList.size(); ++i) 780 windowList.at(i)->webView()->setZoomLevel(windowList.at(i)->webView()->zoomLevel() + 1); 781 result->setNull(); 782 } 783 784 void EventSender::zoomPageOut(const CppArgumentList&, CppVariant* result) 785 { 786 const vector<WebTestProxyBase*>& windowList = m_testInterfaces->windowList(); 787 788 for (size_t i = 0; i < windowList.size(); ++i) 789 windowList.at(i)->webView()->setZoomLevel(windowList.at(i)->webView()->zoomLevel() - 1); 790 result->setNull(); 791 } 792 793 void EventSender::setPageScaleFactor(const CppArgumentList& arguments, CppVariant* result) 794 { 795 if (arguments.size() < 3 || !arguments[0].isNumber() || !arguments[1].isNumber() || !arguments[2].isNumber()) 796 return; 797 798 float scaleFactor = static_cast<float>(arguments[0].toDouble()); 799 int x = arguments[1].toInt32(); 800 int y = arguments[2].toInt32(); 801 webview()->setPageScaleFactorLimits(scaleFactor, scaleFactor); 802 webview()->setPageScaleFactor(scaleFactor, WebPoint(x, y)); 803 result->setNull(); 804 } 805 806 void EventSender::mouseScrollBy(const CppArgumentList& arguments, CppVariant* result) 807 { 808 WebMouseWheelEvent event; 809 initMouseWheelEvent(arguments, result, false, &event); 810 webview()->handleInputEvent(event); 811 } 812 813 void EventSender::continuousMouseScrollBy(const CppArgumentList& arguments, CppVariant* result) 814 { 815 WebMouseWheelEvent event; 816 initMouseWheelEvent(arguments, result, true, &event); 817 webview()->handleInputEvent(event); 818 } 819 820 void EventSender::replaySavedEvents() 821 { 822 replayingSavedEvents = true; 823 while (!mouseEventQueue.empty()) { 824 SavedEvent e = mouseEventQueue.front(); 825 mouseEventQueue.pop_front(); 826 827 switch (e.type) { 828 case SavedEvent::MouseMove: { 829 WebMouseEvent event; 830 initMouseEvent(WebInputEvent::MouseMove, pressedButton, e.pos, &event, getCurrentEventTimeSec(m_delegate)); 831 doMouseMove(event); 832 break; 833 } 834 case SavedEvent::LeapForward: 835 doLeapForward(e.milliseconds); 836 break; 837 case SavedEvent::MouseUp: { 838 WebMouseEvent event; 839 initMouseEvent(WebInputEvent::MouseUp, e.buttonType, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 840 doMouseUp(event); 841 break; 842 } 843 default: 844 BLINK_ASSERT_NOT_REACHED(); 845 } 846 } 847 848 replayingSavedEvents = false; 849 } 850 851 // Because actual context menu is implemented by the browser side, 852 // this function does only what LayoutTests are expecting: 853 // - Many test checks the count of items. So returning non-zero value makes sense. 854 // - Some test compares the count before and after some action. So changing the count based on flags 855 // also makes sense. This function is doing such for some flags. 856 // - Some test even checks actual string content. So providing it would be also helpful. 857 // 858 static vector<WebString> makeMenuItemStringsFor(WebContextMenuData* contextMenu, WebTestDelegate* delegate) 859 { 860 // These constants are based on Safari's context menu because tests are made for it. 861 static const char* nonEditableMenuStrings[] = { "Back", "Reload Page", "Open in Dashbaord", "<separator>", "View Source", "Save Page As", "Print Page", "Inspect Element", 0 }; 862 static const char* editableMenuStrings[] = { "Cut", "Copy", "<separator>", "Paste", "Spelling and Grammar", "Substitutions, Transformations", "Font", "Speech", "Paragraph Direction", "<separator>", 0 }; 863 864 // This is possible because mouse events are cancelleable. 865 if (!contextMenu) 866 return vector<WebString>(); 867 868 vector<WebString> strings; 869 870 if (contextMenu->isEditable) { 871 for (const char** item = editableMenuStrings; *item; ++item) 872 strings.push_back(WebString::fromUTF8(*item)); 873 WebVector<WebString> suggestions; 874 MockSpellCheck::fillSuggestionList(contextMenu->misspelledWord, &suggestions); 875 for (size_t i = 0; i < suggestions.size(); ++i) 876 strings.push_back(suggestions[i]); 877 } else { 878 for (const char** item = nonEditableMenuStrings; *item; ++item) 879 strings.push_back(WebString::fromUTF8(*item)); 880 } 881 882 return strings; 883 } 884 885 void EventSender::contextClick(const CppArgumentList& arguments, CppVariant* result) 886 { 887 if (shouldForceLayoutOnEvents()) 888 webview()->layout(); 889 890 updateClickCountForButton(WebMouseEvent::ButtonRight); 891 892 // Clears last context menu data because we need to know if the context menu be requested 893 // after following mouse events. 894 m_lastContextMenuData.reset(); 895 896 // Generate right mouse down and up. 897 WebMouseEvent event; 898 // This is a hack to work around only allowing a single pressed button since we want to 899 // test the case where both the left and right mouse buttons are pressed. 900 if (pressedButton == WebMouseEvent::ButtonNone) 901 pressedButton = WebMouseEvent::ButtonRight; 902 initMouseEvent(WebInputEvent::MouseDown, WebMouseEvent::ButtonRight, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 903 webview()->handleInputEvent(event); 904 905 #ifdef WIN32 906 initMouseEvent(WebInputEvent::MouseUp, WebMouseEvent::ButtonRight, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 907 webview()->handleInputEvent(event); 908 909 pressedButton = WebMouseEvent::ButtonNone; 910 #endif 911 912 NPObject* resultArray = WebBindings::makeStringArray(makeMenuItemStringsFor(m_lastContextMenuData.get(), m_delegate)); 913 result->set(resultArray); 914 WebBindings::releaseObject(resultArray); 915 916 m_lastContextMenuData.reset(); 917 } 918 919 class MouseDownTask: public WebMethodTask<EventSender> { 920 public: 921 MouseDownTask(EventSender* obj, const CppArgumentList& arg) 922 : WebMethodTask<EventSender>(obj), m_arguments(arg) { } 923 virtual void runIfValid() { m_object->mouseDown(m_arguments, 0); } 924 925 private: 926 CppArgumentList m_arguments; 927 }; 928 929 class MouseUpTask: public WebMethodTask<EventSender> { 930 public: 931 MouseUpTask(EventSender* obj, const CppArgumentList& arg) 932 : WebMethodTask<EventSender>(obj), m_arguments(arg) { } 933 virtual void runIfValid() { m_object->mouseUp(m_arguments, 0); } 934 935 private: 936 CppArgumentList m_arguments; 937 }; 938 939 void EventSender::scheduleAsynchronousClick(const CppArgumentList& arguments, CppVariant* result) 940 { 941 result->setNull(); 942 m_delegate->postTask(new MouseDownTask(this, arguments)); 943 m_delegate->postTask(new MouseUpTask(this, arguments)); 944 } 945 946 class KeyDownTask : public WebMethodTask<EventSender> { 947 public: 948 KeyDownTask(EventSender* obj, const CppArgumentList& arg) 949 : WebMethodTask<EventSender>(obj), m_arguments(arg) { } 950 virtual void runIfValid() { m_object->keyDown(m_arguments, 0); } 951 952 private: 953 CppArgumentList m_arguments; 954 }; 955 956 void EventSender::scheduleAsynchronousKeyDown(const CppArgumentList& arguments, CppVariant* result) 957 { 958 result->setNull(); 959 m_delegate->postTask(new KeyDownTask(this, arguments)); 960 } 961 962 void EventSender::beginDragWithFiles(const CppArgumentList& arguments, CppVariant* result) 963 { 964 currentDragData.initialize(); 965 vector<string> files = arguments[0].toStringVector(); 966 WebVector<WebString> absoluteFilenames(files.size()); 967 for (size_t i = 0; i < files.size(); ++i) { 968 WebDragData::Item item; 969 item.storageType = WebDragData::Item::StorageTypeFilename; 970 item.filenameData = m_delegate->getAbsoluteWebStringFromUTF8Path(files[i]); 971 currentDragData.addItem(item); 972 absoluteFilenames[i] = item.filenameData; 973 } 974 currentDragData.setFilesystemId(m_delegate->registerIsolatedFileSystem(absoluteFilenames)); 975 currentDragEffectsAllowed = blink::WebDragOperationCopy; 976 977 // Provide a drag source. 978 webview()->dragTargetDragEnter(currentDragData, lastMousePos, lastMousePos, currentDragEffectsAllowed, 0); 979 980 // dragMode saves events and then replays them later. We don't need/want that. 981 dragMode.set(false); 982 983 // Make the rest of eventSender think a drag is in progress. 984 pressedButton = WebMouseEvent::ButtonLeft; 985 986 result->setNull(); 987 } 988 989 void EventSender::addTouchPoint(const CppArgumentList& arguments, CppVariant* result) 990 { 991 result->setNull(); 992 993 WebTouchPoint touchPoint; 994 touchPoint.state = WebTouchPoint::StatePressed; 995 touchPoint.position = WebPoint(arguments[0].toInt32(), arguments[1].toInt32()); 996 touchPoint.screenPosition = touchPoint.position; 997 998 if (arguments.size() > 2) { 999 int radiusX = arguments[2].toInt32(); 1000 int radiusY = radiusX; 1001 if (arguments.size() > 3) 1002 radiusY = arguments[3].toInt32(); 1003 1004 touchPoint.radiusX = radiusX; 1005 touchPoint.radiusY = radiusY; 1006 } 1007 1008 int lowestId = 0; 1009 for (size_t i = 0; i < touchPoints.size(); i++) { 1010 if (touchPoints[i].id == lowestId) 1011 lowestId++; 1012 } 1013 touchPoint.id = lowestId; 1014 touchPoints.push_back(touchPoint); 1015 } 1016 1017 void EventSender::clearTouchPoints(const CppArgumentList&, CppVariant* result) 1018 { 1019 result->setNull(); 1020 touchPoints.clear(); 1021 } 1022 1023 void EventSender::releaseTouchPoint(const CppArgumentList& arguments, CppVariant* result) 1024 { 1025 result->setNull(); 1026 1027 const unsigned index = arguments[0].toInt32(); 1028 BLINK_ASSERT(index < touchPoints.size()); 1029 1030 WebTouchPoint* touchPoint = &touchPoints[index]; 1031 touchPoint->state = WebTouchPoint::StateReleased; 1032 } 1033 1034 void EventSender::setTouchModifier(const CppArgumentList& arguments, CppVariant* result) 1035 { 1036 result->setNull(); 1037 1038 int mask = 0; 1039 const string keyName = arguments[0].toString(); 1040 if (keyName == "shift") 1041 mask = WebInputEvent::ShiftKey; 1042 else if (keyName == "alt") 1043 mask = WebInputEvent::AltKey; 1044 else if (keyName == "ctrl") 1045 mask = WebInputEvent::ControlKey; 1046 else if (keyName == "meta") 1047 mask = WebInputEvent::MetaKey; 1048 1049 if (arguments[1].toBoolean()) 1050 touchModifiers |= mask; 1051 else 1052 touchModifiers &= ~mask; 1053 } 1054 1055 void EventSender::updateTouchPoint(const CppArgumentList& arguments, CppVariant* result) 1056 { 1057 result->setNull(); 1058 1059 const unsigned index = arguments[0].toInt32(); 1060 BLINK_ASSERT(index < touchPoints.size()); 1061 1062 WebPoint position(arguments[1].toInt32(), arguments[2].toInt32()); 1063 WebTouchPoint* touchPoint = &touchPoints[index]; 1064 touchPoint->state = WebTouchPoint::StateMoved; 1065 touchPoint->position = position; 1066 touchPoint->screenPosition = position; 1067 } 1068 1069 void EventSender::cancelTouchPoint(const CppArgumentList& arguments, CppVariant* result) 1070 { 1071 result->setNull(); 1072 1073 const unsigned index = arguments[0].toInt32(); 1074 BLINK_ASSERT(index < touchPoints.size()); 1075 1076 WebTouchPoint* touchPoint = &touchPoints[index]; 1077 touchPoint->state = WebTouchPoint::StateCancelled; 1078 } 1079 1080 void EventSender::sendCurrentTouchEvent(const WebInputEvent::Type type) 1081 { 1082 BLINK_ASSERT(static_cast<unsigned>(WebTouchEvent::touchesLengthCap) > touchPoints.size()); 1083 if (shouldForceLayoutOnEvents()) 1084 webview()->layout(); 1085 1086 WebTouchEvent touchEvent; 1087 touchEvent.type = type; 1088 touchEvent.modifiers = touchModifiers; 1089 touchEvent.timeStampSeconds = getCurrentEventTimeSec(m_delegate); 1090 touchEvent.touchesLength = touchPoints.size(); 1091 for (unsigned i = 0; i < touchPoints.size(); ++i) 1092 touchEvent.touches[i] = touchPoints[i]; 1093 webview()->handleInputEvent(touchEvent); 1094 1095 for (unsigned i = 0; i < touchPoints.size(); ++i) { 1096 WebTouchPoint* touchPoint = &touchPoints[i]; 1097 if (touchPoint->state == WebTouchPoint::StateReleased) { 1098 touchPoints.erase(touchPoints.begin() + i); 1099 --i; 1100 } else 1101 touchPoint->state = WebTouchPoint::StateStationary; 1102 } 1103 } 1104 1105 void EventSender::mouseDragBegin(const CppArgumentList& arguments, CppVariant* result) 1106 { 1107 WebMouseWheelEvent event; 1108 initMouseEvent(WebInputEvent::MouseWheel, WebMouseEvent::ButtonNone, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 1109 event.phase = WebMouseWheelEvent::PhaseBegan; 1110 event.hasPreciseScrollingDeltas = true; 1111 webview()->handleInputEvent(event); 1112 } 1113 1114 void EventSender::mouseDragEnd(const CppArgumentList& arguments, CppVariant* result) 1115 { 1116 WebMouseWheelEvent event; 1117 initMouseEvent(WebInputEvent::MouseWheel, WebMouseEvent::ButtonNone, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 1118 event.phase = WebMouseWheelEvent::PhaseEnded; 1119 event.hasPreciseScrollingDeltas = true; 1120 webview()->handleInputEvent(event); 1121 } 1122 1123 void EventSender::mouseMomentumBegin(const CppArgumentList& arguments, CppVariant* result) 1124 { 1125 WebMouseWheelEvent event; 1126 initMouseEvent(WebInputEvent::MouseWheel, WebMouseEvent::ButtonNone, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 1127 event.momentumPhase = WebMouseWheelEvent::PhaseBegan; 1128 event.hasPreciseScrollingDeltas = true; 1129 webview()->handleInputEvent(event); 1130 } 1131 1132 void EventSender::mouseMomentumScrollBy(const CppArgumentList& arguments, CppVariant* result) 1133 { 1134 WebMouseWheelEvent event; 1135 initMouseWheelEvent(arguments, result, true, &event); 1136 event.momentumPhase = WebMouseWheelEvent::PhaseChanged; 1137 event.hasPreciseScrollingDeltas = true; 1138 webview()->handleInputEvent(event); 1139 } 1140 1141 void EventSender::mouseMomentumEnd(const CppArgumentList& arguments, CppVariant* result) 1142 { 1143 WebMouseWheelEvent event; 1144 initMouseEvent(WebInputEvent::MouseWheel, WebMouseEvent::ButtonNone, lastMousePos, &event, getCurrentEventTimeSec(m_delegate)); 1145 event.momentumPhase = WebMouseWheelEvent::PhaseEnded; 1146 event.hasPreciseScrollingDeltas = true; 1147 webview()->handleInputEvent(event); 1148 } 1149 1150 void EventSender::initMouseWheelEvent(const CppArgumentList& arguments, CppVariant* result, bool continuous, WebMouseWheelEvent* event) 1151 { 1152 result->setNull(); 1153 1154 if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) 1155 return; 1156 1157 // Force a layout here just to make sure every position has been 1158 // determined before we send events (as well as all the other methods 1159 // that send an event do). 1160 if (shouldForceLayoutOnEvents()) 1161 webview()->layout(); 1162 1163 int horizontal = arguments[0].toInt32(); 1164 int vertical = arguments[1].toInt32(); 1165 int paged = false; 1166 int hasPreciseScrollingDeltas = false; 1167 1168 if (arguments.size() > 2 && arguments[2].isBool()) 1169 paged = arguments[2].toBoolean(); 1170 1171 if (arguments.size() > 3 && arguments[3].isBool()) 1172 hasPreciseScrollingDeltas = arguments[3].toBoolean(); 1173 1174 initMouseEvent(WebInputEvent::MouseWheel, pressedButton, lastMousePos, event, getCurrentEventTimeSec(m_delegate)); 1175 event->wheelTicksX = static_cast<float>(horizontal); 1176 event->wheelTicksY = static_cast<float>(vertical); 1177 event->deltaX = event->wheelTicksX; 1178 event->deltaY = event->wheelTicksY; 1179 event->scrollByPage = paged; 1180 event->hasPreciseScrollingDeltas = hasPreciseScrollingDeltas; 1181 1182 if (continuous) { 1183 event->wheelTicksX /= scrollbarPixelsPerTick; 1184 event->wheelTicksY /= scrollbarPixelsPerTick; 1185 } else { 1186 event->deltaX *= scrollbarPixelsPerTick; 1187 event->deltaY *= scrollbarPixelsPerTick; 1188 } 1189 } 1190 1191 void EventSender::touchEnd(const CppArgumentList&, CppVariant* result) 1192 { 1193 result->setNull(); 1194 sendCurrentTouchEvent(WebInputEvent::TouchEnd); 1195 } 1196 1197 void EventSender::touchMove(const CppArgumentList&, CppVariant* result) 1198 { 1199 result->setNull(); 1200 sendCurrentTouchEvent(WebInputEvent::TouchMove); 1201 } 1202 1203 void EventSender::touchStart(const CppArgumentList&, CppVariant* result) 1204 { 1205 result->setNull(); 1206 sendCurrentTouchEvent(WebInputEvent::TouchStart); 1207 } 1208 1209 void EventSender::touchCancel(const CppArgumentList&, CppVariant* result) 1210 { 1211 result->setNull(); 1212 sendCurrentTouchEvent(WebInputEvent::TouchCancel); 1213 } 1214 1215 void EventSender::gestureScrollBegin(const CppArgumentList& arguments, CppVariant* result) 1216 { 1217 result->setNull(); 1218 gestureEvent(WebInputEvent::GestureScrollBegin, arguments); 1219 } 1220 1221 void EventSender::gestureScrollEnd(const CppArgumentList& arguments, CppVariant* result) 1222 { 1223 result->setNull(); 1224 gestureEvent(WebInputEvent::GestureScrollEnd, arguments); 1225 } 1226 1227 void EventSender::gestureScrollUpdate(const CppArgumentList& arguments, CppVariant* result) 1228 { 1229 result->setNull(); 1230 gestureEvent(WebInputEvent::GestureScrollUpdate, arguments); 1231 } 1232 1233 void EventSender::gestureScrollUpdateWithoutPropagation(const CppArgumentList& arguments, CppVariant* result) 1234 { 1235 result->setNull(); 1236 gestureEvent(WebInputEvent::GestureScrollUpdateWithoutPropagation, arguments); 1237 } 1238 1239 void EventSender::gestureTap(const CppArgumentList& arguments, CppVariant* result) 1240 { 1241 result->setNull(); 1242 gestureEvent(WebInputEvent::GestureTap, arguments); 1243 } 1244 1245 void EventSender::gestureTapDown(const CppArgumentList& arguments, CppVariant* result) 1246 { 1247 result->setNull(); 1248 gestureEvent(WebInputEvent::GestureTapDown, arguments); 1249 } 1250 1251 void EventSender::gestureShowPress(const CppArgumentList& arguments, CppVariant* result) 1252 { 1253 result->setNull(); 1254 gestureEvent(WebInputEvent::GestureShowPress, arguments); 1255 } 1256 1257 void EventSender::gestureTapCancel(const CppArgumentList& arguments, CppVariant* result) 1258 { 1259 result->setNull(); 1260 gestureEvent(WebInputEvent::GestureTapCancel, arguments); 1261 } 1262 1263 void EventSender::gestureLongPress(const CppArgumentList& arguments, CppVariant* result) 1264 { 1265 result->setNull(); 1266 gestureEvent(WebInputEvent::GestureLongPress, arguments); 1267 } 1268 1269 void EventSender::gestureLongTap(const CppArgumentList& arguments, CppVariant* result) 1270 { 1271 result->setNull(); 1272 gestureEvent(WebInputEvent::GestureLongTap, arguments); 1273 } 1274 1275 void EventSender::gestureTwoFingerTap(const CppArgumentList& arguments, CppVariant* result) 1276 { 1277 result->setNull(); 1278 gestureEvent(WebInputEvent::GestureTwoFingerTap, arguments); 1279 } 1280 1281 void EventSender::gestureScrollFirstPoint(const CppArgumentList& arguments, CppVariant* result) 1282 { 1283 result->setNull(); 1284 if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) 1285 return; 1286 1287 WebPoint point(arguments[0].toInt32(), arguments[1].toInt32()); 1288 m_currentGestureLocation = point; 1289 } 1290 1291 void EventSender::gestureEvent(WebInputEvent::Type type, const CppArgumentList& arguments) 1292 { 1293 if (arguments.size() < 2 || !arguments[0].isNumber() || !arguments[1].isNumber()) 1294 return; 1295 1296 WebPoint point(arguments[0].toInt32(), arguments[1].toInt32()); 1297 1298 WebGestureEvent event; 1299 event.type = type; 1300 1301 switch (type) { 1302 case WebInputEvent::GestureScrollUpdate: 1303 case WebInputEvent::GestureScrollUpdateWithoutPropagation: 1304 event.data.scrollUpdate.deltaX = static_cast<float>(arguments[0].toDouble()); 1305 event.data.scrollUpdate.deltaY = static_cast<float>(arguments[1].toDouble()); 1306 event.x = m_currentGestureLocation.x; 1307 event.y = m_currentGestureLocation.y; 1308 m_currentGestureLocation.x = m_currentGestureLocation.x + event.data.scrollUpdate.deltaX; 1309 m_currentGestureLocation.y = m_currentGestureLocation.y + event.data.scrollUpdate.deltaY; 1310 break; 1311 1312 case WebInputEvent::GestureScrollBegin: 1313 m_currentGestureLocation = WebPoint(point.x, point.y); 1314 event.x = m_currentGestureLocation.x; 1315 event.y = m_currentGestureLocation.y; 1316 break; 1317 case WebInputEvent::GestureScrollEnd: 1318 case WebInputEvent::GestureFlingStart: 1319 event.x = m_currentGestureLocation.x; 1320 event.y = m_currentGestureLocation.y; 1321 break; 1322 case WebInputEvent::GestureTap: 1323 if (arguments.size() >= 3) 1324 event.data.tap.tapCount = static_cast<float>(arguments[2].toDouble()); 1325 else 1326 event.data.tap.tapCount = 1; 1327 event.x = point.x; 1328 event.y = point.y; 1329 break; 1330 case WebInputEvent::GestureTapUnconfirmed: 1331 if (arguments.size() >= 3) 1332 event.data.tap.tapCount = static_cast<float>(arguments[2].toDouble()); 1333 else 1334 event.data.tap.tapCount = 1; 1335 event.x = point.x; 1336 event.y = point.y; 1337 break; 1338 case WebInputEvent::GestureTapDown: 1339 event.x = point.x; 1340 event.y = point.y; 1341 if (arguments.size() >= 4) { 1342 event.data.tapDown.width = static_cast<float>(arguments[2].toDouble()); 1343 event.data.tapDown.height = static_cast<float>(arguments[3].toDouble()); 1344 } 1345 break; 1346 case WebInputEvent::GestureShowPress: 1347 event.x = point.x; 1348 event.y = point.y; 1349 if (arguments.size() >= 4) { 1350 event.data.showPress.width = static_cast<float>(arguments[2].toDouble()); 1351 event.data.showPress.height = static_cast<float>(arguments[3].toDouble()); 1352 } 1353 break; 1354 case WebInputEvent::GestureTapCancel: 1355 event.x = point.x; 1356 event.y = point.y; 1357 break; 1358 case WebInputEvent::GestureLongPress: 1359 event.x = point.x; 1360 event.y = point.y; 1361 if (arguments.size() >= 4) { 1362 event.data.longPress.width = static_cast<float>(arguments[2].toDouble()); 1363 event.data.longPress.height = static_cast<float>(arguments[3].toDouble()); 1364 } 1365 break; 1366 case WebInputEvent::GestureLongTap: 1367 event.x = point.x; 1368 event.y = point.y; 1369 if (arguments.size() >= 4) { 1370 event.data.longPress.width = static_cast<float>(arguments[2].toDouble()); 1371 event.data.longPress.height = static_cast<float>(arguments[3].toDouble()); 1372 } 1373 break; 1374 case WebInputEvent::GestureTwoFingerTap: 1375 event.x = point.x; 1376 event.y = point.y; 1377 if (arguments.size() >= 4) { 1378 event.data.twoFingerTap.firstFingerWidth = static_cast<float>(arguments[2].toDouble()); 1379 event.data.twoFingerTap.firstFingerHeight = static_cast<float>(arguments[3].toDouble()); 1380 } 1381 break; 1382 default: 1383 BLINK_ASSERT_NOT_REACHED(); 1384 } 1385 1386 event.globalX = event.x; 1387 event.globalY = event.y; 1388 event.timeStampSeconds = getCurrentEventTimeSec(m_delegate); 1389 1390 if (shouldForceLayoutOnEvents()) 1391 webview()->layout(); 1392 1393 webview()->handleInputEvent(event); 1394 1395 // Long press might start a drag drop session. Complete it if so. 1396 if (type == WebInputEvent::GestureLongPress && !currentDragData.isNull()) { 1397 WebMouseEvent mouseEvent; 1398 initMouseEvent(WebInputEvent::MouseDown, pressedButton, point, &mouseEvent, getCurrentEventTimeSec(m_delegate)); 1399 finishDragAndDrop(mouseEvent, blink::WebDragOperationNone); 1400 } 1401 } 1402 1403 void EventSender::gestureFlingCancel(const CppArgumentList&, CppVariant* result) 1404 { 1405 result->setNull(); 1406 1407 WebGestureEvent event; 1408 event.type = WebInputEvent::GestureFlingCancel; 1409 event.timeStampSeconds = getCurrentEventTimeSec(m_delegate); 1410 1411 if (shouldForceLayoutOnEvents()) 1412 webview()->layout(); 1413 1414 webview()->handleInputEvent(event); 1415 } 1416 1417 void EventSender::gestureFlingStart(const CppArgumentList& arguments, CppVariant* result) 1418 { 1419 result->setNull(); 1420 if (arguments.size() < 4) 1421 return; 1422 1423 for (int i = 0; i < 4; i++) 1424 if (!arguments[i].isNumber()) 1425 return; 1426 1427 WebGestureEvent event; 1428 event.type = WebInputEvent::GestureFlingStart; 1429 1430 event.x = static_cast<float>(arguments[0].toDouble()); 1431 event.y = static_cast<float>(arguments[1].toDouble()); 1432 event.globalX = event.x; 1433 event.globalY = event.y; 1434 1435 event.data.flingStart.velocityX = static_cast<float>(arguments[2].toDouble()); 1436 event.data.flingStart.velocityY = static_cast<float>(arguments[3].toDouble()); 1437 event.timeStampSeconds = getCurrentEventTimeSec(m_delegate); 1438 1439 if (shouldForceLayoutOnEvents()) 1440 webview()->layout(); 1441 1442 webview()->handleInputEvent(event); 1443 } 1444 1445 // 1446 // Unimplemented stubs 1447 // 1448 1449 void EventSender::enableDOMUIEventLogging(const CppArgumentList&, CppVariant* result) 1450 { 1451 result->setNull(); 1452 } 1453 1454 void EventSender::fireKeyboardEventsToElement(const CppArgumentList&, CppVariant* result) 1455 { 1456 result->setNull(); 1457 } 1458 1459 void EventSender::clearKillRing(const CppArgumentList&, CppVariant* result) 1460 { 1461 result->setNull(); 1462 } 1463 1464 } 1465