1 /* 2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2009 Zan Dobersek <zandobersek (at) gmail.com> 4 * Copyright (C) 2009 Holger Hans Peter Freyther 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 16 * its contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "EventSender.h" 33 34 #include "DumpRenderTree.h" 35 36 #include <JavaScriptCore/JSObjectRef.h> 37 #include <JavaScriptCore/JSRetainPtr.h> 38 #include <JavaScriptCore/JSStringRef.h> 39 #include <webkit/webkitwebframe.h> 40 #include <webkit/webkitwebview.h> 41 #include <wtf/ASCIICType.h> 42 #include <wtf/Platform.h> 43 44 #include <gdk/gdk.h> 45 #include <gdk/gdkkeysyms.h> 46 #include <string.h> 47 48 // TODO: Currently drag and drop related code is left out and 49 // should be merged once we have drag and drop support in WebCore. 50 51 extern "C" { 52 extern void webkit_web_frame_layout(WebKitWebFrame* frame); 53 } 54 55 static bool down = false; 56 static bool currentEventButton = 1; 57 static bool dragMode = true; 58 static bool replayingSavedEvents = false; 59 static int lastMousePositionX; 60 static int lastMousePositionY; 61 62 static int lastClickPositionX; 63 static int lastClickPositionY; 64 static int clickCount = 0; 65 66 struct DelayedMessage { 67 GdkEvent event; 68 gulong delay; 69 gboolean isDragEvent; 70 }; 71 72 static DelayedMessage msgQueue[1024]; 73 74 static unsigned endOfQueue; 75 static unsigned startOfQueue; 76 77 static const float zoomMultiplierRatio = 1.2f; 78 79 // Key event location code defined in DOM Level 3. 80 enum KeyLocationCode { 81 DOM_KEY_LOCATION_STANDARD = 0x00, 82 DOM_KEY_LOCATION_LEFT = 0x01, 83 DOM_KEY_LOCATION_RIGHT = 0x02, 84 DOM_KEY_LOCATION_NUMPAD = 0x03 85 }; 86 87 static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) 88 { 89 return JSValueMakeBoolean(context, dragMode); 90 } 91 92 static bool setDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) 93 { 94 dragMode = JSValueToBoolean(context, value); 95 return true; 96 } 97 98 static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 99 { 100 // FIXME: Add proper support for forward leaps 101 return JSValueMakeUndefined(context); 102 } 103 104 static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 105 { 106 webkit_web_frame_layout(mainFrame); 107 108 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 109 if (!view) 110 return JSValueMakeUndefined(context); 111 112 GdkEvent event; 113 memset(&event, 0, sizeof(event)); 114 event.button.button = 3; 115 event.button.x = lastMousePositionX; 116 event.button.y = lastMousePositionY; 117 event.button.window = GTK_WIDGET(view)->window; 118 119 gboolean return_val; 120 down = true; 121 event.type = GDK_BUTTON_PRESS; 122 g_signal_emit_by_name(view, "button_press_event", &event, &return_val); 123 124 down = false; 125 event.type = GDK_BUTTON_RELEASE; 126 g_signal_emit_by_name(view, "button_release_event", &event, &return_val); 127 128 return JSValueMakeUndefined(context); 129 } 130 131 static void updateClickCount(int button) 132 { 133 // FIXME: take the last clicked button number and the time of last click into account. 134 if (lastClickPositionX != lastMousePositionX || lastClickPositionY != lastMousePositionY || currentEventButton != button) 135 clickCount = 1; 136 else 137 clickCount++; 138 } 139 140 #if !GTK_CHECK_VERSION(2,17,3) 141 static void getRootCoords(GtkWidget* view, int* rootX, int* rootY) 142 { 143 GtkWidget* window = gtk_widget_get_toplevel(GTK_WIDGET(view)); 144 int tmpX, tmpY; 145 146 gtk_widget_translate_coordinates(view, window, lastMousePositionX, lastMousePositionY, &tmpX, &tmpY); 147 148 gdk_window_get_origin(window->window, rootX, rootY); 149 150 *rootX += tmpX; 151 *rootY += tmpY; 152 } 153 #endif 154 155 static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 156 { 157 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 158 if (!view) 159 return JSValueMakeUndefined(context); 160 161 down = true; 162 163 GdkEvent event; 164 memset(&event, 0, sizeof(event)); 165 event.type = GDK_BUTTON_PRESS; 166 event.button.button = 1; 167 168 if (argumentCount == 1) { 169 event.button.button = (int)JSValueToNumber(context, arguments[0], exception) + 1; 170 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); 171 } 172 173 currentEventButton = event.button.button; 174 175 event.button.x = lastMousePositionX; 176 event.button.y = lastMousePositionY; 177 event.button.window = GTK_WIDGET(view)->window; 178 event.button.time = GDK_CURRENT_TIME; 179 event.button.device = gdk_device_get_core_pointer(); 180 181 int x_root, y_root; 182 #if GTK_CHECK_VERSION(2,17,3) 183 gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root); 184 #else 185 getRootCoords(GTK_WIDGET(view), &x_root, &y_root); 186 #endif 187 188 event.button.x_root = x_root; 189 event.button.y_root = y_root; 190 191 updateClickCount(event.button.button); 192 193 if (!msgQueue[endOfQueue].delay) { 194 webkit_web_frame_layout(mainFrame); 195 196 gboolean return_val; 197 g_signal_emit_by_name(view, "button_press_event", &event, &return_val); 198 if (clickCount == 2) { 199 event.type = GDK_2BUTTON_PRESS; 200 g_signal_emit_by_name(view, "button_press_event", &event, &return_val); 201 } 202 } else { 203 // replaySavedEvents should have the required logic to make leapForward delays work 204 msgQueue[endOfQueue++].event = event; 205 replaySavedEvents(); 206 } 207 208 return JSValueMakeUndefined(context); 209 } 210 211 static guint getStateFlags() 212 { 213 guint state = 0; 214 215 if (down) { 216 if (currentEventButton == 1) 217 state = GDK_BUTTON1_MASK; 218 else if (currentEventButton == 2) 219 state = GDK_BUTTON2_MASK; 220 else if (currentEventButton == 3) 221 state = GDK_BUTTON3_MASK; 222 } else 223 state = 0; 224 225 return state; 226 } 227 228 static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 229 { 230 231 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 232 if (!view) 233 return JSValueMakeUndefined(context); 234 235 GdkEvent event; 236 memset(&event, 0, sizeof(event)); 237 event.type = GDK_BUTTON_RELEASE; 238 event.button.button = 1; 239 240 if (argumentCount == 1) { 241 event.button.button = (int)JSValueToNumber(context, arguments[0], exception) + 1; 242 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); 243 } 244 245 currentEventButton = event.button.button; 246 247 event.button.x = lastMousePositionX; 248 event.button.y = lastMousePositionY; 249 event.button.window = GTK_WIDGET(view)->window; 250 event.button.time = GDK_CURRENT_TIME; 251 event.button.device = gdk_device_get_core_pointer(); 252 event.button.state = getStateFlags(); 253 254 down = false; 255 256 int x_root, y_root; 257 #if GTK_CHECK_VERSION(2,17,3) 258 gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root); 259 #else 260 getRootCoords(GTK_WIDGET(view), &x_root, &y_root); 261 #endif 262 263 event.button.x_root = x_root; 264 event.button.y_root = y_root; 265 266 if ((dragMode && !replayingSavedEvents) || msgQueue[endOfQueue].delay) { 267 msgQueue[endOfQueue].event = event; 268 msgQueue[endOfQueue++].isDragEvent = true; 269 replaySavedEvents(); 270 } else { 271 webkit_web_frame_layout(mainFrame); 272 273 gboolean return_val; 274 g_signal_emit_by_name(view, "button_release_event", &event, &return_val); 275 } 276 277 lastClickPositionX = lastMousePositionX; 278 lastClickPositionY = lastMousePositionY; 279 280 return JSValueMakeUndefined(context); 281 } 282 283 static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 284 { 285 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 286 if (!view) 287 return JSValueMakeUndefined(context); 288 289 if (argumentCount < 2) 290 return JSValueMakeUndefined(context); 291 292 lastMousePositionX = (int)JSValueToNumber(context, arguments[0], exception); 293 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); 294 lastMousePositionY = (int)JSValueToNumber(context, arguments[1], exception); 295 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); 296 297 GdkEvent event; 298 memset(&event, 0, sizeof(event)); 299 event.type = GDK_MOTION_NOTIFY; 300 event.motion.x = lastMousePositionX; 301 event.motion.y = lastMousePositionY; 302 event.motion.time = GDK_CURRENT_TIME; 303 event.motion.window = GTK_WIDGET(view)->window; 304 event.motion.device = gdk_device_get_core_pointer(); 305 306 int x_root, y_root; 307 #if GTK_CHECK_VERSION(2,17,3) 308 gdk_window_get_root_coords(GTK_WIDGET(view)->window, lastMousePositionX, lastMousePositionY, &x_root, &y_root); 309 #else 310 getRootCoords(GTK_WIDGET(view), &x_root, &y_root); 311 #endif 312 313 event.motion.x_root = x_root; 314 event.motion.y_root = y_root; 315 316 event.motion.state = getStateFlags(); 317 318 if (dragMode && down && !replayingSavedEvents) { 319 msgQueue[endOfQueue].event = event; 320 msgQueue[endOfQueue++].isDragEvent = true; 321 } else { 322 webkit_web_frame_layout(mainFrame); 323 324 gboolean return_val; 325 g_signal_emit_by_name(view, "motion_notify_event", &event, &return_val); 326 } 327 328 return JSValueMakeUndefined(context); 329 } 330 331 static JSValueRef mouseWheelToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 332 { 333 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 334 if (!view) 335 return JSValueMakeUndefined(context); 336 337 if (argumentCount < 2) 338 return JSValueMakeUndefined(context); 339 340 int horizontal = (int)JSValueToNumber(context, arguments[0], exception); 341 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); 342 int vertical = (int)JSValueToNumber(context, arguments[1], exception); 343 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); 344 345 // GTK+ doesn't support multiple direction scrolls in the same event! 346 g_return_val_if_fail((!vertical || !horizontal), JSValueMakeUndefined(context)); 347 348 GdkEvent event; 349 event.type = GDK_SCROLL; 350 event.scroll.x = lastMousePositionX; 351 event.scroll.y = lastMousePositionY; 352 event.scroll.time = GDK_CURRENT_TIME; 353 event.scroll.window = GTK_WIDGET(view)->window; 354 355 if (horizontal < 0) 356 event.scroll.direction = GDK_SCROLL_LEFT; 357 else if (horizontal > 0) 358 event.scroll.direction = GDK_SCROLL_RIGHT; 359 else if (vertical < 0) 360 event.scroll.direction = GDK_SCROLL_UP; 361 else if (vertical > 0) 362 event.scroll.direction = GDK_SCROLL_DOWN; 363 else 364 g_assert_not_reached(); 365 366 if (dragMode && down && !replayingSavedEvents) { 367 msgQueue[endOfQueue].event = event; 368 msgQueue[endOfQueue++].isDragEvent = true; 369 } else { 370 webkit_web_frame_layout(mainFrame); 371 gtk_main_do_event(&event); 372 } 373 374 return JSValueMakeUndefined(context); 375 } 376 377 static JSValueRef beginDragWithFilesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 378 { 379 if (argumentCount < 1) 380 return JSValueMakeUndefined(context); 381 382 // FIXME: Implement this completely once WebCore has complete drag and drop support 383 return JSValueMakeUndefined(context); 384 } 385 386 void replaySavedEvents() 387 { 388 // FIXME: This doesn't deal with forward leaps, but it should. 389 390 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 391 if (!view) 392 return; 393 394 replayingSavedEvents = true; 395 396 for (unsigned queuePos = 0; queuePos < endOfQueue; queuePos++) { 397 GdkEvent event = msgQueue[queuePos].event; 398 gboolean return_val; 399 400 switch (event.type) { 401 case GDK_BUTTON_RELEASE: 402 g_signal_emit_by_name(view, "button_release_event", &event, &return_val); 403 break; 404 case GDK_BUTTON_PRESS: 405 g_signal_emit_by_name(view, "button_press_event", &event, &return_val); 406 break; 407 case GDK_MOTION_NOTIFY: 408 g_signal_emit_by_name(view, "motion_notify_event", &event, &return_val); 409 break; 410 default: 411 continue; 412 } 413 414 startOfQueue++; 415 } 416 417 int numQueuedMessages = endOfQueue - startOfQueue; 418 if (!numQueuedMessages) { 419 startOfQueue = 0; 420 endOfQueue = 0; 421 replayingSavedEvents = false; 422 return; 423 } 424 425 startOfQueue = 0; 426 endOfQueue = 0; 427 428 replayingSavedEvents = false; 429 } 430 431 static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 432 { 433 if (argumentCount < 1) 434 return JSValueMakeUndefined(context); 435 436 static const JSStringRef lengthProperty = JSStringCreateWithUTF8CString("length"); 437 438 webkit_web_frame_layout(mainFrame); 439 440 // handle modifier keys. 441 int state = 0; 442 if (argumentCount > 1) { 443 JSObjectRef modifiersArray = JSValueToObject(context, arguments[1], exception); 444 if (modifiersArray) { 445 for (int i = 0; i < JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty, 0), 0); ++i) { 446 JSValueRef value = JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0); 447 JSStringRef string = JSValueToStringCopy(context, value, 0); 448 if (JSStringIsEqualToUTF8CString(string, "ctrlKey")) 449 state |= GDK_CONTROL_MASK; 450 else if (JSStringIsEqualToUTF8CString(string, "shiftKey")) 451 state |= GDK_SHIFT_MASK; 452 else if (JSStringIsEqualToUTF8CString(string, "altKey")) 453 state |= GDK_MOD1_MASK; 454 455 JSStringRelease(string); 456 } 457 } 458 } 459 460 // handle location argument. 461 int location = DOM_KEY_LOCATION_STANDARD; 462 if (argumentCount > 2) 463 location = (int)JSValueToNumber(context, arguments[2], exception); 464 465 JSStringRef character = JSValueToStringCopy(context, arguments[0], exception); 466 g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context)); 467 int gdkKeySym = GDK_VoidSymbol; 468 if (location == DOM_KEY_LOCATION_NUMPAD) { 469 if (JSStringIsEqualToUTF8CString(character, "leftArrow")) 470 gdkKeySym = GDK_KP_Left; 471 else if (JSStringIsEqualToUTF8CString(character, "rightArrow")) 472 gdkKeySym = GDK_KP_Right; 473 else if (JSStringIsEqualToUTF8CString(character, "upArrow")) 474 gdkKeySym = GDK_KP_Up; 475 else if (JSStringIsEqualToUTF8CString(character, "downArrow")) 476 gdkKeySym = GDK_KP_Down; 477 else if (JSStringIsEqualToUTF8CString(character, "pageUp")) 478 gdkKeySym = GDK_KP_Page_Up; 479 else if (JSStringIsEqualToUTF8CString(character, "pageDown")) 480 gdkKeySym = GDK_KP_Page_Down; 481 else if (JSStringIsEqualToUTF8CString(character, "home")) 482 gdkKeySym = GDK_KP_Home; 483 else if (JSStringIsEqualToUTF8CString(character, "end")) 484 gdkKeySym = GDK_KP_End; 485 else 486 // Assume we only get arrow/pgUp/pgDn/home/end keys with 487 // location=NUMPAD for now. 488 g_assert_not_reached(); 489 } else { 490 if (JSStringIsEqualToUTF8CString(character, "leftArrow")) 491 gdkKeySym = GDK_Left; 492 else if (JSStringIsEqualToUTF8CString(character, "rightArrow")) 493 gdkKeySym = GDK_Right; 494 else if (JSStringIsEqualToUTF8CString(character, "upArrow")) 495 gdkKeySym = GDK_Up; 496 else if (JSStringIsEqualToUTF8CString(character, "downArrow")) 497 gdkKeySym = GDK_Down; 498 else if (JSStringIsEqualToUTF8CString(character, "pageUp")) 499 gdkKeySym = GDK_Page_Up; 500 else if (JSStringIsEqualToUTF8CString(character, "pageDown")) 501 gdkKeySym = GDK_Page_Down; 502 else if (JSStringIsEqualToUTF8CString(character, "home")) 503 gdkKeySym = GDK_Home; 504 else if (JSStringIsEqualToUTF8CString(character, "end")) 505 gdkKeySym = GDK_End; 506 else if (JSStringIsEqualToUTF8CString(character, "delete")) 507 gdkKeySym = GDK_BackSpace; 508 else if (JSStringIsEqualToUTF8CString(character, "F1")) 509 gdkKeySym = GDK_F1; 510 else if (JSStringIsEqualToUTF8CString(character, "F2")) 511 gdkKeySym = GDK_F2; 512 else if (JSStringIsEqualToUTF8CString(character, "F3")) 513 gdkKeySym = GDK_F3; 514 else if (JSStringIsEqualToUTF8CString(character, "F4")) 515 gdkKeySym = GDK_F4; 516 else if (JSStringIsEqualToUTF8CString(character, "F5")) 517 gdkKeySym = GDK_F5; 518 else if (JSStringIsEqualToUTF8CString(character, "F6")) 519 gdkKeySym = GDK_F6; 520 else if (JSStringIsEqualToUTF8CString(character, "F7")) 521 gdkKeySym = GDK_F7; 522 else if (JSStringIsEqualToUTF8CString(character, "F8")) 523 gdkKeySym = GDK_F8; 524 else if (JSStringIsEqualToUTF8CString(character, "F9")) 525 gdkKeySym = GDK_F9; 526 else if (JSStringIsEqualToUTF8CString(character, "F10")) 527 gdkKeySym = GDK_F10; 528 else if (JSStringIsEqualToUTF8CString(character, "F11")) 529 gdkKeySym = GDK_F11; 530 else if (JSStringIsEqualToUTF8CString(character, "F12")) 531 gdkKeySym = GDK_F12; 532 else { 533 int charCode = JSStringGetCharactersPtr(character)[0]; 534 if (charCode == '\n' || charCode == '\r') 535 gdkKeySym = GDK_Return; 536 else if (charCode == '\t') 537 gdkKeySym = GDK_Tab; 538 else if (charCode == '\x8') 539 gdkKeySym = GDK_BackSpace; 540 else { 541 gdkKeySym = gdk_unicode_to_keyval(charCode); 542 if (WTF::isASCIIUpper(charCode)) 543 state |= GDK_SHIFT_MASK; 544 } 545 } 546 } 547 JSStringRelease(character); 548 549 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 550 if (!view) 551 return JSValueMakeUndefined(context); 552 553 // create and send the event 554 GdkEvent event; 555 memset(&event, 0, sizeof(event)); 556 event.key.keyval = gdkKeySym; 557 event.key.state = state; 558 event.key.window = GTK_WIDGET(view)->window; 559 560 gboolean return_val; 561 event.key.type = GDK_KEY_PRESS; 562 g_signal_emit_by_name(view, "key-press-event", &event.key, &return_val); 563 564 event.key.type = GDK_KEY_RELEASE; 565 g_signal_emit_by_name(view, "key-release-event", &event.key, &return_val); 566 567 return JSValueMakeUndefined(context); 568 } 569 570 static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 571 { 572 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 573 if (!view) 574 return JSValueMakeUndefined(context); 575 576 gfloat currentZoom = webkit_web_view_get_zoom_level(view); 577 webkit_web_view_set_zoom_level(view, currentZoom * zoomMultiplierRatio); 578 579 return JSValueMakeUndefined(context); 580 } 581 582 static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 583 { 584 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 585 if (!view) 586 return JSValueMakeUndefined(context); 587 588 gfloat currentZoom = webkit_web_view_get_zoom_level(view); 589 webkit_web_view_set_zoom_level(view, currentZoom / zoomMultiplierRatio); 590 591 return JSValueMakeUndefined(context); 592 } 593 594 static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 595 { 596 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 597 if (!view) 598 return JSValueMakeUndefined(context); 599 600 webkit_web_view_zoom_in(view); 601 return JSValueMakeUndefined(context); 602 } 603 604 static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) 605 { 606 WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame); 607 if (!view) 608 return JSValueMakeUndefined(context); 609 610 webkit_web_view_zoom_out(view); 611 return JSValueMakeUndefined(context); 612 } 613 614 static JSStaticFunction staticFunctions[] = { 615 { "mouseWheelTo", mouseWheelToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 616 { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 617 { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 618 { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 619 { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 620 { "beginDragWithFiles", beginDragWithFilesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 621 { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 622 { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 623 { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 624 { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 625 { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 626 { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete }, 627 { 0, 0, 0 } 628 }; 629 630 static JSStaticValue staticValues[] = { 631 { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone }, 632 { 0, 0, 0, 0 } 633 }; 634 635 static JSClassRef getClass(JSContextRef context) 636 { 637 static JSClassRef eventSenderClass = 0; 638 639 if (!eventSenderClass) { 640 JSClassDefinition classDefinition = { 641 0, 0, 0, 0, 0, 0, 642 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 643 classDefinition.staticFunctions = staticFunctions; 644 classDefinition.staticValues = staticValues; 645 646 eventSenderClass = JSClassCreate(&classDefinition); 647 } 648 649 return eventSenderClass; 650 } 651 652 JSObjectRef makeEventSender(JSContextRef context) 653 { 654 down = false; 655 dragMode = true; 656 lastMousePositionX = lastMousePositionY = 0; 657 lastClickPositionX = lastClickPositionY = 0; 658 659 if (!replayingSavedEvents) { 660 // This function can be called in the middle of a test, even 661 // while replaying saved events. Resetting these while doing that 662 // can break things. 663 endOfQueue = 0; 664 startOfQueue = 0; 665 } 666 667 return JSObjectMake(context, getClass(context), 0); 668 } 669