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 |
|
63 static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) |
|
64 { |
|
65 return JSValueMakeBoolean(context, dragMode); |
|
66 } |
|
67 |
|
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 |
|
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 |
|
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 |
|
106 static DWORD currentEventTime() |
|
107 { |
|
108 return ::GetTickCount() + timeOffset; |
|
109 } |
|
110 |
|
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 |
|
124 static LRESULT dispatchMessage(const MSG* msg) |
|
125 { |
|
126 ASSERT(msg); |
|
127 ::TranslateMessage(msg); |
|
128 return ::DispatchMessage(msg); |
|
129 } |
|
130 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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_DELETE; |
|
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) |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
670 JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame) |
|
671 { |
|
672 if (isTopFrame) { |
|
673 down = false; |
|
674 dragMode = true; |
|
675 replayingSavedEvents = false; |
|
676 timeOffset = 0; |
|
677 lastMousePosition.x = 0; |
|
678 lastMousePosition.y = 0; |
|
679 |
|
680 endOfQueue = 0; |
|
681 startOfQueue = 0; |
|
682 |
|
683 didDragEnter = false; |
|
684 draggingInfo = 0; |
|
685 } |
|
686 return JSObjectMake(context, getClass(context), 0); |
|
687 } |
|