• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getDragModeCallback(JSContextRef context,JSObjectRef object,JSStringRef propertyName,JSValueRef * exception)63 static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
64 {
65     return JSValueMakeBoolean(context, dragMode);
66 }
67 
setDragModeCallback(JSContextRef context,JSObjectRef object,JSStringRef propertyName,JSValueRef value,JSValueRef * exception)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 
getConstantCallback(JSContextRef context,JSObjectRef object,JSStringRef propertyName,JSValueRef * exception)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 
leapForwardCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
currentEventTime()106 static DWORD currentEventTime()
107 {
108     return ::GetTickCount() + timeOffset;
109 }
110 
makeMsg(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)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 
dispatchMessage(const MSG * msg)124 static LRESULT dispatchMessage(const MSG* msg)
125 {
126     ASSERT(msg);
127     ::TranslateMessage(msg);
128     return ::DispatchMessage(msg);
129 }
130 
contextClickCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
buildModifierFlags(JSContextRef context,const JSValueRef modifiers)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 
mouseDownCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
pointl(const POINT & point)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 
doMouseUp(MSG msg,HRESULT * oleDragAndDropReturnValue=0)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 
mouseUpCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
doMouseMove(MSG msg)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 
mouseMoveToCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
replaySavedEvents(HRESULT * oleDragAndDropReturnValue)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 
keyDownCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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_BACK;
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)
dispatchMessageCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
textZoomInCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
textZoomOutCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
zoomPageInCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
zoomPageOutCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)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 
getClass(JSContextRef context)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 
makeEventSender(JSContextRef context)670 JSObjectRef makeEventSender(JSContextRef context)
671 {
672     down = false;
673     dragMode = true;
674     replayingSavedEvents = false;
675     timeOffset = 0;
676     lastMousePosition.x = 0;
677     lastMousePosition.y = 0;
678 
679     endOfQueue = 0;
680     startOfQueue = 0;
681 
682     didDragEnter = false;
683     draggingInfo = 0;
684 
685     return JSObjectMake(context, getClass(context), 0);
686 }
687