WebKitTools/DumpRenderTree/win/EventSender.cpp
changeset 2 303757a437d3
parent 0 4f2f89ce4247
equal deleted inserted replaced
0:4f2f89ce4247 2:303757a437d3
     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, "delete"))
       
   471         virtualKeyCode = VK_DELETE;
       
   472     else {
       
   473         charCode = JSStringGetCharactersPtr(character)[0];
       
   474         virtualKeyCode = LOBYTE(VkKeyScan(charCode));
       
   475         if (WTF::isASCIIUpper(charCode))
       
   476             needsShiftKeyModifier = true;
       
   477     }
       
   478     JSStringRelease(character);
       
   479 
       
   480     BYTE keyState[256];
       
   481     if (argumentCount > 1 || needsShiftKeyModifier) {
       
   482         ::GetKeyboardState(keyState);
       
   483 
       
   484         BYTE newKeyState[256];
       
   485         memcpy(newKeyState, keyState, sizeof(keyState));
       
   486 
       
   487         if (needsShiftKeyModifier)
       
   488             newKeyState[VK_SHIFT] = 0x80;
       
   489 
       
   490         if (argumentCount > 1) {
       
   491             JSObjectRef modifiersArray = JSValueToObject(context, arguments[1], 0);
       
   492             if (modifiersArray) {
       
   493                 int modifiersCount = JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty, 0), 0);
       
   494                 for (int i = 0; i < modifiersCount; ++i) {
       
   495                     JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0);
       
   496                     JSStringRef string = JSValueToStringCopy(context, value, 0);
       
   497                     if (JSStringIsEqualToUTF8CString(string, "ctrlKey") || JSStringIsEqualToUTF8CString(string, "addSelectionKey"))
       
   498                         newKeyState[VK_CONTROL] = 0x80;
       
   499                     else if (JSStringIsEqualToUTF8CString(string, "shiftKey") || JSStringIsEqualToUTF8CString(string, "rangeSelectionKey"))
       
   500                         newKeyState[VK_SHIFT] = 0x80;
       
   501                     else if (JSStringIsEqualToUTF8CString(string, "altKey"))
       
   502                         newKeyState[VK_MENU] = 0x80;
       
   503 
       
   504                     JSStringRelease(string);
       
   505                 }
       
   506             }
       
   507         }
       
   508 
       
   509         ::SetKeyboardState(newKeyState);
       
   510     }
       
   511 
       
   512     MSG msg = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYDOWN : WM_KEYDOWN, virtualKeyCode, keyData);
       
   513     if (virtualKeyCode != 255)
       
   514         dispatchMessage(&msg);
       
   515     else {
       
   516         // For characters that do not exist in the active keyboard layout,
       
   517         // ::Translate will not work, so we post an WM_CHAR event ourselves.
       
   518         ::PostMessage(webViewWindow, WM_CHAR, charCode, 0);
       
   519     }
       
   520 
       
   521     // Tests expect that all messages are processed by the time keyDown() returns.
       
   522     if (::PeekMessage(&msg, webViewWindow, WM_CHAR, WM_CHAR, PM_REMOVE) || ::PeekMessage(&msg, webViewWindow, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE))
       
   523         ::DispatchMessage(&msg);
       
   524 
       
   525     MSG msgUp = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYUP : WM_KEYUP, virtualKeyCode, keyData);
       
   526     ::DispatchMessage(&msgUp);
       
   527 
       
   528     if (argumentCount > 1 || needsShiftKeyModifier)
       
   529         ::SetKeyboardState(keyState);
       
   530 
       
   531     return JSValueMakeUndefined(context);
       
   532 }
       
   533 
       
   534 // eventSender.dispatchMessage(message, wParam, lParam, time = currentEventTime(), x = lastMousePosition.x, y = lastMousePosition.y)
       
   535 static JSValueRef dispatchMessageCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   536 {
       
   537     if (argumentCount < 3)
       
   538         return JSValueMakeUndefined(context);
       
   539 
       
   540     COMPtr<IWebFramePrivate> framePrivate;
       
   541     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
       
   542         framePrivate->layout();
       
   543     
       
   544     MSG msg = {};
       
   545     msg.hwnd = webViewWindow;
       
   546     msg.message = JSValueToNumber(context, arguments[0], exception);
       
   547     ASSERT(!*exception);
       
   548     msg.wParam = JSValueToNumber(context, arguments[1], exception);
       
   549     ASSERT(!*exception);
       
   550     msg.lParam = static_cast<ULONG_PTR>(JSValueToNumber(context, arguments[2], exception));
       
   551     ASSERT(!*exception);
       
   552     if (argumentCount >= 4) {
       
   553         msg.time = JSValueToNumber(context, arguments[3], exception);
       
   554         ASSERT(!*exception);
       
   555     }
       
   556     if (!msg.time)
       
   557         msg.time = currentEventTime();
       
   558     if (argumentCount >= 6) {
       
   559         msg.pt.x = JSValueToNumber(context, arguments[4], exception);
       
   560         ASSERT(!*exception);
       
   561         msg.pt.y = JSValueToNumber(context, arguments[5], exception);
       
   562         ASSERT(!*exception);
       
   563     } else
       
   564         msg.pt = lastMousePosition;
       
   565 
       
   566     ::DispatchMessage(&msg);
       
   567 
       
   568     return JSValueMakeUndefined(context);
       
   569 }
       
   570 
       
   571 static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   572 {
       
   573     COMPtr<IWebView> webView;
       
   574     if (FAILED(frame->webView(&webView)))
       
   575         return JSValueMakeUndefined(context);
       
   576 
       
   577     COMPtr<IWebIBActions> webIBActions(Query, webView);
       
   578     if (!webIBActions)
       
   579         return JSValueMakeUndefined(context);
       
   580 
       
   581     webIBActions->makeTextLarger(0);
       
   582     return JSValueMakeUndefined(context);
       
   583 }
       
   584 
       
   585 static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   586 {
       
   587     COMPtr<IWebView> webView;
       
   588     if (FAILED(frame->webView(&webView)))
       
   589         return JSValueMakeUndefined(context);
       
   590 
       
   591     COMPtr<IWebIBActions> webIBActions(Query, webView);
       
   592     if (!webIBActions)
       
   593         return JSValueMakeUndefined(context);
       
   594 
       
   595     webIBActions->makeTextSmaller(0);
       
   596     return JSValueMakeUndefined(context);
       
   597 }
       
   598 
       
   599 static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   600 {
       
   601     COMPtr<IWebView> webView;
       
   602     if (FAILED(frame->webView(&webView)))
       
   603         return JSValueMakeUndefined(context);
       
   604 
       
   605     COMPtr<IWebIBActions> webIBActions(Query, webView);
       
   606     if (!webIBActions)
       
   607         return JSValueMakeUndefined(context);
       
   608 
       
   609     webIBActions->zoomPageIn(0);
       
   610     return JSValueMakeUndefined(context);
       
   611 }
       
   612 
       
   613 static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   614 {
       
   615     COMPtr<IWebView> webView;
       
   616     if (FAILED(frame->webView(&webView)))
       
   617         return JSValueMakeUndefined(context);
       
   618 
       
   619     COMPtr<IWebIBActions> webIBActions(Query, webView);
       
   620     if (!webIBActions)
       
   621         return JSValueMakeUndefined(context);
       
   622 
       
   623     webIBActions->zoomPageOut(0);
       
   624     return JSValueMakeUndefined(context);
       
   625 }
       
   626 
       
   627 static JSStaticFunction staticFunctions[] = {
       
   628     { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   629     { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   630     { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   631     { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   632     { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   633     { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   634     { "dispatchMessage", dispatchMessageCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   635     { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   636     { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   637     { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   638     { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   639     { 0, 0, 0 }
       
   640 };
       
   641 
       
   642 static JSStaticValue staticValues[] = {
       
   643     { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone },
       
   644     { "WM_KEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
       
   645     { "WM_KEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
       
   646     { "WM_CHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
       
   647     { "WM_DEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
       
   648     { "WM_SYSKEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
       
   649     { "WM_SYSKEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
       
   650     { "WM_SYSCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
       
   651     { "WM_SYSDEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
       
   652     { 0, 0, 0, 0 }
       
   653 };
       
   654 
       
   655 static JSClassRef getClass(JSContextRef context)
       
   656 {
       
   657     static JSClassRef eventSenderClass = 0;
       
   658 
       
   659     if (!eventSenderClass) {
       
   660         JSClassDefinition classDefinition = {0};
       
   661         classDefinition.staticFunctions = staticFunctions;
       
   662         classDefinition.staticValues = staticValues;
       
   663 
       
   664         eventSenderClass = JSClassCreate(&classDefinition);
       
   665     }
       
   666 
       
   667     return eventSenderClass;
       
   668 }
       
   669 
       
   670 JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame)
       
   671 {
       
   672     if (isTopFrame) {
       
   673         down = false;
       
   674         dragMode = true;
       
   675         replayingSavedEvents = false;
       
   676         timeOffset = 0;
       
   677         lastMousePosition.x = 0;
       
   678         lastMousePosition.y = 0;
       
   679 
       
   680         endOfQueue = 0;
       
   681         startOfQueue = 0;
       
   682 
       
   683         didDragEnter = false;
       
   684         draggingInfo = 0;
       
   685     }
       
   686     return JSObjectMake(context, getClass(context), 0);
       
   687 }