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 | |
87 | using namespace WebKit; |
88 | using namespace WebCore; |
89 | |
90 | struct ClickCounter { |
91 | public: |
92 | void reset() |
93 | { |
94 | currentClickCount = 0; |
95 | previousClickPoint = IntPoint(); |
96 | previousClickTime = 0; |
97 | previousClickButton = 0; |
98 | } |
99 | |
100 | int currentClickCountForGdkButtonEvent(GdkEvent* event) |
101 | { |
102 | int doubleClickDistance = 250; |
103 | int doubleClickTime = 5; |
104 | g_object_get(gtk_settings_get_for_screen(gdk_event_get_screen(event)), |
105 | "gtk-double-click-distance" , &doubleClickDistance, "gtk-double-click-time" , &doubleClickTime, nullptr); |
106 | |
107 | // GTK+ only counts up to triple clicks, but WebCore wants to know about |
108 | // quadruple clicks, quintuple clicks, ad infinitum. Here, we replicate the |
109 | // GDK logic for counting clicks. |
110 | guint32 eventTime = gdk_event_get_time(event); |
111 | if (!eventTime) { |
112 | // Real events always have a non-zero time, but events synthesized |
113 | // by the WTR do not and we must calculate a time manually. This time |
114 | // is not calculated in the WTR, because GTK+ does not work well with |
115 | // anything other than GDK_CURRENT_TIME on synthesized events. |
116 | GTimeVal timeValue; |
117 | g_get_current_time(&timeValue); |
118 | eventTime = (timeValue.tv_sec * 1000) + (timeValue.tv_usec / 1000); |
119 | } |
120 | |
121 | if ((event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) |
122 | || ((std::abs(event->button.x - previousClickPoint.x()) < doubleClickDistance) |
123 | && (std::abs(event->button.y - previousClickPoint.y()) < doubleClickDistance) |
124 | && (eventTime - previousClickTime < static_cast<unsigned>(doubleClickTime)) |
125 | && (event->button.button == previousClickButton))) |
126 | currentClickCount++; |
127 | else |
128 | currentClickCount = 1; |
129 | |
130 | double x, y; |
131 | gdk_event_get_coords(event, &x, &y); |
132 | previousClickPoint = IntPoint(x, y); |
133 | previousClickButton = event->button.button; |
134 | previousClickTime = eventTime; |
135 | |
136 | return currentClickCount; |
137 | } |
138 | |
139 | private: |
140 | int currentClickCount; |
141 | IntPoint previousClickPoint; |
142 | unsigned previousClickButton; |
143 | int previousClickTime; |
144 | }; |
145 | |
146 | typedef HashMap<GtkWidget*, IntRect> WebKitWebViewChildrenMap; |
147 | typedef HashMap<uint32_t, GUniquePtr<GdkEvent>> TouchEventsMap; |
148 | |
149 | struct _WebKitWebViewBasePrivate { |
150 | _WebKitWebViewBasePrivate() |
151 | : updateActivityStateTimer(RunLoop::main(), this, &_WebKitWebViewBasePrivate::updateActivityStateTimerFired) |
152 | #if GTK_CHECK_VERSION(3, 24, 0) |
153 | , releaseEmojiChooserTimer(RunLoop::main(), this, &_WebKitWebViewBasePrivate::releaseEmojiChooserTimerFired) |
154 | #endif |
155 | { |
156 | #if GTK_CHECK_VERSION(3, 24, 0) |
157 | releaseEmojiChooserTimer.setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer); |
158 | #endif |
159 | } |
160 | |
161 | void updateActivityStateTimerFired() |
162 | { |
163 | if (!pageProxy) |
164 | return; |
165 | pageProxy->activityStateDidChange(activityStateFlagsToUpdate); |
166 | activityStateFlagsToUpdate = { }; |
167 | } |
168 | |
169 | #if GTK_CHECK_VERSION(3, 24, 0) |
170 | void releaseEmojiChooserTimerFired() |
171 | { |
172 | if (emojiChooser) { |
173 | gtk_widget_destroy(emojiChooser); |
174 | emojiChooser = nullptr; |
175 | } |
176 | } |
177 | #endif |
178 | |
179 | WebKitWebViewChildrenMap children; |
180 | std::unique_ptr<PageClientImpl> pageClient; |
181 | RefPtr<WebPageProxy> pageProxy; |
182 | bool shouldForwardNextKeyEvent { false }; |
183 | bool shouldForwardNextWheelEvent { false }; |
184 | ClickCounter clickCounter; |
185 | CString tooltipText; |
186 | IntRect tooltipArea; |
187 | GRefPtr<AtkObject> accessible; |
188 | GtkWidget* dialog { nullptr }; |
189 | GtkWidget* inspectorView { nullptr }; |
190 | AttachmentSide inspectorAttachmentSide { AttachmentSide::Bottom }; |
191 | unsigned inspectorViewSize { 0 }; |
192 | GUniquePtr<GdkEvent> ; |
193 | WebContextMenuProxyGtk* { nullptr }; |
194 | InputMethodFilter inputMethodFilter; |
195 | KeyBindingTranslator keyBindingTranslator; |
196 | TouchEventsMap touchEvents; |
197 | IntSize contentsSize; |
198 | |
199 | GtkWindow* toplevelOnScreenWindow { nullptr }; |
200 | unsigned long toplevelFocusInEventID { 0 }; |
201 | unsigned long toplevelFocusOutEventID { 0 }; |
202 | unsigned long toplevelWindowStateEventID { 0 }; |
203 | unsigned long toplevelWindowRealizedID { 0 }; |
204 | unsigned long themeChangedID { 0 }; |
205 | unsigned long applicationPreferDarkThemeID { 0 }; |
206 | |
207 | // View State. |
208 | OptionSet<ActivityState::Flag> activityState; |
209 | OptionSet<ActivityState::Flag> activityStateFlagsToUpdate; |
210 | RunLoop::Timer<WebKitWebViewBasePrivate> updateActivityStateTimer; |
211 | |
212 | #if ENABLE(FULLSCREEN_API) |
213 | bool fullScreenModeActive { false }; |
214 | std::unique_ptr<PAL::SleepDisabler> sleepDisabler; |
215 | #endif |
216 | |
217 | std::unique_ptr<AcceleratedBackingStore> acceleratedBackingStore; |
218 | |
219 | #if ENABLE(DRAG_SUPPORT) |
220 | std::unique_ptr<DragAndDropHandler> dragAndDropHandler; |
221 | #endif |
222 | |
223 | #if HAVE(GTK_GESTURES) |
224 | std::unique_ptr<GestureController> gestureController; |
225 | #endif |
226 | std::unique_ptr<ViewGestureController> viewGestureController; |
227 | bool isBackForwardNavigationGestureEnabled { false }; |
228 | |
229 | #if GTK_CHECK_VERSION(3, 24, 0) |
230 | GtkWidget* emojiChooser; |
231 | CompletionHandler<void(String)> emojiChooserCompletionHandler; |
232 | RunLoop::Timer<WebKitWebViewBasePrivate> releaseEmojiChooserTimer; |
233 | #endif |
234 | }; |
235 | |
236 | WEBKIT_DEFINE_TYPE(WebKitWebViewBase, webkit_web_view_base, GTK_TYPE_CONTAINER) |
237 | |
238 | static void webkitWebViewBaseScheduleUpdateActivityState(WebKitWebViewBase* webViewBase, OptionSet<ActivityState::Flag> flagsToUpdate) |
239 | { |
240 | WebKitWebViewBasePrivate* priv = webViewBase->priv; |
241 | priv->activityStateFlagsToUpdate.add(flagsToUpdate); |
242 | if (priv->updateActivityStateTimer.isActive()) |
243 | return; |
244 | |
245 | priv->updateActivityStateTimer.startOneShot(0_s); |
246 | } |
247 | |
248 | static gboolean toplevelWindowFocusInEvent(GtkWidget* widget, GdkEventFocus*, WebKitWebViewBase* webViewBase) |
249 | { |
250 | // Spurious focus in events can occur when the window is hidden. |
251 | if (!gtk_widget_get_visible(widget)) |
252 | return FALSE; |
253 | |
254 | WebKitWebViewBasePrivate* priv = webViewBase->priv; |
255 | if (priv->activityState & ActivityState::WindowIsActive) |
256 | return FALSE; |
257 | |
258 | priv->activityState.add(ActivityState::WindowIsActive); |
259 | webkitWebViewBaseScheduleUpdateActivityState(webViewBase, ActivityState::WindowIsActive); |
260 | |
261 | return FALSE; |
262 | } |
263 | |
264 | static gboolean toplevelWindowFocusOutEvent(GtkWidget*, GdkEventFocus*, WebKitWebViewBase* webViewBase) |
265 | { |
266 | WebKitWebViewBasePrivate* priv = webViewBase->priv; |
267 | if (!(priv->activityState & ActivityState::WindowIsActive)) |
268 | return FALSE; |
269 | |
270 | priv->activityState.remove(ActivityState::WindowIsActive); |
271 | webkitWebViewBaseScheduleUpdateActivityState(webViewBase, ActivityState::WindowIsActive); |
272 | |
273 | return FALSE; |
274 | } |
275 | |
276 | static gboolean toplevelWindowStateEvent(GtkWidget*, GdkEventWindowState* event, WebKitWebViewBase* webViewBase) |
277 | { |
278 | WebKitWebViewBasePrivate* priv = webViewBase->priv; |
279 | if (!(event->changed_mask & GDK_WINDOW_STATE_ICONIFIED)) |
280 | return FALSE; |
281 | |
282 | bool visible = !(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED); |
283 | if ((visible && priv->activityState & ActivityState::IsVisible) || (!visible && !(priv->activityState & ActivityState::IsVisible))) |
284 | return FALSE; |
285 | |
286 | if (visible) |
287 | priv->activityState.add(ActivityState::IsVisible); |
288 | else |
289 | priv->activityState.remove(ActivityState::IsVisible); |
290 | webkitWebViewBaseScheduleUpdateActivityState(webViewBase, ActivityState::IsVisible); |
291 | |
292 | return FALSE; |
293 | } |
294 | |
295 | static void themeChanged(WebKitWebViewBase* webViewBase) |
296 | { |
297 | webViewBase->priv->pageProxy->effectiveAppearanceDidChange(); |
298 | } |
299 | |
300 | static void toplevelWindowRealized(WebKitWebViewBase* webViewBase) |
301 | { |
302 | gtk_widget_realize(GTK_WIDGET(webViewBase)); |
303 | |
304 | WebKitWebViewBasePrivate* priv = webViewBase->priv; |
305 | if (priv->toplevelWindowRealizedID) { |
306 | g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelWindowRealizedID); |
307 | priv->toplevelWindowRealizedID = 0; |
308 | } |
309 | } |
310 | |
311 | static void webkitWebViewBaseSetToplevelOnScreenWindow(WebKitWebViewBase* webViewBase, GtkWindow* window) |
312 | { |
313 | WebKitWebViewBasePrivate* priv = webViewBase->priv; |
314 | if (priv->toplevelOnScreenWindow == window) |
315 | return; |
316 | |
317 | if (priv->toplevelFocusInEventID) { |
318 | g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelFocusInEventID); |
319 | priv->toplevelFocusInEventID = 0; |
320 | } |
321 | if (priv->toplevelFocusOutEventID) { |
322 | g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelFocusOutEventID); |
323 | priv->toplevelFocusOutEventID = 0; |
324 | } |
325 | if (priv->toplevelWindowStateEventID) { |
326 | g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelWindowStateEventID); |
327 | priv->toplevelWindowStateEventID = 0; |
328 | } |
329 | if (priv->toplevelWindowRealizedID) { |
330 | g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelWindowRealizedID); |
331 | priv->toplevelWindowRealizedID = 0; |
332 | } |
333 | if (priv->themeChangedID || priv->applicationPreferDarkThemeID) { |
334 | auto* settings = gtk_widget_get_settings(GTK_WIDGET(priv->toplevelOnScreenWindow)); |
335 | if (priv->themeChangedID) { |
336 | g_signal_handler_disconnect(settings, priv->themeChangedID); |
337 | priv->themeChangedID = 0; |
338 | } |
339 | if (priv->applicationPreferDarkThemeID) { |
340 | g_signal_handler_disconnect(settings, priv->applicationPreferDarkThemeID); |
341 | priv->applicationPreferDarkThemeID = 0; |
342 | } |
343 | } |
344 | |
345 | priv->toplevelOnScreenWindow = window; |
346 | |
347 | if (!priv->toplevelOnScreenWindow) { |
348 | OptionSet<ActivityState::Flag> flagsToUpdate; |
349 | if (priv->activityState & ActivityState::IsInWindow) { |
350 | priv->activityState.remove(ActivityState::IsInWindow); |
351 | flagsToUpdate.add(ActivityState::IsInWindow); |
352 | } |
353 | if (priv->activityState & ActivityState::WindowIsActive) { |
354 | priv->activityState.remove(ActivityState::WindowIsActive); |
355 | flagsToUpdate.add(ActivityState::IsInWindow); |
356 | } |
357 | if (flagsToUpdate) |
358 | webkitWebViewBaseScheduleUpdateActivityState(webViewBase, flagsToUpdate); |
359 | |
360 | return; |
361 | } |
362 | |
363 | priv->toplevelFocusInEventID = |
364 | g_signal_connect(priv->toplevelOnScreenWindow, "focus-in-event" , |
365 | G_CALLBACK(toplevelWindowFocusInEvent), webViewBase); |
366 | priv->toplevelFocusOutEventID = |
367 | g_signal_connect(priv->toplevelOnScreenWindow, "focus-out-event" , |
368 | G_CALLBACK(toplevelWindowFocusOutEvent), webViewBase); |
369 | priv->toplevelWindowStateEventID = |
370 | g_signal_connect(priv->toplevelOnScreenWindow, "window-state-event" , G_CALLBACK(toplevelWindowStateEvent), webViewBase); |
371 | |
372 | auto* settings = gtk_widget_get_settings(GTK_WIDGET(priv->toplevelOnScreenWindow)); |
373 | priv->themeChangedID = |
374 | g_signal_connect_swapped(settings, "notify::gtk-theme-name" , G_CALLBACK(themeChanged), webViewBase); |
375 | priv->applicationPreferDarkThemeID = |
376 | g_signal_connect_swapped(settings, "notify::gtk-application-prefer-dark-theme" , G_CALLBACK(themeChanged), webViewBase); |
377 | |
378 | if (gtk_widget_get_realized(GTK_WIDGET(window))) |
379 | gtk_widget_realize(GTK_WIDGET(webViewBase)); |
380 | else |
381 | priv->toplevelWindowRealizedID = g_signal_connect_swapped(window, "realize" , G_CALLBACK(toplevelWindowRealized), webViewBase); |
382 | } |
383 | |
384 | static void webkitWebViewBaseRealize(GtkWidget* widget) |
385 | { |
386 | WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(widget); |
387 | WebKitWebViewBasePrivate* priv = webView->priv; |
388 | |
389 | gtk_widget_set_realized(widget, TRUE); |
390 | |
391 | GtkAllocation allocation; |
392 | gtk_widget_get_allocation(widget, &allocation); |
393 | |
394 | GdkWindowAttr attributes; |
395 | attributes.window_type = GDK_WINDOW_CHILD; |
396 | attributes.x = allocation.x; |
397 | attributes.y = allocation.y; |
398 | attributes.width = allocation.width; |
399 | attributes.height = allocation.height; |
400 | attributes.wclass = GDK_INPUT_OUTPUT; |
401 | attributes.visual = gtk_widget_get_visual(widget); |
402 | attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK |
403 | | GDK_EXPOSURE_MASK |
404 | | GDK_BUTTON_PRESS_MASK |
405 | | GDK_BUTTON_RELEASE_MASK |
406 | | GDK_SCROLL_MASK |
407 | | GDK_SMOOTH_SCROLL_MASK |
408 | | GDK_POINTER_MOTION_MASK |
409 | | GDK_ENTER_NOTIFY_MASK |
410 | | GDK_LEAVE_NOTIFY_MASK |
411 | | GDK_KEY_PRESS_MASK |
412 | | GDK_KEY_RELEASE_MASK |
413 | | GDK_BUTTON_MOTION_MASK |
414 | | GDK_BUTTON1_MOTION_MASK |
415 | | GDK_BUTTON2_MOTION_MASK |
416 | | GDK_BUTTON3_MOTION_MASK |
417 | | GDK_TOUCH_MASK; |
418 | #if HAVE(GTK_GESTURES) |
419 | attributes.event_mask |= GDK_TOUCHPAD_GESTURE_MASK; |
420 | #endif |
421 | |
422 | gint attributesMask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; |
423 | |
424 | GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributesMask); |
425 | gtk_widget_set_window(widget, window); |
426 | gdk_window_set_user_data(window, widget); |
427 | |
428 | gtk_im_context_set_client_window(priv->inputMethodFilter.context(), window); |
429 | } |
430 | |
431 | static 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 | |
439 | static bool webkitWebViewChildIsInternalWidget(WebKitWebViewBase* webViewBase, GtkWidget* widget) |
440 | { |
441 | WebKitWebViewBasePrivate* priv = webViewBase->priv; |
442 | return widget == priv->inspectorView || widget == priv->dialog; |
443 | } |
444 | |
445 | static 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 | |
461 | void 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 | |
472 | void 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 | |
488 | static 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 | |
512 | static 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 | |
529 | void 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) |
540 | static 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 | |
547 | static 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 | |
562 | static 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 | |
577 | static 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 | |
610 | static 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 | |
626 | static 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 | |
670 | static 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 | |
677 | static 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 | |
684 | static 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 | |
706 | static 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 | |
719 | static 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 | |
728 | static 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 | |
737 | static 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 | |
787 | static 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 | |
806 | static 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 | |
849 | static 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 | |
862 | static 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 | |
875 | static 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 | |
889 | static 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 | |
926 | static gboolean (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 | |
940 | static 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 | |
955 | static 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) |
1005 | static 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 | |
1017 | static 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 | |
1036 | static 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 | |
1050 | static 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) |
1093 | class TouchGestureController final : public GestureControllerClient { |
1094 | WTF_MAKE_FAST_ALLOCATED; |
1095 | |
1096 | public: |
1097 | explicit TouchGestureController(WebKitWebViewBase* webViewBase) |
1098 | : m_webView(webViewBase) |
1099 | { |
1100 | } |
1101 | |
1102 | private: |
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 | |
1208 | GestureController& 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 | |
1217 | void 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 | |
1229 | ViewGestureController* webkitWebViewBaseViewGestureController(WebKitWebViewBase* webViewBase) |
1230 | { |
1231 | return webViewBase->priv->viewGestureController.get(); |
1232 | } |
1233 | |
1234 | static 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) |
1258 | static 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 | |
1265 | static 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 | |
1272 | static 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 | |
1278 | static 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 | |
1288 | static 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) |
1306 | static 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 | |
1312 | static 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 | |
1319 | static 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 | |
1327 | static 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 | |
1342 | static 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 | |
1356 | static 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 | |
1365 | static 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 | |
1420 | WebKitWebViewBase* 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 | |
1427 | GtkIMContext* webkitWebViewBaseGetIMContext(WebKitWebViewBase* webkitWebViewBase) |
1428 | { |
1429 | return webkitWebViewBase->priv->inputMethodFilter.context(); |
1430 | } |
1431 | |
1432 | WebPageProxy* webkitWebViewBaseGetPage(WebKitWebViewBase* webkitWebViewBase) |
1433 | { |
1434 | return webkitWebViewBase->priv->pageProxy.get(); |
1435 | } |
1436 | |
1437 | #if HAVE(GTK_SCALE_FACTOR) |
1438 | static 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 | |
1444 | void 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 | |
1461 | void 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 | |
1475 | void webkitWebViewBaseSetTooltipArea(WebKitWebViewBase* webViewBase, const IntRect& tooltipArea) |
1476 | { |
1477 | webViewBase->priv->tooltipArea = tooltipArea; |
1478 | } |
1479 | |
1480 | #if ENABLE(DRAG_SUPPORT) |
1481 | DragAndDropHandler& 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 | |
1490 | void webkitWebViewBaseForwardNextKeyEvent(WebKitWebViewBase* webkitWebViewBase) |
1491 | { |
1492 | webkitWebViewBase->priv->shouldForwardNextKeyEvent = TRUE; |
1493 | } |
1494 | |
1495 | void webkitWebViewBaseForwardNextWheelEvent(WebKitWebViewBase* webkitWebViewBase) |
1496 | { |
1497 | webkitWebViewBase->priv->shouldForwardNextWheelEvent = true; |
1498 | } |
1499 | |
1500 | void 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 | |
1518 | void 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 | |
1536 | bool webkitWebViewBaseIsFullScreen(WebKitWebViewBase* webkitWebViewBase) |
1537 | { |
1538 | #if ENABLE(FULLSCREEN_API) |
1539 | return webkitWebViewBase->priv->fullScreenModeActive; |
1540 | #else |
1541 | return false; |
1542 | #endif |
1543 | } |
1544 | |
1545 | void 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 | |
1554 | static void (GtkMenu* , WebKitWebViewBase* webViewBase) |
1555 | { |
1556 | if (webViewBase->priv->activeContextMenuProxy && webViewBase->priv->activeContextMenuProxy->gtkMenu() == menu) |
1557 | webViewBase->priv->activeContextMenuProxy = nullptr; |
1558 | } |
1559 | |
1560 | void (WebKitWebViewBase* webkitWebViewBase, WebContextMenuProxyGtk* ) |
1561 | { |
1562 | webkitWebViewBase->priv->activeContextMenuProxy = contextMenuProxy; |
1563 | g_signal_connect_object(contextMenuProxy->gtkMenu(), "unmap" , G_CALLBACK(activeContextMenuUnmapped), webkitWebViewBase, static_cast<GConnectFlags>(0)); |
1564 | } |
1565 | |
1566 | WebContextMenuProxyGtk* (WebKitWebViewBase* webkitWebViewBase) |
1567 | { |
1568 | return webkitWebViewBase->priv->activeContextMenuProxy; |
1569 | } |
1570 | |
1571 | GdkEvent* (WebKitWebViewBase* webkitWebViewBase) |
1572 | { |
1573 | return webkitWebViewBase->priv->contextMenuEvent.release(); |
1574 | } |
1575 | |
1576 | void 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 | |
1601 | bool webkitWebViewBaseIsInWindowActive(WebKitWebViewBase* webViewBase) |
1602 | { |
1603 | return webViewBase->priv->activityState.contains(ActivityState::WindowIsActive); |
1604 | } |
1605 | |
1606 | bool webkitWebViewBaseIsFocused(WebKitWebViewBase* webViewBase) |
1607 | { |
1608 | return webViewBase->priv->activityState.contains(ActivityState::IsFocused); |
1609 | } |
1610 | |
1611 | bool webkitWebViewBaseIsVisible(WebKitWebViewBase* webViewBase) |
1612 | { |
1613 | return webViewBase->priv->activityState.contains(ActivityState::IsVisible); |
1614 | } |
1615 | |
1616 | bool webkitWebViewBaseIsInWindow(WebKitWebViewBase* webViewBase) |
1617 | { |
1618 | return webViewBase->priv->activityState.contains(ActivityState::IsInWindow); |
1619 | } |
1620 | |
1621 | void webkitWebViewBaseSetInputMethodState(WebKitWebViewBase* webkitWebViewBase, bool enabled) |
1622 | { |
1623 | webkitWebViewBase->priv->inputMethodFilter.setEnabled(enabled); |
1624 | } |
1625 | |
1626 | void 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 | |
1633 | void 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 | |
1641 | void webkitWebViewBaseResetClickCounter(WebKitWebViewBase* webkitWebViewBase) |
1642 | { |
1643 | webkitWebViewBase->priv->clickCounter.reset(); |
1644 | } |
1645 | |
1646 | void webkitWebViewBaseEnterAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase, const LayerTreeContext& layerTreeContext) |
1647 | { |
1648 | webkitWebViewBase->priv->acceleratedBackingStore->update(layerTreeContext); |
1649 | } |
1650 | |
1651 | void webkitWebViewBaseUpdateAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase, const LayerTreeContext& layerTreeContext) |
1652 | { |
1653 | webkitWebViewBase->priv->acceleratedBackingStore->update(layerTreeContext); |
1654 | } |
1655 | |
1656 | void webkitWebViewBaseExitAcceleratedCompositingMode(WebKitWebViewBase* webkitWebViewBase) |
1657 | { |
1658 | webkitWebViewBase->priv->acceleratedBackingStore->update(LayerTreeContext()); |
1659 | } |
1660 | |
1661 | bool webkitWebViewBaseMakeGLContextCurrent(WebKitWebViewBase* webkitWebViewBase) |
1662 | { |
1663 | return webkitWebViewBase->priv->acceleratedBackingStore->makeContextCurrent(); |
1664 | } |
1665 | |
1666 | void 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 | |
1676 | void webkitWebViewBasePageClosed(WebKitWebViewBase* webkitWebViewBase) |
1677 | { |
1678 | webkitWebViewBase->priv->acceleratedBackingStore->update(LayerTreeContext()); |
1679 | } |
1680 | |
1681 | RefPtr<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 | |
1704 | void webkitWebViewBaseDidStartProvisionalLoadForMainFrame(WebKitWebViewBase* webkitWebViewBase) |
1705 | { |
1706 | ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase); |
1707 | if (controller && controller->isSwipeGestureEnabled()) |
1708 | controller->didStartProvisionalLoadForMainFrame(); |
1709 | } |
1710 | |
1711 | void webkitWebViewBaseDidFirstVisuallyNonEmptyLayoutForMainFrame(WebKitWebViewBase* webkitWebViewBase) |
1712 | { |
1713 | ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase); |
1714 | if (controller && controller->isSwipeGestureEnabled()) |
1715 | controller->didFirstVisuallyNonEmptyLayoutForMainFrame(); |
1716 | } |
1717 | |
1718 | void webkitWebViewBaseDidFinishLoadForMainFrame(WebKitWebViewBase* webkitWebViewBase) |
1719 | { |
1720 | ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase); |
1721 | if (controller && controller->isSwipeGestureEnabled()) |
1722 | controller->didFinishLoadForMainFrame(); |
1723 | } |
1724 | |
1725 | void webkitWebViewBaseDidFailLoadForMainFrame(WebKitWebViewBase* webkitWebViewBase) |
1726 | { |
1727 | ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase); |
1728 | if (controller && controller->isSwipeGestureEnabled()) |
1729 | controller->didFailLoadForMainFrame(); |
1730 | } |
1731 | |
1732 | void webkitWebViewBaseDidSameDocumentNavigationForMainFrame(WebKitWebViewBase* webkitWebViewBase, SameDocumentNavigationType type) |
1733 | { |
1734 | ViewGestureController* controller = webkitWebViewBaseViewGestureController(webkitWebViewBase); |
1735 | if (controller && controller->isSwipeGestureEnabled()) |
1736 | controller->didSameDocumentNavigationForMainFrame(type); |
1737 | } |
1738 | |
1739 | void 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) |
1747 | static void emojiChooserEmojiPicked(WebKitWebViewBase* webkitWebViewBase, const char* text) |
1748 | { |
1749 | webkitWebViewBaseCompleteEmojiChooserRequest(webkitWebViewBase, String::fromUTF8(text)); |
1750 | } |
1751 | |
1752 | static void emojiChooserClosed(WebKitWebViewBase* webkitWebViewBase) |
1753 | { |
1754 | webkitWebViewBaseCompleteEmojiChooserRequest(webkitWebViewBase, emptyString()); |
1755 | webkitWebViewBase->priv->releaseEmojiChooserTimer.startOneShot(2_min); |
1756 | } |
1757 | #endif |
1758 | |
1759 | void 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) |
1785 | int webkitWebViewBaseRenderHostFileDescriptor(WebKitWebViewBase* webkitWebViewBase) |
1786 | { |
1787 | return webkitWebViewBase->priv->acceleratedBackingStore->renderHostFileDescriptor(); |
1788 | } |
1789 | #endif |
1790 | |