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