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
mouseDownCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)147 static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
148 {
149 COMPtr<IWebFramePrivate> framePrivate;
150 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
151 framePrivate->layout();
152
153 down = true;
154 MSG msg = makeMsg(webViewWindow, WM_LBUTTONDOWN, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
155 if (!msgQueue[endOfQueue].delay)
156 dispatchMessage(&msg);
157 else {
158 // replaySavedEvents has the required logic to make leapForward delays work
159 msgQueue[endOfQueue++].msg = msg;
160 replaySavedEvents();
161 }
162
163 return JSValueMakeUndefined(context);
164 }
165
pointl(const POINT & point)166 static inline POINTL pointl(const POINT& point)
167 {
168 POINTL result;
169 result.x = point.x;
170 result.y = point.y;
171 return result;
172 }
173
doMouseUp(MSG msg)174 static void doMouseUp(MSG msg)
175 {
176 COMPtr<IWebFramePrivate> framePrivate;
177 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
178 framePrivate->layout();
179
180 dispatchMessage(&msg);
181 down = false;
182
183 if (draggingInfo) {
184 COMPtr<IWebView> webView;
185 COMPtr<IDropTarget> webViewDropTarget;
186 if (SUCCEEDED(frame->webView(&webView)) && SUCCEEDED(webView->QueryInterface(IID_IDropTarget, (void**)&webViewDropTarget))) {
187 POINT screenPoint = msg.pt;
188 DWORD effect = 0;
189 ::ClientToScreen(webViewWindow, &screenPoint);
190 if (!didDragEnter) {
191 webViewDropTarget->DragEnter(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect);
192 didDragEnter = true;
193 }
194 HRESULT hr = draggingInfo->dropSource()->QueryContinueDrag(0, 0);
195 webViewDropTarget->DragOver(0, pointl(screenPoint), &effect);
196 if (hr == DRAGDROP_S_DROP && effect != DROPEFFECT_NONE) {
197 DWORD effect = 0;
198 webViewDropTarget->Drop(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect);
199 } else
200 webViewDropTarget->DragLeave();
201
202 delete draggingInfo;
203 draggingInfo = 0;
204 }
205 }
206 }
207
mouseUpCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)208 static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
209 {
210 MSG msg = makeMsg(webViewWindow, WM_LBUTTONUP, 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
211
212 if ((dragMode && !replayingSavedEvents) || msgQueue[endOfQueue].delay) {
213 msgQueue[endOfQueue++].msg = msg;
214 replaySavedEvents();
215 } else
216 doMouseUp(msg);
217
218 return JSValueMakeUndefined(context);
219 }
220
doMouseMove(MSG msg)221 static void doMouseMove(MSG msg)
222 {
223 COMPtr<IWebFramePrivate> framePrivate;
224 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
225 framePrivate->layout();
226
227 dispatchMessage(&msg);
228
229 if (down && draggingInfo) {
230 POINT screenPoint = msg.pt;
231 ::ClientToScreen(webViewWindow, &screenPoint);
232
233 IWebView* webView;
234 COMPtr<IDropTarget> webViewDropTarget;
235 if (SUCCEEDED(frame->webView(&webView)) && SUCCEEDED(webView->QueryInterface(IID_IDropTarget, (void**)&webViewDropTarget))) {
236 DWORD effect = 0;
237 if (didDragEnter)
238 webViewDropTarget->DragOver(MK_LBUTTON, pointl(screenPoint), &effect);
239 else {
240 webViewDropTarget->DragEnter(draggingInfo->dataObject(), 0, pointl(screenPoint), &effect);
241 didDragEnter = true;
242 }
243 draggingInfo->dropSource()->GiveFeedback(effect);
244 }
245 }
246 }
247
mouseMoveToCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)248 static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
249 {
250 if (argumentCount < 2)
251 return JSValueMakeUndefined(context);
252
253 lastMousePosition.x = (int)JSValueToNumber(context, arguments[0], exception);
254 ASSERT(!exception || !*exception);
255 lastMousePosition.y = (int)JSValueToNumber(context, arguments[1], exception);
256 ASSERT(!exception || !*exception);
257
258 MSG msg = makeMsg(webViewWindow, WM_MOUSEMOVE, down ? MK_LBUTTON : 0, MAKELPARAM(lastMousePosition.x, lastMousePosition.y));
259
260 if (dragMode && down && !replayingSavedEvents) {
261 msgQueue[endOfQueue++].msg = msg;
262 return JSValueMakeUndefined(context);
263 }
264
265 doMouseMove(msg);
266
267 return JSValueMakeUndefined(context);
268 }
269
replaySavedEvents()270 void replaySavedEvents()
271 {
272 replayingSavedEvents = true;
273
274 MSG msg = { 0 };
275
276 while (startOfQueue < endOfQueue && !msgQueue[startOfQueue].delay) {
277 msg = msgQueue[startOfQueue++].msg;
278 switch (msg.message) {
279 case WM_LBUTTONUP:
280 doMouseUp(msg);
281 break;
282 case WM_MOUSEMOVE:
283 doMouseMove(msg);
284 break;
285 case WM_LBUTTONDOWN:
286 dispatchMessage(&msg);
287 break;
288 default:
289 // Not reached
290 break;
291 }
292 }
293
294 int numQueuedMessages = endOfQueue - startOfQueue;
295 if (!numQueuedMessages) {
296 startOfQueue = 0;
297 endOfQueue = 0;
298 replayingSavedEvents = false;
299 ASSERT(!down);
300 return;
301 }
302
303 if (msgQueue[startOfQueue].delay) {
304 ::Sleep(msgQueue[startOfQueue].delay);
305 msgQueue[startOfQueue].delay = 0;
306 }
307
308 ::PostMessage(webViewWindow, WM_DRT_SEND_QUEUED_EVENT, 0, 0);
309 while (::GetMessage(&msg, webViewWindow, 0, 0)) {
310 // FIXME: Why do we get a WM_MOUSELEAVE? it breaks tests
311 if (msg.message == WM_MOUSELEAVE)
312 continue;
313 if (msg.message != WM_DRT_SEND_QUEUED_EVENT) {
314 dispatchMessage(&msg);
315 continue;
316 }
317 msg = msgQueue[startOfQueue++].msg;
318 switch (msg.message) {
319 case WM_LBUTTONUP:
320 doMouseUp(msg);
321 break;
322 case WM_MOUSEMOVE:
323 doMouseMove(msg);
324 break;
325 case WM_LBUTTONDOWN:
326 dispatchMessage(&msg);
327 break;
328 default:
329 // Not reached
330 break;
331 }
332 if (startOfQueue >= endOfQueue)
333 break;
334 ::Sleep(msgQueue[startOfQueue].delay);
335 msgQueue[startOfQueue].delay = 0;
336 ::PostMessage(webViewWindow, WM_DRT_SEND_QUEUED_EVENT, 0, 0);
337 }
338 startOfQueue = 0;
339 endOfQueue = 0;
340
341 replayingSavedEvents = false;
342 }
343
keyDownCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)344 static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
345 {
346 if (argumentCount < 1)
347 return JSValueMakeUndefined(context);
348
349 static const JSStringRef lengthProperty = JSStringCreateWithUTF8CString("length");
350
351 COMPtr<IWebFramePrivate> framePrivate;
352 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
353 framePrivate->layout();
354
355 JSStringRef character = JSValueToStringCopy(context, arguments[0], exception);
356 ASSERT(!*exception);
357 int virtualKeyCode;
358 int charCode = 0;
359 int keyData = 1;
360 bool needsShiftKeyModifier = false;
361 if (JSStringIsEqualToUTF8CString(character, "leftArrow")) {
362 virtualKeyCode = VK_LEFT;
363 keyData += KF_EXTENDED << 16; // In this case, extended means "not keypad".
364 } else if (JSStringIsEqualToUTF8CString(character, "rightArrow")) {
365 virtualKeyCode = VK_RIGHT;
366 keyData += KF_EXTENDED << 16;
367 } else if (JSStringIsEqualToUTF8CString(character, "upArrow")) {
368 virtualKeyCode = VK_UP;
369 keyData += KF_EXTENDED << 16;
370 } else if (JSStringIsEqualToUTF8CString(character, "downArrow")) {
371 virtualKeyCode = VK_DOWN;
372 keyData += KF_EXTENDED << 16;
373 } else if (JSStringIsEqualToUTF8CString(character, "pageUp"))
374 virtualKeyCode = VK_PRIOR;
375 else if (JSStringIsEqualToUTF8CString(character, "pageDown"))
376 virtualKeyCode = VK_NEXT;
377 else if (JSStringIsEqualToUTF8CString(character, "home"))
378 virtualKeyCode = VK_HOME;
379 else if (JSStringIsEqualToUTF8CString(character, "end"))
380 virtualKeyCode = VK_END;
381 else if (JSStringIsEqualToUTF8CString(character, "delete"))
382 virtualKeyCode = VK_BACK;
383 else {
384 charCode = JSStringGetCharactersPtr(character)[0];
385 virtualKeyCode = LOBYTE(VkKeyScan(charCode));
386 if (WTF::isASCIIUpper(charCode))
387 needsShiftKeyModifier = true;
388 }
389 JSStringRelease(character);
390
391 BYTE keyState[256];
392 if (argumentCount > 1 || needsShiftKeyModifier) {
393 ::GetKeyboardState(keyState);
394
395 BYTE newKeyState[256];
396 memcpy(newKeyState, keyState, sizeof(keyState));
397
398 if (needsShiftKeyModifier)
399 newKeyState[VK_SHIFT] = 0x80;
400
401 if (argumentCount > 1) {
402 JSObjectRef modifiersArray = JSValueToObject(context, arguments[1], 0);
403 if (modifiersArray) {
404 int modifiersCount = JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty, 0), 0);
405 for (int i = 0; i < modifiersCount; ++i) {
406 JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0);
407 JSStringRef string = JSValueToStringCopy(context, value, 0);
408 if (JSStringIsEqualToUTF8CString(string, "ctrlKey"))
409 newKeyState[VK_CONTROL] = 0x80;
410 else if (JSStringIsEqualToUTF8CString(string, "shiftKey"))
411 newKeyState[VK_SHIFT] = 0x80;
412 else if (JSStringIsEqualToUTF8CString(string, "altKey"))
413 newKeyState[VK_MENU] = 0x80;
414
415 JSStringRelease(string);
416 }
417 }
418 }
419
420 ::SetKeyboardState(newKeyState);
421 }
422
423 MSG msg = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYDOWN : WM_KEYDOWN, virtualKeyCode, keyData);
424 if (virtualKeyCode != 255)
425 dispatchMessage(&msg);
426 else {
427 // For characters that do not exist in the active keyboard layout,
428 // ::Translate will not work, so we post an WM_CHAR event ourselves.
429 ::PostMessage(webViewWindow, WM_CHAR, charCode, 0);
430 }
431
432 // Tests expect that all messages are processed by the time keyDown() returns.
433 if (::PeekMessage(&msg, webViewWindow, WM_CHAR, WM_CHAR, PM_REMOVE) || ::PeekMessage(&msg, webViewWindow, WM_SYSCHAR, WM_SYSCHAR, PM_REMOVE))
434 ::DispatchMessage(&msg);
435
436 MSG msgUp = makeMsg(webViewWindow, (::GetKeyState(VK_MENU) & 0x8000) ? WM_SYSKEYUP : WM_KEYUP, virtualKeyCode, keyData);
437 ::DispatchMessage(&msgUp);
438
439 if (argumentCount > 1 || needsShiftKeyModifier)
440 ::SetKeyboardState(keyState);
441
442 return JSValueMakeUndefined(context);
443 }
444
445 // 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)446 static JSValueRef dispatchMessageCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
447 {
448 if (argumentCount < 3)
449 return JSValueMakeUndefined(context);
450
451 COMPtr<IWebFramePrivate> framePrivate;
452 if (SUCCEEDED(frame->QueryInterface(&framePrivate)))
453 framePrivate->layout();
454
455 MSG msg = {};
456 msg.hwnd = webViewWindow;
457 msg.message = JSValueToNumber(context, arguments[0], exception);
458 ASSERT(!*exception);
459 msg.wParam = JSValueToNumber(context, arguments[1], exception);
460 ASSERT(!*exception);
461 msg.lParam = static_cast<ULONG_PTR>(JSValueToNumber(context, arguments[2], exception));
462 ASSERT(!*exception);
463 if (argumentCount >= 4) {
464 msg.time = JSValueToNumber(context, arguments[3], exception);
465 ASSERT(!*exception);
466 }
467 if (!msg.time)
468 msg.time = currentEventTime();
469 if (argumentCount >= 6) {
470 msg.pt.x = JSValueToNumber(context, arguments[4], exception);
471 ASSERT(!*exception);
472 msg.pt.y = JSValueToNumber(context, arguments[5], exception);
473 ASSERT(!*exception);
474 } else
475 msg.pt = lastMousePosition;
476
477 ::DispatchMessage(&msg);
478
479 return JSValueMakeUndefined(context);
480 }
481
textZoomInCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)482 static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
483 {
484 COMPtr<IWebView> webView;
485 if (FAILED(frame->webView(&webView)))
486 return JSValueMakeUndefined(context);
487
488 COMPtr<IWebIBActions> webIBActions(Query, webView);
489 if (!webIBActions)
490 return JSValueMakeUndefined(context);
491
492 webIBActions->makeTextLarger(0);
493 return JSValueMakeUndefined(context);
494 }
495
textZoomOutCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)496 static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
497 {
498 COMPtr<IWebView> webView;
499 if (FAILED(frame->webView(&webView)))
500 return JSValueMakeUndefined(context);
501
502 COMPtr<IWebIBActions> webIBActions(Query, webView);
503 if (!webIBActions)
504 return JSValueMakeUndefined(context);
505
506 webIBActions->makeTextSmaller(0);
507 return JSValueMakeUndefined(context);
508 }
509
zoomPageInCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)510 static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
511 {
512 COMPtr<IWebView> webView;
513 if (FAILED(frame->webView(&webView)))
514 return JSValueMakeUndefined(context);
515
516 COMPtr<IWebIBActions> webIBActions(Query, webView);
517 if (!webIBActions)
518 return JSValueMakeUndefined(context);
519
520 webIBActions->zoomPageIn(0);
521 return JSValueMakeUndefined(context);
522 }
523
zoomPageOutCallback(JSContextRef context,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef * exception)524 static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
525 {
526 COMPtr<IWebView> webView;
527 if (FAILED(frame->webView(&webView)))
528 return JSValueMakeUndefined(context);
529
530 COMPtr<IWebIBActions> webIBActions(Query, webView);
531 if (!webIBActions)
532 return JSValueMakeUndefined(context);
533
534 webIBActions->zoomPageOut(0);
535 return JSValueMakeUndefined(context);
536 }
537
538 static JSStaticFunction staticFunctions[] = {
539 { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
540 { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
541 { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
542 { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
543 { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
544 { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
545 { "dispatchMessage", dispatchMessageCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
546 { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
547 { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
548 { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
549 { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
550 { 0, 0, 0 }
551 };
552
553 static JSStaticValue staticValues[] = {
554 { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone },
555 { "WM_KEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
556 { "WM_KEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
557 { "WM_CHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
558 { "WM_DEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
559 { "WM_SYSKEYDOWN", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
560 { "WM_SYSKEYUP", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
561 { "WM_SYSCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
562 { "WM_SYSDEADCHAR", getConstantCallback, 0, kJSPropertyAttributeReadOnly | kJSPropertyAttributeNone },
563 { 0, 0, 0, 0 }
564 };
565
getClass(JSContextRef context)566 static JSClassRef getClass(JSContextRef context)
567 {
568 static JSClassRef eventSenderClass = 0;
569
570 if (!eventSenderClass) {
571 JSClassDefinition classDefinition = {0};
572 classDefinition.staticFunctions = staticFunctions;
573 classDefinition.staticValues = staticValues;
574
575 eventSenderClass = JSClassCreate(&classDefinition);
576 }
577
578 return eventSenderClass;
579 }
580
makeEventSender(JSContextRef context)581 JSObjectRef makeEventSender(JSContextRef context)
582 {
583 down = false;
584 dragMode = true;
585 replayingSavedEvents = false;
586 timeOffset = 0;
587 lastMousePosition.x = 0;
588 lastMousePosition.y = 0;
589
590 endOfQueue = 0;
591 startOfQueue = 0;
592
593 didDragEnter = false;
594 draggingInfo = 0;
595
596 return JSObjectMake(context, getClass(context), 0);
597 }
598