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