EventSender.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  1. /*
  2. * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
  3. * Copyright (C) 2009 Zan Dobersek <zandobersek@gmail.com>
  4. * Copyright (C) 2009 Holger Hans Peter Freyther
  5. * Copyright (C) 2010 Igalia S.L.
  6. * Copyright (C) 2012 ChangSeok Oh <shivamidow@gmail.com>
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in the
  16. * documentation and/or other materials provided with the distribution.
  17. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  18. * its contributors may be used to endorse or promote products derived
  19. * from this software without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  22. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  23. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  24. * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  25. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  26. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  27. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  28. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  30. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. #include "config.h"
  33. #include "EventSender.h"
  34. #include "DumpRenderTree.h"
  35. #include "WebCoreSupport/DumpRenderTreeSupportGtk.h"
  36. #include <GOwnPtrGtk.h>
  37. #include <GRefPtrGtk.h>
  38. #include <GtkVersioning.h>
  39. #include <JavaScriptCore/JSObjectRef.h>
  40. #include <JavaScriptCore/JSRetainPtr.h>
  41. #include <JavaScriptCore/JSStringRef.h>
  42. #include <cstring>
  43. #include <gdk/gdk.h>
  44. #include <gdk/gdkkeysyms.h>
  45. #include <webkit/webkitwebframe.h>
  46. #include <webkit/webkitwebview.h>
  47. #include <wtf/ASCIICType.h>
  48. #include <wtf/Platform.h>
  49. #include <wtf/text/CString.h>
  50. extern "C" {
  51. extern GtkMenu* webkit_web_view_get_context_menu(WebKitWebView*);
  52. }
  53. static bool dragMode;
  54. static int timeOffset = 0;
  55. static int lastMousePositionX;
  56. static int lastMousePositionY;
  57. static int lastClickPositionX;
  58. static int lastClickPositionY;
  59. static int lastClickTimeOffset;
  60. static int lastClickButton;
  61. static unsigned buttonCurrentlyDown;
  62. static int clickCount;
  63. GdkDragContext* currentDragSourceContext;
  64. struct DelayedMessage {
  65. GdkEvent* event;
  66. gulong delay;
  67. };
  68. static DelayedMessage msgQueue[1024];
  69. static unsigned endOfQueue;
  70. static unsigned startOfQueue;
  71. static const float zoomMultiplierRatio = 1.2f;
  72. // WebCore and layout tests assume this value.
  73. static const float pixelsPerScrollTick = 40;
  74. // Key event location code defined in DOM Level 3.
  75. enum KeyLocationCode {
  76. DOM_KEY_LOCATION_STANDARD = 0x00,
  77. DOM_KEY_LOCATION_LEFT = 0x01,
  78. DOM_KEY_LOCATION_RIGHT = 0x02,
  79. DOM_KEY_LOCATION_NUMPAD = 0x03
  80. };
  81. static void sendOrQueueEvent(GdkEvent*, bool = true);
  82. static void dispatchEvent(GdkEvent* event);
  83. static guint getStateFlags();
  84. static JSValueRef getDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
  85. {
  86. return JSValueMakeBoolean(context, dragMode);
  87. }
  88. static bool setDragModeCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
  89. {
  90. dragMode = JSValueToBoolean(context, value);
  91. return true;
  92. }
  93. static JSValueRef leapForwardCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  94. {
  95. if (argumentCount > 0) {
  96. msgQueue[endOfQueue].delay = JSValueToNumber(context, arguments[0], exception);
  97. timeOffset += msgQueue[endOfQueue].delay;
  98. ASSERT(!exception || !*exception);
  99. }
  100. return JSValueMakeUndefined(context);
  101. }
  102. bool prepareMouseButtonEvent(GdkEvent* event, int eventSenderButtonNumber, guint modifiers)
  103. {
  104. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  105. if (!view)
  106. return false;
  107. // The logic for mapping EventSender button numbers to GDK button
  108. // numbers originates from the Windows EventSender.
  109. int gdkButtonNumber = 3;
  110. if (eventSenderButtonNumber >= 0 && eventSenderButtonNumber <= 2)
  111. gdkButtonNumber = eventSenderButtonNumber + 1;
  112. // fast/events/mouse-click-events expects the 4th button
  113. // to be event->button = 1, so send a middle-button event.
  114. else if (eventSenderButtonNumber == 3)
  115. gdkButtonNumber = 2;
  116. event->button.button = gdkButtonNumber;
  117. event->button.x = lastMousePositionX;
  118. event->button.y = lastMousePositionY;
  119. event->button.window = gtk_widget_get_window(GTK_WIDGET(view));
  120. g_object_ref(event->button.window);
  121. event->button.device = getDefaultGDKPointerDevice(event->button.window);
  122. event->button.state = modifiers | getStateFlags();
  123. event->button.time = GDK_CURRENT_TIME;
  124. event->button.axes = 0;
  125. int xRoot, yRoot;
  126. gdk_window_get_root_coords(gtk_widget_get_window(GTK_WIDGET(view)), lastMousePositionX, lastMousePositionY, &xRoot, &yRoot);
  127. event->button.x_root = xRoot;
  128. event->button.y_root = yRoot;
  129. return true;
  130. }
  131. static JSValueRef getMenuItemTitleCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception)
  132. {
  133. GtkWidget* widget = GTK_WIDGET(JSObjectGetPrivate(object));
  134. CString label;
  135. if (GTK_IS_SEPARATOR_MENU_ITEM(widget))
  136. label = "<separator>";
  137. else
  138. label = gtk_menu_item_get_label(GTK_MENU_ITEM(widget));
  139. JSRetainPtr<JSStringRef> itemText(Adopt, JSStringCreateWithUTF8CString(label.data()));
  140. return JSValueMakeString(context, itemText.get());
  141. }
  142. static bool setMenuItemTitleCallback(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception)
  143. {
  144. return true;
  145. }
  146. static JSValueRef menuItemClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  147. {
  148. GtkMenuItem* item = GTK_MENU_ITEM(JSObjectGetPrivate(thisObject));
  149. gtk_menu_item_activate(item);
  150. return JSValueMakeUndefined(context);
  151. }
  152. static JSStaticFunction staticMenuItemFunctions[] = {
  153. { "click", menuItemClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  154. { 0, 0, 0 }
  155. };
  156. static JSStaticValue staticMenuItemValues[] = {
  157. { "title", getMenuItemTitleCallback, setMenuItemTitleCallback, kJSPropertyAttributeNone },
  158. { 0, 0, 0, 0 }
  159. };
  160. static JSClassRef getMenuItemClass()
  161. {
  162. static JSClassRef menuItemClass = 0;
  163. if (!menuItemClass) {
  164. JSClassDefinition classDefinition = {
  165. 0, 0, 0, 0, 0, 0,
  166. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  167. classDefinition.staticFunctions = staticMenuItemFunctions;
  168. classDefinition.staticValues = staticMenuItemValues;
  169. menuItemClass = JSClassCreate(&classDefinition);
  170. }
  171. return menuItemClass;
  172. }
  173. static JSValueRef contextClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  174. {
  175. GdkEvent* pressEvent = gdk_event_new(GDK_BUTTON_PRESS);
  176. if (!prepareMouseButtonEvent(pressEvent, 2, 0)) {
  177. gdk_event_free(pressEvent);
  178. return JSObjectMakeArray(context, 0, 0, 0);
  179. }
  180. GdkEvent* releaseEvent = gdk_event_copy(pressEvent);
  181. sendOrQueueEvent(pressEvent);
  182. JSValueRef valueRef = JSObjectMakeArray(context, 0, 0, 0);
  183. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  184. GtkMenu* gtkMenu = webkit_web_view_get_context_menu(view);
  185. if (gtkMenu) {
  186. GOwnPtr<GList> items(gtk_container_get_children(GTK_CONTAINER(gtkMenu)));
  187. JSValueRef arrayValues[g_list_length(items.get())];
  188. int index = 0;
  189. for (GList* item = g_list_first(items.get()); item; item = g_list_next(item)) {
  190. arrayValues[index] = JSObjectMake(context, getMenuItemClass(), item->data);
  191. index++;
  192. }
  193. if (index)
  194. valueRef = JSObjectMakeArray(context, index - 1, arrayValues, 0);
  195. }
  196. releaseEvent->type = GDK_BUTTON_RELEASE;
  197. sendOrQueueEvent(releaseEvent);
  198. return valueRef;
  199. }
  200. static gboolean sendClick(gpointer)
  201. {
  202. GdkEvent* pressEvent = gdk_event_new(GDK_BUTTON_PRESS);
  203. if (!prepareMouseButtonEvent(pressEvent, 1, 0)) {
  204. gdk_event_free(pressEvent);
  205. return FALSE;
  206. }
  207. GdkEvent* releaseEvent = gdk_event_copy(pressEvent);
  208. dispatchEvent(pressEvent);
  209. releaseEvent->type = GDK_BUTTON_RELEASE;
  210. dispatchEvent(releaseEvent);
  211. return FALSE;
  212. }
  213. static JSValueRef scheduleAsynchronousClickCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  214. {
  215. g_timeout_add(0, sendClick, 0);
  216. return JSValueMakeUndefined(context);
  217. }
  218. static void updateClickCount(int button)
  219. {
  220. if (lastClickPositionX != lastMousePositionX
  221. || lastClickPositionY != lastMousePositionY
  222. || lastClickButton != button
  223. || timeOffset - lastClickTimeOffset >= 1)
  224. clickCount = 1;
  225. else
  226. clickCount++;
  227. }
  228. static guint gdkModifierFromJSValue(JSContextRef context, const JSValueRef value)
  229. {
  230. JSStringRef string = JSValueToStringCopy(context, value, 0);
  231. guint gdkModifier = 0;
  232. if (JSStringIsEqualToUTF8CString(string, "ctrlKey")
  233. || JSStringIsEqualToUTF8CString(string, "addSelectionKey"))
  234. gdkModifier = GDK_CONTROL_MASK;
  235. else if (JSStringIsEqualToUTF8CString(string, "shiftKey")
  236. || JSStringIsEqualToUTF8CString(string, "rangeSelectionKey"))
  237. gdkModifier = GDK_SHIFT_MASK;
  238. else if (JSStringIsEqualToUTF8CString(string, "altKey"))
  239. gdkModifier = GDK_MOD1_MASK;
  240. // Currently the metaKey as defined in WebCore/platform/gtk/PlatformMouseEventGtk.cpp
  241. // is GDK_META_MASK. This code must be kept in sync with that file.
  242. else if (JSStringIsEqualToUTF8CString(string, "metaKey"))
  243. gdkModifier = GDK_META_MASK;
  244. JSStringRelease(string);
  245. return gdkModifier;
  246. }
  247. static guint gdkModifersFromJSValue(JSContextRef context, const JSValueRef modifiers)
  248. {
  249. // The value may either be a string with a single modifier or an array of modifiers.
  250. if (JSValueIsString(context, modifiers))
  251. return gdkModifierFromJSValue(context, modifiers);
  252. JSObjectRef modifiersArray = JSValueToObject(context, modifiers, 0);
  253. if (!modifiersArray)
  254. return 0;
  255. guint gdkModifiers = 0;
  256. JSRetainPtr<JSStringRef> lengthProperty(Adopt, JSStringCreateWithUTF8CString("length"));
  257. int modifiersCount = JSValueToNumber(context, JSObjectGetProperty(context, modifiersArray, lengthProperty.get(), 0), 0);
  258. for (int i = 0; i < modifiersCount; ++i)
  259. gdkModifiers |= gdkModifierFromJSValue(context, JSObjectGetPropertyAtIndex(context, modifiersArray, i, 0));
  260. return gdkModifiers;
  261. }
  262. static JSValueRef mouseDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  263. {
  264. int button = 0;
  265. if (argumentCount == 1) {
  266. button = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
  267. g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
  268. }
  269. guint modifiers = argumentCount >= 2 ? gdkModifersFromJSValue(context, arguments[1]) : 0;
  270. GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS);
  271. if (!prepareMouseButtonEvent(event, button, modifiers)) {
  272. gdk_event_free(event);
  273. return JSValueMakeUndefined(context);
  274. }
  275. // If the same mouse button is already in the down position don't send another event as it may confuse Xvfb.
  276. if (buttonCurrentlyDown == event->button.button) {
  277. gdk_event_free(event);
  278. return JSValueMakeUndefined(context);
  279. }
  280. buttonCurrentlyDown = event->button.button;
  281. // Normally GDK will send both GDK_BUTTON_PRESS and GDK_2BUTTON_PRESS for
  282. // the second button press during double-clicks. WebKit GTK+ selectively
  283. // ignores the first GDK_BUTTON_PRESS of that pair using gdk_event_peek.
  284. // Since our events aren't ever going onto the GDK event queue, WebKit won't
  285. // be able to filter out the first GDK_BUTTON_PRESS, so we just don't send
  286. // it here. Eventually this code should probably figure out a way to get all
  287. // appropriate events onto the event queue and this work-around should be
  288. // removed.
  289. updateClickCount(event->button.button);
  290. if (clickCount == 2)
  291. event->type = GDK_2BUTTON_PRESS;
  292. else if (clickCount == 3)
  293. event->type = GDK_3BUTTON_PRESS;
  294. sendOrQueueEvent(event);
  295. return JSValueMakeUndefined(context);
  296. }
  297. static guint getStateFlags()
  298. {
  299. if (buttonCurrentlyDown == 1)
  300. return GDK_BUTTON1_MASK;
  301. if (buttonCurrentlyDown == 2)
  302. return GDK_BUTTON2_MASK;
  303. if (buttonCurrentlyDown == 3)
  304. return GDK_BUTTON3_MASK;
  305. return 0;
  306. }
  307. static JSValueRef mouseUpCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  308. {
  309. int button = 0;
  310. if (argumentCount == 1) {
  311. button = static_cast<int>(JSValueToNumber(context, arguments[0], exception));
  312. g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
  313. }
  314. guint modifiers = argumentCount >= 2 ? gdkModifersFromJSValue(context, arguments[1]) : 0;
  315. GdkEvent* event = gdk_event_new(GDK_BUTTON_RELEASE);
  316. if (!prepareMouseButtonEvent(event, button, modifiers)) {
  317. gdk_event_free(event);
  318. return JSValueMakeUndefined(context);
  319. }
  320. lastClickPositionX = lastMousePositionX;
  321. lastClickPositionY = lastMousePositionY;
  322. lastClickButton = buttonCurrentlyDown;
  323. lastClickTimeOffset = timeOffset;
  324. buttonCurrentlyDown = 0;
  325. sendOrQueueEvent(event);
  326. return JSValueMakeUndefined(context);
  327. }
  328. static JSValueRef mouseMoveToCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  329. {
  330. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  331. if (!view)
  332. return JSValueMakeUndefined(context);
  333. if (argumentCount < 2)
  334. return JSValueMakeUndefined(context);
  335. lastMousePositionX = (int)JSValueToNumber(context, arguments[0], exception);
  336. g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
  337. lastMousePositionY = (int)JSValueToNumber(context, arguments[1], exception);
  338. g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
  339. GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY);
  340. event->motion.x = lastMousePositionX;
  341. event->motion.y = lastMousePositionY;
  342. event->motion.time = GDK_CURRENT_TIME;
  343. event->motion.window = gtk_widget_get_window(GTK_WIDGET(view));
  344. g_object_ref(event->motion.window);
  345. event->button.device = getDefaultGDKPointerDevice(event->motion.window);
  346. guint modifiers = argumentCount >= 3 ? gdkModifersFromJSValue(context, arguments[2]) : 0;
  347. event->motion.state = modifiers | getStateFlags();
  348. event->motion.axes = 0;
  349. int xRoot, yRoot;
  350. gdk_window_get_root_coords(gtk_widget_get_window(GTK_WIDGET(view)), lastMousePositionX, lastMousePositionY, &xRoot, &yRoot);
  351. event->motion.x_root = xRoot;
  352. event->motion.y_root = yRoot;
  353. sendOrQueueEvent(event, false);
  354. return JSValueMakeUndefined(context);
  355. }
  356. static JSValueRef mouseScrollByCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  357. {
  358. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  359. if (!view)
  360. return JSValueMakeUndefined(context);
  361. if (argumentCount < 2)
  362. return JSValueMakeUndefined(context);
  363. int horizontal = (int)JSValueToNumber(context, arguments[0], exception);
  364. g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
  365. int vertical = (int)JSValueToNumber(context, arguments[1], exception);
  366. g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
  367. GdkEvent* event = gdk_event_new(GDK_SCROLL);
  368. event->scroll.x = lastMousePositionX;
  369. event->scroll.y = lastMousePositionY;
  370. event->scroll.time = GDK_CURRENT_TIME;
  371. event->scroll.window = gtk_widget_get_window(GTK_WIDGET(view));
  372. g_object_ref(event->scroll.window);
  373. // GTK+ only supports one tick in each scroll event that is not smooth. For the cases of more than one direction,
  374. // and more than one step in a direction, we can only use smooth events, supported from Gtk 3.3.18.
  375. #if GTK_CHECK_VERSION(3, 3, 18)
  376. if ((horizontal && vertical) || horizontal > 1 || horizontal < -1 || vertical > 1 || vertical < -1) {
  377. event->scroll.direction = GDK_SCROLL_SMOOTH;
  378. event->scroll.delta_x = -horizontal;
  379. event->scroll.delta_y = -vertical;
  380. sendOrQueueEvent(event);
  381. return JSValueMakeUndefined(context);
  382. }
  383. #else
  384. g_return_val_if_fail((!vertical || !horizontal), JSValueMakeUndefined(context));
  385. #endif
  386. if (horizontal < 0)
  387. event->scroll.direction = GDK_SCROLL_RIGHT;
  388. else if (horizontal > 0)
  389. event->scroll.direction = GDK_SCROLL_LEFT;
  390. else if (vertical < 0)
  391. event->scroll.direction = GDK_SCROLL_DOWN;
  392. else if (vertical > 0)
  393. event->scroll.direction = GDK_SCROLL_UP;
  394. else
  395. g_assert_not_reached();
  396. sendOrQueueEvent(event);
  397. return JSValueMakeUndefined(context);
  398. }
  399. static JSValueRef continuousMouseScrollByCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  400. {
  401. #if GTK_CHECK_VERSION(3, 3, 18)
  402. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  403. if (!view)
  404. return JSValueMakeUndefined(context);
  405. if (argumentCount < 2)
  406. return JSValueMakeUndefined(context);
  407. int horizontal = JSValueToNumber(context, arguments[0], exception);
  408. g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
  409. int vertical = JSValueToNumber(context, arguments[1], exception);
  410. g_return_val_if_fail((!exception || !*exception), JSValueMakeUndefined(context));
  411. // We do not yet support continuous scrolling by page.
  412. if (argumentCount >= 3 && JSValueToBoolean(context, arguments[2]))
  413. return JSValueMakeUndefined(context);
  414. GdkEvent* event = gdk_event_new(GDK_SCROLL);
  415. event->scroll.x = lastMousePositionX;
  416. event->scroll.y = lastMousePositionY;
  417. event->scroll.time = GDK_CURRENT_TIME;
  418. event->scroll.window = gtk_widget_get_window(GTK_WIDGET(view));
  419. g_object_ref(event->scroll.window);
  420. event->scroll.direction = GDK_SCROLL_SMOOTH;
  421. event->scroll.delta_x = -horizontal / pixelsPerScrollTick;
  422. event->scroll.delta_y = -vertical / pixelsPerScrollTick;
  423. sendOrQueueEvent(event);
  424. #endif
  425. return JSValueMakeUndefined(context);
  426. }
  427. static void dragWithFilesDragDataGetCallback(GtkWidget*, GdkDragContext*, GtkSelectionData *data, guint, guint, gpointer userData)
  428. {
  429. gtk_selection_data_set_uris(data, static_cast<gchar**>(userData));
  430. }
  431. static void dragWithFilesDragEndCallback(GtkWidget* widget, GdkDragContext*, gpointer userData)
  432. {
  433. g_signal_handlers_disconnect_by_func(widget, reinterpret_cast<void*>(dragWithFilesDragEndCallback), userData);
  434. g_signal_handlers_disconnect_by_func(widget, reinterpret_cast<void*>(dragWithFilesDragDataGetCallback), userData);
  435. g_strfreev(static_cast<gchar**>(userData));
  436. }
  437. static JSValueRef beginDragWithFilesCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  438. {
  439. if (argumentCount < 1)
  440. return JSValueMakeUndefined(context);
  441. JSObjectRef filesArray = JSValueToObject(context, arguments[0], exception);
  442. ASSERT(!exception || !*exception);
  443. const gchar* mainFrameURI = webkit_web_frame_get_uri(mainFrame);
  444. GRefPtr<GFile> testFile(adoptGRef(g_file_new_for_uri(mainFrameURI)));
  445. GRefPtr<GFile> parentDirectory(g_file_get_parent(testFile.get()));
  446. if (!parentDirectory)
  447. return JSValueMakeUndefined(context);
  448. // If this is an HTTP test, we still need to pass a local file path
  449. // to WebCore. Even though the file doesn't exist, this should be fine
  450. // for most tests.
  451. GOwnPtr<gchar> scheme(g_file_get_uri_scheme(parentDirectory.get()));
  452. if (g_str_equal(scheme.get(), "http") || g_str_equal(scheme.get(), "https")) {
  453. GOwnPtr<gchar> currentDirectory(g_get_current_dir());
  454. parentDirectory = g_file_new_for_path(currentDirectory.get());
  455. }
  456. JSStringRef lengthProperty = JSStringCreateWithUTF8CString("length");
  457. int filesArrayLength = JSValueToNumber(context, JSObjectGetProperty(context, filesArray, lengthProperty, 0), 0);
  458. JSStringRelease(lengthProperty);
  459. gchar** draggedFilesURIList = g_new0(gchar*, filesArrayLength + 1);
  460. for (int i = 0; i < filesArrayLength; ++i) {
  461. JSStringRef filenameString = JSValueToStringCopy(context,
  462. JSObjectGetPropertyAtIndex(context, filesArray, i, 0), 0);
  463. size_t bufferSize = JSStringGetMaximumUTF8CStringSize(filenameString);
  464. GOwnPtr<gchar> filenameBuffer(static_cast<gchar*>(g_malloc(bufferSize)));
  465. JSStringGetUTF8CString(filenameString, filenameBuffer.get(), bufferSize);
  466. JSStringRelease(filenameString);
  467. GRefPtr<GFile> dragFile(g_file_get_child(parentDirectory.get(), filenameBuffer.get()));
  468. draggedFilesURIList[i] = g_file_get_uri(dragFile.get());
  469. }
  470. GtkWidget* view = GTK_WIDGET(webkit_web_frame_get_web_view(mainFrame));
  471. g_object_connect(G_OBJECT(view),
  472. "signal::drag-end", dragWithFilesDragEndCallback, draggedFilesURIList,
  473. "signal::drag-data-get", dragWithFilesDragDataGetCallback, draggedFilesURIList,
  474. NULL);
  475. GdkEvent event;
  476. GdkWindow* viewGDKWindow = gtk_widget_get_window(view);
  477. memset(&event, 0, sizeof(event));
  478. event.type = GDK_MOTION_NOTIFY;
  479. event.motion.x = lastMousePositionX;
  480. event.motion.y = lastMousePositionY;
  481. event.motion.time = GDK_CURRENT_TIME;
  482. event.motion.window = viewGDKWindow;
  483. event.motion.device = getDefaultGDKPointerDevice(viewGDKWindow);
  484. event.motion.state = GDK_BUTTON1_MASK;
  485. int xRoot, yRoot;
  486. gdk_window_get_root_coords(viewGDKWindow, lastMousePositionX, lastMousePositionY, &xRoot, &yRoot);
  487. event.motion.x_root = xRoot;
  488. event.motion.y_root = yRoot;
  489. GtkTargetList* targetList = gtk_target_list_new(0, 0);
  490. gtk_target_list_add_uri_targets(targetList, 0);
  491. gtk_drag_begin(view, targetList, GDK_ACTION_COPY, 1, &event);
  492. gtk_target_list_unref(targetList);
  493. return JSValueMakeUndefined(context);
  494. }
  495. static void sendOrQueueEvent(GdkEvent* event, bool shouldReplaySavedEvents)
  496. {
  497. // Mouse move events are queued if the previous event was queued or if a
  498. // delay was set up by leapForward().
  499. if ((dragMode && buttonCurrentlyDown) || endOfQueue != startOfQueue || msgQueue[endOfQueue].delay) {
  500. msgQueue[endOfQueue++].event = event;
  501. if (shouldReplaySavedEvents)
  502. replaySavedEvents();
  503. return;
  504. }
  505. dispatchEvent(event);
  506. }
  507. static void dispatchEvent(GdkEvent* event)
  508. {
  509. DumpRenderTreeSupportGtk::layoutFrame(mainFrame);
  510. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  511. if (!view) {
  512. gdk_event_free(event);
  513. return;
  514. }
  515. // The widget focus may have been lost in the course of the test,
  516. // so force another explicit focus grab here.
  517. gtk_widget_grab_focus(GTK_WIDGET(view));
  518. gtk_main_do_event(event);
  519. if (!currentDragSourceContext) {
  520. gdk_event_free(event);
  521. return;
  522. }
  523. if (event->type == GDK_MOTION_NOTIFY) {
  524. // WebKit has called gtk_drag_start(), but because the main loop isn't
  525. // running GDK internals don't know that the drag has started yet. Pump
  526. // the main loop a little bit so that GDK is in the correct state.
  527. while (gtk_events_pending())
  528. gtk_main_iteration();
  529. // Simulate a drag motion on the top-level GDK window.
  530. GtkWidget* parentWidget = gtk_widget_get_parent(GTK_WIDGET(view));
  531. GdkWindow* parentWidgetWindow = gtk_widget_get_window(parentWidget);
  532. gdk_drag_motion(currentDragSourceContext, parentWidgetWindow, GDK_DRAG_PROTO_XDND,
  533. event->motion.x_root, event->motion.y_root,
  534. gdk_drag_context_get_selected_action(currentDragSourceContext),
  535. gdk_drag_context_get_actions(currentDragSourceContext),
  536. GDK_CURRENT_TIME);
  537. } else if (currentDragSourceContext && event->type == GDK_BUTTON_RELEASE) {
  538. // We've released the mouse button, we should just be able to spin the
  539. // event loop here and have GTK+ send the appropriate notifications for
  540. // the end of the drag.
  541. while (gtk_events_pending())
  542. gtk_main_iteration();
  543. }
  544. gdk_event_free(event);
  545. }
  546. void replaySavedEvents()
  547. {
  548. // First send all the events that are ready to be sent
  549. while (startOfQueue < endOfQueue) {
  550. if (msgQueue[startOfQueue].delay) {
  551. g_usleep(msgQueue[startOfQueue].delay * 1000);
  552. msgQueue[startOfQueue].delay = 0;
  553. }
  554. dispatchEvent(msgQueue[startOfQueue++].event);
  555. }
  556. startOfQueue = 0;
  557. endOfQueue = 0;
  558. }
  559. static GdkEvent* createKeyPressEvent(JSContextRef context, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  560. {
  561. g_return_val_if_fail(argumentCount >= 1, 0);
  562. guint modifiers = argumentCount >= 2 ? gdkModifersFromJSValue(context, arguments[1]) : 0;
  563. // handle location argument.
  564. int location = DOM_KEY_LOCATION_STANDARD;
  565. if (argumentCount > 2)
  566. location = (int)JSValueToNumber(context, arguments[2], exception);
  567. JSStringRef character = JSValueToStringCopy(context, arguments[0], exception);
  568. g_return_val_if_fail((!exception || !*exception), 0);
  569. int gdkKeySym = GDK_VoidSymbol;
  570. if (location == DOM_KEY_LOCATION_NUMPAD) {
  571. if (JSStringIsEqualToUTF8CString(character, "leftArrow"))
  572. gdkKeySym = GDK_KP_Left;
  573. else if (JSStringIsEqualToUTF8CString(character, "rightArrow"))
  574. gdkKeySym = GDK_KP_Right;
  575. else if (JSStringIsEqualToUTF8CString(character, "upArrow"))
  576. gdkKeySym = GDK_KP_Up;
  577. else if (JSStringIsEqualToUTF8CString(character, "downArrow"))
  578. gdkKeySym = GDK_KP_Down;
  579. else if (JSStringIsEqualToUTF8CString(character, "pageUp"))
  580. gdkKeySym = GDK_KP_Page_Up;
  581. else if (JSStringIsEqualToUTF8CString(character, "pageDown"))
  582. gdkKeySym = GDK_KP_Page_Down;
  583. else if (JSStringIsEqualToUTF8CString(character, "home"))
  584. gdkKeySym = GDK_KP_Home;
  585. else if (JSStringIsEqualToUTF8CString(character, "end"))
  586. gdkKeySym = GDK_KP_End;
  587. else if (JSStringIsEqualToUTF8CString(character, "insert"))
  588. gdkKeySym = GDK_KP_Insert;
  589. else if (JSStringIsEqualToUTF8CString(character, "delete"))
  590. gdkKeySym = GDK_KP_Delete;
  591. else
  592. // If we get some other key specified with the numpad location,
  593. // crash here, so we add it sooner rather than later.
  594. g_assert_not_reached();
  595. } else {
  596. if (JSStringIsEqualToUTF8CString(character, "leftArrow"))
  597. gdkKeySym = GDK_Left;
  598. else if (JSStringIsEqualToUTF8CString(character, "rightArrow"))
  599. gdkKeySym = GDK_Right;
  600. else if (JSStringIsEqualToUTF8CString(character, "upArrow"))
  601. gdkKeySym = GDK_Up;
  602. else if (JSStringIsEqualToUTF8CString(character, "downArrow"))
  603. gdkKeySym = GDK_Down;
  604. else if (JSStringIsEqualToUTF8CString(character, "pageUp"))
  605. gdkKeySym = GDK_Page_Up;
  606. else if (JSStringIsEqualToUTF8CString(character, "pageDown"))
  607. gdkKeySym = GDK_Page_Down;
  608. else if (JSStringIsEqualToUTF8CString(character, "home"))
  609. gdkKeySym = GDK_Home;
  610. else if (JSStringIsEqualToUTF8CString(character, "end"))
  611. gdkKeySym = GDK_End;
  612. else if (JSStringIsEqualToUTF8CString(character, "insert"))
  613. gdkKeySym = GDK_Insert;
  614. else if (JSStringIsEqualToUTF8CString(character, "delete"))
  615. gdkKeySym = GDK_Delete;
  616. else if (JSStringIsEqualToUTF8CString(character, "printScreen"))
  617. gdkKeySym = GDK_Print;
  618. else if (JSStringIsEqualToUTF8CString(character, "menu"))
  619. gdkKeySym = GDK_Menu;
  620. else if (JSStringIsEqualToUTF8CString(character, "F1"))
  621. gdkKeySym = GDK_F1;
  622. else if (JSStringIsEqualToUTF8CString(character, "F2"))
  623. gdkKeySym = GDK_F2;
  624. else if (JSStringIsEqualToUTF8CString(character, "F3"))
  625. gdkKeySym = GDK_F3;
  626. else if (JSStringIsEqualToUTF8CString(character, "F4"))
  627. gdkKeySym = GDK_F4;
  628. else if (JSStringIsEqualToUTF8CString(character, "F5"))
  629. gdkKeySym = GDK_F5;
  630. else if (JSStringIsEqualToUTF8CString(character, "F6"))
  631. gdkKeySym = GDK_F6;
  632. else if (JSStringIsEqualToUTF8CString(character, "F7"))
  633. gdkKeySym = GDK_F7;
  634. else if (JSStringIsEqualToUTF8CString(character, "F8"))
  635. gdkKeySym = GDK_F8;
  636. else if (JSStringIsEqualToUTF8CString(character, "F9"))
  637. gdkKeySym = GDK_F9;
  638. else if (JSStringIsEqualToUTF8CString(character, "F10"))
  639. gdkKeySym = GDK_F10;
  640. else if (JSStringIsEqualToUTF8CString(character, "F11"))
  641. gdkKeySym = GDK_F11;
  642. else if (JSStringIsEqualToUTF8CString(character, "F12"))
  643. gdkKeySym = GDK_F12;
  644. else if (JSStringIsEqualToUTF8CString(character, "leftAlt"))
  645. gdkKeySym = GDK_Alt_L;
  646. else if (JSStringIsEqualToUTF8CString(character, "leftControl"))
  647. gdkKeySym = GDK_Control_L;
  648. else if (JSStringIsEqualToUTF8CString(character, "leftShift"))
  649. gdkKeySym = GDK_Shift_L;
  650. else if (JSStringIsEqualToUTF8CString(character, "rightAlt"))
  651. gdkKeySym = GDK_Alt_R;
  652. else if (JSStringIsEqualToUTF8CString(character, "rightControl"))
  653. gdkKeySym = GDK_Control_R;
  654. else if (JSStringIsEqualToUTF8CString(character, "rightShift"))
  655. gdkKeySym = GDK_Shift_R;
  656. else {
  657. int charCode = JSStringGetCharactersPtr(character)[0];
  658. if (charCode == '\n' || charCode == '\r')
  659. gdkKeySym = GDK_Return;
  660. else if (charCode == '\t')
  661. gdkKeySym = GDK_Tab;
  662. else if (charCode == '\x8')
  663. gdkKeySym = GDK_BackSpace;
  664. else {
  665. gdkKeySym = gdk_unicode_to_keyval(charCode);
  666. if (WTF::isASCIIUpper(charCode))
  667. modifiers |= GDK_SHIFT_MASK;
  668. }
  669. }
  670. }
  671. JSStringRelease(character);
  672. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  673. g_return_val_if_fail(view, 0);
  674. GdkEvent* pressEvent = gdk_event_new(GDK_KEY_PRESS);
  675. pressEvent->key.keyval = gdkKeySym;
  676. pressEvent->key.state = modifiers;
  677. pressEvent->key.window = gtk_widget_get_window(GTK_WIDGET(view));
  678. g_object_ref(pressEvent->key.window);
  679. #ifndef GTK_API_VERSION_2
  680. gdk_event_set_device(pressEvent, getDefaultGDKPointerDevice(pressEvent->key.window));
  681. #endif
  682. // When synthesizing an event, an invalid hardware_keycode value
  683. // can cause it to be badly processed by Gtk+.
  684. GOwnPtr<GdkKeymapKey> keys;
  685. gint nKeys;
  686. if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeySym, &keys.outPtr(), &nKeys))
  687. pressEvent->key.hardware_keycode = keys.get()[0].keycode;
  688. return pressEvent;
  689. }
  690. static void sendKeyDown(GdkEvent* pressEvent)
  691. {
  692. g_return_if_fail(pressEvent);
  693. GdkEvent* releaseEvent = gdk_event_copy(pressEvent);
  694. releaseEvent->type = GDK_KEY_RELEASE;
  695. dispatchEvent(pressEvent);
  696. dispatchEvent(releaseEvent);
  697. DumpRenderTreeSupportGtk::deliverAllMutationsIfNecessary();
  698. }
  699. static JSValueRef keyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  700. {
  701. GdkEvent* pressEvent = createKeyPressEvent(context, argumentCount, arguments, exception);
  702. sendKeyDown(pressEvent);
  703. return JSValueMakeUndefined(context);
  704. }
  705. static void zoomIn(gboolean fullContentsZoom)
  706. {
  707. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  708. if (!view)
  709. return;
  710. webkit_web_view_set_full_content_zoom(view, fullContentsZoom);
  711. gfloat currentZoom = webkit_web_view_get_zoom_level(view);
  712. webkit_web_view_set_zoom_level(view, currentZoom * zoomMultiplierRatio);
  713. }
  714. static void zoomOut(gboolean fullContentsZoom)
  715. {
  716. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  717. if (!view)
  718. return;
  719. webkit_web_view_set_full_content_zoom(view, fullContentsZoom);
  720. gfloat currentZoom = webkit_web_view_get_zoom_level(view);
  721. webkit_web_view_set_zoom_level(view, currentZoom / zoomMultiplierRatio);
  722. }
  723. static JSValueRef textZoomInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  724. {
  725. zoomIn(FALSE);
  726. return JSValueMakeUndefined(context);
  727. }
  728. static JSValueRef textZoomOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  729. {
  730. zoomOut(FALSE);
  731. return JSValueMakeUndefined(context);
  732. }
  733. static JSValueRef zoomPageInCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  734. {
  735. zoomIn(TRUE);
  736. return JSValueMakeUndefined(context);
  737. }
  738. static JSValueRef zoomPageOutCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  739. {
  740. zoomOut(TRUE);
  741. return JSValueMakeUndefined(context);
  742. }
  743. static JSValueRef scalePageByCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  744. {
  745. if (argumentCount < 3)
  746. return JSValueMakeUndefined(context);
  747. float scaleFactor = JSValueToNumber(context, arguments[0], exception);
  748. float x = JSValueToNumber(context, arguments[1], exception);
  749. float y = JSValueToNumber(context, arguments[2], exception);
  750. WebKitWebView* view = webkit_web_frame_get_web_view(mainFrame);
  751. if (!view)
  752. return JSValueMakeUndefined(context);
  753. DumpRenderTreeSupportGtk::scalePageBy(view, scaleFactor, x, y);
  754. return JSValueMakeUndefined(context);
  755. }
  756. static gboolean sendAsynchronousKeyDown(gpointer userData)
  757. {
  758. sendKeyDown(static_cast<GdkEvent*>(userData));
  759. return FALSE;
  760. }
  761. static JSValueRef scheduleAsynchronousKeyDownCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
  762. {
  763. GdkEvent* pressEvent = createKeyPressEvent(context, argumentCount, arguments, exception);
  764. if (pressEvent)
  765. g_timeout_add(0, sendAsynchronousKeyDown, static_cast<gpointer>(pressEvent));
  766. return JSValueMakeUndefined(context);
  767. }
  768. static JSStaticFunction staticFunctions[] = {
  769. { "mouseScrollBy", mouseScrollByCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  770. { "continuousMouseScrollBy", continuousMouseScrollByCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  771. { "contextClick", contextClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  772. { "mouseDown", mouseDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  773. { "mouseUp", mouseUpCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  774. { "mouseMoveTo", mouseMoveToCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  775. { "beginDragWithFiles", beginDragWithFilesCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  776. { "leapForward", leapForwardCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  777. { "keyDown", keyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  778. { "textZoomIn", textZoomInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  779. { "textZoomOut", textZoomOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  780. { "zoomPageIn", zoomPageInCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  781. { "zoomPageOut", zoomPageOutCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  782. { "scheduleAsynchronousClick", scheduleAsynchronousClickCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  783. { "scalePageBy", scalePageByCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  784. { "scheduleAsynchronousKeyDown", scheduleAsynchronousKeyDownCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  785. { 0, 0, 0 }
  786. };
  787. static JSStaticValue staticValues[] = {
  788. { "dragMode", getDragModeCallback, setDragModeCallback, kJSPropertyAttributeNone },
  789. { 0, 0, 0, 0 }
  790. };
  791. static JSClassRef getClass(JSContextRef context)
  792. {
  793. static JSClassRef eventSenderClass = 0;
  794. if (!eventSenderClass) {
  795. JSClassDefinition classDefinition = {
  796. 0, 0, 0, 0, 0, 0,
  797. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  798. classDefinition.staticFunctions = staticFunctions;
  799. classDefinition.staticValues = staticValues;
  800. eventSenderClass = JSClassCreate(&classDefinition);
  801. }
  802. return eventSenderClass;
  803. }
  804. JSObjectRef makeEventSender(JSContextRef context, bool isTopFrame)
  805. {
  806. if (isTopFrame) {
  807. dragMode = true;
  808. // Fly forward in time one second when the main frame loads. This will
  809. // ensure that when a test begins clicking in the same location as
  810. // a previous test, those clicks won't be interpreted as continuations
  811. // of the previous test's click sequences.
  812. timeOffset += 1000;
  813. lastMousePositionX = lastMousePositionY = 0;
  814. lastClickPositionX = lastClickPositionY = 0;
  815. lastClickTimeOffset = 0;
  816. lastClickButton = 0;
  817. buttonCurrentlyDown = 0;
  818. clickCount = 0;
  819. endOfQueue = 0;
  820. startOfQueue = 0;
  821. currentDragSourceContext = 0;
  822. }
  823. return JSObjectMake(context, getClass(context), 0);
  824. }
  825. void dragBeginCallback(GtkWidget*, GdkDragContext* context, gpointer)
  826. {
  827. currentDragSourceContext = context;
  828. }
  829. void dragEndCallback(GtkWidget*, GdkDragContext* context, gpointer)
  830. {
  831. currentDragSourceContext = 0;
  832. }
  833. gboolean dragFailedCallback(GtkWidget*, GdkDragContext* context, gpointer)
  834. {
  835. // Return TRUE here to disable the stupid GTK+ drag failed animation,
  836. // which introduces asynchronous behavior into our drags.
  837. return TRUE;
  838. }