9be93ca49af88f17627db1902ee574c8eb0d0c53
[WebKit-https.git] / Source / WebKit / UIProcess / API / gtk / WebKitWebViewBase.cpp
1 /*
2  * Copyright (C) 2010 Apple Inc. All rights reserved.
3  * Portions Copyright (c) 2010 Motorola Mobility, Inc.  All rights reserved.
4  * Copyright (C) 2011 Igalia S.L.
5  * Copyright (C) 2013 Gustavo Noronha Silva <gns@gnome.org>.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26  * THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "WebKitWebViewBase.h"
31
32 #include "APIPageConfiguration.h"
33 #include "AcceleratedBackingStore.h"
34 #include "DrawingAreaProxyCoordinatedGraphics.h"
35 #include "InputMethodFilter.h"
36 #include "KeyBindingTranslator.h"
37 #include "NativeWebKeyboardEvent.h"
38 #include "NativeWebMouseEvent.h"
39 #include "NativeWebWheelEvent.h"
40 #include "PageClientImpl.h"
41 #include "ViewGestureController.h"
42 #include "WebEventFactory.h"
43 #include "WebInspectorProxy.h"
44 #include "WebKit2Initialize.h"
45 #include "WebKitEmojiChooser.h"
46 #include "WebKitWebViewAccessible.h"
47 #include "WebKitWebViewBasePrivate.h"
48 #include "WebPageGroup.h"
49 #include "WebPageProxy.h"
50 #include "WebPreferences.h"
51 #include "WebProcessPool.h"
52 #include "WebUserContentControllerProxy.h"
53 #include <WebCore/ActivityState.h>
54 #include <WebCore/CairoUtilities.h>
55 #include <WebCore/GUniquePtrGtk.h>
56 #include <WebCore/GtkUtilities.h>
57 #include <WebCore/NotImplemented.h>
58 #include <WebCore/PasteboardHelper.h>
59 #include <WebCore/PlatformDisplay.h>
60 #include <WebCore/RefPtrCairo.h>
61 #include <WebCore/Region.h>
62 #include <gdk/gdk.h>
63 #include <gdk/gdkkeysyms.h>
64 #include <glib/gi18n-lib.h>
65 #include <memory>
66 #include <pal/system/SleepDisabler.h>
67 #include <wtf/Compiler.h>
68 #include <wtf/HashMap.h>
69 #include <wtf/glib/GRefPtr.h>
70 #include <wtf/glib/RunLoopSourcePriority.h>
71 #include <wtf/glib/WTFGType.h>
72 #include <wtf/text/CString.h>
73
74 #if ENABLE(FULLSCREEN_API)
75 #include "WebFullScreenManagerProxy.h"
76 #endif
77
78 #if PLATFORM(X11)
79 #include <gdk/gdkx.h>
80 #endif
81
82 // gtk_widget_get_scale_factor() appeared in GTK 3.10, but we also need
83 // to make sure we have cairo new enough to support cairo_surface_set_device_scale
84 #define HAVE_GTK_SCALE_FACTOR HAVE_CAIRO_SURFACE_SET_DEVICE_SCALE && GTK_CHECK_VERSION(3, 10, 0)
85
86 using namespace WebKit;
87 using namespace WebCore;
88
89 struct ClickCounter {
90 public:
91     void reset()
92     {
93         currentClickCount = 0;
94         previousClickPoint = IntPoint();
95         previousClickTime = 0;
96         previousClickButton = 0;
97     }
98
99     int currentClickCountForGdkButtonEvent(GdkEvent* event)
100     {
101         int doubleClickDistance = 250;
102         int doubleClickTime = 5;
103         g_object_get(gtk_settings_get_for_screen(gdk_event_get_screen(event)),
104             "gtk-double-click-distance", &doubleClickDistance, "gtk-double-click-time", &doubleClickTime, nullptr);
105
106         // GTK+ only counts up to triple clicks, but WebCore wants to know about
107         // quadruple clicks, quintuple clicks, ad infinitum. Here, we replicate the
108         // GDK logic for counting clicks.
109         guint32 eventTime = gdk_event_get_time(event);
110         if (!eventTime) {
111             // Real events always have a non-zero time, but events synthesized
112             // by the WTR do not and we must calculate a time manually. This time
113             // is not calculated in the WTR, because GTK+ does not work well with
114             // anything other than GDK_CURRENT_TIME on synthesized events.
115             GTimeVal timeValue;
116             g_get_current_time(&timeValue);
117             eventTime = (timeValue.tv_sec * 1000) + (timeValue.tv_usec / 1000);
118         }
119
120         GdkEventType type;
121         guint button;
122         double x, y;
123         gdk_event_get_coords(event, &x, &y);
124         gdk_event_get_button(event, &button);
125 #if GTK_CHECK_VERSION(3, 10, 0)
126         type = gdk_event_get_event_type(event);
127 #else
128         type = event->type;
129 #endif
130         if ((type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS)
131             || ((std::abs(x - previousClickPoint.x()) < doubleClickDistance)
132                 && (std::abs(y - previousClickPoint.y()) < doubleClickDistance)
133                 && (eventTime - previousClickTime < static_cast<unsigned>(doubleClickTime))
134                 && (button == previousClickButton)))
135             currentClickCount++;
136         else
137             currentClickCount = 1;
138
139         previousClickPoint = IntPoint(x, y);
140         previousClickButton = button;
141         previousClickTime = eventTime;
142
143         return currentClickCount;
144     }
145
146 private:
147     int currentClickCount;
148     IntPoint previousClickPoint;
149     unsigned previousClickButton;
150     int previousClickTime;
151 };
152
153 typedef HashMap<GtkWidget*, IntRect> WebKitWebViewChildrenMap;
154 typedef HashMap<uint32_t, GUniquePtr<GdkEvent>> TouchEventsMap;
155
156 struct _WebKitWebViewBasePrivate {
157     _WebKitWebViewBasePrivate()
158         : updateActivityStateTimer(RunLoop::main(), this, &_WebKitWebViewBasePrivate::updateActivityStateTimerFired)
159 #if GTK_CHECK_VERSION(3, 24, 0)
160         , releaseEmojiChooserTimer(RunLoop::main(), this, &_WebKitWebViewBasePrivate::releaseEmojiChooserTimerFired)
161 #endif
162     {
163 #if GTK_CHECK_VERSION(3, 24, 0)
164         releaseEmojiChooserTimer.setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer);
165 #endif
166     }
167
168     void updateActivityStateTimerFired()
169     {
170         if (!pageProxy)
171             return;
172         pageProxy->activityStateDidChange(activityStateFlagsToUpdate);
173         activityStateFlagsToUpdate = { };
174     }
175
176 #if GTK_CHECK_VERSION(3, 24, 0)
177     void releaseEmojiChooserTimerFired()
178     {
179         if (emojiChooser) {
180             gtk_widget_destroy(emojiChooser);
181             emojiChooser = nullptr;
182         }
183     }
184 #endif
185
186     WebKitWebViewChildrenMap children;
187     std::unique_ptr<PageClientImpl> pageClient;
188     RefPtr<WebPageProxy> pageProxy;
189     bool shouldForwardNextKeyEvent { false };
190     bool shouldForwardNextWheelEvent { false };
191     ClickCounter clickCounter;
192     CString tooltipText;
193     IntRect tooltipArea;
194     GRefPtr<AtkObject> accessible;
195     GtkWidget* dialog { nullptr };
196     GtkWidget* inspectorView { nullptr };
197     AttachmentSide inspectorAttachmentSide { AttachmentSide::Bottom };
198     unsigned inspectorViewSize { 0 };
199     GUniquePtr<GdkEvent> contextMenuEvent;
200     WebContextMenuProxyGtk* activeContextMenuProxy { nullptr };
201     InputMethodFilter inputMethodFilter;
202     KeyBindingTranslator keyBindingTranslator;
203     TouchEventsMap touchEvents;
204     IntSize contentsSize;
205
206     GtkWindow* toplevelOnScreenWindow { nullptr };
207     unsigned long toplevelFocusInEventID { 0 };
208     unsigned long toplevelFocusOutEventID { 0 };
209     unsigned long toplevelWindowStateEventID { 0 };
210     unsigned long toplevelWindowRealizedID { 0 };
211     unsigned long themeChangedID { 0 };
212     unsigned long applicationPreferDarkThemeID { 0 };
213
214     // View State.
215     OptionSet<ActivityState::Flag> activityState;
216     OptionSet<ActivityState::Flag> activityStateFlagsToUpdate;
217     RunLoop::Timer<WebKitWebViewBasePrivate> updateActivityStateTimer;
218
219 #if ENABLE(FULLSCREEN_API)
220     bool fullScreenModeActive { false };
221     std::unique_ptr<PAL::SleepDisabler> sleepDisabler;
222 #endif
223
224     std::unique_ptr<AcceleratedBackingStore> acceleratedBackingStore;
225
226 #if ENABLE(DRAG_SUPPORT)
227     std::unique_ptr<DragAndDropHandler> dragAndDropHandler;
228 #endif
229
230 #if HAVE(GTK_GESTURES)
231     std::unique_ptr<GestureController> gestureController;
232 #endif
233     std::unique_ptr<ViewGestureController> viewGestureController;
234     bool isBackForwardNavigationGestureEnabled { false };
235
236 #if GTK_CHECK_VERSION(3, 24, 0)
237     GtkWidget* emojiChooser;
238     CompletionHandler<void(String)> emojiChooserCompletionHandler;
239     RunLoop::Timer<WebKitWebViewBasePrivate> releaseEmojiChooserTimer;
240 #endif
241 };
242
243 WEBKIT_DEFINE_TYPE(WebKitWebViewBase, webkit_web_view_base, GTK_TYPE_CONTAINER)
244
245 static void webkitWebViewBaseScheduleUpdateActivityState(WebKitWebViewBase* webViewBase, OptionSet<ActivityState::Flag> flagsToUpdate)
246 {
247     WebKitWebViewBasePrivate* priv = webViewBase->priv;
248     priv->activityStateFlagsToUpdate.add(flagsToUpdate);
249     if (priv->updateActivityStateTimer.isActive())
250         return;
251
252     priv->updateActivityStateTimer.startOneShot(0_s);
253 }
254
255 static gboolean toplevelWindowFocusInEvent(GtkWidget* widget, GdkEventFocus*, WebKitWebViewBase* webViewBase)
256 {
257     // Spurious focus in events can occur when the window is hidden.
258     if (!gtk_widget_get_visible(widget))
259         return FALSE;
260
261     WebKitWebViewBasePrivate* priv = webViewBase->priv;
262     if (priv->activityState & ActivityState::WindowIsActive)
263         return FALSE;
264
265     priv->activityState.add(ActivityState::WindowIsActive);
266     webkitWebViewBaseScheduleUpdateActivityState(webViewBase, ActivityState::WindowIsActive);
267
268     return FALSE;
269 }
270
271 static gboolean toplevelWindowFocusOutEvent(GtkWidget*, GdkEventFocus*, WebKitWebViewBase* webViewBase)
272 {
273     WebKitWebViewBasePrivate* priv = webViewBase->priv;
274     if (!(priv->activityState & ActivityState::WindowIsActive))
275         return FALSE;
276
277     priv->activityState.remove(ActivityState::WindowIsActive);
278     webkitWebViewBaseScheduleUpdateActivityState(webViewBase, ActivityState::WindowIsActive);
279
280     return FALSE;
281 }
282
283 static gboolean toplevelWindowStateEvent(GtkWidget*, GdkEventWindowState* event, WebKitWebViewBase* webViewBase)
284 {
285     WebKitWebViewBasePrivate* priv = webViewBase->priv;
286     if (!(event->changed_mask & GDK_WINDOW_STATE_ICONIFIED))
287         return FALSE;
288
289     bool visible = !(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED);
290     if ((visible && priv->activityState & ActivityState::IsVisible) || (!visible && !(priv->activityState & ActivityState::IsVisible)))
291         return FALSE;
292
293     if (visible)
294         priv->activityState.add(ActivityState::IsVisible);
295     else
296         priv->activityState.remove(ActivityState::IsVisible);
297     webkitWebViewBaseScheduleUpdateActivityState(webViewBase, ActivityState::IsVisible);
298
299     return FALSE;
300 }
301
302 static void themeChanged(WebKitWebViewBase* webViewBase)
303 {
304     webViewBase->priv->pageProxy->effectiveAppearanceDidChange();
305 }
306
307 static void toplevelWindowRealized(WebKitWebViewBase* webViewBase)
308 {
309     gtk_widget_realize(GTK_WIDGET(webViewBase));
310
311     WebKitWebViewBasePrivate* priv = webViewBase->priv;
312     if (priv->toplevelWindowRealizedID) {
313         g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelWindowRealizedID);
314         priv->toplevelWindowRealizedID = 0;
315     }
316 }
317
318 static void webkitWebViewBaseSetToplevelOnScreenWindow(WebKitWebViewBase* webViewBase, GtkWindow* window)
319 {
320     WebKitWebViewBasePrivate* priv = webViewBase->priv;
321     if (priv->toplevelOnScreenWindow == window)
322         return;
323
324     if (priv->toplevelFocusInEventID) {
325         g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelFocusInEventID);
326         priv->toplevelFocusInEventID = 0;
327     }
328     if (priv->toplevelFocusOutEventID) {
329         g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelFocusOutEventID);
330         priv->toplevelFocusOutEventID = 0;
331     }
332     if (priv->toplevelWindowStateEventID) {
333         g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelWindowStateEventID);
334         priv->toplevelWindowStateEventID = 0;
335     }
336     if (priv->toplevelWindowRealizedID) {
337         g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelWindowRealizedID);
338         priv->toplevelWindowRealizedID = 0;
339     }
340     if (priv->themeChangedID || priv->applicationPreferDarkThemeID) {
341         auto* settings = gtk_widget_get_settings(GTK_WIDGET(priv->toplevelOnScreenWindow));
342         if (priv->themeChangedID) {
343             g_signal_handler_disconnect(settings, priv->themeChangedID);
344             priv->themeChangedID = 0;
345         }
346         if (priv->applicationPreferDarkThemeID) {
347             g_signal_handler_disconnect(settings, priv->applicationPreferDarkThemeID);
348             priv->applicationPreferDarkThemeID = 0;
349         }
350     }
351
352     priv->toplevelOnScreenWindow = window;
353
354     if (!priv->toplevelOnScreenWindow) {
355         OptionSet<ActivityState::Flag> flagsToUpdate;
356         if (priv->activityState & ActivityState::IsInWindow) {
357             priv->activityState.remove(ActivityState::IsInWindow);
358             flagsToUpdate.add(ActivityState::IsInWindow);
359         }
360         if (priv->activityState & ActivityState::WindowIsActive) {
361             priv->activityState.remove(ActivityState::WindowIsActive);
362             flagsToUpdate.add(ActivityState::IsInWindow);
363         }
364         if (flagsToUpdate)
365             webkitWebViewBaseScheduleUpdateActivityState(webViewBase, flagsToUpdate);
366
367         return;
368     }
369
370     priv->toplevelFocusInEventID =
371         g_signal_connect(priv->toplevelOnScreenWindow, "focus-in-event",
372                          G_CALLBACK(toplevelWindowFocusInEvent), webViewBase);
373     priv->toplevelFocusOutEventID =
374         g_signal_connect(priv->toplevelOnScreenWindow, "focus-out-event",
375                          G_CALLBACK(toplevelWindowFocusOutEvent), webViewBase);
376     priv->toplevelWindowStateEventID =
377         g_signal_connect(priv->toplevelOnScreenWindow, "window-state-event", G_CALLBACK(toplevelWindowStateEvent), webViewBase);
378
379     auto* settings = gtk_widget_get_settings(GTK_WIDGET(priv->toplevelOnScreenWindow));
380     priv->themeChangedID =
381         g_signal_connect_swapped(settings, "notify::gtk-theme-name", G_CALLBACK(themeChanged), webViewBase);
382     priv->applicationPreferDarkThemeID =
383         g_signal_connect_swapped(settings, "notify::gtk-application-prefer-dark-theme", G_CALLBACK(themeChanged), webViewBase);
384
385     if (gtk_widget_get_realized(GTK_WIDGET(window)))
386         gtk_widget_realize(GTK_WIDGET(webViewBase));
387     else
388         priv->toplevelWindowRealizedID = g_signal_connect_swapped(window, "realize", G_CALLBACK(toplevelWindowRealized), webViewBase);
389 }
390
391 static void webkitWebViewBaseRealize(GtkWidget* widget)
392 {
393     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(widget);
394     WebKitWebViewBasePrivate* priv = webView->priv;
395
396     gtk_widget_set_realized(widget, TRUE);
397
398     GtkAllocation allocation;
399     gtk_widget_get_allocation(widget, &allocation);
400
401     GdkWindowAttr attributes;
402     attributes.window_type = GDK_WINDOW_CHILD;
403     attributes.x = allocation.x;
404     attributes.y = allocation.y;
405     attributes.width = allocation.width;
406     attributes.height = allocation.height;
407     attributes.wclass = GDK_INPUT_OUTPUT;
408     attributes.visual = gtk_widget_get_visual(widget);
409     attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK
410         | GDK_EXPOSURE_MASK
411         | GDK_BUTTON_PRESS_MASK
412         | GDK_BUTTON_RELEASE_MASK
413         | GDK_SCROLL_MASK
414         | GDK_SMOOTH_SCROLL_MASK
415         | GDK_POINTER_MOTION_MASK
416         | GDK_ENTER_NOTIFY_MASK
417         | GDK_LEAVE_NOTIFY_MASK
418         | GDK_KEY_PRESS_MASK
419         | GDK_KEY_RELEASE_MASK
420         | GDK_BUTTON_MOTION_MASK
421         | GDK_BUTTON1_MOTION_MASK
422         | GDK_BUTTON2_MOTION_MASK
423         | GDK_BUTTON3_MOTION_MASK
424         | GDK_TOUCH_MASK;
425 #if HAVE(GTK_GESTURES)
426     attributes.event_mask |= GDK_TOUCHPAD_GESTURE_MASK;
427 #endif
428
429     gint attributesMask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
430
431     GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributesMask);
432     gtk_widget_set_window(widget, window);
433     gdk_window_set_user_data(window, widget);
434
435     gtk_im_context_set_client_window(priv->inputMethodFilter.context(), window);
436 }
437
438 static void webkitWebViewBaseUnrealize(GtkWidget* widget)
439 {
440     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(widget);
441     gtk_im_context_set_client_window(webView->priv->inputMethodFilter.context(), nullptr);
442
443     GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->unrealize(widget);
444 }
445
446 static bool webkitWebViewChildIsInternalWidget(WebKitWebViewBase* webViewBase, GtkWidget* widget)
447 {
448     WebKitWebViewBasePrivate* priv = webViewBase->priv;
449     return widget == priv->inspectorView || widget == priv->dialog;
450 }
451
452 static void webkitWebViewBaseContainerAdd(GtkContainer* container, GtkWidget* widget)
453 {
454     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(container);
455     WebKitWebViewBasePrivate* priv = webView->priv;
456
457     // Internal widgets like the web inspector and authentication dialog have custom
458     // allocations so we don't need to add them to our list of children.
459     if (!webkitWebViewChildIsInternalWidget(webView, widget)) {
460         GtkAllocation childAllocation;
461         gtk_widget_get_allocation(widget, &childAllocation);
462         priv->children.set(widget, childAllocation);
463     }
464
465     gtk_widget_set_parent(widget, GTK_WIDGET(container));
466 }
467
468 void webkitWebViewBaseAddDialog(WebKitWebViewBase* webViewBase, GtkWidget* dialog)
469 {
470     WebKitWebViewBasePrivate* priv = webViewBase->priv;
471     priv->dialog = dialog;
472     gtk_container_add(GTK_CONTAINER(webViewBase), dialog);
473     gtk_widget_show(dialog);
474
475     // We need to draw the shadow over the widget.
476     gtk_widget_queue_draw(GTK_WIDGET(webViewBase));
477 }
478
479 void webkitWebViewBaseAddWebInspector(WebKitWebViewBase* webViewBase, GtkWidget* inspector, AttachmentSide attachmentSide)
480 {
481     if (webViewBase->priv->inspectorView == inspector && webViewBase->priv->inspectorAttachmentSide == attachmentSide)
482         return;
483
484     webViewBase->priv->inspectorAttachmentSide = attachmentSide;
485
486     if (webViewBase->priv->inspectorView == inspector) {
487         gtk_widget_queue_resize(GTK_WIDGET(webViewBase));
488         return;
489     }
490
491     webViewBase->priv->inspectorView = inspector;
492     gtk_container_add(GTK_CONTAINER(webViewBase), inspector);
493 }
494
495 static void webkitWebViewBaseContainerRemove(GtkContainer* container, GtkWidget* widget)
496 {
497     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(container);
498     WebKitWebViewBasePrivate* priv = webView->priv;
499     GtkWidget* widgetContainer = GTK_WIDGET(container);
500
501     gboolean wasVisible = gtk_widget_get_visible(widget);
502     gtk_widget_unparent(widget);
503
504     if (priv->inspectorView == widget) {
505         priv->inspectorView = 0;
506         priv->inspectorViewSize = 0;
507     } else if (priv->dialog == widget) {
508         priv->dialog = nullptr;
509         if (gtk_widget_get_visible(widgetContainer))
510             gtk_widget_grab_focus(widgetContainer);
511     } else {
512         ASSERT(priv->children.contains(widget));
513         priv->children.remove(widget);
514     }
515     if (wasVisible && gtk_widget_get_visible(widgetContainer))
516         gtk_widget_queue_resize(widgetContainer);
517 }
518
519 static void webkitWebViewBaseContainerForall(GtkContainer* container, gboolean includeInternals, GtkCallback callback, gpointer callbackData)
520 {
521     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(container);
522     WebKitWebViewBasePrivate* priv = webView->priv;
523
524     for (const auto& child : copyToVector(priv->children.keys())) {
525         if (priv->children.contains(child))
526             (*callback)(child, callbackData);
527     }
528
529     if (includeInternals && priv->inspectorView)
530         (*callback)(priv->inspectorView, callbackData);
531
532     if (includeInternals && priv->dialog)
533         (*callback)(priv->dialog, callbackData);
534 }
535
536 void webkitWebViewBaseChildMoveResize(WebKitWebViewBase* webView, GtkWidget* child, const IntRect& childRect)
537 {
538     const IntRect& geometry = webView->priv->children.get(child);
539     if (geometry == childRect)
540         return;
541
542     webView->priv->children.set(child, childRect);
543     gtk_widget_queue_resize_no_redraw(GTK_WIDGET(webView));
544 }
545
546 #if GTK_CHECK_VERSION(3, 24, 0)
547 static void webkitWebViewBaseCompleteEmojiChooserRequest(WebKitWebViewBase* webView, const String& text)
548 {
549     if (auto completionHandler = std::exchange(webView->priv->emojiChooserCompletionHandler, nullptr))
550         completionHandler(text);
551 }
552 #endif
553
554 static void webkitWebViewBaseDispose(GObject* gobject)
555 {
556     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(gobject);
557     webkitWebViewBaseSetToplevelOnScreenWindow(webView, nullptr);
558     if (webView->priv->accessible)
559         webkitWebViewAccessibleSetWebView(WEBKIT_WEB_VIEW_ACCESSIBLE(webView->priv->accessible.get()), nullptr);
560 #if GTK_CHECK_VERSION(3, 24, 0)
561     webkitWebViewBaseCompleteEmojiChooserRequest(webView, emptyString());
562 #endif
563     webView->priv->pageProxy->close();
564     webView->priv->acceleratedBackingStore = nullptr;
565     webView->priv->sleepDisabler = nullptr;
566     G_OBJECT_CLASS(webkit_web_view_base_parent_class)->dispose(gobject);
567 }
568
569 static void webkitWebViewBaseConstructed(GObject* object)
570 {
571     G_OBJECT_CLASS(webkit_web_view_base_parent_class)->constructed(object);
572
573     GtkWidget* viewWidget = GTK_WIDGET(object);
574     gtk_widget_set_can_focus(viewWidget, TRUE);
575     gtk_drag_dest_set(viewWidget, static_cast<GtkDestDefaults>(0), nullptr, 0,
576         static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE));
577     gtk_drag_dest_set_target_list(viewWidget, PasteboardHelper::singleton().targetList());
578
579     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(object)->priv;
580     priv->pageClient = std::make_unique<PageClientImpl>(viewWidget);
581     priv->dialog = nullptr;
582 }
583
584 static gboolean webkitWebViewBaseDraw(GtkWidget* widget, cairo_t* cr)
585 {
586     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
587     auto* drawingArea = static_cast<DrawingAreaProxyCoordinatedGraphics*>(webViewBase->priv->pageProxy->drawingArea());
588     if (!drawingArea)
589         return FALSE;
590
591     GdkRectangle clipRect;
592     if (!gdk_cairo_get_clip_rectangle(cr, &clipRect))
593         return FALSE;
594
595     bool showingNavigationSnapshot = webViewBase->priv->pageProxy->isShowingNavigationGestureSnapshot();
596     if (showingNavigationSnapshot)
597         cairo_push_group(cr);
598
599     if (drawingArea->isInAcceleratedCompositingMode())
600         webViewBase->priv->acceleratedBackingStore->paint(cr, clipRect);
601     else {
602         WebCore::Region unpaintedRegion; // This is simply unused.
603         drawingArea->paint(cr, clipRect, unpaintedRegion);
604     }
605
606     if (showingNavigationSnapshot) {
607         RefPtr<cairo_pattern_t> group = adoptRef(cairo_pop_group(cr));
608         if (auto* controller = webkitWebViewBaseViewGestureController(webViewBase))
609             controller->draw(cr, group.get());
610     }
611
612     GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->draw(widget, cr);
613
614     return FALSE;
615 }
616
617 static void webkitWebViewBaseChildAllocate(GtkWidget* child, gpointer userData)
618 {
619     if (!gtk_widget_get_visible(child))
620         return;
621
622     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(userData);
623     WebKitWebViewBasePrivate* priv = webViewBase->priv;
624     const IntRect& geometry = priv->children.get(child);
625     if (geometry.isEmpty())
626         return;
627
628     GtkAllocation childAllocation = geometry;
629     gtk_widget_size_allocate(child, &childAllocation);
630     priv->children.set(child, IntRect());
631 }
632
633 static void webkitWebViewBaseSizeAllocate(GtkWidget* widget, GtkAllocation* allocation)
634 {
635     GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->size_allocate(widget, allocation);
636
637     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
638     gtk_container_foreach(GTK_CONTAINER(webViewBase), webkitWebViewBaseChildAllocate, webViewBase);
639
640     IntRect viewRect(allocation->x, allocation->y, allocation->width, allocation->height);
641     WebKitWebViewBasePrivate* priv = webViewBase->priv;
642     if (priv->inspectorView) {
643         GtkAllocation childAllocation = viewRect;
644
645         if (priv->inspectorAttachmentSide == AttachmentSide::Bottom) {
646             int inspectorViewHeight = std::min(static_cast<int>(priv->inspectorViewSize), allocation->height);
647             childAllocation.x = 0;
648             childAllocation.y = allocation->height - inspectorViewHeight;
649             childAllocation.height = inspectorViewHeight;
650             viewRect.setHeight(std::max(allocation->height - inspectorViewHeight, 1));
651         } else {
652             int inspectorViewWidth = std::min(static_cast<int>(priv->inspectorViewSize), allocation->width);
653             childAllocation.y = 0;
654             childAllocation.x = allocation->width - inspectorViewWidth;
655             childAllocation.width = inspectorViewWidth;
656             viewRect.setWidth(std::max(allocation->width - inspectorViewWidth, 1));
657         }
658
659         gtk_widget_size_allocate(priv->inspectorView, &childAllocation);
660     }
661
662     // The dialogs are centered in the view rect, which means that it
663     // never overlaps the web inspector. Thus, we need to calculate the allocation here
664     // after calculating the inspector allocation.
665     if (priv->dialog) {
666         GtkRequisition minimumSize;
667         gtk_widget_get_preferred_size(priv->dialog, &minimumSize, nullptr);
668
669         GtkAllocation childAllocation = { 0, 0, std::max(minimumSize.width, viewRect.width()), std::max(minimumSize.height, viewRect.height()) };
670         gtk_widget_size_allocate(priv->dialog, &childAllocation);
671     }
672
673     if (auto* drawingArea = static_cast<DrawingAreaProxyCoordinatedGraphics*>(priv->pageProxy->drawingArea()))
674         drawingArea->setSize(viewRect.size());
675 }
676
677 static void webkitWebViewBaseGetPreferredWidth(GtkWidget* widget, gint* minimumSize, gint* naturalSize)
678 {
679     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
680     *minimumSize = 0;
681     *naturalSize = priv->contentsSize.width();
682 }
683
684 static void webkitWebViewBaseGetPreferredHeight(GtkWidget* widget, gint* minimumSize, gint* naturalSize)
685 {
686     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
687     *minimumSize = 0;
688     *naturalSize = priv->contentsSize.height();
689 }
690
691 static void webkitWebViewBaseMap(GtkWidget* widget)
692 {
693     GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->map(widget);
694
695     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
696     WebKitWebViewBasePrivate* priv = webViewBase->priv;
697     OptionSet<ActivityState::Flag> flagsToUpdate;
698     if (!(priv->activityState & ActivityState::IsVisible))
699         flagsToUpdate.add(ActivityState::IsVisible);
700     if (priv->toplevelOnScreenWindow) {
701         if (!(priv->activityState & ActivityState::IsInWindow))
702             flagsToUpdate.add(ActivityState::IsInWindow);
703         if (gtk_window_is_active(GTK_WINDOW(priv->toplevelOnScreenWindow)) && !(priv->activityState & ActivityState::WindowIsActive))
704             flagsToUpdate.add(ActivityState::WindowIsActive);
705     }
706     if (!flagsToUpdate)
707         return;
708
709     priv->activityState.add(flagsToUpdate);
710     webkitWebViewBaseScheduleUpdateActivityState(webViewBase, flagsToUpdate);
711 }
712
713 static void webkitWebViewBaseUnmap(GtkWidget* widget)
714 {
715     GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->unmap(widget);
716
717     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
718     WebKitWebViewBasePrivate* priv = webViewBase->priv;
719     if (!(priv->activityState & ActivityState::IsVisible))
720         return;
721
722     priv->activityState.remove(ActivityState::IsVisible);
723     webkitWebViewBaseScheduleUpdateActivityState(webViewBase, ActivityState::IsVisible);
724 }
725
726 static gboolean webkitWebViewBaseFocusInEvent(GtkWidget* widget, GdkEventFocus* event)
727 {
728     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
729     webkitWebViewBaseSetFocus(webViewBase, true);
730     webViewBase->priv->inputMethodFilter.notifyFocusedIn();
731
732     return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->focus_in_event(widget, event);
733 }
734
735 static gboolean webkitWebViewBaseFocusOutEvent(GtkWidget* widget, GdkEventFocus* event)
736 {
737     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
738     webkitWebViewBaseSetFocus(webViewBase, false);
739     webViewBase->priv->inputMethodFilter.notifyFocusedOut();
740
741     return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->focus_out_event(widget, event);
742 }
743
744 static gboolean webkitWebViewBaseKeyPressEvent(GtkWidget* widget, GdkEventKey* keyEvent)
745 {
746     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
747     WebKitWebViewBasePrivate* priv = webViewBase->priv;
748
749     GdkModifierType state;
750     guint keyval;
751     gdk_event_get_state(reinterpret_cast<GdkEvent*>(keyEvent), &state);
752     gdk_event_get_keyval(reinterpret_cast<GdkEvent*>(keyEvent), &keyval);
753
754 #if ENABLE(DEVELOPER_MODE) && OS(LINUX)
755     if ((state & GDK_CONTROL_MASK) && (state & GDK_SHIFT_MASK) && keyval == GDK_KEY_G) {
756         auto& preferences = priv->pageProxy->preferences();
757         preferences.setResourceUsageOverlayVisible(!preferences.resourceUsageOverlayVisible());
758         priv->shouldForwardNextKeyEvent = FALSE;
759         return GDK_EVENT_STOP;
760     }
761 #endif
762
763     if (priv->dialog)
764         return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->key_press_event(widget, keyEvent);
765
766 #if ENABLE(FULLSCREEN_API)
767     if (priv->fullScreenModeActive) {
768         switch (keyval) {
769         case GDK_KEY_Escape:
770         case GDK_KEY_f:
771         case GDK_KEY_F:
772             priv->pageProxy->fullScreenManager()->requestExitFullScreen();
773             return GDK_EVENT_STOP;
774         default:
775             break;
776         }
777     }
778 #endif
779
780     // Since WebProcess key event handling is not synchronous, handle the event in two passes.
781     // When WebProcess processes the input event, it will call PageClientImpl::doneWithKeyEvent
782     // with event handled status which determines whether to pass the input event to parent or not
783     // using gtk_main_do_event().
784     if (priv->shouldForwardNextKeyEvent) {
785         priv->shouldForwardNextKeyEvent = FALSE;
786         return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->key_press_event(widget, keyEvent);
787     }
788
789     // We need to copy the event as otherwise it could be destroyed before we reach the lambda body.
790     GUniquePtr<GdkEvent> event(gdk_event_copy(reinterpret_cast<GdkEvent*>(keyEvent)));
791     priv->inputMethodFilter.filterKeyEvent(keyEvent, [priv, event = WTFMove(event)](const WebCore::CompositionResults& compositionResults, InputMethodFilter::EventFakedForComposition faked) {
792         priv->pageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(event.get(), compositionResults, faked,
793             !compositionResults.compositionUpdated() ? priv->keyBindingTranslator.commandsForKeyEvent(&event->key) : Vector<String>()));
794     });
795
796     return GDK_EVENT_STOP;
797 }
798
799 static gboolean webkitWebViewBaseKeyReleaseEvent(GtkWidget* widget, GdkEventKey* keyEvent)
800 {
801     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
802     WebKitWebViewBasePrivate* priv = webViewBase->priv;
803
804     if (priv->shouldForwardNextKeyEvent) {
805         priv->shouldForwardNextKeyEvent = FALSE;
806         return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->key_release_event(widget, keyEvent);
807     }
808
809     // We need to copy the event as otherwise it could be destroyed before we reach the lambda body.
810     GUniquePtr<GdkEvent> event(gdk_event_copy(reinterpret_cast<GdkEvent*>(keyEvent)));
811     priv->inputMethodFilter.filterKeyEvent(keyEvent, [priv, event = WTFMove(event)](const WebCore::CompositionResults& compositionResults, InputMethodFilter::EventFakedForComposition faked) {
812         priv->pageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(event.get(), compositionResults, faked, { }));
813     });
814
815     return GDK_EVENT_STOP;
816 }
817
818 static void webkitWebViewBaseHandleMouseEvent(WebKitWebViewBase* webViewBase, GdkEvent* event)
819 {
820     WebKitWebViewBasePrivate* priv = webViewBase->priv;
821     ASSERT(!priv->dialog);
822
823     int clickCount = 0;
824 #if GTK_CHECK_VERSION(3, 10, 0)
825     GdkEventType eventType = gdk_event_get_event_type(event);
826 #else
827     GdkEventType eventType = event->type;
828 #endif
829     switch (eventType) {
830     case GDK_BUTTON_PRESS:
831     case GDK_2BUTTON_PRESS:
832     case GDK_3BUTTON_PRESS: {
833         // For double and triple clicks GDK sends both a normal button press event
834         // and a specific type (like GDK_2BUTTON_PRESS). If we detect a special press
835         // coming up, ignore this event as it certainly generated the double or triple
836         // click. The consequence of not eating this event is two DOM button press events
837         // are generated.
838         GUniquePtr<GdkEvent> nextEvent(gdk_event_peek());
839         if (nextEvent && (nextEvent->any.type == GDK_2BUTTON_PRESS || nextEvent->any.type == GDK_3BUTTON_PRESS))
840             return;
841
842         priv->inputMethodFilter.notifyMouseButtonPress();
843
844         guint button;
845         gdk_event_get_button(event, &button);
846         // If it's a right click event save it as a possible context menu event.
847         if (button == GDK_BUTTON_SECONDARY)
848             priv->contextMenuEvent.reset(gdk_event_copy(event));
849
850         clickCount = priv->clickCounter.currentClickCountForGdkButtonEvent(event);
851     }
852         FALLTHROUGH;
853     case GDK_BUTTON_RELEASE:
854         gtk_widget_grab_focus(GTK_WIDGET(webViewBase));
855         break;
856     case GDK_MOTION_NOTIFY:
857     case GDK_ENTER_NOTIFY:
858     case GDK_LEAVE_NOTIFY:
859         break;
860     default:
861         ASSERT_NOT_REACHED();
862     }
863
864     priv->pageProxy->handleMouseEvent(NativeWebMouseEvent(event, clickCount));
865 }
866
867 static gboolean webkitWebViewBaseButtonPressEvent(GtkWidget* widget, GdkEventButton* event)
868 {
869     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
870     WebKitWebViewBasePrivate* priv = webViewBase->priv;
871
872     if (priv->dialog)
873         return GDK_EVENT_STOP;
874
875     webkitWebViewBaseHandleMouseEvent(webViewBase, reinterpret_cast<GdkEvent*>(event));
876
877     return GDK_EVENT_STOP;
878 }
879
880 static gboolean webkitWebViewBaseButtonReleaseEvent(GtkWidget* widget, GdkEventButton* event)
881 {
882     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
883     WebKitWebViewBasePrivate* priv = webViewBase->priv;
884
885     if (priv->dialog)
886         return GDK_EVENT_STOP;
887
888     webkitWebViewBaseHandleMouseEvent(webViewBase, reinterpret_cast<GdkEvent*>(event));
889
890     return GDK_EVENT_STOP;
891 }
892
893 static void webkitWebViewBaseHandleWheelEvent(WebKitWebViewBase* webViewBase, GdkEvent* event, Optional<WebWheelEvent::Phase> phase = WTF::nullopt, Optional<WebWheelEvent::Phase> momentum = WTF::nullopt)
894 {
895     ViewGestureController* controller = webkitWebViewBaseViewGestureController(webViewBase);
896     if (controller && controller->isSwipeGestureEnabled() && controller->handleScrollWheelEvent(reinterpret_cast<GdkEventScroll*>(event)))
897         return;
898
899     WebKitWebViewBasePrivate* priv = webViewBase->priv;
900     ASSERT(!priv->dialog);
901     if (phase)
902         priv->pageProxy->handleWheelEvent(NativeWebWheelEvent(event, phase.value(), momentum.valueOr(WebWheelEvent::Phase::PhaseNone)));
903     else
904         priv->pageProxy->handleWheelEvent(NativeWebWheelEvent(event));
905 }
906
907 static gboolean webkitWebViewBaseScrollEvent(GtkWidget* widget, GdkEventScroll* event)
908 {
909     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
910     WebKitWebViewBasePrivate* priv = webViewBase->priv;
911
912     if (std::exchange(priv->shouldForwardNextWheelEvent, false))
913         return GDK_EVENT_PROPAGATE;
914
915     if (priv->dialog)
916         return GDK_EVENT_PROPAGATE;
917
918     // Shift+Wheel scrolls in the perpendicular direction.
919     if (event->state & GDK_SHIFT_MASK) {
920         switch (event->direction) {
921         case GDK_SCROLL_UP:
922             event->direction = GDK_SCROLL_LEFT;
923             break;
924         case GDK_SCROLL_LEFT:
925             event->direction = GDK_SCROLL_UP;
926             break;
927         case GDK_SCROLL_DOWN:
928             event->direction = GDK_SCROLL_RIGHT;
929             break;
930         case GDK_SCROLL_RIGHT:
931             event->direction = GDK_SCROLL_DOWN;
932             break;
933         case GDK_SCROLL_SMOOTH:
934             std::swap(event->delta_x, event->delta_y);
935             break;
936         }
937     }
938
939     webkitWebViewBaseHandleWheelEvent(webViewBase, reinterpret_cast<GdkEvent*>(event));
940
941     return GDK_EVENT_STOP;
942 }
943
944 static gboolean webkitWebViewBasePopupMenu(GtkWidget* widget)
945 {
946     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
947     WebKitWebViewBasePrivate* priv = webViewBase->priv;
948
949     GdkEvent* currentEvent = gtk_get_current_event();
950     if (!currentEvent)
951         currentEvent = gdk_event_new(GDK_NOTHING);
952     priv->contextMenuEvent.reset(currentEvent);
953     priv->pageProxy->handleContextMenuKeyEvent();
954
955     return TRUE;
956 }
957
958 static gboolean webkitWebViewBaseMotionNotifyEvent(GtkWidget* widget, GdkEventMotion* event)
959 {
960     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
961     WebKitWebViewBasePrivate* priv = webViewBase->priv;
962
963     if (priv->dialog) {
964         auto* widgetClass = GTK_WIDGET_CLASS(webkit_web_view_base_parent_class);
965         return widgetClass->motion_notify_event ? widgetClass->motion_notify_event(widget, event) : GDK_EVENT_PROPAGATE;
966     }
967
968     webkitWebViewBaseHandleMouseEvent(webViewBase, reinterpret_cast<GdkEvent*>(event));
969
970     return GDK_EVENT_PROPAGATE;
971 }
972
973 static gboolean webkitWebViewBaseCrossingNotifyEvent(GtkWidget* widget, GdkEventCrossing* crossingEvent)
974 {
975     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
976     WebKitWebViewBasePrivate* priv = webViewBase->priv;
977
978     if (priv->dialog)
979         return GDK_EVENT_PROPAGATE;
980
981 #if ENABLE(DEVELOPER_MODE)
982     // Do not send mouse move events to the WebProcess for crossing events during testing.
983     // WTR never generates crossing events and they can confuse tests.
984     // https://bugs.webkit.org/show_bug.cgi?id=185072.
985     if (UNLIKELY(priv->pageProxy->process().processPool().configuration().fullySynchronousModeIsAllowedForTesting()))
986         return GDK_EVENT_PROPAGATE;
987 #endif
988
989     // In the case of crossing events, it's very important the actual coordinates the WebProcess receives, because once the mouse leaves
990     // the web view, the WebProcess won't receive more events until the mouse enters again in the web view. So, if the coordinates of the leave
991     // event are not accurate, the WebProcess might not know the mouse left the view. This can happen because of double to integer conversion,
992     // if the coordinates of the leave event are for example (25.2, -0.9), the WebProcess will receive (25, 0) and any hit test will succeed
993     // because those coordinates are inside the web view.
994     GtkAllocation allocation;
995     gtk_widget_get_allocation(widget, &allocation);
996     double xEvent, yEvent;
997     gdk_event_get_coords(reinterpret_cast<GdkEvent*>(crossingEvent), &xEvent, &yEvent);
998     double width = allocation.width;
999     double height = allocation.height;
1000     double x = xEvent;
1001     double y = yEvent;
1002     if (x < 0 && x > -1)
1003         x = -1;
1004     else if (x >= width && x < width + 1)
1005         x = width + 1;
1006     if (y < 0 && y > -1)
1007         y = -1;
1008     else if (y >= height && y < height + 1)
1009         y = height + 1;
1010
1011     GdkEvent* event = reinterpret_cast<GdkEvent*>(crossingEvent);
1012     GUniquePtr<GdkEvent> copiedEvent;
1013     if (x != xEvent || y != yEvent) {
1014         copiedEvent.reset(gdk_event_copy(event));
1015         copiedEvent->crossing.x = x;
1016         copiedEvent->crossing.y = y;
1017     }
1018
1019     webkitWebViewBaseHandleMouseEvent(webViewBase, copiedEvent ? copiedEvent.get() : event);
1020
1021     return GDK_EVENT_PROPAGATE;
1022 }
1023
1024 #if ENABLE(TOUCH_EVENTS)
1025 static void appendTouchEvent(Vector<WebPlatformTouchPoint>& touchPoints, const GdkEvent* event, WebPlatformTouchPoint::TouchPointState state)
1026 {
1027     gdouble x, y;
1028     gdk_event_get_coords(event, &x, &y);
1029
1030     gdouble xRoot, yRoot;
1031     gdk_event_get_root_coords(event, &xRoot, &yRoot);
1032
1033     uint32_t identifier = GPOINTER_TO_UINT(gdk_event_get_event_sequence(event));
1034     touchPoints.uncheckedAppend(WebPlatformTouchPoint(identifier, state, IntPoint(xRoot, yRoot), IntPoint(x, y)));
1035 }
1036
1037 static inline WebPlatformTouchPoint::TouchPointState touchPointStateForEvents(const GdkEvent* current, const GdkEvent* event)
1038 {
1039     if (gdk_event_get_event_sequence(current) != gdk_event_get_event_sequence(event))
1040         return WebPlatformTouchPoint::TouchStationary;
1041
1042     switch (current->type) {
1043     case GDK_TOUCH_UPDATE:
1044         return WebPlatformTouchPoint::TouchMoved;
1045     case GDK_TOUCH_BEGIN:
1046         return WebPlatformTouchPoint::TouchPressed;
1047     case GDK_TOUCH_END:
1048         return WebPlatformTouchPoint::TouchReleased;
1049     case GDK_TOUCH_CANCEL:
1050         return WebPlatformTouchPoint::TouchCancelled;
1051     default:
1052         return WebPlatformTouchPoint::TouchStationary;
1053     }
1054 }
1055
1056 static void webkitWebViewBaseGetTouchPointsForEvent(WebKitWebViewBase* webViewBase, GdkEvent* event, Vector<WebPlatformTouchPoint>& touchPoints)
1057 {
1058     WebKitWebViewBasePrivate* priv = webViewBase->priv;
1059 #if GTK_CHECK_VERSION(3, 10, 0)
1060     GdkEventType type = gdk_event_get_event_type(event);
1061 #else
1062     GdkEventType type = event->type;
1063 #endif
1064     bool touchEnd = (type == GDK_TOUCH_END) || (type == GDK_TOUCH_CANCEL);
1065     touchPoints.reserveInitialCapacity(touchEnd ? priv->touchEvents.size() + 1 : priv->touchEvents.size());
1066
1067     for (const auto& it : priv->touchEvents)
1068         appendTouchEvent(touchPoints, it.value.get(), touchPointStateForEvents(it.value.get(), event));
1069
1070     // Touch was already removed from the TouchEventsMap, add it here.
1071     if (touchEnd)
1072         appendTouchEvent(touchPoints, event, WebPlatformTouchPoint::TouchReleased);
1073 }
1074
1075 static gboolean webkitWebViewBaseTouchEvent(GtkWidget* widget, GdkEventTouch* event)
1076 {
1077     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
1078     WebKitWebViewBasePrivate* priv = webViewBase->priv;
1079
1080     if (priv->dialog)
1081         return GDK_EVENT_STOP;
1082
1083     GdkEvent* touchEvent = reinterpret_cast<GdkEvent*>(event);
1084     uint32_t sequence = GPOINTER_TO_UINT(gdk_event_get_event_sequence(touchEvent));
1085
1086 #if GTK_CHECK_VERSION(3, 10, 0)
1087     GdkEventType type = gdk_event_get_event_type(touchEvent);
1088 #else
1089     GdkEventType type = touchEvent->type
1090 #endif
1091     switch (type) {
1092     case GDK_TOUCH_BEGIN: {
1093         ASSERT(!priv->touchEvents.contains(sequence));
1094         GUniquePtr<GdkEvent> event(gdk_event_copy(touchEvent));
1095         priv->touchEvents.add(sequence, WTFMove(event));
1096         break;
1097     }
1098     case GDK_TOUCH_UPDATE: {
1099         auto it = priv->touchEvents.find(sequence);
1100         ASSERT(it != priv->touchEvents.end());
1101         it->value.reset(gdk_event_copy(touchEvent));
1102         break;
1103     }
1104     case GDK_TOUCH_CANCEL:
1105         FALLTHROUGH;
1106     case GDK_TOUCH_END:
1107         ASSERT(priv->touchEvents.contains(sequence));
1108         priv->touchEvents.remove(sequence);
1109         break;
1110     default:
1111         break;
1112     }
1113
1114     Vector<WebPlatformTouchPoint> touchPoints;
1115     webkitWebViewBaseGetTouchPointsForEvent(webViewBase, touchEvent, touchPoints);
1116     priv->pageProxy->handleTouchEvent(NativeWebTouchEvent(reinterpret_cast<GdkEvent*>(event), WTFMove(touchPoints)));
1117
1118     return GDK_EVENT_STOP;
1119 }
1120 #endif // ENABLE(TOUCH_EVENTS)
1121
1122 #if HAVE(GTK_GESTURES)
1123 class TouchGestureController final : public GestureControllerClient {
1124     WTF_MAKE_FAST_ALLOCATED;
1125
1126 public:
1127     explicit TouchGestureController(WebKitWebViewBase* webViewBase)
1128         : m_webView(webViewBase)
1129     {
1130     }
1131
1132 private:
1133     static GUniquePtr<GdkEvent> createScrollEvent(GdkEventTouch* event, const FloatPoint& point, const FloatPoint& delta, bool isStop = false)
1134     {
1135         GUniquePtr<GdkEvent> scrollEvent(gdk_event_new(GDK_SCROLL));
1136         scrollEvent->scroll.time = event->time;
1137         scrollEvent->scroll.x = point.x();
1138         scrollEvent->scroll.y = point.y();
1139         scrollEvent->scroll.x_root = event->x_root;
1140         scrollEvent->scroll.y_root = event->y_root;
1141         scrollEvent->scroll.direction = GDK_SCROLL_SMOOTH;
1142         scrollEvent->scroll.delta_x = delta.x();
1143         scrollEvent->scroll.delta_y = delta.y();
1144         scrollEvent->scroll.state = event->state;
1145 #if GTK_CHECK_VERSION(3, 20, 0)
1146         scrollEvent->scroll.is_stop = isStop;
1147 #else
1148         UNUSED_PARAM(isStop);
1149 #endif
1150         scrollEvent->scroll.window = event->window ? GDK_WINDOW(g_object_ref(event->window)) : nullptr;
1151         auto* touchEvent = reinterpret_cast<GdkEvent*>(event);
1152         gdk_event_set_screen(scrollEvent.get(), gdk_event_get_screen(touchEvent));
1153         gdk_event_set_device(scrollEvent.get(), gdk_event_get_device(touchEvent));
1154         gdk_event_set_source_device(scrollEvent.get(), gdk_event_get_source_device(touchEvent));
1155         return scrollEvent;
1156     }
1157
1158     void simulateMouseClick(GdkEventTouch* event, unsigned button)
1159     {
1160         GUniquePtr<GdkEvent> pointerEvent(gdk_event_new(GDK_MOTION_NOTIFY));
1161         pointerEvent->motion.time = event->time;
1162         pointerEvent->motion.x = event->x;
1163         pointerEvent->motion.y = event->y;
1164         pointerEvent->motion.x_root = event->x_root;
1165         pointerEvent->motion.y_root = event->y_root;
1166         pointerEvent->motion.state = event->state;
1167         pointerEvent->motion.window = event->window ? GDK_WINDOW(g_object_ref(event->window)) : nullptr;
1168         auto* touchEvent = reinterpret_cast<GdkEvent*>(event);
1169         gdk_event_set_screen(pointerEvent.get(), gdk_event_get_screen(touchEvent));
1170         gdk_event_set_device(pointerEvent.get(), gdk_event_get_device(touchEvent));
1171         gdk_event_set_source_device(pointerEvent.get(), gdk_event_get_source_device(touchEvent));
1172         webkitWebViewBaseHandleMouseEvent(m_webView, pointerEvent.get());
1173
1174         pointerEvent.reset(gdk_event_new(GDK_BUTTON_PRESS));
1175         pointerEvent->button.button = button;
1176         pointerEvent->button.time = event->time;
1177         pointerEvent->button.x = event->x;
1178         pointerEvent->button.y = event->y;
1179         pointerEvent->button.x_root = event->x_root;
1180         pointerEvent->button.y_root = event->y_root;
1181         pointerEvent->button.window = event->window ? GDK_WINDOW(g_object_ref(event->window)) : nullptr;
1182         gdk_event_set_screen(pointerEvent.get(), gdk_event_get_screen(touchEvent));
1183         gdk_event_set_device(pointerEvent.get(), gdk_event_get_device(touchEvent));
1184         gdk_event_set_source_device(pointerEvent.get(), gdk_event_get_source_device(touchEvent));
1185         webkitWebViewBaseHandleMouseEvent(m_webView, pointerEvent.get());
1186
1187         pointerEvent->type = GDK_BUTTON_RELEASE;
1188         webkitWebViewBaseHandleMouseEvent(m_webView, pointerEvent.get());
1189     }
1190
1191     void tap(GdkEventTouch* event) final
1192     {
1193         simulateMouseClick(event, GDK_BUTTON_PRIMARY);
1194     }
1195
1196     void startDrag(GdkEventTouch* event, const FloatPoint& startPoint) final
1197     {
1198         GUniquePtr<GdkEvent> scrollEvent = createScrollEvent(event, startPoint, { });
1199         webkitWebViewBaseHandleWheelEvent(m_webView, scrollEvent.get(), WebWheelEvent::Phase::PhaseBegan);
1200     }
1201
1202     void drag(GdkEventTouch* event, const FloatPoint& point, const FloatPoint& delta) final
1203     {
1204         GUniquePtr<GdkEvent> scrollEvent = createScrollEvent(event, point, delta);
1205         webkitWebViewBaseHandleWheelEvent(m_webView, scrollEvent.get(), WebWheelEvent::Phase::PhaseChanged);
1206     }
1207
1208     void swipe(GdkEventTouch* event, const FloatPoint& velocity) final
1209     {
1210         double x, y;
1211         gdk_event_get_coords(reinterpret_cast<GdkEvent*>(event), &x, &y);
1212         GUniquePtr<GdkEvent> scrollEvent = createScrollEvent(event, FloatPoint::narrowPrecision(x, y), velocity, true);
1213         webkitWebViewBaseHandleWheelEvent(m_webView, scrollEvent.get(), WebWheelEvent::Phase::PhaseNone, WebWheelEvent::Phase::PhaseBegan);
1214     }
1215
1216     void startZoom(const IntPoint& center, double& initialScale, IntPoint& initialPoint) final
1217     {
1218         auto* page = m_webView->priv->pageProxy.get();
1219         ASSERT(page);
1220         initialScale = page->pageScaleFactor();
1221         page->getCenterForZoomGesture(center, initialPoint);
1222     }
1223
1224     void zoom(double scale, const IntPoint& origin) final
1225     {
1226         auto* page = m_webView->priv->pageProxy.get();
1227         ASSERT(page);
1228
1229         page->scalePage(scale, origin);
1230     }
1231
1232     void longPress(GdkEventTouch* event) final
1233     {
1234         simulateMouseClick(event, GDK_BUTTON_SECONDARY);
1235     }
1236
1237     WebKitWebViewBase* m_webView;
1238 };
1239
1240 GestureController& webkitWebViewBaseGestureController(WebKitWebViewBase* webViewBase)
1241 {
1242     WebKitWebViewBasePrivate* priv = webViewBase->priv;
1243     if (!priv->gestureController)
1244         priv->gestureController = std::make_unique<GestureController>(GTK_WIDGET(webViewBase), std::make_unique<TouchGestureController>(webViewBase));
1245     return *priv->gestureController;
1246 }
1247 #endif
1248
1249 void webkitWebViewBaseSetEnableBackForwardNavigationGesture(WebKitWebViewBase* webViewBase, bool enabled)
1250 {
1251     WebKitWebViewBasePrivate* priv = webViewBase->priv;
1252
1253     priv->isBackForwardNavigationGestureEnabled = enabled;
1254
1255     if (auto* controller = webkitWebViewBaseViewGestureController(webViewBase))
1256         controller->setSwipeGestureEnabled(enabled);
1257
1258     priv->pageProxy->setShouldRecordNavigationSnapshots(enabled);
1259 }
1260
1261 ViewGestureController* webkitWebViewBaseViewGestureController(WebKitWebViewBase* webViewBase)
1262 {
1263     return webViewBase->priv->viewGestureController.get();
1264 }
1265
1266 bool webkitWebViewBaseBeginBackSwipeForTesting(WebKitWebViewBase* webViewBase)
1267 {
1268     if (auto* gestureController = webkitWebViewBaseViewGestureController(webViewBase))
1269         return gestureController->beginSimulatedSwipeInDirectionForTesting(ViewGestureController::SwipeDirection::Back);
1270
1271     return FALSE;
1272 }
1273
1274 bool webkitWebViewBaseCompleteBackSwipeForTesting(WebKitWebViewBase* webViewBase)
1275 {
1276     if (auto* gestureController = webkitWebViewBaseViewGestureController(webViewBase))
1277         return gestureController->completeSimulatedSwipeInDirectionForTesting(ViewGestureController::SwipeDirection::Back);
1278
1279     return FALSE;
1280 }
1281
1282 static gboolean webkitWebViewBaseQueryTooltip(GtkWidget* widget, gint /* x */, gint /* y */, gboolean keyboardMode, GtkTooltip* tooltip)
1283 {
1284     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
1285
1286     if (keyboardMode) {
1287         // TODO: https://bugs.webkit.org/show_bug.cgi?id=61732.
1288         notImplemented();
1289         return FALSE;
1290     }
1291
1292     if (priv->tooltipText.length() <= 0)
1293         return FALSE;
1294
1295     if (!priv->tooltipArea.isEmpty()) {
1296         GdkRectangle area = priv->tooltipArea;
1297         gtk_tooltip_set_tip_area(tooltip, &area);
1298     } else
1299         gtk_tooltip_set_tip_area(tooltip, 0);
1300     gtk_tooltip_set_text(tooltip, priv->tooltipText.data());
1301
1302     return TRUE;
1303 }
1304
1305 #if ENABLE(DRAG_SUPPORT)
1306 static void webkitWebViewBaseDragDataGet(GtkWidget* widget, GdkDragContext* context, GtkSelectionData* selectionData, guint info, guint /* time */)
1307 {
1308     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
1309     ASSERT(priv->dragAndDropHandler);
1310     priv->dragAndDropHandler->fillDragData(context, selectionData, info);
1311 }
1312
1313 static void webkitWebViewBaseDragEnd(GtkWidget* widget, GdkDragContext* context)
1314 {
1315     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
1316     ASSERT(priv->dragAndDropHandler);
1317     priv->dragAndDropHandler->finishDrag(context);
1318 }
1319
1320 static void webkitWebViewBaseDragDataReceived(GtkWidget* widget, GdkDragContext* context, gint /* x */, gint /* y */, GtkSelectionData* selectionData, guint info, guint time)
1321 {
1322     webkitWebViewBaseDragAndDropHandler(WEBKIT_WEB_VIEW_BASE(widget)).dragEntered(context, selectionData, info, time);
1323 }
1324 #endif // ENABLE(DRAG_SUPPORT)
1325
1326 static gboolean webkitWebViewBaseEvent(GtkWidget* widget, GdkEvent* event)
1327 {
1328 #if HAVE(GTK_GESTURES)
1329 #if GTK_CHECK_VERSION(3, 10, 0)
1330     if (gdk_event_get_event_type(event) == GDK_TOUCHPAD_PINCH)
1331 #else
1332     if (event->type == GDK_TOUCHPAD_PINCH)
1333 #endif // GTK_CHECK_VERSION(3, 10, 0)
1334         webkitWebViewBaseGestureController(WEBKIT_WEB_VIEW_BASE(widget)).handleEvent(event);
1335 #endif // HAVE(GTK_GESTURES)
1336
1337     return GDK_EVENT_PROPAGATE;
1338 }
1339
1340 static AtkObject* webkitWebViewBaseGetAccessible(GtkWidget* widget)
1341 {
1342     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
1343     if (!priv->accessible) {
1344         // Create the accessible object and associate it to the widget.
1345         priv->accessible = adoptGRef(ATK_OBJECT(webkitWebViewAccessibleNew(widget)));
1346
1347         // Set the parent to not break bottom-up navigation.
1348         if (auto* parentWidget = gtk_widget_get_parent(widget)) {
1349             if (auto* axParent = gtk_widget_get_accessible(parentWidget))
1350                 atk_object_set_parent(priv->accessible.get(), axParent);
1351         }
1352     }
1353
1354     return priv->accessible.get();
1355 }
1356
1357 #if ENABLE(DRAG_SUPPORT)
1358 static gboolean webkitWebViewBaseDragMotion(GtkWidget* widget, GdkDragContext* context, gint x, gint y, guint time)
1359 {
1360     webkitWebViewBaseDragAndDropHandler(WEBKIT_WEB_VIEW_BASE(widget)).dragMotion(context, IntPoint(x, y), time);
1361     return TRUE;
1362 }
1363
1364 static void webkitWebViewBaseDragLeave(GtkWidget* widget, GdkDragContext* context, guint /* time */)
1365 {
1366     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
1367     ASSERT(priv->dragAndDropHandler);
1368     priv->dragAndDropHandler->dragLeave(context);
1369 }
1370
1371 static gboolean webkitWebViewBaseDragDrop(GtkWidget* widget, GdkDragContext* context, gint x, gint y, guint time)
1372 {
1373     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
1374     ASSERT(priv->dragAndDropHandler);
1375     return priv->dragAndDropHandler->drop(context, IntPoint(x, y), time);
1376 }
1377 #endif // ENABLE(DRAG_SUPPORT)
1378
1379 static void webkitWebViewBaseHierarchyChanged(GtkWidget* widget, GtkWidget* oldToplevel)
1380 {
1381     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
1382     if (widgetIsOnscreenToplevelWindow(oldToplevel) && GTK_WINDOW(oldToplevel) == priv->toplevelOnScreenWindow) {
1383         webkitWebViewBaseSetToplevelOnScreenWindow(WEBKIT_WEB_VIEW_BASE(widget), nullptr);
1384         return;
1385     }
1386
1387     if (!oldToplevel) {
1388         GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
1389         if (widgetIsOnscreenToplevelWindow(toplevel))
1390             webkitWebViewBaseSetToplevelOnScreenWindow(WEBKIT_WEB_VIEW_BASE(widget), GTK_WINDOW(toplevel));
1391     }
1392 }
1393
1394 static gboolean webkitWebViewBaseFocus(GtkWidget* widget, GtkDirectionType direction)
1395 {
1396     // If a dialog is active, we need to forward focus events there. This
1397     // ensures that you can tab between elements in the box.
1398     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
1399     if (priv->dialog) {
1400         gboolean returnValue;
1401         g_signal_emit_by_name(priv->dialog, "focus", direction, &returnValue);
1402         return returnValue;
1403     }
1404
1405     return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->focus(widget, direction);
1406 }
1407
1408 static void webkitWebViewBaseDestroy(GtkWidget* widget)
1409 {
1410     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
1411     if (priv->dialog)
1412         gtk_widget_destroy(priv->dialog);
1413
1414     GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->destroy(widget);
1415 }
1416
1417 static void webkit_web_view_base_class_init(WebKitWebViewBaseClass* webkitWebViewBaseClass)
1418 {
1419     GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(webkitWebViewBaseClass);
1420     widgetClass->realize = webkitWebViewBaseRealize;
1421     widgetClass->unrealize = webkitWebViewBaseUnrealize;
1422     widgetClass->draw = webkitWebViewBaseDraw;
1423     widgetClass->size_allocate = webkitWebViewBaseSizeAllocate;
1424     widgetClass->get_preferred_width = webkitWebViewBaseGetPreferredWidth;
1425     widgetClass->get_preferred_height = webkitWebViewBaseGetPreferredHeight;
1426     widgetClass->map = webkitWebViewBaseMap;
1427     widgetClass->unmap = webkitWebViewBaseUnmap;
1428     widgetClass->focus = webkitWebViewBaseFocus;
1429     widgetClass->focus_in_event = webkitWebViewBaseFocusInEvent;
1430     widgetClass->focus_out_event = webkitWebViewBaseFocusOutEvent;
1431     widgetClass->key_press_event = webkitWebViewBaseKeyPressEvent;
1432     widgetClass->key_release_event = webkitWebViewBaseKeyReleaseEvent;
1433     widgetClass->button_press_event = webkitWebViewBaseButtonPressEvent;
1434     widgetClass->button_release_event = webkitWebViewBaseButtonReleaseEvent;
1435     widgetClass->scroll_event = webkitWebViewBaseScrollEvent;
1436     widgetClass->popup_menu = webkitWebViewBasePopupMenu;
1437     widgetClass->motion_notify_event = webkitWebViewBaseMotionNotifyEvent;
1438     widgetClass->enter_notify_event = webkitWebViewBaseCrossingNotifyEvent;
1439     widgetClass->leave_notify_event = webkitWebViewBaseCrossingNotifyEvent;
1440 #if ENABLE(TOUCH_EVENTS)
1441     widgetClass->touch_event = webkitWebViewBaseTouchEvent;
1442 #endif
1443     widgetClass->query_tooltip = webkitWebViewBaseQueryTooltip;
1444 #if ENABLE(DRAG_SUPPORT)
1445     widgetClass->drag_end = webkitWebViewBaseDragEnd;
1446     widgetClass->drag_data_get = webkitWebViewBaseDragDataGet;
1447     widgetClass->drag_motion = webkitWebViewBaseDragMotion;
1448     widgetClass->drag_leave = webkitWebViewBaseDragLeave;
1449     widgetClass->drag_drop = webkitWebViewBaseDragDrop;
1450     widgetClass->drag_data_received = webkitWebViewBaseDragDataReceived;
1451 #endif // ENABLE(DRAG_SUPPORT)
1452     widgetClass->event = webkitWebViewBaseEvent;
1453     widgetClass->get_accessible = webkitWebViewBaseGetAccessible;
1454     widgetClass->hierarchy_changed = webkitWebViewBaseHierarchyChanged;
1455     widgetClass->destroy = webkitWebViewBaseDestroy;
1456
1457     GObjectClass* gobjectClass = G_OBJECT_CLASS(webkitWebViewBaseClass);
1458     gobjectClass->constructed = webkitWebViewBaseConstructed;
1459     gobjectClass->dispose = webkitWebViewBaseDispose;
1460
1461     GtkContainerClass* containerClass = GTK_CONTAINER_CLASS(webkitWebViewBaseClass);
1462     containerClass->add = webkitWebViewBaseContainerAdd;
1463     containerClass->remove = webkitWebViewBaseContainerRemove;
1464     containerClass->forall = webkitWebViewBaseContainerForall;
1465
1466     // Before creating a WebKitWebViewBasePriv we need to be sure that WebKit is started.
1467     // Usually starting a context triggers InitializeWebKit2, but in case
1468     // we create a view without asking before for a default_context we get a crash.
1469     WebKit::InitializeWebKit2();
1470 }
1471
1472 WebKitWebViewBase* webkitWebViewBaseCreate(const API::PageConfiguration& configuration)
1473 {
1474     WebKitWebViewBase* webkitWebViewBase = WEBKIT_WEB_VIEW_BASE(g_object_new(WEBKIT_TYPE_WEB_VIEW_BASE, nullptr));
1475     webkitWebViewBaseCreateWebPage(webkitWebViewBase, configuration.copy());
1476     return webkitWebViewBase;
1477 }
1478
1479 GtkIMContext* webkitWebViewBaseGetIMContext(WebKitWebViewBase* webkitWebViewBase)
1480 {
1481     return webkitWebViewBase->priv->inputMethodFilter.context();
1482 }
1483
1484 WebPageProxy* webkitWebViewBaseGetPage(WebKitWebViewBase* webkitWebViewBase)
1485 {
1486     return webkitWebViewBase->priv->pageProxy.get();
1487 }
1488
1489 #if HAVE(GTK_SCALE_FACTOR)
1490 static void deviceScaleFactorChanged(WebKitWebViewBase* webkitWebViewBase)
1491 {
1492     webkitWebViewBase->priv->pageProxy->setIntrinsicDeviceScaleFactor(gtk_widget_get_scale_factor(GTK_WIDGET(webkitWebViewBase)));
1493 }
1494 #endif // HAVE(GTK_SCALE_FACTOR)
1495
1496 void webkitWebViewBaseCreateWebPage(WebKitWebViewBase* webkitWebViewBase, Ref<API::PageConfiguration>&& configuration)
1497 {
1498     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
1499     WebProcessPool* processPool = configuration->processPool();
1500     priv->pageProxy = processPool->createWebPage(*priv->pageClient, WTFMove(configuration));
1501     priv->acceleratedBackingStore = AcceleratedBackingStore::create(*priv->pageProxy);
1502     priv->pageProxy->initializeWebPage();
1503
1504     priv->inputMethodFilter.setPage(priv->pageProxy.get());
1505
1506 #if HAVE(GTK_SCALE_FACTOR)
1507     // We attach this here, because changes in scale factor are passed directly to the page proxy.
1508     priv->pageProxy->setIntrinsicDeviceScaleFactor(gtk_widget_get_scale_factor(GTK_WIDGET(webkitWebViewBase)));
1509     g_signal_connect(webkitWebViewBase, "notify::scale-factor", G_CALLBACK(deviceScaleFactorChanged), nullptr);
1510 #endif
1511 }
1512
1513 void webkitWebViewBaseSetTooltipText(WebKitWebViewBase* webViewBase, const char* tooltip)
1514 {
1515     WebKitWebViewBasePrivate* priv = webViewBase->priv;
1516     if (tooltip && tooltip[0] != '\0') {
1517         priv->tooltipText = tooltip;
1518         gtk_widget_set_has_tooltip(GTK_WIDGET(webViewBase), TRUE);
1519     } else {
1520         priv->tooltipText = "";
1521         gtk_widget_set_has_tooltip(GTK_WIDGET(webViewBase), FALSE);
1522     }
1523
1524     gtk_widget_trigger_tooltip_query(GTK_WIDGET(webViewBase));
1525 }
1526
1527 void webkitWebViewBaseSetTooltipArea(WebKitWebViewBase* webViewBase, const IntRect& tooltipArea)
1528 {
1529     webViewBase->priv->tooltipArea = tooltipArea;
1530 }
1531
1532 #if ENABLE(DRAG_SUPPORT)
1533 DragAndDropHandler& webkitWebViewBaseDragAndDropHandler(WebKitWebViewBase* webViewBase)
1534 {
1535     WebKitWebViewBasePrivate* priv = webViewBase->priv;
1536     if (!priv->dragAndDropHandler)
1537         priv->dragAndDropHandler = std::make_unique<DragAndDropHandler>(*priv->pageProxy);
1538     return *priv->dragAndDropHandler;
1539 }
1540 #endif // ENABLE(DRAG_SUPPORT)
1541
1542 void webkitWebViewBaseForwardNextKeyEvent(WebKitWebViewBase* webkitWebViewBase)
1543 {
1544     webkitWebViewBase->priv->shouldForwardNextKeyEvent = TRUE;
1545 }
1546
1547 void webkitWebViewBaseForwardNextWheelEvent(WebKitWebViewBase* webkitWebViewBase)
1548 {
1549     webkitWebViewBase->priv->shouldForwardNextWheelEvent = true;
1550 }
1551
1552 void webkitWebViewBaseEnterFullScreen(WebKitWebViewBase* webkitWebViewBase)
1553 {
1554 #if ENABLE(FULLSCREEN_API)
1555     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
1556     ASSERT(!priv->fullScreenModeActive);
1557
1558     WebFullScreenManagerProxy* fullScreenManagerProxy = priv->pageProxy->fullScreenManager();
1559     fullScreenManagerProxy->willEnterFullScreen();
1560
1561     GtkWidget* topLevelWindow = gtk_widget_get_toplevel(GTK_WIDGET(webkitWebViewBase));
1562     if (gtk_widget_is_toplevel(topLevelWindow))
1563         gtk_window_fullscreen(GTK_WINDOW(topLevelWindow));
1564     fullScreenManagerProxy->didEnterFullScreen();
1565     priv->fullScreenModeActive = true;
1566     priv->sleepDisabler = PAL::SleepDisabler::create(_("Website running in fullscreen mode"), PAL::SleepDisabler::Type::Display);
1567 #endif
1568 }
1569
1570 void webkitWebViewBaseExitFullScreen(WebKitWebViewBase* webkitWebViewBase)
1571 {
1572 #if ENABLE(FULLSCREEN_API)
1573     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
1574     ASSERT(priv->fullScreenModeActive);
1575
1576     WebFullScreenManagerProxy* fullScreenManagerProxy = priv->pageProxy->fullScreenManager();
1577     fullScreenManagerProxy->willExitFullScreen();
1578
1579     GtkWidget* topLevelWindow = gtk_widget_get_toplevel(GTK_WIDGET(webkitWebViewBase));
1580     if (gtk_widget_is_toplevel(topLevelWindow))
1581         gtk_window_unfullscreen(GTK_WINDOW(topLevelWindow));
1582     fullScreenManagerProxy->didExitFullScreen();
1583     priv->fullScreenModeActive = false;
1584     priv->sleepDisabler = nullptr;
1585 #endif
1586 }
1587
1588 bool webkitWebViewBaseIsFullScreen(WebKitWebViewBase* webkitWebViewBase)
1589 {
1590 #if ENABLE(FULLSCREEN_API)
1591     return webkitWebViewBase->priv->fullScreenModeActive;
1592 #else
1593     return false;
1594 #endif
1595 }
1596
1597 void webkitWebViewBaseSetInspectorViewSize(WebKitWebViewBase* webkitWebViewBase, unsigned size)
1598 {
1599     if (webkitWebViewBase->priv->inspectorViewSize == size)
1600         return;
1601     webkitWebViewBase->priv->inspectorViewSize = size;
1602     if (webkitWebViewBase->priv->inspectorView)
1603         gtk_widget_queue_resize_no_redraw(GTK_WIDGET(webkitWebViewBase));
1604 }
1605
1606 static void activeContextMenuUnmapped(GtkMenu* menu, WebKitWebViewBase* webViewBase)
1607 {
1608     if (webViewBase->priv->activeContextMenuProxy && webViewBase->priv->activeContextMenuProxy->gtkMenu() == menu)
1609         webViewBase->priv->activeContextMenuProxy = nullptr;
1610 }
1611
1612 void webkitWebViewBaseSetActiveContextMenuProxy(WebKitWebViewBase* webkitWebViewBase, WebContextMenuProxyGtk* contextMenuProxy)
1613 {
1614     webkitWebViewBase->priv->activeContextMenuProxy = contextMenuProxy;
1615     g_signal_connect_object(contextMenuProxy->gtkMenu(), "unmap", G_CALLBACK(activeContextMenuUnmapped), webkitWebViewBase, static_cast<GConnectFlags>(0));
1616 }
1617
1618 WebContextMenuProxyGtk* webkitWebViewBaseGetActiveContextMenuProxy(WebKitWebViewBase* webkitWebViewBase)
1619 {
1620     return webkitWebViewBase->priv->activeContextMenuProxy;
1621 }
1622
1623 GdkEvent* webkitWebViewBaseTakeContextMenuEvent(WebKitWebViewBase* webkitWebViewBase)
1624 {
1625     return webkitWebViewBase->priv->contextMenuEvent.release();
1626 }
1627
1628 void webkitWebViewBaseSetFocus(WebKitWebViewBase* webViewBase, bool focused)
1629 {
1630     WebKitWebViewBasePrivate* priv = webViewBase->priv;
1631     if ((focused && priv->activityState & ActivityState::IsFocused) || (!focused && !(priv->activityState & ActivityState::IsFocused)))
1632         return;
1633
1634     OptionSet<ActivityState::Flag> flagsToUpdate { ActivityState::IsFocused };
1635     if (focused) {
1636         priv->activityState.add(ActivityState::IsFocused);
1637
1638         // If the view has received the focus and the window is not active
1639         // mark the current window as active now. This can happen if the
1640         // toplevel window is a GTK_WINDOW_POPUP and the focus has been
1641         // set programatically like WebKitTestRunner does, because POPUP
1642         // can't be focused.
1643         if (!(priv->activityState & ActivityState::WindowIsActive)) {
1644             priv->activityState.add(ActivityState::WindowIsActive);
1645             flagsToUpdate.add(ActivityState::WindowIsActive);
1646         }
1647     } else
1648         priv->activityState.remove(ActivityState::IsFocused);
1649
1650     webkitWebViewBaseScheduleUpdateActivityState(webViewBase, flagsToUpdate);
1651 }
1652
1653 bool webkitWebViewBaseIsInWindowActive(WebKitWebViewBase* webViewBase)
1654 {
1655     return webViewBase->priv->activityState.contains(ActivityState::WindowIsActive);
1656 }
1657
1658 bool webkitWebViewBaseIsFocused(WebKitWebViewBase* webViewBase)
1659 {
1660     return webViewBase->priv->activityState.contains(ActivityState::IsFocused);
1661 }
1662
1663 bool webkitWebViewBaseIsVisible(WebKitWebViewBase* webViewBase)
1664 {
1665     return webViewBase->priv->activityState.contains(ActivityState::IsVisible);
1666 }
1667
1668 bool webkitWebViewBaseIsInWindow(WebKitWebViewBase* webViewBase)
1669 {
1670     return webViewBase->priv->activityState.contains(ActivityState::IsInWindow);
1671 }
1672
1673 void webkitWebViewBaseSetInputMethodState(WebKitWebViewBase* webkitWebViewBase, bool enabled)
1674 {
1675     webkitWebViewBase->priv->inputMethodFilter.setEnabled(enabled);
1676 }
1677
1678 void webkitWebViewBaseUpdateTextInputState(WebKitWebViewBase* webkitWebViewBase)
1679 {
1680     const auto& editorState = webkitWebViewBase->priv->pageProxy->editorState();
1681     if (!editorState.isMissingPostLayoutData)
1682         webkitWebViewBase->priv->inputMethodFilter.setCursorRect(editorState.postLayoutData().caretRectAtStart);
1683 }
1684
1685 void webkitWebViewBaseSetContentsSize(WebKitWebViewBase* webkitWebViewBase, const IntSize& contentsSize)
1686 {
1687     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
1688     if (priv->contentsSize == contentsSize)
1689         return;
1690     priv->contentsSize = contentsSize;
1691 }
1692
1693 void webkitWebViewBaseResetClickCounter(WebKitWebViewBase* webkitWebViewBase)
1694 {
1695     webkitWebViewBase->priv->clickCounter.reset();
1696 }
1697
1698 void webkitWebViewBaseEnterAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase, const LayerTreeContext& layerTreeContext)
1699 {
1700     webkitWebViewBase->priv->acceleratedBackingStore->update(layerTreeContext);
1701 }
1702
1703 void webkitWebViewBaseUpdateAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase, const LayerTreeContext& layerTreeContext)
1704 {
1705     webkitWebViewBase->priv->acceleratedBackingStore->update(layerTreeContext);
1706 }
1707
1708 void webkitWebViewBaseExitAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase)
1709 {
1710     webkitWebViewBase->priv->acceleratedBackingStore->update(LayerTreeContext());
1711 }
1712
1713 bool webkitWebViewBaseMakeGLContextCurrent(WebKitWebViewBase* webkitWebViewBase)
1714 {
1715     return webkitWebViewBase->priv->acceleratedBackingStore->makeContextCurrent();
1716 }
1717
1718 void webkitWebViewBaseWillSwapWebProcess(WebKitWebViewBase* webkitWebViewBase)
1719 {
1720     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
1721
1722     if (priv->viewGestureController)
1723         priv->viewGestureController->disconnectFromProcess();
1724 }
1725
1726 void webkitWebViewBaseDidExitWebProcess(WebKitWebViewBase* webkitWebViewBase)
1727 {
1728     webkitWebViewBase->priv->viewGestureController = nullptr;
1729 }
1730
1731 void webkitWebViewBaseDidRelaunchWebProcess(WebKitWebViewBase* webkitWebViewBase)
1732 {
1733     // Queue a resize to ensure the new DrawingAreaProxy is resized.
1734     gtk_widget_queue_resize_no_redraw(GTK_WIDGET(webkitWebViewBase));
1735
1736     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
1737
1738     if (priv->viewGestureController)
1739         priv->viewGestureController->connectToProcess();
1740     else {
1741         priv->viewGestureController = std::make_unique<WebKit::ViewGestureController>(*priv->pageProxy);
1742         priv->viewGestureController->setSwipeGestureEnabled(priv->isBackForwardNavigationGestureEnabled);
1743     }
1744 }
1745
1746 void webkitWebViewBasePageClosed(WebKitWebViewBase* webkitWebViewBase)
1747 {
1748     webkitWebViewBase->priv->acceleratedBackingStore->update(LayerTreeContext());
1749 }
1750
1751 RefPtr<WebKit::ViewSnapshot> webkitWebViewBaseTakeViewSnapshot(WebKitWebViewBase* webkitWebViewBase)
1752 {
1753     WebPageProxy* page = webkitWebViewBase->priv->pageProxy.get();
1754
1755     IntSize size = page->viewSize();
1756
1757 #if HAVE_GTK_SCALE_FACTOR
1758     float deviceScale = page->deviceScaleFactor();
1759     size.scale(deviceScale);
1760 #endif
1761
1762     RefPtr<cairo_surface_t> surface = adoptRef(cairo_image_surface_create(CAIRO_FORMAT_RGB24, size.width(), size.height()));
1763
1764 #if HAVE_GTK_SCALE_FACTOR
1765     cairoSurfaceSetDeviceScale(surface.get(), deviceScale, deviceScale);
1766 #endif
1767
1768     RefPtr<cairo_t> cr = adoptRef(cairo_create(surface.get()));
1769     webkitWebViewBaseDraw(GTK_WIDGET(webkitWebViewBase), cr.get());
1770
1771     return ViewSnapshot::create(WTFMove(surface));
1772 }
1773
1774 void webkitWebViewBaseDidStartProvisionalLoadForMainFrame(WebKitWebViewBase* webkitWebViewBase)
1775 {
1776     ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1777     if (controller && controller->isSwipeGestureEnabled())
1778         controller->didStartProvisionalLoadForMainFrame();
1779 }
1780
1781 void webkitWebViewBaseDidFirstVisuallyNonEmptyLayoutForMainFrame(WebKitWebViewBase* webkitWebViewBase)
1782 {
1783     ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1784     if (controller && controller->isSwipeGestureEnabled())
1785         controller->didFirstVisuallyNonEmptyLayoutForMainFrame();
1786 }
1787
1788 void webkitWebViewBaseDidFinishLoadForMainFrame(WebKitWebViewBase* webkitWebViewBase)
1789 {
1790     ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1791     if (controller && controller->isSwipeGestureEnabled())
1792         controller->didFinishLoadForMainFrame();
1793 }
1794
1795 void webkitWebViewBaseDidFailLoadForMainFrame(WebKitWebViewBase* webkitWebViewBase)
1796 {
1797     ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1798     if (controller && controller->isSwipeGestureEnabled())
1799         controller->didFailLoadForMainFrame();
1800 }
1801
1802 void webkitWebViewBaseDidSameDocumentNavigationForMainFrame(WebKitWebViewBase* webkitWebViewBase, SameDocumentNavigationType type)
1803 {
1804     ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1805     if (controller && controller->isSwipeGestureEnabled())
1806         controller->didSameDocumentNavigationForMainFrame(type);
1807 }
1808
1809 void webkitWebViewBaseDidRestoreScrollPosition(WebKitWebViewBase* webkitWebViewBase)
1810 {
1811     ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1812     if (controller && controller->isSwipeGestureEnabled())
1813         webkitWebViewBase->priv->viewGestureController->didRestoreScrollPosition();
1814 }
1815
1816 #if GTK_CHECK_VERSION(3, 24, 0)
1817 static void emojiChooserEmojiPicked(WebKitWebViewBase* webkitWebViewBase, const char* text)
1818 {
1819     webkitWebViewBaseCompleteEmojiChooserRequest(webkitWebViewBase, String::fromUTF8(text));
1820 }
1821
1822 static void emojiChooserClosed(WebKitWebViewBase* webkitWebViewBase)
1823 {
1824     webkitWebViewBaseCompleteEmojiChooserRequest(webkitWebViewBase, emptyString());
1825     webkitWebViewBase->priv->releaseEmojiChooserTimer.startOneShot(2_min);
1826 }
1827 #endif
1828
1829 void webkitWebViewBaseShowEmojiChooser(WebKitWebViewBase* webkitWebViewBase, const IntRect& caretRect, CompletionHandler<void(String)>&& completionHandler)
1830 {
1831 #if GTK_CHECK_VERSION(3, 24, 0)
1832     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
1833     priv->releaseEmojiChooserTimer.stop();
1834
1835     if (!priv->emojiChooser) {
1836         priv->emojiChooser = webkitEmojiChooserNew();
1837         g_signal_connect_swapped(priv->emojiChooser, "emoji-picked", G_CALLBACK(emojiChooserEmojiPicked), webkitWebViewBase);
1838         g_signal_connect_swapped(priv->emojiChooser, "closed", G_CALLBACK(emojiChooserClosed), webkitWebViewBase);
1839         gtk_popover_set_relative_to(GTK_POPOVER(priv->emojiChooser), GTK_WIDGET(webkitWebViewBase));
1840     }
1841
1842     priv->emojiChooserCompletionHandler = WTFMove(completionHandler);
1843
1844     GdkRectangle gdkCaretRect = caretRect;
1845     gtk_popover_set_pointing_to(GTK_POPOVER(priv->emojiChooser), &gdkCaretRect);
1846     gtk_popover_popup(GTK_POPOVER(priv->emojiChooser));
1847 #else
1848     UNUSED_PARAM(webkitWebViewBase);
1849     UNUSED_PARAM(caretRect);
1850     completionHandler(emptyString());
1851 #endif
1852 }
1853
1854 #if USE(WPE_RENDERER)
1855 int webkitWebViewBaseRenderHostFileDescriptor(WebKitWebViewBase* webkitWebViewBase)
1856 {
1857     return webkitWebViewBase->priv->acceleratedBackingStore->renderHostFileDescriptor();
1858 }
1859 #endif