ce92b0722499dbad52f5f87819df0838d5eeaf6a
[WebKit-https.git] / Source / WebKit / UIProcess / Automation / gtk / WebAutomationSessionGtk.cpp
1 /*
2  * Copyright (C) 2017 Igalia S.L.
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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebAutomationSession.h"
28
29 #include "WebAutomationSessionMacros.h"
30 #include "WebPageProxy.h"
31 #include <WebCore/GtkUtilities.h>
32 #include <gtk/gtk.h>
33
34 namespace WebKit {
35 using namespace WebCore;
36
37 static unsigned modifiersToEventState(OptionSet<WebEvent::Modifier> modifiers)
38 {
39     unsigned state = 0;
40     if (modifiers.contains(WebEvent::Modifier::ControlKey))
41         state |= GDK_CONTROL_MASK;
42     if (modifiers.contains(WebEvent::Modifier::ShiftKey))
43         state |= GDK_SHIFT_MASK;
44     if (modifiers.contains(WebEvent::Modifier::AltKey))
45         state |= GDK_META_MASK;
46     if (modifiers.contains(WebEvent::Modifier::CapsLockKey))
47         state |= GDK_LOCK_MASK;
48     return state;
49 }
50
51 static unsigned mouseButtonToGdkButton(WebMouseEvent::Button button)
52 {
53     switch (button) {
54     case WebMouseEvent::NoButton:
55     case WebMouseEvent::LeftButton:
56         return GDK_BUTTON_PRIMARY;
57     case WebMouseEvent::MiddleButton:
58         return GDK_BUTTON_MIDDLE;
59     case WebMouseEvent::RightButton:
60         return GDK_BUTTON_SECONDARY;
61     }
62     return GDK_BUTTON_PRIMARY;
63 }
64
65 static void doMouseEvent(GdkEventType type, GtkWidget* widget, const WebCore::IntPoint& location, unsigned button, unsigned state)
66 {
67     ASSERT(type == GDK_BUTTON_PRESS || type == GDK_BUTTON_RELEASE);
68
69     GUniquePtr<GdkEvent> event(gdk_event_new(type));
70     event->button.window = gtk_widget_get_window(widget);
71     g_object_ref(event->button.window);
72     event->button.time = GDK_CURRENT_TIME;
73     event->button.x = location.x();
74     event->button.y = location.y();
75     event->button.axes = 0;
76     event->button.state = state;
77     event->button.button = button;
78 #if GTK_CHECK_VERSION(3, 20, 0)
79     event->button.device = gdk_seat_get_pointer(gdk_display_get_default_seat(gtk_widget_get_display(widget)));
80 #else
81     event->button.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(widget)));
82 #endif
83     int xRoot, yRoot;
84     gdk_window_get_root_coords(gtk_widget_get_window(widget), location.x(), location.y(), &xRoot, &yRoot);
85     event->button.x_root = xRoot;
86     event->button.y_root = yRoot;
87     gtk_main_do_event(event.get());
88 }
89
90 static void doMotionEvent(GtkWidget* widget, const WebCore::IntPoint& location, unsigned state)
91 {
92     GUniquePtr<GdkEvent> event(gdk_event_new(GDK_MOTION_NOTIFY));
93     event->motion.window = gtk_widget_get_window(widget);
94     g_object_ref(event->motion.window);
95     event->motion.time = GDK_CURRENT_TIME;
96     event->motion.x = location.x();
97     event->motion.y = location.y();
98     event->motion.axes = 0;
99     event->motion.state = state;
100 #if GTK_CHECK_VERSION(3, 20, 0)
101     event->motion.device = gdk_seat_get_pointer(gdk_display_get_default_seat(gtk_widget_get_display(widget)));
102 #else
103     event->motion.device = gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(widget)));
104 #endif
105     int xRoot, yRoot;
106     gdk_window_get_root_coords(gtk_widget_get_window(widget), location.x(), location.y(), &xRoot, &yRoot);
107     event->motion.x_root = xRoot;
108     event->motion.y_root = yRoot;
109     gtk_main_do_event(event.get());
110 }
111
112 void WebAutomationSession::platformSimulateMouseInteraction(WebPageProxy& page, MouseInteraction interaction, WebMouseEvent::Button button, const WebCore::IntPoint& locationInView, OptionSet<WebEvent::Modifier> keyModifiers)
113 {
114     unsigned gdkButton = mouseButtonToGdkButton(button);
115     auto modifier = stateModifierForGdkButton(gdkButton);
116     unsigned state = modifiersToEventState(keyModifiers) | m_currentModifiers;
117
118     switch (interaction) {
119     case MouseInteraction::Move:
120         doMotionEvent(page.viewWidget(), locationInView, state);
121         break;
122     case MouseInteraction::Down:
123         doMouseEvent(GDK_BUTTON_PRESS, page.viewWidget(), locationInView, gdkButton, state);
124         m_currentModifiers |= modifier;
125         break;
126     case MouseInteraction::Up:
127         doMouseEvent(GDK_BUTTON_RELEASE, page.viewWidget(), locationInView, gdkButton, state);
128         m_currentModifiers &= ~modifier;
129         break;
130     case MouseInteraction::SingleClick:
131         doMouseEvent(GDK_BUTTON_PRESS, page.viewWidget(), locationInView, gdkButton, state);
132         doMouseEvent(GDK_BUTTON_RELEASE, page.viewWidget(), locationInView, gdkButton, state | modifier);
133         break;
134     case MouseInteraction::DoubleClick:
135         doMouseEvent(GDK_BUTTON_PRESS, page.viewWidget(), locationInView, gdkButton, state);
136         doMouseEvent(GDK_BUTTON_RELEASE, page.viewWidget(), locationInView, gdkButton, state | modifier);
137         doMouseEvent(GDK_BUTTON_PRESS, page.viewWidget(), locationInView, gdkButton, state);
138         doMouseEvent(GDK_BUTTON_RELEASE, page.viewWidget(), locationInView, gdkButton, state | modifier);
139         break;
140     }
141 }
142
143 static void doKeyStrokeEvent(GdkEventType type, GtkWidget* widget, unsigned keyVal, unsigned state, bool doReleaseAfterPress = false)
144 {
145     ASSERT(type == GDK_KEY_PRESS || type == GDK_KEY_RELEASE);
146
147     GUniquePtr<GdkEvent> event(gdk_event_new(type));
148     event->key.keyval = keyVal;
149
150     event->key.time = GDK_CURRENT_TIME;
151     event->key.window = gtk_widget_get_window(widget);
152     g_object_ref(event->key.window);
153
154 #if GTK_CHECK_VERSION(3, 20, 0)
155     gdk_event_set_device(event.get(), gdk_seat_get_pointer(gdk_display_get_default_seat(gtk_widget_get_display(widget))));
156 #else
157     gdk_event_set_device(event.get(), gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gtk_widget_get_display(widget))));
158 #endif
159     event->key.state = state;
160
161     // When synthesizing an event, an invalid hardware_keycode value can cause it to be badly processed by GTK+.
162     GUniqueOutPtr<GdkKeymapKey> keys;
163     int keysCount;
164     if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), keyVal, &keys.outPtr(), &keysCount) && keysCount)
165         event->key.hardware_keycode = keys.get()[0].keycode;
166
167     gtk_main_do_event(event.get());
168     if (doReleaseAfterPress) {
169         ASSERT(type == GDK_KEY_PRESS);
170         event->key.type = GDK_KEY_RELEASE;
171         gtk_main_do_event(event.get());
172     }
173 }
174
175 static int keyCodeForVirtualKey(Inspector::Protocol::Automation::VirtualKey key)
176 {
177     switch (key) {
178     case Inspector::Protocol::Automation::VirtualKey::Shift:
179         return GDK_KEY_Shift_R;
180     case Inspector::Protocol::Automation::VirtualKey::Control:
181         return GDK_KEY_Control_R;
182     case Inspector::Protocol::Automation::VirtualKey::Alternate:
183         return GDK_KEY_Alt_L;
184     case Inspector::Protocol::Automation::VirtualKey::Meta:
185         return GDK_KEY_Meta_R;
186     case Inspector::Protocol::Automation::VirtualKey::Command:
187         return GDK_KEY_Execute;
188     case Inspector::Protocol::Automation::VirtualKey::Help:
189         return GDK_KEY_Help;
190     case Inspector::Protocol::Automation::VirtualKey::Backspace:
191         return GDK_KEY_BackSpace;
192     case Inspector::Protocol::Automation::VirtualKey::Tab:
193         return GDK_KEY_Tab;
194     case Inspector::Protocol::Automation::VirtualKey::Clear:
195         return GDK_KEY_Clear;
196     case Inspector::Protocol::Automation::VirtualKey::Enter:
197         return GDK_KEY_Return;
198     case Inspector::Protocol::Automation::VirtualKey::Pause:
199         return GDK_KEY_Pause;
200     case Inspector::Protocol::Automation::VirtualKey::Cancel:
201         return GDK_KEY_Cancel;
202     case Inspector::Protocol::Automation::VirtualKey::Escape:
203         return GDK_KEY_Escape;
204     case Inspector::Protocol::Automation::VirtualKey::PageUp:
205         return GDK_KEY_Page_Up;
206     case Inspector::Protocol::Automation::VirtualKey::PageDown:
207         return GDK_KEY_Page_Down;
208     case Inspector::Protocol::Automation::VirtualKey::End:
209         return GDK_KEY_End;
210     case Inspector::Protocol::Automation::VirtualKey::Home:
211         return GDK_KEY_Home;
212     case Inspector::Protocol::Automation::VirtualKey::LeftArrow:
213         return GDK_KEY_Left;
214     case Inspector::Protocol::Automation::VirtualKey::UpArrow:
215         return GDK_KEY_Up;
216     case Inspector::Protocol::Automation::VirtualKey::RightArrow:
217         return GDK_KEY_Right;
218     case Inspector::Protocol::Automation::VirtualKey::DownArrow:
219         return GDK_KEY_Down;
220     case Inspector::Protocol::Automation::VirtualKey::Insert:
221         return GDK_KEY_Insert;
222     case Inspector::Protocol::Automation::VirtualKey::Delete:
223         return GDK_KEY_Delete;
224     case Inspector::Protocol::Automation::VirtualKey::Space:
225         return GDK_KEY_space;
226     case Inspector::Protocol::Automation::VirtualKey::Semicolon:
227         return GDK_KEY_semicolon;
228     case Inspector::Protocol::Automation::VirtualKey::Equals:
229         return GDK_KEY_equal;
230     case Inspector::Protocol::Automation::VirtualKey::Return:
231         return GDK_KEY_Return;
232     case Inspector::Protocol::Automation::VirtualKey::NumberPad0:
233         return GDK_KEY_KP_0;
234     case Inspector::Protocol::Automation::VirtualKey::NumberPad1:
235         return GDK_KEY_KP_1;
236     case Inspector::Protocol::Automation::VirtualKey::NumberPad2:
237         return GDK_KEY_KP_2;
238     case Inspector::Protocol::Automation::VirtualKey::NumberPad3:
239         return GDK_KEY_KP_3;
240     case Inspector::Protocol::Automation::VirtualKey::NumberPad4:
241         return GDK_KEY_KP_4;
242     case Inspector::Protocol::Automation::VirtualKey::NumberPad5:
243         return GDK_KEY_KP_5;
244     case Inspector::Protocol::Automation::VirtualKey::NumberPad6:
245         return GDK_KEY_KP_6;
246     case Inspector::Protocol::Automation::VirtualKey::NumberPad7:
247         return GDK_KEY_KP_7;
248     case Inspector::Protocol::Automation::VirtualKey::NumberPad8:
249         return GDK_KEY_KP_8;
250     case Inspector::Protocol::Automation::VirtualKey::NumberPad9:
251         return GDK_KEY_KP_9;
252     case Inspector::Protocol::Automation::VirtualKey::NumberPadMultiply:
253         return GDK_KEY_KP_Multiply;
254     case Inspector::Protocol::Automation::VirtualKey::NumberPadAdd:
255         return GDK_KEY_KP_Add;
256     case Inspector::Protocol::Automation::VirtualKey::NumberPadSubtract:
257         return GDK_KEY_KP_Subtract;
258     case Inspector::Protocol::Automation::VirtualKey::NumberPadSeparator:
259         return GDK_KEY_KP_Separator;
260     case Inspector::Protocol::Automation::VirtualKey::NumberPadDecimal:
261         return GDK_KEY_KP_Decimal;
262     case Inspector::Protocol::Automation::VirtualKey::NumberPadDivide:
263         return GDK_KEY_KP_Divide;
264     case Inspector::Protocol::Automation::VirtualKey::Function1:
265         return GDK_KEY_F1;
266     case Inspector::Protocol::Automation::VirtualKey::Function2:
267         return GDK_KEY_F2;
268     case Inspector::Protocol::Automation::VirtualKey::Function3:
269         return GDK_KEY_F3;
270     case Inspector::Protocol::Automation::VirtualKey::Function4:
271         return GDK_KEY_F4;
272     case Inspector::Protocol::Automation::VirtualKey::Function5:
273         return GDK_KEY_F5;
274     case Inspector::Protocol::Automation::VirtualKey::Function6:
275         return GDK_KEY_F6;
276     case Inspector::Protocol::Automation::VirtualKey::Function7:
277         return GDK_KEY_F7;
278     case Inspector::Protocol::Automation::VirtualKey::Function8:
279         return GDK_KEY_F8;
280     case Inspector::Protocol::Automation::VirtualKey::Function9:
281         return GDK_KEY_F9;
282     case Inspector::Protocol::Automation::VirtualKey::Function10:
283         return GDK_KEY_F10;
284     case Inspector::Protocol::Automation::VirtualKey::Function11:
285         return GDK_KEY_F11;
286     case Inspector::Protocol::Automation::VirtualKey::Function12:
287         return GDK_KEY_F12;
288     }
289
290     ASSERT_NOT_REACHED();
291     return 0;
292 }
293
294 static unsigned modifiersForKeyCode(unsigned keyCode)
295 {
296     switch (keyCode) {
297     case GDK_KEY_Shift_R:
298         return GDK_SHIFT_MASK;
299     case GDK_KEY_Control_R:
300         return GDK_CONTROL_MASK;
301     case GDK_KEY_Alt_L:
302         return GDK_MOD1_MASK;
303     case GDK_KEY_Meta_R:
304         return GDK_META_MASK;
305     }
306     return 0;
307 }
308
309 void WebAutomationSession::platformSimulateKeyboardInteraction(WebPageProxy& page, KeyboardInteraction interaction, WTF::Variant<VirtualKey, CharKey>&& key)
310 {
311     unsigned keyCode;
312     WTF::switchOn(key,
313         [&] (VirtualKey virtualKey) {
314             keyCode = keyCodeForVirtualKey(virtualKey);
315         },
316         [&] (CharKey charKey) {
317             keyCode = gdk_unicode_to_keyval(g_utf8_get_char(&charKey));
318         }
319     );
320     unsigned modifiers = modifiersForKeyCode(keyCode);
321
322     switch (interaction) {
323     case KeyboardInteraction::KeyPress:
324         m_currentModifiers |= modifiers;
325         doKeyStrokeEvent(GDK_KEY_PRESS, page.viewWidget(), keyCode, m_currentModifiers);
326         break;
327     case KeyboardInteraction::KeyRelease:
328         m_currentModifiers &= ~modifiers;
329         doKeyStrokeEvent(GDK_KEY_RELEASE, page.viewWidget(), keyCode, m_currentModifiers);
330         break;
331     case KeyboardInteraction::InsertByKey:
332         doKeyStrokeEvent(GDK_KEY_PRESS, page.viewWidget(), keyCode, m_currentModifiers, true);
333         break;
334     }
335 }
336
337 void WebAutomationSession::platformSimulateKeySequence(WebPageProxy& page, const String& keySequence)
338 {
339     CString keySequenceUTF8 = keySequence.utf8();
340     const char* p = keySequenceUTF8.data();
341     do {
342         doKeyStrokeEvent(GDK_KEY_PRESS, page.viewWidget(), gdk_unicode_to_keyval(g_utf8_get_char(p)), m_currentModifiers, true);
343         p = g_utf8_next_char(p);
344     } while (*p);
345 }
346
347 } // namespace WebKit