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.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25 * THE POSSIBILITY OF SUCH DAMAGE.
29 #include "WebKitWebViewBase.h"
31 #include "DrawingAreaProxyImpl.h"
32 #include "NativeWebMouseEvent.h"
33 #include "NativeWebWheelEvent.h"
34 #include "PageClientImpl.h"
35 #include "WebContext.h"
36 #include "WebEventFactory.h"
37 #include "WebFullScreenClientGtk.h"
38 #include "WebInspectorProxy.h"
39 #include "WebKitPrivate.h"
40 #include "WebKitWebViewBaseAccessible.h"
41 #include "WebKitWebViewBasePrivate.h"
42 #include "WebPageProxy.h"
43 #include "WebViewBaseInputMethodFilter.h"
44 #include <WebCore/ClipboardGtk.h>
45 #include <WebCore/ClipboardUtilitiesGtk.h>
46 #include <WebCore/DataObjectGtk.h>
47 #include <WebCore/DragData.h>
48 #include <WebCore/DragIcon.h>
49 #include <WebCore/GOwnPtrGtk.h>
50 #include <WebCore/GtkClickCounter.h>
51 #include <WebCore/GtkDragAndDropHelper.h>
52 #include <WebCore/GtkUtilities.h>
53 #include <WebCore/GtkVersioning.h>
54 #include <WebCore/NotImplemented.h>
55 #include <WebCore/PasteboardHelper.h>
56 #include <WebCore/RefPtrCairo.h>
57 #include <WebCore/Region.h>
59 #include <gdk/gdkkeysyms.h>
60 #include <wtf/HashMap.h>
61 #include <wtf/gobject/GOwnPtr.h>
62 #include <wtf/gobject/GRefPtr.h>
63 #include <wtf/text/CString.h>
65 #if ENABLE(FULLSCREEN_API)
66 #include "WebFullScreenManagerProxy.h"
69 #if USE(TEXTURE_MAPPER_GL) && defined(GDK_WINDOWING_X11)
70 #include <WebCore/RedirectedXCompositeWindow.h>
73 using namespace WebKit;
74 using namespace WebCore;
76 typedef HashMap<GtkWidget*, IntRect> WebKitWebViewChildrenMap;
78 #if USE(TEXTURE_MAPPER_GL)
79 void redirectedWindowDamagedCallback(void* data);
82 struct _WebKitWebViewBasePrivate {
83 _WebKitWebViewBasePrivate()
84 #if USE(TEXTURE_MAPPER_GL)
85 : redirectedWindow(RedirectedXCompositeWindow::create(IntSize(1, 1), RedirectedXCompositeWindow::DoNotCreateGLContext))
90 ~_WebKitWebViewBasePrivate()
95 WebKitWebViewChildrenMap children;
96 OwnPtr<PageClientImpl> pageClient;
97 RefPtr<WebPageProxy> pageProxy;
98 bool shouldForwardNextKeyEvent;
99 GtkClickCounter clickCounter;
102 GtkDragAndDropHelper dragAndDropHelper;
105 GRefPtr<AtkObject> accessible;
106 bool needsResizeOnMap;
107 WebKit2GtkAuthenticationDialog* authenticationDialog;
108 GtkWidget* inspectorView;
109 unsigned inspectorViewHeight;
110 GOwnPtr<GdkEvent> contextMenuEvent;
111 WebContextMenuProxyGtk* activeContextMenuProxy;
112 WebViewBaseInputMethodFilter inputMethodFilter;
114 GtkWindow* toplevelOnScreenWindow;
115 unsigned long toplevelResizeGripVisibilityID;
116 unsigned long toplevelFocusInEventID;
117 unsigned long toplevelFocusOutEventID;
120 bool isInWindowActive : 1;
124 WebKitWebViewBaseDownloadRequestHandler downloadHandler;
126 #if ENABLE(FULLSCREEN_API)
127 bool fullScreenModeActive;
128 WebFullScreenClientGtk fullScreenClient;
131 #if USE(TEXTURE_MAPPER_GL)
132 OwnPtr<RedirectedXCompositeWindow> redirectedWindow;
136 WEBKIT_DEFINE_TYPE(WebKitWebViewBase, webkit_web_view_base, GTK_TYPE_CONTAINER)
138 static void webkitWebViewBaseNotifyResizerSize(WebKitWebViewBase* webViewBase)
140 WebKitWebViewBasePrivate* priv = webViewBase->priv;
141 if (!priv->toplevelOnScreenWindow)
144 gboolean resizerVisible;
145 g_object_get(G_OBJECT(priv->toplevelOnScreenWindow), "resize-grip-visible", &resizerVisible, NULL);
148 if (resizerVisible) {
149 GdkRectangle resizerRect;
150 gtk_window_get_resize_grip_area(priv->toplevelOnScreenWindow, &resizerRect);
151 GdkRectangle allocation;
152 gtk_widget_get_allocation(GTK_WIDGET(webViewBase), &allocation);
153 if (gdk_rectangle_intersect(&resizerRect, &allocation, 0))
154 resizerSize = IntSize(resizerRect.width, resizerRect.height);
157 if (resizerSize != priv->resizerSize) {
158 priv->resizerSize = resizerSize;
159 priv->pageProxy->setWindowResizerSize(resizerSize);
163 static void toplevelWindowResizeGripVisibilityChanged(GObject*, GParamSpec*, WebKitWebViewBase* webViewBase)
165 webkitWebViewBaseNotifyResizerSize(webViewBase);
168 static gboolean toplevelWindowFocusInEvent(GtkWidget* widget, GdkEventFocus*, WebKitWebViewBase* webViewBase)
170 WebKitWebViewBasePrivate* priv = webViewBase->priv;
171 if (!priv->isInWindowActive) {
172 priv->isInWindowActive = true;
173 priv->pageProxy->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
179 static gboolean toplevelWindowFocusOutEvent(GtkWidget* widget, GdkEventFocus*, WebKitWebViewBase* webViewBase)
181 WebKitWebViewBasePrivate* priv = webViewBase->priv;
182 if (priv->isInWindowActive) {
183 priv->isInWindowActive = false;
184 priv->pageProxy->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
190 static void webkitWebViewBaseSetToplevelOnScreenWindow(WebKitWebViewBase* webViewBase, GtkWindow* window)
192 WebKitWebViewBasePrivate* priv = webViewBase->priv;
193 if (priv->toplevelOnScreenWindow == window)
196 if (priv->toplevelResizeGripVisibilityID) {
197 g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelResizeGripVisibilityID);
198 priv->toplevelResizeGripVisibilityID = 0;
200 if (priv->toplevelFocusInEventID) {
201 g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelFocusInEventID);
202 priv->toplevelFocusInEventID = 0;
204 if (priv->toplevelFocusOutEventID) {
205 g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelFocusOutEventID);
206 priv->toplevelFocusOutEventID = 0;
209 priv->toplevelOnScreenWindow = window;
210 priv->pageProxy->viewStateDidChange(WebPageProxy::ViewIsInWindow);
211 if (!priv->toplevelOnScreenWindow)
214 webkitWebViewBaseNotifyResizerSize(webViewBase);
216 priv->toplevelResizeGripVisibilityID =
217 g_signal_connect(priv->toplevelOnScreenWindow, "notify::resize-grip-visible",
218 G_CALLBACK(toplevelWindowResizeGripVisibilityChanged), webViewBase);
219 priv->toplevelFocusInEventID =
220 g_signal_connect(priv->toplevelOnScreenWindow, "focus-in-event",
221 G_CALLBACK(toplevelWindowFocusInEvent), webViewBase);
222 priv->toplevelFocusOutEventID =
223 g_signal_connect(priv->toplevelOnScreenWindow, "focus-out-event",
224 G_CALLBACK(toplevelWindowFocusOutEvent), webViewBase);
227 static void webkitWebViewBaseRealize(GtkWidget* widget)
229 gtk_widget_set_realized(widget, TRUE);
231 GtkAllocation allocation;
232 gtk_widget_get_allocation(widget, &allocation);
234 GdkWindowAttr attributes;
235 attributes.window_type = GDK_WINDOW_CHILD;
236 attributes.x = allocation.x;
237 attributes.y = allocation.y;
238 attributes.width = allocation.width;
239 attributes.height = allocation.height;
240 attributes.wclass = GDK_INPUT_OUTPUT;
241 attributes.visual = gtk_widget_get_visual(widget);
242 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK
244 | GDK_BUTTON_PRESS_MASK
245 | GDK_BUTTON_RELEASE_MASK
247 #if GTK_CHECK_VERSION(3, 3, 18)
248 | GDK_SMOOTH_SCROLL_MASK
250 | GDK_POINTER_MOTION_MASK
252 | GDK_KEY_RELEASE_MASK
253 | GDK_BUTTON_MOTION_MASK
254 | GDK_BUTTON1_MOTION_MASK
255 | GDK_BUTTON2_MOTION_MASK
256 | GDK_BUTTON3_MOTION_MASK;
258 gint attributesMask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
260 GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributesMask);
261 gtk_widget_set_window(widget, window);
262 gdk_window_set_user_data(window, widget);
264 gtk_style_context_set_background(gtk_widget_get_style_context(widget), window);
266 WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(widget);
267 GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
268 if (widgetIsOnscreenToplevelWindow(toplevel))
269 webkitWebViewBaseSetToplevelOnScreenWindow(webView, GTK_WINDOW(toplevel));
272 static bool webkitWebViewChildIsInternalWidget(WebKitWebViewBase* webViewBase, GtkWidget* widget)
274 WebKitWebViewBasePrivate* priv = webViewBase->priv;
275 return widget == priv->inspectorView || (priv->authenticationDialog && priv->authenticationDialog->widget() == widget);
278 static void webkitWebViewBaseContainerAdd(GtkContainer* container, GtkWidget* widget)
280 WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(container);
281 WebKitWebViewBasePrivate* priv = webView->priv;
283 // Internal widgets like the web inspector and authentication dialog have custom
284 // allocations so we don't need to add them to our list of children.
285 if (!webkitWebViewChildIsInternalWidget(webView, widget)) {
286 GtkAllocation childAllocation;
287 gtk_widget_get_allocation(widget, &childAllocation);
288 priv->children.set(widget, childAllocation);
291 gtk_widget_set_parent(widget, GTK_WIDGET(container));
294 void webkitWebViewBaseAddAuthenticationDialog(WebKitWebViewBase* webViewBase, WebKit2GtkAuthenticationDialog* dialog)
296 webViewBase->priv->authenticationDialog = dialog;
297 gtk_container_add(GTK_CONTAINER(webViewBase), dialog->widget());
298 gtk_widget_queue_draw(GTK_WIDGET(webViewBase)); // We need to draw the shadow over the widget.
301 void webkitWebViewBaseCancelAuthenticationDialog(WebKitWebViewBase* webViewBase)
303 WebKitWebViewBasePrivate* priv = webViewBase->priv;
304 if (priv->authenticationDialog)
305 priv->authenticationDialog->destroy();
308 void webkitWebViewBaseAddWebInspector(WebKitWebViewBase* webViewBase, GtkWidget* inspector)
310 webViewBase->priv->inspectorView = inspector;
311 gtk_container_add(GTK_CONTAINER(webViewBase), inspector);
314 static void webkitWebViewBaseContainerRemove(GtkContainer* container, GtkWidget* widget)
316 WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(container);
317 WebKitWebViewBasePrivate* priv = webView->priv;
318 GtkWidget* widgetContainer = GTK_WIDGET(container);
320 gboolean wasVisible = gtk_widget_get_visible(widget);
321 gtk_widget_unparent(widget);
323 if (priv->inspectorView == widget) {
324 priv->inspectorView = 0;
325 priv->inspectorViewHeight = 0;
326 } else if (priv->authenticationDialog && priv->authenticationDialog->widget() == widget) {
327 priv->authenticationDialog = 0;
329 ASSERT(priv->children.contains(widget));
330 priv->children.remove(widget);
332 if (wasVisible && gtk_widget_get_visible(widgetContainer))
333 gtk_widget_queue_resize(widgetContainer);
336 static void webkitWebViewBaseContainerForall(GtkContainer* container, gboolean includeInternals, GtkCallback callback, gpointer callbackData)
338 WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(container);
339 WebKitWebViewBasePrivate* priv = webView->priv;
341 WebKitWebViewChildrenMap children = priv->children;
342 WebKitWebViewChildrenMap::const_iterator end = children.end();
343 for (WebKitWebViewChildrenMap::const_iterator current = children.begin(); current != end; ++current)
344 (*callback)(current->key, callbackData);
346 if (includeInternals && priv->inspectorView)
347 (*callback)(priv->inspectorView, callbackData);
349 if (includeInternals && priv->authenticationDialog)
350 (*callback)(priv->authenticationDialog->widget(), callbackData);
353 void webkitWebViewBaseChildMoveResize(WebKitWebViewBase* webView, GtkWidget* child, const IntRect& childRect)
355 const IntRect& geometry = webView->priv->children.get(child);
356 if (geometry == childRect)
359 webView->priv->children.set(child, childRect);
360 gtk_widget_queue_resize_no_redraw(GTK_WIDGET(webView));
363 static void webkitWebViewBaseDispose(GObject* gobject)
365 webkitWebViewBaseSetToplevelOnScreenWindow(WEBKIT_WEB_VIEW_BASE(gobject), 0);
366 G_OBJECT_CLASS(webkit_web_view_base_parent_class)->dispose(gobject);
369 static void webkitWebViewBaseConstructed(GObject* object)
371 G_OBJECT_CLASS(webkit_web_view_base_parent_class)->constructed(object);
373 GtkWidget* viewWidget = GTK_WIDGET(object);
374 gtk_widget_set_can_focus(viewWidget, TRUE);
375 gtk_drag_dest_set(viewWidget, static_cast<GtkDestDefaults>(0), 0, 0,
376 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE));
377 gtk_drag_dest_set_target_list(viewWidget, PasteboardHelper::defaultPasteboardHelper()->targetList());
379 WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(object)->priv;
380 priv->pageClient = PageClientImpl::create(viewWidget);
381 priv->dragAndDropHelper.setWidget(viewWidget);
383 #if USE(TEXTURE_MAPPER_GL)
384 if (priv->redirectedWindow)
385 priv->redirectedWindow->setDamageNotifyCallback(redirectedWindowDamagedCallback, object);
388 priv->authenticationDialog = 0;
391 #if USE(TEXTURE_MAPPER_GL)
392 static bool webkitWebViewRenderAcceleratedCompositingResults(WebKitWebViewBase* webViewBase, DrawingAreaProxyImpl* drawingArea, cairo_t* cr, GdkRectangle* clipRect)
394 if (!drawingArea->isInAcceleratedCompositingMode())
397 // To avoid flashes when initializing accelerated compositing for the first
398 // time, we wait until we know there's a frame ready before rendering.
399 WebKitWebViewBasePrivate* priv = webViewBase->priv;
400 if (!priv->redirectedWindow)
403 cairo_rectangle(cr, clipRect->x, clipRect->y, clipRect->width, clipRect->height);
404 cairo_surface_t* surface = priv->redirectedWindow->cairoSurfaceForWidget(GTK_WIDGET(webViewBase));
405 cairo_set_source_surface(cr, surface, 0, 0);
411 static gboolean webkitWebViewBaseDraw(GtkWidget* widget, cairo_t* cr)
413 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
414 DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(webViewBase->priv->pageProxy->drawingArea());
418 GdkRectangle clipRect;
419 if (!gdk_cairo_get_clip_rectangle(cr, &clipRect))
422 #if USE(TEXTURE_MAPPER_GL)
423 if (webkitWebViewRenderAcceleratedCompositingResults(webViewBase, drawingArea, cr, &clipRect))
427 WebCore::Region unpaintedRegion; // This is simply unused.
428 drawingArea->paint(cr, clipRect, unpaintedRegion);
430 if (webViewBase->priv->authenticationDialog) {
431 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
432 cairo_set_source_rgba(cr, 0, 0, 0, 0.5);
439 static void webkitWebViewBaseChildAllocate(GtkWidget* child, gpointer userData)
441 if (!gtk_widget_get_visible(child))
444 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(userData);
445 WebKitWebViewBasePrivate* priv = webViewBase->priv;
446 const IntRect& geometry = priv->children.get(child);
447 if (geometry.isEmpty())
450 GtkAllocation childAllocation = geometry;
451 gtk_widget_size_allocate(child, &childAllocation);
452 priv->children.set(child, IntRect());
455 static void resizeWebKitWebViewBaseFromAllocation(WebKitWebViewBase* webViewBase, GtkAllocation* allocation, bool sizeChanged)
457 gtk_container_foreach(GTK_CONTAINER(webViewBase), webkitWebViewBaseChildAllocate, webViewBase);
459 IntRect viewRect(allocation->x, allocation->y, allocation->width, allocation->height);
460 WebKitWebViewBasePrivate* priv = webViewBase->priv;
461 if (priv->inspectorView) {
462 int inspectorViewHeight = std::min(static_cast<int>(priv->inspectorViewHeight), allocation->height);
463 GtkAllocation childAllocation = viewRect;
464 childAllocation.y = allocation->height - inspectorViewHeight;
465 childAllocation.height = inspectorViewHeight;
466 gtk_widget_size_allocate(priv->inspectorView, &childAllocation);
468 viewRect.setHeight(std::max(allocation->height - inspectorViewHeight, 1));
471 // The authentication dialog is centered in the view rect, which means that it
472 // never overlaps the web inspector. Thus, we need to calculate the allocation here
473 // after calculating the inspector allocation.
474 if (priv->authenticationDialog) {
475 GtkRequisition naturalSize;
476 gtk_widget_get_preferred_size(priv->authenticationDialog->widget(), 0, &naturalSize);
478 GtkAllocation childAllocation = {
479 (viewRect.width() - naturalSize.width) / 2,
480 (viewRect.height() - naturalSize.height) / 2,
484 gtk_widget_size_allocate(priv->authenticationDialog->widget(), &childAllocation);
487 #if USE(TEXTURE_MAPPER_GL)
488 if (sizeChanged && webViewBase->priv->redirectedWindow)
489 webViewBase->priv->redirectedWindow->resize(viewRect.size());
492 if (priv->pageProxy->drawingArea())
493 priv->pageProxy->drawingArea()->setSize(viewRect.size(), IntSize());
495 webkitWebViewBaseNotifyResizerSize(webViewBase);
498 static void webkitWebViewBaseSizeAllocate(GtkWidget* widget, GtkAllocation* allocation)
500 bool sizeChanged = gtk_widget_get_allocated_width(widget) != allocation->width
501 || gtk_widget_get_allocated_height(widget) != allocation->height;
503 GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->size_allocate(widget, allocation);
505 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
506 if (sizeChanged && !gtk_widget_get_mapped(widget) && !webViewBase->priv->pageProxy->drawingArea()->size().isEmpty()) {
507 webViewBase->priv->needsResizeOnMap = true;
511 resizeWebKitWebViewBaseFromAllocation(webViewBase, allocation, sizeChanged);
514 static void webkitWebViewBaseMap(GtkWidget* widget)
516 GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->map(widget);
518 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
519 WebKitWebViewBasePrivate* priv = webViewBase->priv;
520 if (!priv->isVisible) {
521 priv->isVisible = true;
522 priv->pageProxy->viewStateDidChange(WebPageProxy::ViewIsVisible);
525 if (!priv->needsResizeOnMap)
528 GtkAllocation allocation;
529 gtk_widget_get_allocation(widget, &allocation);
530 resizeWebKitWebViewBaseFromAllocation(webViewBase, &allocation, true /* sizeChanged */);
531 priv->needsResizeOnMap = false;
534 static void webkitWebViewBaseUnmap(GtkWidget* widget)
536 GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->unmap(widget);
538 WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
539 if (priv->isVisible) {
540 priv->isVisible = false;
541 priv->pageProxy->viewStateDidChange(WebPageProxy::ViewIsVisible);
545 static gboolean webkitWebViewBaseFocusInEvent(GtkWidget* widget, GdkEventFocus* event)
547 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
548 webkitWebViewBaseSetFocus(webViewBase, true);
549 webViewBase->priv->inputMethodFilter.notifyFocusedIn();
551 return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->focus_in_event(widget, event);
554 static gboolean webkitWebViewBaseFocusOutEvent(GtkWidget* widget, GdkEventFocus* event)
556 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
557 webkitWebViewBaseSetFocus(webViewBase, false);
558 webViewBase->priv->inputMethodFilter.notifyFocusedOut();
560 return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->focus_out_event(widget, event);
563 static gboolean webkitWebViewBaseKeyPressEvent(GtkWidget* widget, GdkEventKey* event)
565 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
566 WebKitWebViewBasePrivate* priv = webViewBase->priv;
568 if (priv->authenticationDialog)
569 return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->key_press_event(widget, event);
571 #if ENABLE(FULLSCREEN_API)
572 if (priv->fullScreenModeActive) {
573 switch (event->keyval) {
577 webkitWebViewBaseExitFullScreen(webViewBase);
585 // Since WebProcess key event handling is not synchronous, handle the event in two passes.
586 // When WebProcess processes the input event, it will call PageClientImpl::doneWithKeyEvent
587 // with event handled status which determines whether to pass the input event to parent or not
588 // using gtk_main_do_event().
589 if (priv->shouldForwardNextKeyEvent) {
590 priv->shouldForwardNextKeyEvent = FALSE;
591 return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->key_press_event(widget, event);
593 priv->inputMethodFilter.filterKeyEvent(event);
597 static gboolean webkitWebViewBaseKeyReleaseEvent(GtkWidget* widget, GdkEventKey* event)
599 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
600 WebKitWebViewBasePrivate* priv = webViewBase->priv;
602 if (priv->shouldForwardNextKeyEvent) {
603 priv->shouldForwardNextKeyEvent = FALSE;
604 return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->key_release_event(widget, event);
606 priv->inputMethodFilter.filterKeyEvent(event);
610 static gboolean webkitWebViewBaseButtonPressEvent(GtkWidget* widget, GdkEventButton* buttonEvent)
612 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
613 WebKitWebViewBasePrivate* priv = webViewBase->priv;
615 if (priv->authenticationDialog)
618 gtk_widget_grab_focus(widget);
620 priv->inputMethodFilter.notifyMouseButtonPress();
622 if (!priv->clickCounter.shouldProcessButtonEvent(buttonEvent))
625 // If it's a right click event save it as a possible context menu event.
626 if (buttonEvent->button == 3)
627 priv->contextMenuEvent.set(gdk_event_copy(reinterpret_cast<GdkEvent*>(buttonEvent)));
628 priv->pageProxy->handleMouseEvent(NativeWebMouseEvent(reinterpret_cast<GdkEvent*>(buttonEvent),
629 priv->clickCounter.clickCountForGdkButtonEvent(widget, buttonEvent)));
633 static gboolean webkitWebViewBaseButtonReleaseEvent(GtkWidget* widget, GdkEventButton* event)
635 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
636 WebKitWebViewBasePrivate* priv = webViewBase->priv;
638 if (priv->authenticationDialog)
641 gtk_widget_grab_focus(widget);
642 priv->pageProxy->handleMouseEvent(NativeWebMouseEvent(reinterpret_cast<GdkEvent*>(event), 0 /* currentClickCount */));
647 static gboolean webkitWebViewBaseScrollEvent(GtkWidget* widget, GdkEventScroll* event)
649 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
650 WebKitWebViewBasePrivate* priv = webViewBase->priv;
652 if (priv->authenticationDialog)
655 priv->pageProxy->handleWheelEvent(NativeWebWheelEvent(reinterpret_cast<GdkEvent*>(event)));
660 static gboolean webkitWebViewBaseMotionNotifyEvent(GtkWidget* widget, GdkEventMotion* event)
662 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
663 WebKitWebViewBasePrivate* priv = webViewBase->priv;
665 if (priv->authenticationDialog)
668 priv->pageProxy->handleMouseEvent(NativeWebMouseEvent(reinterpret_cast<GdkEvent*>(event), 0 /* currentClickCount */));
673 static gboolean webkitWebViewBaseQueryTooltip(GtkWidget* widget, gint x, gint y, gboolean keyboardMode, GtkTooltip* tooltip)
675 WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
678 // TODO: https://bugs.webkit.org/show_bug.cgi?id=61732.
683 if (priv->tooltipText.length() <= 0)
686 if (!priv->tooltipArea.isEmpty()) {
687 GdkRectangle area = priv->tooltipArea;
688 gtk_tooltip_set_tip_area(tooltip, &area);
690 gtk_tooltip_set_tip_area(tooltip, 0);
691 gtk_tooltip_set_text(tooltip, priv->tooltipText.data());
696 static void webkitWebViewBaseDragDataGet(GtkWidget* widget, GdkDragContext* context, GtkSelectionData* selectionData, guint info, guint time)
698 WEBKIT_WEB_VIEW_BASE(widget)->priv->dragAndDropHelper.handleGetDragData(context, selectionData, info);
701 static void webkitWebViewBaseDragEnd(GtkWidget* widget, GdkDragContext* context)
703 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
704 if (!webViewBase->priv->dragAndDropHelper.handleDragEnd(context))
707 GdkDevice* device = gdk_drag_context_get_device(context);
709 gdk_device_get_window_at_position(device, &x, &y);
710 int xRoot = 0, yRoot = 0;
711 gdk_device_get_position(device, 0, &xRoot, &yRoot);
712 webViewBase->priv->pageProxy->dragEnded(IntPoint(x, y), IntPoint(xRoot, yRoot),
713 gdkDragActionToDragOperation(gdk_drag_context_get_selected_action(context)));
716 static void webkitWebViewBaseDragDataReceived(GtkWidget* widget, GdkDragContext* context, gint x, gint y, GtkSelectionData* selectionData, guint info, guint time)
718 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
720 DataObjectGtk* dataObject = webViewBase->priv->dragAndDropHelper.handleDragDataReceived(context, selectionData, info, position);
724 DragData dragData(dataObject, position, convertWidgetPointToScreenPoint(widget, position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)));
725 webViewBase->priv->pageProxy->resetDragOperation();
726 webViewBase->priv->pageProxy->dragEntered(&dragData);
727 DragOperation operation = webViewBase->priv->pageProxy->dragSession().operation;
728 gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time);
731 static AtkObject* webkitWebViewBaseGetAccessible(GtkWidget* widget)
733 // If the socket has already been created and embedded a plug ID, return it.
734 WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
735 if (priv->accessible && atk_socket_is_occupied(ATK_SOCKET(priv->accessible.get())))
736 return priv->accessible.get();
738 // Create the accessible object and associate it to the widget.
739 if (!priv->accessible) {
740 priv->accessible = adoptGRef(ATK_OBJECT(webkitWebViewBaseAccessibleNew(widget)));
742 // Set the parent not to break bottom-up navigation.
743 GtkWidget* parentWidget = gtk_widget_get_parent(widget);
744 AtkObject* axParent = parentWidget ? gtk_widget_get_accessible(parentWidget) : 0;
746 atk_object_set_parent(priv->accessible.get(), axParent);
749 // Try to embed the plug in the socket, if posssible.
750 String plugID = priv->pageProxy->accessibilityPlugID();
752 return priv->accessible.get();
754 atk_socket_embed(ATK_SOCKET(priv->accessible.get()), const_cast<gchar*>(plugID.utf8().data()));
756 return priv->accessible.get();
759 static gboolean webkitWebViewBaseDragMotion(GtkWidget* widget, GdkDragContext* context, gint x, gint y, guint time)
761 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
762 IntPoint position(x, y);
763 DataObjectGtk* dataObject = webViewBase->priv->dragAndDropHelper.handleDragMotion(context, position, time);
767 DragData dragData(dataObject, position, convertWidgetPointToScreenPoint(widget, position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)));
768 webViewBase->priv->pageProxy->dragUpdated(&dragData);
769 DragOperation operation = webViewBase->priv->pageProxy->dragSession().operation;
770 gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time);
774 static void dragExitedCallback(GtkWidget* widget, DragData* dragData, bool dropHappened)
776 // Don't call dragExited if we have just received a drag-drop signal. This
777 // happens in the case of a successful drop onto the view.
781 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
782 webViewBase->priv->pageProxy->dragExited(dragData);
783 webViewBase->priv->pageProxy->resetDragOperation();
786 static void webkitWebViewBaseDragLeave(GtkWidget* widget, GdkDragContext* context, guint time)
788 WEBKIT_WEB_VIEW_BASE(widget)->priv->dragAndDropHelper.handleDragLeave(context, dragExitedCallback);
791 static gboolean webkitWebViewBaseDragDrop(GtkWidget* widget, GdkDragContext* context, gint x, gint y, guint time)
793 WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
794 DataObjectGtk* dataObject = webViewBase->priv->dragAndDropHelper.handleDragDrop(context);
798 IntPoint position(x, y);
799 DragData dragData(dataObject, position, convertWidgetPointToScreenPoint(widget, position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)));
800 SandboxExtension::Handle handle;
801 SandboxExtension::HandleArray sandboxExtensionForUpload;
802 webViewBase->priv->pageProxy->performDrag(&dragData, String(), handle, sandboxExtensionForUpload);
803 gtk_drag_finish(context, TRUE, FALSE, time);
807 static void webkitWebViewBaseParentSet(GtkWidget* widget, GtkWidget* oldParent)
809 if (!gtk_widget_get_parent(widget))
810 webkitWebViewBaseSetToplevelOnScreenWindow(WEBKIT_WEB_VIEW_BASE(widget), 0);
813 static gboolean webkitWebViewBaseFocus(GtkWidget* widget, GtkDirectionType direction)
815 // If the authentication dialog is active, we need to forward focus events there. This
816 // ensures that you can tab between elements in the box.
817 WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
818 if (priv->authenticationDialog) {
819 gboolean returnValue;
820 g_signal_emit_by_name(priv->authenticationDialog->widget(), "focus", direction, &returnValue);
824 return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->focus(widget, direction);
827 static void webkit_web_view_base_class_init(WebKitWebViewBaseClass* webkitWebViewBaseClass)
829 GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(webkitWebViewBaseClass);
830 widgetClass->realize = webkitWebViewBaseRealize;
831 widgetClass->draw = webkitWebViewBaseDraw;
832 widgetClass->size_allocate = webkitWebViewBaseSizeAllocate;
833 widgetClass->map = webkitWebViewBaseMap;
834 widgetClass->unmap = webkitWebViewBaseUnmap;
835 widgetClass->focus = webkitWebViewBaseFocus;
836 widgetClass->focus_in_event = webkitWebViewBaseFocusInEvent;
837 widgetClass->focus_out_event = webkitWebViewBaseFocusOutEvent;
838 widgetClass->key_press_event = webkitWebViewBaseKeyPressEvent;
839 widgetClass->key_release_event = webkitWebViewBaseKeyReleaseEvent;
840 widgetClass->button_press_event = webkitWebViewBaseButtonPressEvent;
841 widgetClass->button_release_event = webkitWebViewBaseButtonReleaseEvent;
842 widgetClass->scroll_event = webkitWebViewBaseScrollEvent;
843 widgetClass->motion_notify_event = webkitWebViewBaseMotionNotifyEvent;
844 widgetClass->query_tooltip = webkitWebViewBaseQueryTooltip;
845 widgetClass->drag_end = webkitWebViewBaseDragEnd;
846 widgetClass->drag_data_get = webkitWebViewBaseDragDataGet;
847 widgetClass->drag_motion = webkitWebViewBaseDragMotion;
848 widgetClass->drag_leave = webkitWebViewBaseDragLeave;
849 widgetClass->drag_drop = webkitWebViewBaseDragDrop;
850 widgetClass->drag_data_received = webkitWebViewBaseDragDataReceived;
851 widgetClass->get_accessible = webkitWebViewBaseGetAccessible;
852 widgetClass->parent_set = webkitWebViewBaseParentSet;
854 GObjectClass* gobjectClass = G_OBJECT_CLASS(webkitWebViewBaseClass);
855 gobjectClass->constructed = webkitWebViewBaseConstructed;
856 gobjectClass->dispose = webkitWebViewBaseDispose;
858 GtkContainerClass* containerClass = GTK_CONTAINER_CLASS(webkitWebViewBaseClass);
859 containerClass->add = webkitWebViewBaseContainerAdd;
860 containerClass->remove = webkitWebViewBaseContainerRemove;
861 containerClass->forall = webkitWebViewBaseContainerForall;
864 WebKitWebViewBase* webkitWebViewBaseCreate(WebContext* context, WebPageGroup* pageGroup)
866 WebKitWebViewBase* webkitWebViewBase = WEBKIT_WEB_VIEW_BASE(g_object_new(WEBKIT_TYPE_WEB_VIEW_BASE, NULL));
867 webkitWebViewBaseCreateWebPage(webkitWebViewBase, context, pageGroup);
868 return webkitWebViewBase;
871 GtkIMContext* webkitWebViewBaseGetIMContext(WebKitWebViewBase* webkitWebViewBase)
873 return webkitWebViewBase->priv->inputMethodFilter.context();
876 WebPageProxy* webkitWebViewBaseGetPage(WebKitWebViewBase* webkitWebViewBase)
878 return webkitWebViewBase->priv->pageProxy.get();
881 void webkitWebViewBaseCreateWebPage(WebKitWebViewBase* webkitWebViewBase, WebContext* context, WebPageGroup* pageGroup)
883 WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
885 priv->pageProxy = context->createWebPage(priv->pageClient.get(), pageGroup);
886 priv->pageProxy->initializeWebPage();
888 #if ENABLE(FULLSCREEN_API)
889 priv->pageProxy->fullScreenManager()->setWebView(webkitWebViewBase);
892 #if USE(TEXTURE_MAPPER_GL)
893 if (priv->redirectedWindow)
894 priv->pageProxy->setAcceleratedCompositingWindowId(priv->redirectedWindow->windowId());
897 // This must happen here instead of the instance initializer, because the input method
898 // filter must have access to the page.
899 priv->inputMethodFilter.setWebView(webkitWebViewBase);
902 void webkitWebViewBaseSetTooltipText(WebKitWebViewBase* webViewBase, const char* tooltip)
904 WebKitWebViewBasePrivate* priv = webViewBase->priv;
905 if (tooltip && tooltip[0] != '\0') {
906 priv->tooltipText = tooltip;
907 gtk_widget_set_has_tooltip(GTK_WIDGET(webViewBase), TRUE);
909 priv->tooltipText = "";
910 gtk_widget_set_has_tooltip(GTK_WIDGET(webViewBase), FALSE);
913 gtk_widget_trigger_tooltip_query(GTK_WIDGET(webViewBase));
916 void webkitWebViewBaseSetTooltipArea(WebKitWebViewBase* webViewBase, const IntRect& tooltipArea)
918 webViewBase->priv->tooltipArea = tooltipArea;
921 void webkitWebViewBaseStartDrag(WebKitWebViewBase* webViewBase, const DragData& dragData, PassRefPtr<ShareableBitmap> dragImage)
923 WebKitWebViewBasePrivate* priv = webViewBase->priv;
925 RefPtr<DataObjectGtk> dataObject = adoptRef(dragData.platformData());
926 GRefPtr<GtkTargetList> targetList = adoptGRef(PasteboardHelper::defaultPasteboardHelper()->targetListForDataObject(dataObject.get()));
927 GOwnPtr<GdkEvent> currentEvent(gtk_get_current_event());
928 GdkDragContext* context = gtk_drag_begin(GTK_WIDGET(webViewBase),
930 dragOperationToGdkDragActions(dragData.draggingSourceOperationMask()),
933 priv->dragAndDropHelper.startedDrag(context, dataObject.get());
936 // A drag starting should prevent a double-click from happening. This might
937 // happen if a drag is followed very quickly by another click (like in the DRT).
938 priv->clickCounter.reset();
941 RefPtr<cairo_surface_t> image(dragImage->createCairoSurface());
942 priv->dragIcon.setImage(image.get());
943 priv->dragIcon.useForDrag(context);
945 gtk_drag_set_icon_default(context);
948 void webkitWebViewBaseForwardNextKeyEvent(WebKitWebViewBase* webkitWebViewBase)
950 webkitWebViewBase->priv->shouldForwardNextKeyEvent = TRUE;
953 void webkitWebViewBaseEnterFullScreen(WebKitWebViewBase* webkitWebViewBase)
955 #if ENABLE(FULLSCREEN_API)
956 WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
957 if (priv->fullScreenModeActive)
960 if (!priv->fullScreenClient.willEnterFullScreen())
963 WebFullScreenManagerProxy* fullScreenManagerProxy = priv->pageProxy->fullScreenManager();
964 fullScreenManagerProxy->willEnterFullScreen();
966 GtkWidget* topLevelWindow = gtk_widget_get_toplevel(GTK_WIDGET(webkitWebViewBase));
967 if (gtk_widget_is_toplevel(topLevelWindow))
968 gtk_window_fullscreen(GTK_WINDOW(topLevelWindow));
969 fullScreenManagerProxy->didEnterFullScreen();
970 priv->fullScreenModeActive = true;
974 void webkitWebViewBaseExitFullScreen(WebKitWebViewBase* webkitWebViewBase)
976 #if ENABLE(FULLSCREEN_API)
977 WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
978 if (!priv->fullScreenModeActive)
981 if (!priv->fullScreenClient.willExitFullScreen())
984 WebFullScreenManagerProxy* fullScreenManagerProxy = priv->pageProxy->fullScreenManager();
985 fullScreenManagerProxy->willExitFullScreen();
987 GtkWidget* topLevelWindow = gtk_widget_get_toplevel(GTK_WIDGET(webkitWebViewBase));
988 if (gtk_widget_is_toplevel(topLevelWindow))
989 gtk_window_unfullscreen(GTK_WINDOW(topLevelWindow));
990 fullScreenManagerProxy->didExitFullScreen();
991 priv->fullScreenModeActive = false;
995 void webkitWebViewBaseInitializeFullScreenClient(WebKitWebViewBase* webkitWebViewBase, const WKFullScreenClientGtk* wkClient)
997 webkitWebViewBase->priv->fullScreenClient.initialize(wkClient);
1000 void webkitWebViewBaseSetInspectorViewHeight(WebKitWebViewBase* webkitWebViewBase, unsigned height)
1002 if (webkitWebViewBase->priv->inspectorViewHeight == height)
1004 webkitWebViewBase->priv->inspectorViewHeight = height;
1005 if (webkitWebViewBase->priv->inspectorView)
1006 gtk_widget_queue_resize_no_redraw(GTK_WIDGET(webkitWebViewBase));
1009 void webkitWebViewBaseSetActiveContextMenuProxy(WebKitWebViewBase* webkitWebViewBase, WebContextMenuProxyGtk* contextMenuProxy)
1011 webkitWebViewBase->priv->activeContextMenuProxy = contextMenuProxy;
1014 WebContextMenuProxyGtk* webkitWebViewBaseGetActiveContextMenuProxy(WebKitWebViewBase* webkitWebViewBase)
1016 return webkitWebViewBase->priv->activeContextMenuProxy;
1019 GdkEvent* webkitWebViewBaseTakeContextMenuEvent(WebKitWebViewBase* webkitWebViewBase)
1021 return webkitWebViewBase->priv->contextMenuEvent.release();
1024 #if USE(TEXTURE_MAPPER_GL)
1025 void redirectedWindowDamagedCallback(void* data)
1027 gtk_widget_queue_draw(GTK_WIDGET(data));
1031 void webkitWebViewBaseSetFocus(WebKitWebViewBase* webViewBase, bool focused)
1033 WebKitWebViewBasePrivate* priv = webViewBase->priv;
1034 if (priv->isFocused == focused)
1037 unsigned viewStateFlags = WebPageProxy::ViewIsFocused;
1038 priv->isFocused = focused;
1040 // If the view has received the focus and the window is not active
1041 // mark the current window as active now. This can happen if the
1042 // toplevel window is a GTK_WINDOW_POPUP and the focus has been
1043 // set programatically like WebKitTestRunner does, because POPUP
1044 // can't be focused.
1045 if (priv->isFocused && !priv->isInWindowActive) {
1046 priv->isInWindowActive = true;
1047 viewStateFlags |= WebPageProxy::ViewWindowIsActive;
1049 priv->pageProxy->viewStateDidChange(viewStateFlags);
1052 bool webkitWebViewBaseIsInWindowActive(WebKitWebViewBase* webViewBase)
1054 return webViewBase->priv->isInWindowActive;
1057 bool webkitWebViewBaseIsFocused(WebKitWebViewBase* webViewBase)
1059 return webViewBase->priv->isFocused;
1062 bool webkitWebViewBaseIsVisible(WebKitWebViewBase* webViewBase)
1064 return webViewBase->priv->isVisible;
1067 bool webkitWebViewBaseIsInWindow(WebKitWebViewBase* webViewBase)
1069 return webViewBase->priv->toplevelOnScreenWindow;
1072 void webkitWebViewBaseSetDownloadRequestHandler(WebKitWebViewBase* webViewBase, WebKitWebViewBaseDownloadRequestHandler downloadHandler)
1074 webViewBase->priv->downloadHandler = downloadHandler;
1077 void webkitWebViewBaseHandleDownloadRequest(WebKitWebViewBase* webViewBase, DownloadProxy* download)
1079 if (webViewBase->priv->downloadHandler)
1080 webViewBase->priv->downloadHandler(webViewBase, download);
1083 void webkitWebViewBaseSetInputMethodState(WebKitWebViewBase* webkitWebViewBase, bool enabled)
1085 webkitWebViewBase->priv->inputMethodFilter.setEnabled(enabled);
1088 void webkitWebViewBaseUpdateTextInputState(WebKitWebViewBase* webkitWebViewBase)
1090 webkitWebViewBase->priv->inputMethodFilter.setCursorRect(webkitWebViewBase->priv->pageProxy->editorState().cursorRect);