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