1 /* 2 * Copyright (C) 2007, 2008 Apple 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 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "config.h" 30 #include "EventSender.h" 31 32 #include "DraggingInfo.h" 33 #include "DumpRenderTree.h" 34 35 #include <WebCore/COMPtr.h> 36 #include <wtf/ASCIICType.h> 37 #include <wtf/Platform.h> 38 #include <JavaScriptCore/JavaScriptCore.h> 39 #include <JavaScriptCore/Assertions.h> 40 #include <WebKit/WebKit.h> 41 #include <windows.h> 42 43 #define WM_DRT_SEND_QUEUED_EVENT (WM_APP+1) 44 45 static bool down; 46 static bool dragMode = true; 47 static bool replayingSavedEvents; 48 static int timeOffset; 49 static POINT lastMousePosition; 50 51 struct DelayedMessage { 52 MSG msg; 53 unsigned delay; 54 }; 55 56 static DelayedMessage msgQueue[1024]; 57 static unsigned endOfQueue; 58 static unsigned startOfQueue; 59 60 static bool didDragEnter; 61 DraggingInfo* draggingInfo = 0; 62 63 static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) 64 { 65 return JSValueMakeBoolean(context, dragMode); 66 } 67 68 static bool setDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) 69 { 70 dragMode = JSValueToBoolean(context, value); 71 return true; 72 } 73 74 static JSValueRef getConstantCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) 75 { 76 if (JSStringIsEqualToUTF8CString(propertyName, "WM_KEYDOWN")) 77 return JSValueMakeNumber(context, WM_KEYDOWN); 78 if (JSStringIsEqualToUTF8CString(propertyName, "WM_KEYUP")) 79 return JSValueMakeNumber(context, WM_KEYUP); 80 if (JSStringIsEqualToUTF8CString(propertyName, "WM_CHAR")) 81 return JSValueMakeNumber(context, WM_CHAR); 82 if (JSStringIsEqualToUTF8CString(propertyName, "WM_DEADCHAR")) 83 return JSValueMakeNumber(context, WM_DEADCHAR); 84 if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSKEYDOWN")) 85 return JSValueMakeNumber(context, WM_SYSKEYDOWN); 86 if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSKEYUP")) 87 return JSValueMakeNumber(context, WM_SYSKEYUP); 88 if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSCHAR")) 89 return JSValueMakeNumber(context, WM_SYSCHAR); 90 if (JSStringIsEqualToUTF8CString(propertyName, "WM_SYSDEADCHAR")) 91 return JSValueMakeNumber(context, WM_SYSDEADCHAR); 92 ASSERT_NOT_REACHED(); 93 return JSValueMakeUndefined(context); 94 } 95 96 static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 97 { 98 if (argumentCount > 0) { 99 msgQueue[endOfQueue].delay = JSValueToNumber(context, arguments[0], exception); 100 ASSERT(!exception || !*exception); 101 } 102 103 return JSValueMakeUndefined(context); 104 } 105 106 static DWORD currentEventTime() 107 { 108 return ::GetTickCount() + timeOffset; 109 } 110 111 static MSG makeMsg(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 112 { 113 MSG result = {0}; 114 result.hwnd = hwnd; 115 result.message = message; 116 result.wParam = wParam; 117 result.lParam = lParam; 118 result.time = currentEventTime(); 119 result.pt = lastMousePosition; 120 121 return result; 122 } 123 124 static LRESULT dispatchMessage(const MSG* msg) 125 { 126 ASSERT(msg); 127 ::TranslateMessage(msg); 128 return ::DispatchMessage(msg); 129 } 130 131 static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 132 { 133 COMPtr<IWebFramePrivate> framePrivate; 134 if (SUCCEEDED(frame->QueryInterface(&framePrivate))) 135 framePrivate->layout(); 136 137 down = true; 138 MSG msg = makeMsg(webViewWindow, WM_RBUTTONDOWN, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); 139 dispatchMessage(&msg); 140 down = false; 141 msg = makeMsg(webViewWindow, WM_RBUTTONUP, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); 142 dispatchMessage(&msg); 143 144 return JSValueMakeUndefined(context); 145 } 146 147 static WPARAM buildModifierFlags(JSContextRef context, const JSValueRef modifiers) 148 { 149 JSObjectRef modifiersArray = JSValueToObject(context, modifiers, 0); 150 if (!modifiersArray) 151 return 0; 152 153 WPARAM flags = 0; 154 int modifiersCount = JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, JSStringCreateWithUTF8CString("length"), 0), 0); 155 for (int i = 0; i < modifiersCount; ++i) { 156 JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0); 157 JSStringRef string = JSValueToStringCopy(context, value, 0); 158 if (JSStringIsEqualToUTF8CString(string, "ctrlKey") 159 || JSStringIsEqualToUTF8CString(string, "addSelectionKey")) 160 flags |= MK_CONTROL; 161 else if (JSStringIsEqualToUTF8CString(string, "shiftKey") 162 || JSStringIsEqualToUTF8CString(string, "rangeSelectionKey")) 163 flags |= MK_SHIFT; 164 // No way to specifiy altKey in a MSG. 165 166 JSStringRelease(string); 167 } 168 return flags; 169 } 170 171 static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 172 { 173 COMPtr<IWebFramePrivate> framePrivate; 174 if (SUCCEEDED(frame->QueryInterface(&framePrivate))) 175 framePrivate->layout(); 176 177 down = true; 178 int mouseType = WM_LBUTTONDOWN; 179 if (argumentCount >= 1) { 180 int mouseNumber = JSValueToNumber(context, arguments[0], exception); 181 switch (mouseNumber) { 182 case 0: 183 mouseType = WM_LBUTTONDOWN; 184 break; 185 case 1: 186 mouseType = WM_MBUTTONDOWN; 187 break; 188 case 2: 189 mouseType = WM_RBUTTONDOWN; 190 break; 191 case 3: 192 // fast/events/mouse-click-events expects the 4th button has event.button = 1, so send an WM_BUTTONDOWN 193 mouseType = WM_MBUTTONDOWN; 194 break; 195 default: 196 mouseType = WM_LBUTTONDOWN; 197 break; 198 } 199 } 200 201 WPARAM wparam = 0; 202 if (argumentCount >= 2) 203 wparam |= buildModifierFlags(context, arguments[1]); 204 205 MSG msg = makeMsg(webViewWindow, mouseType, wparam, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); 206 if (!msgQueue[endOfQueue].delay) 207 dispatchMessage(&msg); 208 else { 209 // replaySavedEvents has the required logic to make leapForward delays work 210 msgQueue[endOfQueue++].msg = msg; 211 replaySavedEvents(); 212 } 213 214 return JSValueMakeUndefined(context); 215 } 216 217 static inline POINTL pointl(const POINT& point) 218 { 219 POINTL result; 220 result.x = point.x; 221 result.y = point.y; 222 return result; 223 } 224 225 static void doMouseUp(MSG msg, HRESULT* oleDragAndDropReturnValue = 0) 226 { 227 COMPtr<IWebFramePrivate> framePrivate; 228 if (SUCCEEDED(frame->QueryInterface(&framePrivate))) 229 framePrivate->layout(); 230 231 dispatchMessage(&msg); 232 down = false; 233 234 if (draggingInfo) { 235 COMPtr<IWebView> webView; 236 COMPtr<IDropTarget> webViewDropTarget; 237 if (SUCCEEDED(frame->webView(&webView)) && SUCCEEDED(webView->QueryInterface(IID_IDropTarget, (void**)&webViewDropTarget))) { 238 POINT screenPoint = msg.pt; 239 DWORD effect = 0; 240 ::ClientToScreen(webViewWindow, &screenPoint); 241 if (!didDragEnter) { 242 webViewDropTarget->DragEnter(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect); 243 didDragEnter = true; 244 } 245 HRESULT hr = draggingInfo->dropSource()->QueryContinueDrag(0, 0); 246 if (oleDragAndDropReturnValue) 247 *oleDragAndDropReturnValue = hr; 248 webViewDropTarget->DragOver(0, pointl(screenPoint), &effect); 249 if (hr == DRAGDROP_S_DROP && effect != DROPEFFECT_NONE) { 250 DWORD effect = 0; 251 webViewDropTarget->Drop(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect); 252 draggingInfo->setPerformedDropEffect(effect); 253 } else 254 webViewDropTarget->DragLeave(); 255 256 // Reset didDragEnter so that another drag started within the same frame works properly. 257 didDragEnter = false; 258 } 259 } 260 } 261 262 static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 263 { 264 int mouseType = WM_LBUTTONUP; 265 if (argumentCount >= 1) { 266 int mouseNumber = JSValueToNumber(context, arguments[0], exception); 267 switch (mouseNumber) { 268 case 0: 269 mouseType = WM_LBUTTONUP; 270 break; 271 case 1: 272 mouseType = WM_MBUTTONUP; 273 break; 274 case 2: 275 mouseType = WM_RBUTTONUP; 276 break; 277 case 3: 278 // fast/events/mouse-click-events expects the 4th button has event.button = 1, so send an WM_MBUTTONUP 279 mouseType = WM_MBUTTONUP; 280 break; 281 default: 282 mouseType = WM_LBUTTONUP; 283 break; 284 } 285 } 286 287 WPARAM wparam = 0; 288 if (argumentCount >= 2) 289 wparam |= buildModifierFlags(context, arguments[1]); 290 291 MSG msg = makeMsg(webViewWindow, mouseType, wparam, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); 292 293 if ((dragMode && !replayingSavedEvents) || msgQueue[endOfQueue].delay) { 294 msgQueue[endOfQueue++].msg = msg; 295 replaySavedEvents(); 296 } else 297 doMouseUp(msg); 298 299 return JSValueMakeUndefined(context); 300 } 301 302 static void doMouseMove(MSG msg) 303 { 304 COMPtr<IWebFramePrivate> framePrivate; 305 if (SUCCEEDED(frame->QueryInterface(&framePrivate))) 306 framePrivate->layout(); 307 308 dispatchMessage(&msg); 309 310 if (down && draggingInfo) { 311 POINT screenPoint = msg.pt; 312 ::ClientToScreen(webViewWindow, &screenPoint); 313 314 IWebView* webView; 315 COMPtr<IDropTarget> webViewDropTarget; 316 if (SUCCEEDED(frame->webView(&webView)) && SUCCEEDED(webView->QueryInterface(IID_IDropTarget, (void**)&webViewDropTarget))) { 317 DWORD effect = 0; 318 if (didDragEnter) 319 webViewDropTarget->DragOver(MK_LBUTTON, pointl(screenPoint), &effect); 320 else { 321 webViewDropTarget->DragEnter(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect); 322 didDragEnter = true; 323 } 324 draggingInfo->dropSource()->GiveFeedback(effect); 325 } 326 } 327 } 328 329 static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 330 { 331 if (argumentCount < 2) 332 return JSValueMakeUndefined(context); 333 334 lastMousePosition.x = (int)JSValueToNumber(context, arguments[0], exception); 335 ASSERT(!exception || !*exception); 336 lastMousePosition.y = (int)JSValueToNumber(context, arguments[1], exception); 337 ASSERT(!exception || !*exception); 338 339 MSG msg = makeMsg(webViewWindow, WM_MOUSEMOVE, down ? MK_LBUTTON : 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y)); 340 341 if (dragMode && down && !replayingSavedEvents) { 342 msgQueue[endOfQueue++].msg = msg; 343 return JSValueMakeUndefined(context); 344 } 345 346 doMouseMove(msg); 347 348 return JSValueMakeUndefined(context); 349 } 350 351 void replaySavedEvents(HRESULT* oleDragAndDropReturnValue) 352 { 353 replayingSavedEvents = true; 354 355 MSG msg = { 0 }; 356 357 while (startOfQueue < endOfQueue && !msgQueue[startOfQueue].delay) { 358 msg = msgQueue[startOfQueue++].msg; 359 switch (msg.message) { 360 case WM_LBUTTONUP: 361 case WM_RBUTTONUP: 362 case WM_MBUTTONUP: 363 doMouseUp(msg, oleDragAndDropReturnValue); 364 break; 365 case WM_MOUSEMOVE: 366 doMouseMove(msg); 367 break; 368 case WM_LBUTTONDOWN: 369 case WM_RBUTTONDOWN: 370 case WM_MBUTTONDOWN: 371 dispatchMessage(&msg); 372 break; 373 default: 374 // Not reached 375 break; 376 } 377 } 378 379 int numQueuedMessages = endOfQueue - startOfQueue; 380 if (!numQueuedMessages) { 381 startOfQueue = 0; 382 endOfQueue = 0; 383 replayingSavedEvents = false; 384 ASSERT(!down); 385 return; 386 } 387 388 if (msgQueue[startOfQueue].delay) { 389 ::Sleep(msgQueue[startOfQueue].delay); 390 msgQueue[startOfQueue].delay = 0; 391 } 392 393 ::PostMessage(webViewWindow, WM_DRT_SEND_QUEUED_EVENT, 0, 0); 394 while (::GetMessage(&msg, webViewWindow, 0, 0)) { 395 // FIXME: Why do we get a WM_MOUSELEAVE? it breaks tests 396 if (msg.message == WM_MOUSELEAVE) 397 continue; 398 if (msg.message != WM_DRT_SEND_QUEUED_EVENT) { 399 dispatchMessage(&msg); 400 continue; 401 } 402 msg = msgQueue[startOfQueue++].msg; 403 switch (msg.message) { 404 case WM_LBUTTONUP: 405 case WM_RBUTTONUP: 406 case WM_MBUTTONUP: 407 doMouseUp(msg, oleDragAndDropReturnValue); 408 break; 409 case WM_MOUSEMOVE: 410 doMouseMove(msg); 411 break; 412 case WM_LBUTTONDOWN: 413 case WM_RBUTTONDOWN: 414 case WM_MBUTTONDOWN: 415 dispatchMessage(&msg); 416 break; 417 default: 418 // Not reached 419 break; 420 } 421 if (startOfQueue >= endOfQueue) 422 break; 423 ::Sleep(msgQueue[startOfQueue].delay); 424 msgQueue[startOfQueue].delay = 0; 425 ::PostMessage(webViewWindow, WM_DRT_SEND_QUEUED_EVENT, 0, 0); 426 } 427 startOfQueue = 0; 428 endOfQueue = 0; 429 430 replayingSavedEvents = false; 431 } 432 433 static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 434 { 435 if (argumentCount < 1) 436 return JSValueMakeUndefined(context); 437 438 static const JSStringRef lengthProperty = JSStringCreateWithUTF8CString("length"); 439 440 COMPtr<IWebFramePrivate> framePrivate; 441 if (SUCCEEDED(frame->QueryInterface(&framePrivate))) 442 framePrivate->layout(); 443 444 JSStringRef character = JSValueToStringCopy(context, arguments[0], exception); 445 ASSERT(!*exception); 446 int virtualKeyCode; 447 int charCode = 0; 448 int keyData = 1; 449 bool needsShiftKeyModifier = false; 450 if (JSStringIsEqualToUTF8CString(character, "leftArrow")) { 451 virtualKeyCode = VK_LEFT; 452 keyData += KF_EXTENDED << 16; // In this case, extended means "not keypad". 453 } else if (JSStringIsEqualToUTF8CString(character, "rightArrow")) { 454 virtualKeyCode = VK_RIGHT; 455 keyData += KF_EXTENDED << 16; 456 } else if (JSStringIsEqualToUTF8CString(character, "upArrow")) { 457 virtualKeyCode = VK_UP; 458 keyData += KF_EXTENDED << 16; 459 } else if (JSStringIsEqualToUTF8CString(character, "downArrow")) { 460 virtualKeyCode = VK_DOWN; 461 keyData += KF_EXTENDED << 16; 462 } else if (JSStringIsEqualToUTF8CString(character, "pageUp")) 463 virtualKeyCode = VK_PRIOR; 464 else if (JSStringIsEqualToUTF8CString(character, "pageDown")) 465 virtualKeyCode = VK_NEXT; 466 else if (JSStringIsEqualToUTF8CString(character, "home")) 467 virtualKeyCode = VK_HOME; 468 else if (JSStringIsEqualToUTF8CString(character, "end")) 469 virtualKeyCode = VK_END; 470 else if (JSStringIsEqualToUTF8CString(character, "insert")) 471 virtualKeyCode = VK_INSERT; 472 else if (JSStringIsEqualToUTF8CString(character, "delete")) 473 virtualKeyCode = VK_DELETE; 474 else if (JSStringIsEqualToUTF8CString(character, "printScreen")) 475 virtualKeyCode = VK_SNAPSHOT; 476 else if (JSStringIsEqualToUTF8CString(character, "menu")) 477 virtualKeyCode = VK_APPS; 478 else { 479 charCode = JSStringGetCharactersPtr(character)[0]; 480 virtualKeyCode = LOBYTE(VkKeyScan(charCode)); 481 if (WTF::isASCIIUpper(charCode)) 482 needsShiftKeyModifier = true; 483 } 484 JSStringRelease(character); 485 486 BYTE keyState[256]; 487 if (argumentCount > 1 || needsShiftKeyModifier) { 488 ::GetKeyboardState(keyState); 489 490 BYTE newKeyState[256]; 491 memcpy(newKeyState, keyState, sizeof(keyState)); 492 493 if (needsShiftKeyModifier) 494 newKeyState[VK_SHIFT] = 0x80; 495 496 if (argumentCount > 1) { 497 JSObjectRef modifiersArray = JSValueToObject(context, arguments[1], 0); 498 if (modifiersArray) { 499 int modifiersCount = JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty, 0), 0); 500 for (int i = 0; i < modifiersCount; ++i) { 501 JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0); 502 JSStringRef string = JSValueToStringCopy(context, value, 0); 503 if (JSStringIsEqualToUTF8CString(string, "ctrlKey") || JSStringIsEqualToUTF8CString(string, "addSelectionKey")) 504 newKeyState[VK_CONTROL] = 0x80; 505 else if (JSStringIsEqualToUTF8CString(string, "shiftKey") || JSStringIsEqualToUTF8CString(string, "rangeSelectionKey")) 506 newKeyState[VK_SHIFT] = 0x80; 507 else if (JSStringIsEqualToUTF8CString(string, "altKey")) 508 newKeyState[VK_MENU] = 0x80; 509 510 JSStringRelease(string); 511 } 512 } 513 } 514 515 ::SetKeyboardState(newKeyState); 516 } 517 518 MSG msg = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYDOWN : WM_KEYDOWN, virtualKeyCode, keyData); 519 if (virtualKeyCode != 255) 520 dispatchMessage(&msg); 521 else { 522 // For characters that do not exist in the active keyboard layout, 523 // ::Translate will not work, so we post an WM_CHAR event ourselves. 524 ::PostMessage(webViewWindow, WM_CHAR, charCode, 0); 525 } 526 527 // Tests expect that all messages are processed by the time keyDown() returns. 528 if (::PeekMessage(&msg, webViewWindow, WM_CHAR, WM_CHAR, PM_REMOVE) || ::PeekMessage(&msg, webViewWindow, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE)) 529 ::DispatchMessage(&msg); 530 531 MSG msgUp = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYUP : WM_KEYUP, virtualKeyCode, keyData); 532 ::DispatchMessage(&msgUp); 533 534 if (argumentCount > 1 || needsShiftKeyModifier) 535 ::SetKeyboardState(keyState); 536 537 return JSValueMakeUndefined(context); 538 } 539 540 // eventSender.dispatchMessage(message, wParam, lParam, time = currentEventTime(), x = lastMousePosition.x, y = lastMousePosition.y) 541 static JSValueRef dispatchMessageCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 542 { 543 if (argumentCount < 3) 544 return JSValueMakeUndefined(context); 545 546 COMPtr<IWebFramePrivate> framePrivate; 547 if (SUCCEEDED(frame->QueryInterface(&framePrivate))) 548 framePrivate->layout(); 549 550 MSG msg = {}; 551 msg.hwnd = webViewWindow; 552 msg.message = JSValueToNumber(context, arguments[0], exception); 553 ASSERT(!*exception); 554 msg.wParam = JSValueToNumber(context, arguments[1], exception); 555 ASSERT(!*exception); 556 msg.lParam = static_cast<ULONG_PTR>(JSValueToNumber(context, arguments[2], exception)); 557 ASSERT(!*exception); 558 if (argumentCount >= 4) { 559 msg.time = JSValueToNumber(context, arguments[3], exception); 560 ASSERT(!*exception); 561 } 562 if (!msg.time) 563 msg.time = currentEventTime(); 564 if (argumentCount >= 6) { 565 msg.pt.x = JSValueToNumber(context, arguments[4], exception); 566 ASSERT(!*exception); 567 msg.pt.y = JSValueToNumber(context, arguments[5], exception); 568 ASSERT(!*exception); 569 } else 570 msg.pt = lastMousePosition; 571 572 ::DispatchMessage(&msg); 573 574 return JSValueMakeUndefined(context); 575 } 576 577 static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 578 { 579 COMPtr<IWebView> webView; 580 if (FAILED(frame->webView(&webView))) 581 return JSValueMakeUndefined(context); 582 583 COMPtr<IWebIBActions> webIBActions(Query, webView); 584 if (!webIBActions) 585 return JSValueMakeUndefined(context); 586 587 webIBActions->makeTextLarger(0); 588 return JSValueMakeUndefined(context); 589 } 590 591 static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 592 { 593 COMPtr<IWebView> webView; 594 if (FAILED(frame->webView(&webView))) 595 return JSValueMakeUndefined(context); 596 597 COMPtr<IWebIBActions> webIBActions(Query, webView); 598 if (!webIBActions) 599 return JSValueMakeUndefined(context); 600 601 webIBActions->makeTextSmaller(0); 602 return JSValueMakeUndefined(context); 603 } 604 605 static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 606 { 607 COMPtr<IWebView> webView; 608 if (FAILED(frame->webView(&webView))) 609 return JSValueMakeUndefined(context); 610 611 COMPtr<IWebIBActions> webIBActions(Query, webView); 612 if (!webIBActions) 613 return JSValueMakeUndefined(context); 614 615 webIBActions->zoomPageIn(0); 616 return JSValueMakeUndefined(context); 617 } 618 619 static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 620 { 621 COMPtr<IWebView> webView; 622 if (FAILED(frame->webView(&webView))) 623 return JSValueMakeUndefined(context); 624 625 COMPtr<IWebIBActions> webIBActions(Query, webView); 626 if (!webIBActions) 627 return JSValueMakeUndefined(context); 628 629 webIBActions->zoomPageOut(0); 630 return JSValueMakeUndefined(context); 631 } 632 633 static JSStaticFunction staticFunctions[] = { 634 { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 635 { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 636 { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 637 { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 638 { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 639 { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 640 { "dispatchMessage", dispatchMessageCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 641 { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 642 { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 643 { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 644 { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 645 { 0, 0, 0 } 646 }; 647 648 static JSStaticValue staticValues[] = { 649 { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone }, 650 { "WM_KEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, 651 { "WM_KEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, 652 { "WM_CHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, 653 { "WM_DEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, 654 { "WM_SYSKEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, 655 { "WM_SYSKEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, 656 { "WM_SYSCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, 657 { "WM_SYSDEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone }, 658 { 0, 0, 0, 0 } 659 }; 660 661 static JSClassRef getClass(JSContextRef context) 662 { 663 static JSClassRef eventSenderClass = 0; 664 665 if (!eventSenderClass) { 666 JSClassDefinition classDefinition = {0}; 667 classDefinition.staticFunctions = staticFunctions; 668 classDefinition.staticValues = staticValues; 669 670 eventSenderClass = JSClassCreate(&classDefinition); 671 } 672 673 return eventSenderClass; 674 } 675 676 JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame) 677 { 678 if (isTopFrame) { 679 down = false; 680 dragMode = true; 681 replayingSavedEvents = false; 682 timeOffset = 0; 683 lastMousePosition.x = 0; 684 lastMousePosition.y = 0; 685 686 endOfQueue = 0; 687 startOfQueue = 0; 688 689 didDragEnter = false; 690 draggingInfo = 0; 691 } 692 return JSObjectMake(context, getClass(context), 0); 693 } 694