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