EventSenderProxyGtk.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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) 2010 Motorola Mobility, Inc. All rights reserved.
  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 "EventSenderProxy.h"
  34. #include "PlatformWebView.h"
  35. #include "TestController.h"
  36. #include <wtf/OwnArrayPtr.h>
  37. #include <wtf/PassOwnArrayPtr.h>
  38. #include <gdk/gdkkeysyms.h>
  39. #include <gtk/gtk.h>
  40. #include <wtf/gobject/GOwnPtr.h>
  41. #include <wtf/text/WTFString.h>
  42. namespace WTR {
  43. // WebCore and layout tests assume this value
  44. static const float pixelsPerScrollTick = 40;
  45. // Key event location code defined in DOM Level 3.
  46. enum KeyLocationCode {
  47. DOMKeyLocationStandard = 0x00,
  48. DOMKeyLocationLeft = 0x01,
  49. DOMKeyLocationRight = 0x02,
  50. DOMKeyLocationNumpad = 0x03
  51. };
  52. struct WTREventQueueItem {
  53. GdkEvent* event;
  54. gulong delay;
  55. WTREventQueueItem()
  56. : event(0)
  57. , delay(0)
  58. {
  59. }
  60. WTREventQueueItem(GdkEvent* event, gulong delay)
  61. : event(event)
  62. , delay(delay)
  63. {
  64. }
  65. };
  66. EventSenderProxy::EventSenderProxy(TestController* testController)
  67. : m_testController(testController)
  68. , m_time(0)
  69. , m_leftMouseButtonDown(false)
  70. , m_clickCount(0)
  71. , m_clickTime(0)
  72. , m_clickButton(kWKEventMouseButtonNoButton)
  73. , m_mouseButtonCurrentlyDown(0)
  74. {
  75. }
  76. EventSenderProxy::~EventSenderProxy()
  77. {
  78. }
  79. static guint getMouseButtonModifiers(int gdkButton)
  80. {
  81. if (gdkButton == 1)
  82. return GDK_BUTTON1_MASK;
  83. if (gdkButton == 2)
  84. return GDK_BUTTON2_MASK;
  85. if (gdkButton == 3)
  86. return GDK_BUTTON3_MASK;
  87. return 0;
  88. }
  89. static unsigned eventSenderButtonToGDKButton(unsigned button)
  90. {
  91. int mouseButton = 3;
  92. if (button <= 2)
  93. mouseButton = button + 1;
  94. // fast/events/mouse-click-events expects the 4th button to be treated as the middle button.
  95. else if (button == 3)
  96. mouseButton = 2;
  97. return mouseButton;
  98. }
  99. GdkEvent* EventSenderProxy::createMouseButtonEvent(GdkEventType eventType, unsigned button, WKEventModifiers modifiers)
  100. {
  101. GdkEvent* mouseEvent = gdk_event_new(eventType);
  102. mouseEvent->button.button = eventSenderButtonToGDKButton(button);
  103. mouseEvent->button.x = m_position.x;
  104. mouseEvent->button.y = m_position.y;
  105. mouseEvent->button.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
  106. g_object_ref(mouseEvent->button.window);
  107. gdk_event_set_device(mouseEvent, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(mouseEvent->button.window))));
  108. mouseEvent->button.state = modifiers | getMouseButtonModifiers(mouseEvent->button.button);
  109. mouseEvent->button.time = GDK_CURRENT_TIME;
  110. mouseEvent->button.axes = 0;
  111. int xRoot, yRoot;
  112. gdk_window_get_root_coords(mouseEvent->button.window, m_position.x, m_position.y, &xRoot, &yRoot);
  113. mouseEvent->button.x_root = xRoot;
  114. mouseEvent->button.y_root = yRoot;
  115. return mouseEvent;
  116. }
  117. void EventSenderProxy::updateClickCountForButton(int button)
  118. {
  119. if (m_time - m_clickTime < 1 && m_position == m_clickPosition && button == m_clickButton) {
  120. ++m_clickCount;
  121. m_clickTime = m_time;
  122. return;
  123. }
  124. m_clickCount = 1;
  125. m_clickTime = m_time;
  126. m_clickPosition = m_position;
  127. m_clickButton = button;
  128. }
  129. static void dispatchEvent(GdkEvent* event)
  130. {
  131. gtk_main_do_event(event);
  132. gdk_event_free(event);
  133. }
  134. void EventSenderProxy::replaySavedEvents()
  135. {
  136. while (!m_eventQueue.isEmpty()) {
  137. WTREventQueueItem item = m_eventQueue.first();
  138. if (item.delay)
  139. g_usleep(item.delay * 1000);
  140. dispatchEvent(item.event);
  141. m_eventQueue.remove(0);
  142. }
  143. }
  144. void EventSenderProxy::sendOrQueueEvent(GdkEvent* event)
  145. {
  146. if (m_eventQueue.isEmpty() || !m_eventQueue.last().delay) {
  147. dispatchEvent(event);
  148. return;
  149. }
  150. m_eventQueue.last().event = event;
  151. replaySavedEvents();
  152. }
  153. static guint webkitModifiersToGDKModifiers(WKEventModifiers wkModifiers)
  154. {
  155. guint modifiers = 0;
  156. if (wkModifiers & kWKEventModifiersControlKey)
  157. modifiers |= GDK_CONTROL_MASK;
  158. if (wkModifiers & kWKEventModifiersShiftKey)
  159. modifiers |= GDK_SHIFT_MASK;
  160. if (wkModifiers & kWKEventModifiersAltKey)
  161. modifiers |= GDK_MOD1_MASK;
  162. if (wkModifiers & kWKEventModifiersMetaKey)
  163. modifiers |= GDK_META_MASK;
  164. return modifiers;
  165. }
  166. int getGDKKeySymForKeyRef(WKStringRef keyRef, unsigned location, guint* modifiers)
  167. {
  168. if (location == DOMKeyLocationNumpad) {
  169. if (WKStringIsEqualToUTF8CString(keyRef, "leftArrow"))
  170. return GDK_KEY_KP_Left;
  171. if (WKStringIsEqualToUTF8CString(keyRef, "rightArror"))
  172. return GDK_KEY_KP_Right;
  173. if (WKStringIsEqualToUTF8CString(keyRef, "upArrow"))
  174. return GDK_KEY_KP_Up;
  175. if (WKStringIsEqualToUTF8CString(keyRef, "downArrow"))
  176. return GDK_KEY_KP_Down;
  177. if (WKStringIsEqualToUTF8CString(keyRef, "pageUp"))
  178. return GDK_KEY_KP_Page_Up;
  179. if (WKStringIsEqualToUTF8CString(keyRef, "pageDown"))
  180. return GDK_KEY_KP_Page_Down;
  181. if (WKStringIsEqualToUTF8CString(keyRef, "home"))
  182. return GDK_KEY_KP_Home;
  183. if (WKStringIsEqualToUTF8CString(keyRef, "end"))
  184. return GDK_KEY_KP_End;
  185. if (WKStringIsEqualToUTF8CString(keyRef, "insert"))
  186. return GDK_KEY_KP_Insert;
  187. if (WKStringIsEqualToUTF8CString(keyRef, "delete"))
  188. return GDK_KEY_KP_Delete;
  189. return GDK_KEY_VoidSymbol;
  190. }
  191. if (WKStringIsEqualToUTF8CString(keyRef, "leftArrow"))
  192. return GDK_KEY_Left;
  193. if (WKStringIsEqualToUTF8CString(keyRef, "rightArrow"))
  194. return GDK_KEY_Right;
  195. if (WKStringIsEqualToUTF8CString(keyRef, "upArrow"))
  196. return GDK_KEY_Up;
  197. if (WKStringIsEqualToUTF8CString(keyRef, "downArrow"))
  198. return GDK_KEY_Down;
  199. if (WKStringIsEqualToUTF8CString(keyRef, "pageUp"))
  200. return GDK_KEY_Page_Up;
  201. if (WKStringIsEqualToUTF8CString(keyRef, "pageDown"))
  202. return GDK_KEY_Page_Down;
  203. if (WKStringIsEqualToUTF8CString(keyRef, "home"))
  204. return GDK_KEY_Home;
  205. if (WKStringIsEqualToUTF8CString(keyRef, "end"))
  206. return GDK_KEY_End;
  207. if (WKStringIsEqualToUTF8CString(keyRef, "insert"))
  208. return GDK_KEY_Insert;
  209. if (WKStringIsEqualToUTF8CString(keyRef, "delete"))
  210. return GDK_KEY_Delete;
  211. if (WKStringIsEqualToUTF8CString(keyRef, "printScreen"))
  212. return GDK_KEY_Print;
  213. if (WKStringIsEqualToUTF8CString(keyRef, "menu"))
  214. return GDK_KEY_Menu;
  215. if (WKStringIsEqualToUTF8CString(keyRef, "F1"))
  216. return GDK_KEY_F1;
  217. if (WKStringIsEqualToUTF8CString(keyRef, "F2"))
  218. return GDK_KEY_F2;
  219. if (WKStringIsEqualToUTF8CString(keyRef, "F3"))
  220. return GDK_KEY_F3;
  221. if (WKStringIsEqualToUTF8CString(keyRef, "F4"))
  222. return GDK_KEY_F4;
  223. if (WKStringIsEqualToUTF8CString(keyRef, "F5"))
  224. return GDK_KEY_F5;
  225. if (WKStringIsEqualToUTF8CString(keyRef, "F6"))
  226. return GDK_KEY_F6;
  227. if (WKStringIsEqualToUTF8CString(keyRef, "F7"))
  228. return GDK_KEY_F7;
  229. if (WKStringIsEqualToUTF8CString(keyRef, "F8"))
  230. return GDK_KEY_F8;
  231. if (WKStringIsEqualToUTF8CString(keyRef, "F9"))
  232. return GDK_KEY_F9;
  233. if (WKStringIsEqualToUTF8CString(keyRef, "F10"))
  234. return GDK_KEY_F10;
  235. if (WKStringIsEqualToUTF8CString(keyRef, "F11"))
  236. return GDK_KEY_F11;
  237. if (WKStringIsEqualToUTF8CString(keyRef, "F12"))
  238. return GDK_KEY_F12;
  239. size_t bufferSize = WKStringGetMaximumUTF8CStringSize(keyRef);
  240. OwnArrayPtr<char> buffer = adoptArrayPtr(new char[bufferSize]);
  241. WKStringGetUTF8CString(keyRef, buffer.get(), bufferSize);
  242. char charCode = buffer.get()[0];
  243. if (charCode == '\n' || charCode == '\r')
  244. return GDK_KEY_Return;
  245. if (charCode == '\t')
  246. return GDK_KEY_Tab;
  247. if (charCode == '\x8')
  248. return GDK_KEY_BackSpace;
  249. if (WTF::isASCIIUpper(charCode))
  250. *modifiers |= GDK_SHIFT_MASK;
  251. return gdk_unicode_to_keyval(static_cast<guint32>(buffer.get()[0]));
  252. }
  253. void EventSenderProxy::keyDown(WKStringRef keyRef, WKEventModifiers wkModifiers, unsigned location)
  254. {
  255. guint modifiers = webkitModifiersToGDKModifiers(wkModifiers);
  256. int gdkKeySym = getGDKKeySymForKeyRef(keyRef, location, &modifiers);
  257. GdkEvent* pressEvent = gdk_event_new(GDK_KEY_PRESS);
  258. pressEvent->key.keyval = gdkKeySym;
  259. pressEvent->key.state = modifiers;
  260. pressEvent->key.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformWindow()));
  261. g_object_ref(pressEvent->key.window);
  262. gdk_event_set_device(pressEvent, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(pressEvent->key.window))));
  263. GOwnPtr<GdkKeymapKey> keys;
  264. gint nKeys;
  265. if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeySym, &keys.outPtr(), &nKeys))
  266. pressEvent->key.hardware_keycode = keys.get()[0].keycode;
  267. GdkEvent* releaseEvent = gdk_event_copy(pressEvent);
  268. dispatchEvent(pressEvent);
  269. releaseEvent->key.type = GDK_KEY_RELEASE;
  270. dispatchEvent(releaseEvent);
  271. }
  272. void EventSenderProxy::mouseDown(unsigned button, WKEventModifiers wkModifiers)
  273. {
  274. // If the same mouse button is already in the down position don't
  275. // send another event as it may confuse Xvfb.
  276. unsigned gdkButton = eventSenderButtonToGDKButton(button);
  277. if (m_mouseButtonCurrentlyDown == gdkButton)
  278. return;
  279. m_mouseButtonCurrentlyDown = gdkButton;
  280. // Normally GDK will send both GDK_BUTTON_PRESS and GDK_2BUTTON_PRESS for
  281. // the second button press during double-clicks. WebKit GTK+ selectively
  282. // ignores the first GDK_BUTTON_PRESS of that pair using gdk_event_peek.
  283. // Since our events aren't ever going onto the GDK event queue, WebKit won't
  284. // be able to filter out the first GDK_BUTTON_PRESS, so we just don't send
  285. // it here. Eventually this code should probably figure out a way to get all
  286. // appropriate events onto the event queue and this work-around should be
  287. // removed.
  288. updateClickCountForButton(button);
  289. GdkEventType eventType;
  290. if (m_clickCount == 2)
  291. eventType = GDK_2BUTTON_PRESS;
  292. else if (m_clickCount == 3)
  293. eventType = GDK_3BUTTON_PRESS;
  294. else
  295. eventType = GDK_BUTTON_PRESS;
  296. GdkEvent* event = createMouseButtonEvent(eventType, button, wkModifiers);
  297. sendOrQueueEvent(event);
  298. }
  299. void EventSenderProxy::mouseUp(unsigned button, WKEventModifiers wkModifiers)
  300. {
  301. m_clickButton = kWKEventMouseButtonNoButton;
  302. GdkEvent* event = createMouseButtonEvent(GDK_BUTTON_RELEASE, button, wkModifiers);
  303. sendOrQueueEvent(event);
  304. if (m_mouseButtonCurrentlyDown == event->button.button)
  305. m_mouseButtonCurrentlyDown = 0;
  306. m_clickPosition = m_position;
  307. m_clickTime = GDK_CURRENT_TIME;
  308. }
  309. void EventSenderProxy::mouseMoveTo(double x, double y)
  310. {
  311. m_position.x = x;
  312. m_position.y = y;
  313. GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY);
  314. event->motion.x = m_position.x;
  315. event->motion.y = m_position.y;
  316. event->motion.time = GDK_CURRENT_TIME;
  317. event->motion.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
  318. g_object_ref(event->motion.window);
  319. gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(event->motion.window))));
  320. event->motion.state = 0 | getMouseButtonModifiers(m_mouseButtonCurrentlyDown);
  321. event->motion.axes = 0;
  322. int xRoot, yRoot;
  323. gdk_window_get_root_coords(gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView())), m_position.x, m_position.y , &xRoot, &yRoot);
  324. event->motion.x_root = xRoot;
  325. event->motion.y_root = yRoot;
  326. sendOrQueueEvent(event);
  327. }
  328. void EventSenderProxy::mouseScrollBy(int horizontal, int vertical)
  329. {
  330. GdkEvent* event = gdk_event_new(GDK_SCROLL);
  331. event->scroll.x = m_position.x;
  332. event->scroll.y = m_position.y;
  333. event->scroll.time = GDK_CURRENT_TIME;
  334. event->scroll.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
  335. g_object_ref(event->scroll.window);
  336. gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(event->scroll.window))));
  337. // For more than one tick in a scroll, we need smooth scroll event
  338. if ((horizontal && vertical) || horizontal > 1 || horizontal < -1 || vertical > 1 || vertical < -1) {
  339. event->scroll.direction = GDK_SCROLL_SMOOTH;
  340. event->scroll.delta_x = -horizontal;
  341. event->scroll.delta_y = -vertical;
  342. sendOrQueueEvent(event);
  343. return;
  344. }
  345. if (horizontal < 0)
  346. event->scroll.direction = GDK_SCROLL_RIGHT;
  347. else if (horizontal > 0)
  348. event->scroll.direction = GDK_SCROLL_LEFT;
  349. else if (vertical < 0)
  350. event->scroll.direction = GDK_SCROLL_DOWN;
  351. else if (vertical > 0)
  352. event->scroll.direction = GDK_SCROLL_UP;
  353. else
  354. g_assert_not_reached();
  355. sendOrQueueEvent(event);
  356. }
  357. void EventSenderProxy::continuousMouseScrollBy(int horizontal, int vertical, bool paged)
  358. {
  359. // Gtk+ does not support paged scroll events.
  360. g_return_if_fail(!paged);
  361. GdkEvent* event = gdk_event_new(GDK_SCROLL);
  362. event->scroll.x = m_position.x;
  363. event->scroll.y = m_position.y;
  364. event->scroll.time = GDK_CURRENT_TIME;
  365. event->scroll.window = gtk_widget_get_window(GTK_WIDGET(m_testController->mainWebView()->platformView()));
  366. g_object_ref(event->scroll.window);
  367. gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_window_get_display(event->scroll.window))));
  368. event->scroll.direction = GDK_SCROLL_SMOOTH;
  369. event->scroll.delta_x = -horizontal / pixelsPerScrollTick;
  370. event->scroll.delta_y = -vertical / pixelsPerScrollTick;
  371. sendOrQueueEvent(event);
  372. }
  373. void EventSenderProxy::leapForward(int milliseconds)
  374. {
  375. if (m_eventQueue.isEmpty())
  376. m_eventQueue.append(WTREventQueueItem());
  377. m_eventQueue.last().delay = milliseconds;
  378. m_time += milliseconds / 1000.0;
  379. }
  380. } // namespace WTR