webengine/osswebengine/WebKitTools/DumpRenderTree/win/EventSender.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * Copyright (C) 2007 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 "DumpRenderTree.h"
       
    30 #include "EventSender.h"
       
    31 
       
    32 #include "DraggingInfo.h"
       
    33 
       
    34 #include <WebCore/COMPtr.h>
       
    35 #include <wtf/Platform.h>
       
    36 #include <JavaScriptCore/JavaScriptCore.h>
       
    37 #include <JavaScriptCore/Assertions.h>
       
    38 #include <WebKit/IWebFrame.h>
       
    39 #include <WebKit/IWebFramePrivate.h>
       
    40 #include <windows.h>
       
    41 
       
    42 static bool down;
       
    43 static bool dragMode = true;
       
    44 static bool replayingSavedEvents;
       
    45 static int timeOffset;
       
    46 static POINT lastMousePosition;
       
    47 
       
    48 static MSG msgQueue[1024];
       
    49 static unsigned endOfQueue;
       
    50 static unsigned startOfQueue;
       
    51 
       
    52 static bool didDragEnter;
       
    53 DraggingInfo* draggingInfo = 0;
       
    54 
       
    55 static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
       
    56 {
       
    57     return JSValueMakeBoolean(context, dragMode);
       
    58 }
       
    59 
       
    60 static bool setDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
       
    61 {
       
    62     dragMode = JSValueToBoolean(context, value);
       
    63     return true;
       
    64 }
       
    65 
       
    66 static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
    67 {
       
    68     if (argumentCount > 0) {
       
    69         timeOffset += JSValueToNumber(context, arguments[0], exception);
       
    70         ASSERT(!exception || !*exception);
       
    71     }
       
    72 
       
    73     return JSValueMakeUndefined(context);
       
    74 }
       
    75 
       
    76 static DWORD currentEventTime()
       
    77 {
       
    78     return ::GetTickCount() + timeOffset;
       
    79 }
       
    80 
       
    81 static MSG makeMsg(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       
    82 {
       
    83     MSG result = {0};
       
    84     result.hwnd = hwnd;
       
    85     result.message = message;
       
    86     result.wParam = wParam;
       
    87     result.lParam = lParam;
       
    88     result.time = currentEventTime();
       
    89     result.pt = lastMousePosition;
       
    90 
       
    91     return result;
       
    92 }
       
    93 
       
    94 static LRESULT dispatchMessage(const MSG* msg)
       
    95 {
       
    96     ASSERT(msg);
       
    97 
       
    98     ::TranslateMessage(msg);
       
    99     return ::DispatchMessage(msg);
       
   100 }
       
   101 
       
   102 static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   103 {
       
   104     COMPtr<IWebFramePrivate> framePrivate;
       
   105     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
       
   106         framePrivate->layout();
       
   107 
       
   108     down = true;
       
   109     MSG msg = makeMsg(webViewWindow, WM_LBUTTONDOWN, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
       
   110     dispatchMessage(&msg);
       
   111 
       
   112     return JSValueMakeUndefined(context);
       
   113 }
       
   114 
       
   115 static inline POINTL pointl(const POINT& point)
       
   116 {
       
   117     POINTL result;
       
   118     result.x = point.x;
       
   119     result.y = point.y;
       
   120     return result;
       
   121 }
       
   122 
       
   123 static void doMouseUp(MSG msg)
       
   124 {
       
   125     COMPtr<IWebFramePrivate> framePrivate;
       
   126     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
       
   127         framePrivate->layout();
       
   128 
       
   129     dispatchMessage(&msg);
       
   130     down = false;
       
   131 
       
   132     if (draggingInfo) {
       
   133         COMPtr<IWebView> webView;
       
   134         COMPtr<IDropTarget> webViewDropTarget;
       
   135         if (SUCCEEDED(frame->webView(&webView)) && SUCCEEDED(webView->QueryInterface(IID_IDropTarget, (void**)&webViewDropTarget))) {
       
   136             POINT screenPoint = msg.pt;
       
   137             ::ClientToScreen(webViewWindow, &screenPoint);
       
   138             HRESULT hr = draggingInfo->dropSource()->QueryContinueDrag(0, 0);
       
   139             DWORD effect = 0;
       
   140             webViewDropTarget->DragOver(0, pointl(screenPoint), &effect);
       
   141             if (hr == DRAGDROP_S_DROP && effect != DROPEFFECT_NONE) {
       
   142                 DWORD effect = 0;
       
   143                 webViewDropTarget->Drop(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect);
       
   144             } else
       
   145                 webViewDropTarget->DragLeave();
       
   146 
       
   147             delete draggingInfo;
       
   148             draggingInfo = 0;
       
   149         }
       
   150     }
       
   151 }
       
   152 
       
   153 static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   154 {
       
   155     MSG msg = makeMsg(webViewWindow, WM_LBUTTONUP, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
       
   156 
       
   157     if (dragMode && !replayingSavedEvents) {
       
   158         msgQueue[endOfQueue++] = msg;
       
   159         replaySavedEvents();
       
   160     } else
       
   161         doMouseUp(msg);
       
   162 
       
   163     return JSValueMakeUndefined(context);
       
   164 }
       
   165 
       
   166 static void doMouseMove(MSG msg)
       
   167 {
       
   168     COMPtr<IWebFramePrivate> framePrivate;
       
   169     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
       
   170         framePrivate->layout();
       
   171 
       
   172     dispatchMessage(&msg);
       
   173 
       
   174     if (down && draggingInfo) {
       
   175         POINT screenPoint = msg.pt;
       
   176         ::ClientToScreen(webViewWindow, &screenPoint);
       
   177 
       
   178         IWebView* webView;
       
   179         COMPtr<IDropTarget> webViewDropTarget;
       
   180         if (SUCCEEDED(frame->webView(&webView)) && SUCCEEDED(webView->QueryInterface(IID_IDropTarget, (void**)&webViewDropTarget))) {
       
   181             DWORD effect = 0;
       
   182             if (didDragEnter)
       
   183                 webViewDropTarget->DragOver(MK_LBUTTON, pointl(screenPoint), &effect);
       
   184             else {
       
   185                 webViewDropTarget->DragEnter(draggingInfo->dataObject(), MK_LBUTTON, pointl(screenPoint), &effect);
       
   186                 didDragEnter = true;
       
   187             }
       
   188             draggingInfo->dropSource()->GiveFeedback(effect);
       
   189         }
       
   190     }
       
   191 }
       
   192 
       
   193 static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   194 {
       
   195     if (argumentCount < 2)
       
   196         return JSValueMakeUndefined(context);
       
   197 
       
   198     lastMousePosition.x = (int)JSValueToNumber(context, arguments[0], exception);
       
   199     ASSERT(!exception || !*exception);
       
   200     lastMousePosition.y = (int)JSValueToNumber(context, arguments[1], exception);
       
   201     ASSERT(!exception || !*exception);
       
   202 
       
   203     MSG msg = makeMsg(webViewWindow, WM_MOUSEMOVE, down ? MK_LBUTTON : 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
       
   204 
       
   205     if (dragMode && down && !replayingSavedEvents) {
       
   206         msgQueue[endOfQueue++] = msg;
       
   207         return JSValueMakeUndefined(context);
       
   208     }
       
   209 
       
   210     doMouseMove(msg);
       
   211 
       
   212     return JSValueMakeUndefined(context);
       
   213 }
       
   214 
       
   215 void replaySavedEvents()
       
   216 {
       
   217     replayingSavedEvents = true;
       
   218 
       
   219     MSG emptyMsg = {0};
       
   220     while (startOfQueue < endOfQueue) {
       
   221         MSG msg = msgQueue[startOfQueue++];
       
   222         switch (msg.message) {
       
   223             case WM_LBUTTONUP:
       
   224                 doMouseUp(msg);
       
   225                 break;
       
   226             case WM_MOUSEMOVE:
       
   227                 doMouseMove(msg);
       
   228                 break;
       
   229             default:
       
   230                 // Not reached
       
   231                 break;
       
   232         }
       
   233     }
       
   234     startOfQueue = 0;
       
   235     endOfQueue = 0;
       
   236 
       
   237     replayingSavedEvents = false;
       
   238 }
       
   239 
       
   240 static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   241 {
       
   242     if (argumentCount < 1)
       
   243         return JSValueMakeUndefined(context);
       
   244 
       
   245     static JSStringRef ctrlKey = JSStringCreateWithUTF8CString("ctrlKey");
       
   246     static JSStringRef shiftKey = JSStringCreateWithUTF8CString("shiftKey");
       
   247     static JSStringRef altKey = JSStringCreateWithUTF8CString("altKey");
       
   248     static JSStringRef metaKey = JSStringCreateWithUTF8CString("metaKey");
       
   249     static JSStringRef lengthProperty = JSStringCreateWithUTF8CString("length");
       
   250 
       
   251     COMPtr<IWebFramePrivate> framePrivate;
       
   252     if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
       
   253         framePrivate->layout();
       
   254     
       
   255     JSStringRef character = JSValueToStringCopy(context, arguments[0], exception);
       
   256     ASSERT(!exception || !*exception);
       
   257     int charCode = JSStringGetCharactersPtr(character)[0];
       
   258     int virtualKeyCode = toupper(LOBYTE(VkKeyScan(charCode)));
       
   259     JSStringRelease(character);
       
   260 
       
   261     // Hack to map option-delete to ctrl-delete
       
   262     // Remove this when we fix <rdar://problem/5102974> layout tests need a way to decide how to choose the appropriate modifier keys
       
   263     bool convertOptionToCtrl = false;
       
   264     if (virtualKeyCode == VK_DELETE || virtualKeyCode == VK_BACK)
       
   265         convertOptionToCtrl = true;
       
   266     
       
   267     BYTE keyState[256];
       
   268     if (argumentCount > 1) {
       
   269         ::GetKeyboardState(keyState);
       
   270 
       
   271         BYTE newKeyState[256];
       
   272         memcpy(newKeyState, keyState, sizeof(keyState));
       
   273 
       
   274         JSObjectRef modifiersArray = JSValueToObject(context, arguments[1], exception);
       
   275         if (modifiersArray) {
       
   276             int modifiersCount = JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty, 0), 0);
       
   277             for (int i = 0; i < modifiersCount; ++i) {
       
   278                 JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0);
       
   279                 JSStringRef string = JSValueToStringCopy(context, value, 0);
       
   280                 if (JSStringIsEqual(string, ctrlKey))
       
   281                     newKeyState[VK_CONTROL] = 0x80;
       
   282                 else if (JSStringIsEqual(string, shiftKey))
       
   283                     newKeyState[VK_SHIFT] = 0x80;
       
   284                 else if (JSStringIsEqual(string, altKey)) {
       
   285                     if (convertOptionToCtrl)
       
   286                         newKeyState[VK_CONTROL] = 0x80;
       
   287                     else
       
   288                         newKeyState[VK_MENU] = 0x80;
       
   289                 } else if (JSStringIsEqual(string, metaKey))
       
   290                     newKeyState[VK_MENU] = 0x80;
       
   291 
       
   292                 JSStringRelease(string);
       
   293             }
       
   294         }
       
   295 
       
   296         ::SetKeyboardState(newKeyState);
       
   297     }
       
   298 
       
   299     MSG msg = makeMsg(webViewWindow, WM_KEYDOWN, virtualKeyCode, 0);
       
   300     dispatchMessage(&msg);
       
   301     
       
   302     if (argumentCount > 1)
       
   303         ::SetKeyboardState(keyState);
       
   304 
       
   305     return JSValueMakeUndefined(context);
       
   306 }
       
   307 
       
   308 static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   309 {
       
   310     COMPtr<IWebView> webView;
       
   311     if (FAILED(frame->webView(&webView)))
       
   312         return JSValueMakeUndefined(context);
       
   313 
       
   314     COMPtr<IWebIBActions> webIBActions;
       
   315     if (FAILED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
       
   316         return JSValueMakeUndefined(context);
       
   317 
       
   318     webIBActions->makeTextLarger(0);
       
   319     return JSValueMakeUndefined(context);
       
   320 }
       
   321 
       
   322 static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
       
   323 {
       
   324     COMPtr<IWebView> webView;
       
   325     if (FAILED(frame->webView(&webView)))
       
   326         return JSValueMakeUndefined(context);
       
   327 
       
   328     COMPtr<IWebIBActions> webIBActions;
       
   329     if (FAILED(webView->QueryInterface(IID_IWebIBActions, (void**)&webIBActions)))
       
   330         return JSValueMakeUndefined(context);
       
   331 
       
   332     webIBActions->makeTextSmaller(0);
       
   333     return JSValueMakeUndefined(context);
       
   334 }
       
   335 
       
   336 static JSStaticFunction staticFunctions[] = {
       
   337     { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   338     { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   339     { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   340     { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   341     { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   342     { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   343     { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
       
   344     { 0, 0, 0 }
       
   345 };
       
   346 
       
   347 static JSStaticValue staticValues[] = {
       
   348     { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone },
       
   349     { 0, 0, 0, 0 }
       
   350 };
       
   351 
       
   352 static JSClassRef getClass(JSContextRef context) {
       
   353     static JSClassRef eventSenderClass = 0;
       
   354 
       
   355     if (!eventSenderClass) {
       
   356         JSClassDefinition classDefinition = {0};
       
   357         classDefinition.staticFunctions = staticFunctions;
       
   358         classDefinition.staticValues = staticValues;
       
   359 
       
   360         eventSenderClass = JSClassCreate(&classDefinition);
       
   361     }
       
   362 
       
   363     return eventSenderClass;
       
   364 }
       
   365 
       
   366 JSObjectRef makeEventSender(JSContextRef context)
       
   367 {
       
   368     return JSObjectMake(context, getClass(context), 0);
       
   369 }