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
87using namespace WebKit;
88using namespace WebCore;
89
90struct ClickCounter {
91public:
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
139private:
140 int currentClickCount;
141 IntPoint previousClickPoint;
142 unsigned previousClickButton;
143 int previousClickTime;
144};
145
146typedef HashMap<GtkWidget*, IntRect> WebKitWebViewChildrenMap;
147typedef HashMap<uint32_t, GUniquePtr<GdkEvent>> TouchEventsMap;
148
149struct _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
236WEBKIT_DEFINE_TYPE(WebKitWebViewBase, webkit_web_view_base, GTK_TYPE_CONTAINER)
237
238static 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
248static 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
264static 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
276static 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
295static void themeChanged(WebKitWebViewBase* webViewBase)
296{
297 webViewBase->priv->pageProxy->effectiveAppearanceDidChange();
298}
299
300static 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
311static 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
384static 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
438static 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
452static bool webkitWebViewChildIsInternalWidget(WebKitWebViewBase* webViewBase, GtkWidget* widget)
453{
454 WebKitWebViewBasePrivate* priv = webViewBase->priv;
455 return widget == priv->inspectorView || widget == priv->dialog;
456}
457
458static 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
474void 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
485void 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
501static 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
525static 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
542void 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)
553static 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
560static 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
573static 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
588static 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
621static 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
637static 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
681static 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
688static 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
695static 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
717static 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
730static 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
739static 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
748static 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
798static 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
817static 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
860static 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
873static 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
886static 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
900static 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
937static 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
951static 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
966static 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)
1016static 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
1028static 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
1047static 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
1061static 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)
1104class TouchGestureController final : public GestureControllerClient {
1105 WTF_MAKE_FAST_ALLOCATED;
1106
1107public:
1108 explicit TouchGestureController(WebKitWebViewBase* webViewBase)
1109 : m_webView(webViewBase)
1110 {
1111 }
1112
1113private:
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
1219GestureController& 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
1228void 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
1240ViewGestureController* webkitWebViewBaseViewGestureController(WebKitWebViewBase* webViewBase)
1241{
1242 return webViewBase->priv->viewGestureController.get();
1243}
1244
1245static 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)
1269static 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
1276static 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
1283static 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
1289static 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
1299static 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)
1317static 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
1323static 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
1330static 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
1338static 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
1353static 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
1367static 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
1376static 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
1431WebKitWebViewBase* 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
1438GtkIMContext* webkitWebViewBaseGetIMContext(WebKitWebViewBase* webkitWebViewBase)
1439{
1440 return webkitWebViewBase->priv->inputMethodFilter.context();
1441}
1442
1443WebPageProxy* webkitWebViewBaseGetPage(WebKitWebViewBase* webkitWebViewBase)
1444{
1445 return webkitWebViewBase->priv->pageProxy.get();
1446}
1447
1448#if HAVE(GTK_SCALE_FACTOR)
1449static 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
1455void 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
1473void 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
1487void webkitWebViewBaseSetTooltipArea(WebKitWebViewBase* webViewBase, const IntRect& tooltipArea)
1488{
1489 webViewBase->priv->tooltipArea = tooltipArea;
1490}
1491
1492#if ENABLE(DRAG_SUPPORT)
1493DragAndDropHandler& 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
1502void webkitWebViewBaseForwardNextKeyEvent(WebKitWebViewBase* webkitWebViewBase)
1503{
1504 webkitWebViewBase->priv->shouldForwardNextKeyEvent = TRUE;
1505}
1506
1507void webkitWebViewBaseForwardNextWheelEvent(WebKitWebViewBase* webkitWebViewBase)
1508{
1509 webkitWebViewBase->priv->shouldForwardNextWheelEvent = true;
1510}
1511
1512void 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
1530void 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
1548bool webkitWebViewBaseIsFullScreen(WebKitWebViewBase* webkitWebViewBase)
1549{
1550#if ENABLE(FULLSCREEN_API)
1551 return webkitWebViewBase->priv->fullScreenModeActive;
1552#else
1553 return false;
1554#endif
1555}
1556
1557void 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
1566static void activeContextMenuUnmapped(GtkMenu* menu, WebKitWebViewBase* webViewBase)
1567{
1568 if (webViewBase->priv->activeContextMenuProxy && webViewBase->priv->activeContextMenuProxy->gtkMenu() == menu)
1569 webViewBase->priv->activeContextMenuProxy = nullptr;
1570}
1571
1572void 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
1578WebContextMenuProxyGtk* webkitWebViewBaseGetActiveContextMenuProxy(WebKitWebViewBase* webkitWebViewBase)
1579{
1580 return webkitWebViewBase->priv->activeContextMenuProxy;
1581}
1582
1583GdkEvent* webkitWebViewBaseTakeContextMenuEvent(WebKitWebViewBase* webkitWebViewBase)
1584{
1585 return webkitWebViewBase->priv->contextMenuEvent.release();
1586}
1587
1588void 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
1613bool webkitWebViewBaseIsInWindowActive(WebKitWebViewBase* webViewBase)
1614{
1615 return webViewBase->priv->activityState.contains(ActivityState::WindowIsActive);
1616}
1617
1618bool webkitWebViewBaseIsFocused(WebKitWebViewBase* webViewBase)
1619{
1620 return webViewBase->priv->activityState.contains(ActivityState::IsFocused);
1621}
1622
1623bool webkitWebViewBaseIsVisible(WebKitWebViewBase* webViewBase)
1624{
1625 return webViewBase->priv->activityState.contains(ActivityState::IsVisible);
1626}
1627
1628bool webkitWebViewBaseIsInWindow(WebKitWebViewBase* webViewBase)
1629{
1630 return webViewBase->priv->activityState.contains(ActivityState::IsInWindow);
1631}
1632
1633void webkitWebViewBaseSetInputMethodState(WebKitWebViewBase* webkitWebViewBase, bool enabled)
1634{
1635 webkitWebViewBase->priv->inputMethodFilter.setEnabled(enabled);
1636}
1637
1638void 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
1645void 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
1653void webkitWebViewBaseResetClickCounter(WebKitWebViewBase* webkitWebViewBase)
1654{
1655 webkitWebViewBase->priv->clickCounter.reset();
1656}
1657
1658void webkitWebViewBaseEnterAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase, const LayerTreeContext& layerTreeContext)
1659{
1660 if (webkitWebViewBase->priv->acceleratedBackingStore)
1661 webkitWebViewBase->priv->acceleratedBackingStore->update(layerTreeContext);
1662}
1663
1664void webkitWebViewBaseUpdateAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase, const LayerTreeContext& layerTreeContext)
1665{
1666 if (webkitWebViewBase->priv->acceleratedBackingStore)
1667 webkitWebViewBase->priv->acceleratedBackingStore->update(layerTreeContext);
1668}
1669
1670void webkitWebViewBaseExitAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase)
1671{
1672 if (webkitWebViewBase->priv->acceleratedBackingStore)
1673 webkitWebViewBase->priv->acceleratedBackingStore->update(LayerTreeContext());
1674}
1675
1676bool webkitWebViewBaseMakeGLContextCurrent(WebKitWebViewBase* webkitWebViewBase)
1677{
1678 if (webkitWebViewBase->priv->acceleratedBackingStore)
1679 return webkitWebViewBase->priv->acceleratedBackingStore->makeContextCurrent();
1680 return false;
1681}
1682
1683void 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
1710void 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
1728RefPtr<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
1751void webkitWebViewBaseDidStartProvisionalLoadForMainFrame(WebKitWebViewBase* webkitWebViewBase)
1752{
1753 ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1754 if (controller && controller->isSwipeGestureEnabled())
1755 controller->didStartProvisionalLoadForMainFrame();
1756}
1757
1758void webkitWebViewBaseDidFirstVisuallyNonEmptyLayoutForMainFrame(WebKitWebViewBase* webkitWebViewBase)
1759{
1760 ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1761 if (controller && controller->isSwipeGestureEnabled())
1762 controller->didFirstVisuallyNonEmptyLayoutForMainFrame();
1763}
1764
1765void webkitWebViewBaseDidFinishLoadForMainFrame(WebKitWebViewBase* webkitWebViewBase)
1766{
1767 ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1768 if (controller && controller->isSwipeGestureEnabled())
1769 controller->didFinishLoadForMainFrame();
1770}
1771
1772void webkitWebViewBaseDidFailLoadForMainFrame(WebKitWebViewBase* webkitWebViewBase)
1773{
1774 ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1775 if (controller && controller->isSwipeGestureEnabled())
1776 controller->didFailLoadForMainFrame();
1777}
1778
1779void webkitWebViewBaseDidSameDocumentNavigationForMainFrame(WebKitWebViewBase* webkitWebViewBase, SameDocumentNavigationType type)
1780{
1781 ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase);
1782 if (controller && controller->isSwipeGestureEnabled())
1783 controller->didSameDocumentNavigationForMainFrame(type);
1784}
1785
1786void 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)
1794static void emojiChooserEmojiPicked(WebKitWebViewBase* webkitWebViewBase, const char* text)
1795{
1796 webkitWebViewBaseCompleteEmojiChooserRequest(webkitWebViewBase, String::fromUTF8(text));
1797}
1798
1799static void emojiChooserClosed(WebKitWebViewBase* webkitWebViewBase)
1800{
1801 webkitWebViewBaseCompleteEmojiChooserRequest(webkitWebViewBase, emptyString());
1802 webkitWebViewBase->priv->releaseEmojiChooserTimer.startOneShot(2_min);
1803}
1804#endif
1805
1806void 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}
1830