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