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