2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "EventSender.h"
32 #include "DraggingInfo.h"
33 #include "DumpRenderTree.h"
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>
43 #define WM_DRT_SEND_QUEUED_EVENT (WM_APP+1)
46 static bool dragMode = true;
47 static bool replayingSavedEvents;
48 static int timeOffset;
49 static POINT lastMousePosition;
51 struct DelayedMessage {
56 static DelayedMessage msgQueue[1024];
57 static unsigned endOfQueue;
58 static unsigned startOfQueue;
60 static bool didDragEnter;
61 DraggingInfo* draggingInfo = 0;
63 static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
65 return JSValueMakeBoolean(context, dragMode);
68 static bool setDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
70 dragMode = JSValueToBoolean(context, value);
74 static JSValueRef getConstantCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
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);
93 return JSValueMakeUndefined(context);
96 static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
98 if (argumentCount > 0) {
99 msgQueue[endOfQueue].delay = JSValueToNumber(context, arguments[0], exception);
100 ASSERT(!exception || !*exception);
103 return JSValueMakeUndefined(context);
106 static DWORD currentEventTime()
108 return ::GetTickCount() + timeOffset;
111 static MSG makeMsg(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
115 result.message = message;
116 result.wParam = wParam;
117 result.lParam = lParam;
118 result.time = currentEventTime();
119 result.pt = lastMousePosition;
124 static LRESULT dispatchMessage(const MSG* msg)
127 ::TranslateMessage(msg);
128 return ::DispatchMessage(msg);
131 static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
133 COMPtr<IWebFramePrivate> framePrivate;
134 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
135 framePrivate->layout();
138 MSG msg = makeMsg(webViewWindow, WM_RBUTTONDOWN, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
139 dispatchMessage(&msg);
141 msg = makeMsg(webViewWindow, WM_RBUTTONUP, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
142 dispatchMessage(&msg);
144 return JSValueMakeUndefined(context);
147 static WPARAM buildModifierFlags(JSContextRef context, const JSValueRef modifiers)
149 JSObjectRef modifiersArray = JSValueToObject(context, modifiers, 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"))
161 else if (JSStringIsEqualToUTF8CString(string, "shiftKey")
162 || JSStringIsEqualToUTF8CString(string, "rangeSelectionKey"))
164 // No way to specifiy altKey in a MSG.
166 JSStringRelease(string);
171 static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
173 COMPtr<IWebFramePrivate> framePrivate;
174 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
175 framePrivate->layout();
178 int mouseType = WM_LBUTTONDOWN;
179 if (argumentCount >= 1) {
180 int mouseNumber = JSValueToNumber(context, arguments[0], exception);
181 switch (mouseNumber) {
183 mouseType = WM_LBUTTONDOWN;
186 mouseType = WM_MBUTTONDOWN;
189 mouseType = WM_RBUTTONDOWN;
192 // fast/events/mouse-click-events expects the 4th button has event.button = 1, so send an WM_BUTTONDOWN
193 mouseType = WM_MBUTTONDOWN;
196 mouseType = WM_LBUTTONDOWN;
202 if (argumentCount >= 2)
203 wparam |= buildModifierFlags(context, arguments[1]);
205 MSG msg = makeMsg(webViewWindow, mouseType, wparam, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
206 if (!msgQueue[endOfQueue].delay)
207 dispatchMessage(&msg);
209 // replaySavedEvents has the required logic to make leapForward delays work
210 msgQueue[endOfQueue++].msg = msg;
214 return JSValueMakeUndefined(context);
217 static inline POINTL pointl(const POINT& point)
225 static void doMouseUp(MSG msg, HRESULT* oleDragAndDropReturnValue = 0)
227 COMPtr<IWebFramePrivate> framePrivate;
228 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
229 framePrivate->layout();
231 dispatchMessage(&msg);
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;
240 ::ClientToScreen(webViewWindow, &screenPoint);
242 webViewDropTarget->DragEnter(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect);
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) {
251 webViewDropTarget->Drop(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect);
252 draggingInfo->setPerformedDropEffect(effect);
254 webViewDropTarget->DragLeave();
256 // Reset didDragEnter so that another drag started within the same frame works properly.
257 didDragEnter = false;
262 static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
264 int mouseType = WM_LBUTTONUP;
265 if (argumentCount >= 1) {
266 int mouseNumber = JSValueToNumber(context, arguments[0], exception);
267 switch (mouseNumber) {
269 mouseType = WM_LBUTTONUP;
272 mouseType = WM_MBUTTONUP;
275 mouseType = WM_RBUTTONUP;
278 // fast/events/mouse-click-events expects the 4th button has event.button = 1, so send an WM_MBUTTONUP
279 mouseType = WM_MBUTTONUP;
282 mouseType = WM_LBUTTONUP;
288 if (argumentCount >= 2)
289 wparam |= buildModifierFlags(context, arguments[1]);
291 MSG msg = makeMsg(webViewWindow, mouseType, wparam, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
293 if ((dragMode && !replayingSavedEvents) || msgQueue[endOfQueue].delay) {
294 msgQueue[endOfQueue++].msg = msg;
299 return JSValueMakeUndefined(context);
302 static void doMouseMove(MSG msg)
304 COMPtr<IWebFramePrivate> framePrivate;
305 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
306 framePrivate->layout();
308 dispatchMessage(&msg);
310 if (down && draggingInfo) {
311 POINT screenPoint = msg.pt;
312 ::ClientToScreen(webViewWindow, &screenPoint);
315 COMPtr<IDropTarget> webViewDropTarget;
316 if (SUCCEEDED(frame->webView(&webView)) && SUCCEEDED(webView->QueryInterface(IID_IDropTarget, (void**)&webViewDropTarget))) {
319 webViewDropTarget->DragOver(MK_LBUTTON, pointl(screenPoint), &effect);
321 webViewDropTarget->DragEnter(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect);
324 draggingInfo->dropSource()->GiveFeedback(effect);
329 static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
331 if (argumentCount < 2)
332 return JSValueMakeUndefined(context);
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);
339 MSG msg = makeMsg(webViewWindow, WM_MOUSEMOVE, down ? MK_LBUTTON : 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
341 if (dragMode && down && !replayingSavedEvents) {
342 msgQueue[endOfQueue++].msg = msg;
343 return JSValueMakeUndefined(context);
348 return JSValueMakeUndefined(context);
351 void replaySavedEvents(HRESULT* oleDragAndDropReturnValue)
353 replayingSavedEvents = true;
357 while (startOfQueue < endOfQueue && !msgQueue[startOfQueue].delay) {
358 msg = msgQueue[startOfQueue++].msg;
359 switch (msg.message) {
363 doMouseUp(msg, oleDragAndDropReturnValue);
371 dispatchMessage(&msg);
379 int numQueuedMessages = endOfQueue - startOfQueue;
380 if (!numQueuedMessages) {
383 replayingSavedEvents = false;
388 if (msgQueue[startOfQueue].delay) {
389 ::Sleep(msgQueue[startOfQueue].delay);
390 msgQueue[startOfQueue].delay = 0;
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)
398 if (msg.message != WM_DRT_SEND_QUEUED_EVENT) {
399 dispatchMessage(&msg);
402 msg = msgQueue[startOfQueue++].msg;
403 switch (msg.message) {
407 doMouseUp(msg, oleDragAndDropReturnValue);
415 dispatchMessage(&msg);
421 if (startOfQueue >= endOfQueue)
423 ::Sleep(msgQueue[startOfQueue].delay);
424 msgQueue[startOfQueue].delay = 0;
425 ::PostMessage(webViewWindow, WM_DRT_SEND_QUEUED_EVENT, 0, 0);
430 replayingSavedEvents = false;
433 static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
435 if (argumentCount < 1)
436 return JSValueMakeUndefined(context);
438 static const JSStringRef lengthProperty = JSStringCreateWithUTF8CString("length");
440 COMPtr<IWebFramePrivate> framePrivate;
441 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
442 framePrivate->layout();
444 JSStringRef character = JSValueToStringCopy(context, arguments[0], exception);
449 bool needsShiftKeyModifier = false;
450 if (JSStringIsEqualToUTF8CString(character, "leftArrow")) {
451 virtualKeyCode = VK_LEFT;
452 keyData += KF_EXTENDED << 16; // In this case, extended means "not keypad".
453 } else if (JSStringIsEqualToUTF8CString(character, "rightArrow")) {
454 virtualKeyCode = VK_RIGHT;
455 keyData += KF_EXTENDED << 16;
456 } else if (JSStringIsEqualToUTF8CString(character, "upArrow")) {
457 virtualKeyCode = VK_UP;
458 keyData += KF_EXTENDED << 16;
459 } else if (JSStringIsEqualToUTF8CString(character, "downArrow")) {
460 virtualKeyCode = VK_DOWN;
461 keyData += KF_EXTENDED << 16;
462 } else if (JSStringIsEqualToUTF8CString(character, "pageUp"))
463 virtualKeyCode = VK_PRIOR;
464 else if (JSStringIsEqualToUTF8CString(character, "pageDown"))
465 virtualKeyCode = VK_NEXT;
466 else if (JSStringIsEqualToUTF8CString(character, "home"))
467 virtualKeyCode = VK_HOME;
468 else if (JSStringIsEqualToUTF8CString(character, "end"))
469 virtualKeyCode = VK_END;
470 else if (JSStringIsEqualToUTF8CString(character, "insert"))
471 virtualKeyCode = VK_INSERT;
472 else if (JSStringIsEqualToUTF8CString(character, "delete"))
473 virtualKeyCode = VK_DELETE;
474 else if (JSStringIsEqualToUTF8CString(character, "printScreen"))
475 virtualKeyCode = VK_SNAPSHOT;
476 else if (JSStringIsEqualToUTF8CString(character, "menu"))
477 virtualKeyCode = VK_APPS;
479 charCode = JSStringGetCharactersPtr(character)[0];
480 virtualKeyCode = LOBYTE(VkKeyScan(charCode));
481 if (WTF::isASCIIUpper(charCode))
482 needsShiftKeyModifier = true;
484 JSStringRelease(character);
487 if (argumentCount > 1 || needsShiftKeyModifier) {
488 ::GetKeyboardState(keyState);
490 BYTE newKeyState[256];
491 memcpy(newKeyState, keyState, sizeof(keyState));
493 if (needsShiftKeyModifier)
494 newKeyState[VK_SHIFT] = 0x80;
496 if (argumentCount > 1) {
497 JSObjectRef modifiersArray = JSValueToObject(context, arguments[1], 0);
498 if (modifiersArray) {
499 int modifiersCount = JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty, 0), 0);
500 for (int i = 0; i < modifiersCount; ++i) {
501 JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0);
502 JSStringRef string = JSValueToStringCopy(context, value, 0);
503 if (JSStringIsEqualToUTF8CString(string, "ctrlKey") || JSStringIsEqualToUTF8CString(string, "addSelectionKey"))
504 newKeyState[VK_CONTROL] = 0x80;
505 else if (JSStringIsEqualToUTF8CString(string, "shiftKey") || JSStringIsEqualToUTF8CString(string, "rangeSelectionKey"))
506 newKeyState[VK_SHIFT] = 0x80;
507 else if (JSStringIsEqualToUTF8CString(string, "altKey"))
508 newKeyState[VK_MENU] = 0x80;
510 JSStringRelease(string);
515 ::SetKeyboardState(newKeyState);
518 MSG msg = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYDOWN : WM_KEYDOWN, virtualKeyCode, keyData);
519 if (virtualKeyCode != 255)
520 dispatchMessage(&msg);
522 // For characters that do not exist in the active keyboard layout,
523 // ::Translate will not work, so we post an WM_CHAR event ourselves.
524 ::PostMessage(webViewWindow, WM_CHAR, charCode, 0);
527 // Tests expect that all messages are processed by the time keyDown() returns.
528 if (::PeekMessage(&msg, webViewWindow, WM_CHAR, WM_CHAR, PM_REMOVE) || ::PeekMessage(&msg, webViewWindow, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE))
529 ::DispatchMessage(&msg);
531 MSG msgUp = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYUP : WM_KEYUP, virtualKeyCode, keyData);
532 ::DispatchMessage(&msgUp);
534 if (argumentCount > 1 || needsShiftKeyModifier)
535 ::SetKeyboardState(keyState);
537 return JSValueMakeUndefined(context);
540 // eventSender.dispatchMessage(message, wParam, lParam, time = currentEventTime(), x = lastMousePosition.x, y = lastMousePosition.y)
541 static JSValueRef dispatchMessageCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
543 if (argumentCount < 3)
544 return JSValueMakeUndefined(context);
546 COMPtr<IWebFramePrivate> framePrivate;
547 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
548 framePrivate->layout();
551 msg.hwnd = webViewWindow;
552 msg.message = JSValueToNumber(context, arguments[0], exception);
554 msg.wParam = JSValueToNumber(context, arguments[1], exception);
556 msg.lParam = static_cast<ULONG_PTR>(JSValueToNumber(context, arguments[2], exception));
558 if (argumentCount >= 4) {
559 msg.time = JSValueToNumber(context, arguments[3], exception);
563 msg.time = currentEventTime();
564 if (argumentCount >= 6) {
565 msg.pt.x = JSValueToNumber(context, arguments[4], exception);
567 msg.pt.y = JSValueToNumber(context, arguments[5], exception);
570 msg.pt = lastMousePosition;
572 ::DispatchMessage(&msg);
574 return JSValueMakeUndefined(context);
577 static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
579 COMPtr<IWebView> webView;
580 if (FAILED(frame->webView(&webView)))
581 return JSValueMakeUndefined(context);
583 COMPtr<IWebIBActions> webIBActions(Query, webView);
585 return JSValueMakeUndefined(context);
587 webIBActions->makeTextLarger(0);
588 return JSValueMakeUndefined(context);
591 static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
593 COMPtr<IWebView> webView;
594 if (FAILED(frame->webView(&webView)))
595 return JSValueMakeUndefined(context);
597 COMPtr<IWebIBActions> webIBActions(Query, webView);
599 return JSValueMakeUndefined(context);
601 webIBActions->makeTextSmaller(0);
602 return JSValueMakeUndefined(context);
605 static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
607 COMPtr<IWebView> webView;
608 if (FAILED(frame->webView(&webView)))
609 return JSValueMakeUndefined(context);
611 COMPtr<IWebIBActions> webIBActions(Query, webView);
613 return JSValueMakeUndefined(context);
615 webIBActions->zoomPageIn(0);
616 return JSValueMakeUndefined(context);
619 static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
621 COMPtr<IWebView> webView;
622 if (FAILED(frame->webView(&webView)))
623 return JSValueMakeUndefined(context);
625 COMPtr<IWebIBActions> webIBActions(Query, webView);
627 return JSValueMakeUndefined(context);
629 webIBActions->zoomPageOut(0);
630 return JSValueMakeUndefined(context);
633 static JSStaticFunction staticFunctions[] = {
634 { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
635 { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
636 { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
637 { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
638 { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
639 { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
640 { "dispatchMessage", dispatchMessageCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
641 { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
642 { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
643 { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
644 { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
648 static JSStaticValue staticValues[] = {
649 { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone },
650 { "WM_KEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
651 { "WM_KEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
652 { "WM_CHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
653 { "WM_DEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
654 { "WM_SYSKEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
655 { "WM_SYSKEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
656 { "WM_SYSCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
657 { "WM_SYSDEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
661 static JSClassRef getClass(JSContextRef context)
663 static JSClassRef eventSenderClass = 0;
665 if (!eventSenderClass) {
666 JSClassDefinition classDefinition = {0};
667 classDefinition.staticFunctions = staticFunctions;
668 classDefinition.staticValues = staticValues;
670 eventSenderClass = JSClassCreate(&classDefinition);
673 return eventSenderClass;
676 JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame)
681 replayingSavedEvents = false;
683 lastMousePosition.x = 0;
684 lastMousePosition.y = 0;
689 didDragEnter = false;
692 return JSObjectMake(context, getClass(context), 0);