[GTK] Use XDamage to simplify RedirectedXCompositeWindow
[WebKit-https.git] / Source / WebKit2 / UIProcess / API / gtk / WebKitWebViewBase.cpp
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  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  *
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.
26  */
27
28 #include "config.h"
29 #include "WebKitWebViewBase.h"
30
31 #include "DrawingAreaProxyImpl.h"
32 #include "NativeWebKeyboardEvent.h"
33 #include "NativeWebMouseEvent.h"
34 #include "NativeWebWheelEvent.h"
35 #include "PageClientImpl.h"
36 #include "WebContext.h"
37 #include "WebEventFactory.h"
38 #include "WebFullScreenClientGtk.h"
39 #include "WebInspectorProxy.h"
40 #include "WebKitPrivate.h"
41 #include "WebKitWebViewBaseAccessible.h"
42 #include "WebKitWebViewBasePrivate.h"
43 #include "WebPageProxy.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>
58 #include <gdk/gdk.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>
64
65 #if ENABLE(FULLSCREEN_API)
66 #include "WebFullScreenManagerProxy.h"
67 #endif
68
69 #if USE(TEXTURE_MAPPER_GL) && defined(GDK_WINDOWING_X11)
70 #include <WebCore/RedirectedXCompositeWindow.h>
71 #endif
72
73 using namespace WebKit;
74 using namespace WebCore;
75
76 typedef HashMap<GtkWidget*, IntRect> WebKitWebViewChildrenMap;
77
78 #if USE(TEXTURE_MAPPER_GL)
79 void redirectedWindowDamagedCallback(void* data);
80 #endif
81
82 struct _WebKitWebViewBasePrivate {
83     WebKitWebViewChildrenMap children;
84     OwnPtr<PageClientImpl> pageClient;
85     RefPtr<WebPageProxy> pageProxy;
86     bool shouldForwardNextKeyEvent;
87     GRefPtr<GtkIMContext> imContext;
88     GtkClickCounter clickCounter;
89     CString tooltipText;
90     IntRect tooltipArea;
91     GtkDragAndDropHelper dragAndDropHelper;
92     DragIcon dragIcon;
93     IntSize resizerSize;
94     GRefPtr<AtkObject> accessible;
95     bool needsResizeOnMap;
96     GtkWidget* inspectorView;
97     unsigned inspectorViewHeight;
98     GOwnPtr<GdkEvent> contextMenuEvent;
99     WebContextMenuProxyGtk* activeContextMenuProxy;
100
101     GtkWindow* toplevelOnScreenWindow;
102     unsigned long toplevelResizeGripVisibilityID;
103     unsigned long toplevelFocusInEventID;
104     unsigned long toplevelFocusOutEventID;
105
106     // View State.
107     bool isInWindowActive : 1;
108     bool isFocused : 1;
109     bool isVisible : 1;
110
111 #if ENABLE(FULLSCREEN_API)
112     bool fullScreenModeActive;
113     WebFullScreenClientGtk fullScreenClient;
114 #endif
115
116 #if USE(TEXTURE_MAPPER_GL)
117     OwnPtr<RedirectedXCompositeWindow> redirectedWindow;
118 #endif
119 };
120
121 G_DEFINE_TYPE(WebKitWebViewBase, webkit_web_view_base, GTK_TYPE_CONTAINER)
122
123 static void webkitWebViewBaseNotifyResizerSize(WebKitWebViewBase* webViewBase)
124 {
125     WebKitWebViewBasePrivate* priv = webViewBase->priv;
126     if (!priv->toplevelOnScreenWindow)
127         return;
128
129     gboolean resizerVisible;
130     g_object_get(G_OBJECT(priv->toplevelOnScreenWindow), "resize-grip-visible", &resizerVisible, NULL);
131
132     IntSize resizerSize;
133     if (resizerVisible) {
134         GdkRectangle resizerRect;
135         gtk_window_get_resize_grip_area(priv->toplevelOnScreenWindow, &resizerRect);
136         GdkRectangle allocation;
137         gtk_widget_get_allocation(GTK_WIDGET(webViewBase), &allocation);
138         if (gdk_rectangle_intersect(&resizerRect, &allocation, 0))
139             resizerSize = IntSize(resizerRect.width, resizerRect.height);
140     }
141
142     if (resizerSize != priv->resizerSize) {
143         priv->resizerSize = resizerSize;
144         priv->pageProxy->setWindowResizerSize(resizerSize);
145     }
146 }
147
148 static void toplevelWindowResizeGripVisibilityChanged(GObject*, GParamSpec*, WebKitWebViewBase* webViewBase)
149 {
150     webkitWebViewBaseNotifyResizerSize(webViewBase);
151 }
152
153 static gboolean toplevelWindowFocusInEvent(GtkWidget* widget, GdkEventFocus*, WebKitWebViewBase* webViewBase)
154 {
155     WebKitWebViewBasePrivate* priv = webViewBase->priv;
156     if (!priv->isInWindowActive) {
157         priv->isInWindowActive = true;
158         priv->pageProxy->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
159     }
160
161     return FALSE;
162 }
163
164 static gboolean toplevelWindowFocusOutEvent(GtkWidget* widget, GdkEventFocus*, WebKitWebViewBase* webViewBase)
165 {
166     WebKitWebViewBasePrivate* priv = webViewBase->priv;
167     if (priv->isInWindowActive) {
168         priv->isInWindowActive = false;
169         priv->pageProxy->viewStateDidChange(WebPageProxy::ViewWindowIsActive);
170     }
171
172     return FALSE;
173 }
174
175 static void webkitWebViewBaseSetToplevelOnScreenWindow(WebKitWebViewBase* webViewBase, GtkWindow* window)
176 {
177     WebKitWebViewBasePrivate* priv = webViewBase->priv;
178     if (priv->toplevelOnScreenWindow == window)
179         return;
180
181     if (priv->toplevelResizeGripVisibilityID) {
182         g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelResizeGripVisibilityID);
183         priv->toplevelResizeGripVisibilityID = 0;
184     }
185     if (priv->toplevelFocusInEventID) {
186         g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelFocusInEventID);
187         priv->toplevelFocusInEventID = 0;
188     }
189     if (priv->toplevelFocusOutEventID) {
190         g_signal_handler_disconnect(priv->toplevelOnScreenWindow, priv->toplevelFocusOutEventID);
191         priv->toplevelFocusOutEventID = 0;
192     }
193
194     priv->toplevelOnScreenWindow = window;
195     priv->pageProxy->viewStateDidChange(WebPageProxy::ViewIsInWindow);
196     if (!priv->toplevelOnScreenWindow)
197         return;
198
199     webkitWebViewBaseNotifyResizerSize(webViewBase);
200
201     priv->toplevelResizeGripVisibilityID =
202         g_signal_connect(priv->toplevelOnScreenWindow, "notify::resize-grip-visible",
203                          G_CALLBACK(toplevelWindowResizeGripVisibilityChanged), webViewBase);
204     priv->toplevelFocusInEventID =
205         g_signal_connect(priv->toplevelOnScreenWindow, "focus-in-event",
206                          G_CALLBACK(toplevelWindowFocusInEvent), webViewBase);
207     priv->toplevelFocusOutEventID =
208         g_signal_connect(priv->toplevelOnScreenWindow, "focus-out-event",
209                          G_CALLBACK(toplevelWindowFocusOutEvent), webViewBase);
210 }
211
212 static void webkitWebViewBaseRealize(GtkWidget* widget)
213 {
214     gtk_widget_set_realized(widget, TRUE);
215
216     GtkAllocation allocation;
217     gtk_widget_get_allocation(widget, &allocation);
218
219     GdkWindowAttr attributes;
220     attributes.window_type = GDK_WINDOW_CHILD;
221     attributes.x = allocation.x;
222     attributes.y = allocation.y;
223     attributes.width = allocation.width;
224     attributes.height = allocation.height;
225     attributes.wclass = GDK_INPUT_OUTPUT;
226     attributes.visual = gtk_widget_get_visual(widget);
227     attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK
228         | GDK_EXPOSURE_MASK
229         | GDK_BUTTON_PRESS_MASK
230         | GDK_BUTTON_RELEASE_MASK
231         | GDK_SCROLL_MASK
232 #if GTK_CHECK_VERSION(3, 3, 18)
233         | GDK_SMOOTH_SCROLL_MASK
234 #endif
235         | GDK_POINTER_MOTION_MASK
236         | GDK_KEY_PRESS_MASK
237         | GDK_KEY_RELEASE_MASK
238         | GDK_BUTTON_MOTION_MASK
239         | GDK_BUTTON1_MOTION_MASK
240         | GDK_BUTTON2_MOTION_MASK
241         | GDK_BUTTON3_MOTION_MASK;
242
243     gint attributesMask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
244
245     GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributesMask);
246     gtk_widget_set_window(widget, window);
247     gdk_window_set_user_data(window, widget);
248
249     gtk_style_context_set_background(gtk_widget_get_style_context(widget), window);
250
251     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(widget);
252     WebKitWebViewBasePrivate* priv = webView->priv;
253     gtk_im_context_set_client_window(priv->imContext.get(), window);
254
255     GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
256     if (widgetIsOnscreenToplevelWindow(toplevel))
257         webkitWebViewBaseSetToplevelOnScreenWindow(webView, GTK_WINDOW(toplevel));
258 }
259
260 static void webkitWebViewBaseContainerAdd(GtkContainer* container, GtkWidget* widget)
261 {
262     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(container);
263     WebKitWebViewBasePrivate* priv = webView->priv;
264
265     if (WEBKIT_IS_WEB_VIEW_BASE(widget)
266         && WebInspectorProxy::isInspectorPage(WEBKIT_WEB_VIEW_BASE(widget)->priv->pageProxy.get())) {
267         ASSERT(!priv->inspectorView);
268         priv->inspectorView = widget;
269     } else {
270         GtkAllocation childAllocation;
271         gtk_widget_get_allocation(widget, &childAllocation);
272         priv->children.set(widget, childAllocation);
273     }
274
275     gtk_widget_set_parent(widget, GTK_WIDGET(container));
276 }
277
278 static void webkitWebViewBaseContainerRemove(GtkContainer* container, GtkWidget* widget)
279 {
280     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(container);
281     WebKitWebViewBasePrivate* priv = webView->priv;
282     GtkWidget* widgetContainer = GTK_WIDGET(container);
283
284     gboolean wasVisible = gtk_widget_get_visible(widget);
285     gtk_widget_unparent(widget);
286
287     if (priv->inspectorView == widget) {
288         priv->inspectorView = 0;
289         priv->inspectorViewHeight = 0;
290     } else {
291         ASSERT(priv->children.contains(widget));
292         priv->children.remove(widget);
293     }
294     if (wasVisible && gtk_widget_get_visible(widgetContainer))
295         gtk_widget_queue_resize(widgetContainer);
296 }
297
298 static void webkitWebViewBaseContainerForall(GtkContainer* container, gboolean includeInternals, GtkCallback callback, gpointer callbackData)
299 {
300     WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(container);
301     WebKitWebViewBasePrivate* priv = webView->priv;
302
303     WebKitWebViewChildrenMap children = priv->children;
304     WebKitWebViewChildrenMap::const_iterator end = children.end();
305     for (WebKitWebViewChildrenMap::const_iterator current = children.begin(); current != end; ++current)
306         (*callback)(current->first, callbackData);
307
308     if (includeInternals && priv->inspectorView)
309         (*callback)(priv->inspectorView, callbackData);
310 }
311
312 void webkitWebViewBaseChildMoveResize(WebKitWebViewBase* webView, GtkWidget* child, const IntRect& childRect)
313 {
314     const IntRect& geometry = webView->priv->children.get(child);
315
316     if (geometry == childRect)
317         return;
318
319     webView->priv->children.set(child, childRect);
320     gtk_widget_queue_resize_no_redraw(GTK_WIDGET(webView));
321 }
322
323 static void webkitWebViewBaseFinalize(GObject* gobject)
324 {
325     WebKitWebViewBase* webkitWebViewBase = WEBKIT_WEB_VIEW_BASE(gobject);
326     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
327     priv->pageProxy->close();
328
329     webkitWebViewBaseSetToplevelOnScreenWindow(webkitWebViewBase, 0);
330
331     priv->~WebKitWebViewBasePrivate();
332     G_OBJECT_CLASS(webkit_web_view_base_parent_class)->finalize(gobject);
333 }
334
335 static void webkit_web_view_base_init(WebKitWebViewBase* webkitWebViewBase)
336 {
337     WebKitWebViewBasePrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(webkitWebViewBase, WEBKIT_TYPE_WEB_VIEW_BASE, WebKitWebViewBasePrivate);
338     webkitWebViewBase->priv = priv;
339     new (priv) WebKitWebViewBasePrivate();
340
341     priv->shouldForwardNextKeyEvent = FALSE;
342
343     GtkWidget* viewWidget = GTK_WIDGET(webkitWebViewBase);
344     gtk_widget_set_can_focus(viewWidget, TRUE);
345     priv->imContext = adoptGRef(gtk_im_multicontext_new());
346
347     priv->pageClient = PageClientImpl::create(viewWidget);
348
349     priv->dragAndDropHelper.setWidget(viewWidget);
350
351     gtk_drag_dest_set(viewWidget, static_cast<GtkDestDefaults>(0), 0, 0,
352                       static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_PRIVATE));
353     gtk_drag_dest_set_target_list(viewWidget, PasteboardHelper::defaultPasteboardHelper()->targetList());
354
355 #if USE(TEXTURE_MAPPER_GL)
356     priv->redirectedWindow = RedirectedXCompositeWindow::create(IntSize(1, 1));
357     if (priv->redirectedWindow)
358         priv->redirectedWindow->setDamageNotifyCallback(redirectedWindowDamagedCallback, webkitWebViewBase);
359 #endif
360 }
361
362 #if USE(TEXTURE_MAPPER_GL)
363 static bool webkitWebViewRenderAcceleratedCompositingResults(WebKitWebViewBase* webViewBase, DrawingAreaProxyImpl* drawingArea, cairo_t* cr, GdkRectangle* clipRect)
364 {
365     if (!drawingArea->isInAcceleratedCompositingMode())
366         return false;
367
368     // To avoid flashes when initializing accelerated compositing for the first
369     // time, we wait until we know there's a frame ready before rendering.
370     WebKitWebViewBasePrivate* priv = webViewBase->priv;
371     if (!priv->redirectedWindow)
372         return false;
373
374     cairo_rectangle(cr, clipRect->x, clipRect->y, clipRect->width, clipRect->height);
375     cairo_surface_t* surface = priv->redirectedWindow->cairoSurfaceForWidget(GTK_WIDGET(webViewBase));
376     cairo_set_source_surface(cr, surface, 0, 0);
377     cairo_fill(cr);
378     return true;
379 }
380 #endif
381
382 static gboolean webkitWebViewBaseDraw(GtkWidget* widget, cairo_t* cr)
383 {
384     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
385     DrawingAreaProxyImpl* drawingArea = static_cast<DrawingAreaProxyImpl*>(webViewBase->priv->pageProxy->drawingArea());
386     if (!drawingArea)
387         return FALSE;
388
389     GdkRectangle clipRect;
390     if (!gdk_cairo_get_clip_rectangle(cr, &clipRect))
391         return FALSE;
392
393 #if USE(TEXTURE_MAPPER_GL)
394     if (webkitWebViewRenderAcceleratedCompositingResults(webViewBase, drawingArea, cr, &clipRect))
395         return FALSE;
396 #endif
397
398     WebCore::Region unpaintedRegion; // This is simply unused.
399     drawingArea->paint(cr, clipRect, unpaintedRegion);
400
401     return FALSE;
402 }
403
404 static void webkitWebViewBaseChildAllocate(GtkWidget* child, gpointer userData)
405 {
406     if (!gtk_widget_get_visible(child))
407         return;
408
409     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(userData);
410     WebKitWebViewBasePrivate* priv = webViewBase->priv;
411     const IntRect& geometry = priv->children.get(child);
412     if (geometry.isEmpty())
413         return;
414
415     GtkAllocation childAllocation = geometry;
416     gtk_widget_size_allocate(child, &childAllocation);
417     priv->children.set(child, IntRect());
418 }
419
420 static void resizeWebKitWebViewBaseFromAllocation(WebKitWebViewBase* webViewBase, GtkAllocation* allocation, bool sizeChanged)
421 {
422     gtk_container_foreach(GTK_CONTAINER(webViewBase), webkitWebViewBaseChildAllocate, webViewBase);
423
424     IntRect viewRect(allocation->x, allocation->y, allocation->width, allocation->height);
425     WebKitWebViewBasePrivate* priv = webViewBase->priv;
426     if (priv->inspectorView) {
427         GtkAllocation childAllocation = viewRect;
428         childAllocation.y = allocation->height - priv->inspectorViewHeight;
429         childAllocation.height = priv->inspectorViewHeight;
430         gtk_widget_size_allocate(priv->inspectorView, &childAllocation);
431
432         viewRect.setHeight(allocation->height - priv->inspectorViewHeight);
433     }
434
435 #if USE(TEXTURE_MAPPER_GL)
436     if (sizeChanged && webViewBase->priv->redirectedWindow)
437         webViewBase->priv->redirectedWindow->resize(viewRect.size());
438 #endif
439
440     if (priv->pageProxy->drawingArea())
441         priv->pageProxy->drawingArea()->setSize(viewRect.size(), IntSize());
442
443     webkitWebViewBaseNotifyResizerSize(webViewBase);
444 }
445
446 static void webkitWebViewBaseSizeAllocate(GtkWidget* widget, GtkAllocation* allocation)
447 {
448     bool sizeChanged = gtk_widget_get_allocated_width(widget) != allocation->width
449                        || gtk_widget_get_allocated_height(widget) != allocation->height;
450
451     GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->size_allocate(widget, allocation);
452
453     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
454     if (sizeChanged && !gtk_widget_get_mapped(widget) && !webViewBase->priv->pageProxy->drawingArea()->size().isEmpty()) {
455         webViewBase->priv->needsResizeOnMap = true;
456         return;
457     }
458
459     resizeWebKitWebViewBaseFromAllocation(webViewBase, allocation, sizeChanged);
460 }
461
462 static void webkitWebViewBaseMap(GtkWidget* widget)
463 {
464     GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->map(widget);
465
466     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
467     WebKitWebViewBasePrivate* priv = webViewBase->priv;
468     if (!priv->isVisible) {
469         priv->isVisible = true;
470         priv->pageProxy->viewStateDidChange(WebPageProxy::ViewIsVisible);
471     }
472
473     if (!priv->needsResizeOnMap)
474         return;
475
476     GtkAllocation allocation;
477     gtk_widget_get_allocation(widget, &allocation);
478     resizeWebKitWebViewBaseFromAllocation(webViewBase, &allocation, true /* sizeChanged */);
479     priv->needsResizeOnMap = false;
480 }
481
482 static void webkitWebViewBaseUnmap(GtkWidget* widget)
483 {
484     GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->unmap(widget);
485
486     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
487     if (priv->isVisible) {
488         priv->isVisible = false;
489         priv->pageProxy->viewStateDidChange(WebPageProxy::ViewIsVisible);
490     }
491 }
492
493 static gboolean webkitWebViewBaseFocusInEvent(GtkWidget* widget, GdkEventFocus* event)
494 {
495     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
496     webkitWebViewBaseSetFocus(webViewBase, true);
497     gtk_im_context_focus_in(webViewBase->priv->imContext.get());
498
499     return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->focus_in_event(widget, event);
500 }
501
502 static gboolean webkitWebViewBaseFocusOutEvent(GtkWidget* widget, GdkEventFocus* event)
503 {
504     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
505     webkitWebViewBaseSetFocus(webViewBase, false);
506     gtk_im_context_focus_out(webViewBase->priv->imContext.get());
507
508     return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->focus_out_event(widget, event);
509 }
510
511 static gboolean webkitWebViewBaseKeyPressEvent(GtkWidget* widget, GdkEventKey* event)
512 {
513     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
514     WebKitWebViewBasePrivate* priv = webViewBase->priv;
515
516 #if ENABLE(FULLSCREEN_API)
517     if (priv->fullScreenModeActive) {
518         switch (event->keyval) {
519         case GDK_KEY_Escape:
520         case GDK_KEY_f:
521         case GDK_KEY_F:
522             webkitWebViewBaseExitFullScreen(webViewBase);
523             return TRUE;
524         default:
525             break;
526         }
527     }
528 #endif
529
530     // Since WebProcess key event handling is not synchronous, handle the event in two passes.
531     // When WebProcess processes the input event, it will call PageClientImpl::doneWithKeyEvent
532     // with event handled status which determines whether to pass the input event to parent or not
533     // using gtk_main_do_event().
534     if (priv->shouldForwardNextKeyEvent) {
535         priv->shouldForwardNextKeyEvent = FALSE;
536         return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->key_press_event(widget, event);
537     }
538     priv->pageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(reinterpret_cast<GdkEvent*>(event)));
539     return TRUE;
540 }
541
542 static gboolean webkitWebViewBaseKeyReleaseEvent(GtkWidget* widget, GdkEventKey* event)
543 {
544     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
545     WebKitWebViewBasePrivate* priv = webViewBase->priv;
546
547     if (gtk_im_context_filter_keypress(priv->imContext.get(), event))
548         return TRUE;
549
550     if (priv->shouldForwardNextKeyEvent) {
551         priv->shouldForwardNextKeyEvent = FALSE;
552         return GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->key_release_event(widget, event);
553     }
554     priv->pageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(reinterpret_cast<GdkEvent*>(event)));
555     return TRUE;
556 }
557
558 static gboolean webkitWebViewBaseButtonPressEvent(GtkWidget* widget, GdkEventButton* buttonEvent)
559 {
560     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
561     WebKitWebViewBasePrivate* priv = webViewBase->priv;
562     gtk_widget_grab_focus(widget);
563
564     if (!priv->clickCounter.shouldProcessButtonEvent(buttonEvent))
565         return TRUE;
566
567     // If it's a right click event save it as a possible context menu event.
568     if (buttonEvent->button == 3)
569         priv->contextMenuEvent.set(gdk_event_copy(reinterpret_cast<GdkEvent*>(buttonEvent)));
570     priv->pageProxy->handleMouseEvent(NativeWebMouseEvent(reinterpret_cast<GdkEvent*>(buttonEvent),
571                                                      priv->clickCounter.clickCountForGdkButtonEvent(widget, buttonEvent)));
572     return TRUE;
573 }
574
575 static gboolean webkitWebViewBaseButtonReleaseEvent(GtkWidget* widget, GdkEventButton* event)
576 {
577     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
578     WebKitWebViewBasePrivate* priv = webViewBase->priv;
579
580     gtk_widget_grab_focus(widget);
581     priv->pageProxy->handleMouseEvent(NativeWebMouseEvent(reinterpret_cast<GdkEvent*>(event), 0 /* currentClickCount */));
582
583     return TRUE;
584 }
585
586 static gboolean webkitWebViewBaseScrollEvent(GtkWidget* widget, GdkEventScroll* event)
587 {
588     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
589     WebKitWebViewBasePrivate* priv = webViewBase->priv;
590
591     priv->pageProxy->handleWheelEvent(NativeWebWheelEvent(reinterpret_cast<GdkEvent*>(event)));
592
593     return TRUE;
594 }
595
596 static gboolean webkitWebViewBaseMotionNotifyEvent(GtkWidget* widget, GdkEventMotion* event)
597 {
598     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
599     WebKitWebViewBasePrivate* priv = webViewBase->priv;
600
601     priv->pageProxy->handleMouseEvent(NativeWebMouseEvent(reinterpret_cast<GdkEvent*>(event), 0 /* currentClickCount */));
602
603     return TRUE;
604 }
605
606 static gboolean webkitWebViewBaseQueryTooltip(GtkWidget* widget, gint x, gint y, gboolean keyboardMode, GtkTooltip* tooltip)
607 {
608     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
609
610     if (keyboardMode) {
611         // TODO: https://bugs.webkit.org/show_bug.cgi?id=61732.
612         notImplemented();
613         return FALSE;
614     }
615
616     if (priv->tooltipText.length() <= 0)
617         return FALSE;
618
619     if (!priv->tooltipArea.isEmpty()) {
620         GdkRectangle area = priv->tooltipArea;
621         gtk_tooltip_set_tip_area(tooltip, &area);
622     } else
623         gtk_tooltip_set_tip_area(tooltip, 0);
624     gtk_tooltip_set_text(tooltip, priv->tooltipText.data());
625
626     return TRUE;
627 }
628
629 static void webkitWebViewBaseDragDataGet(GtkWidget* widget, GdkDragContext* context, GtkSelectionData* selectionData, guint info, guint time)
630 {
631     WEBKIT_WEB_VIEW_BASE(widget)->priv->dragAndDropHelper.handleGetDragData(context, selectionData, info);
632 }
633
634 static void webkitWebViewBaseDragEnd(GtkWidget* widget, GdkDragContext* context)
635 {
636     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
637     if (!webViewBase->priv->dragAndDropHelper.handleDragEnd(context))
638         return;
639
640     GdkDevice* device = gdk_drag_context_get_device(context);
641     int x = 0, y = 0;
642     gdk_device_get_window_at_position(device, &x, &y);
643     int xRoot = 0, yRoot = 0;
644     gdk_device_get_position(device, 0, &xRoot, &yRoot);
645     webViewBase->priv->pageProxy->dragEnded(IntPoint(x, y), IntPoint(xRoot, yRoot),
646                                             gdkDragActionToDragOperation(gdk_drag_context_get_selected_action(context)));
647 }
648
649 static void webkitWebViewBaseDragDataReceived(GtkWidget* widget, GdkDragContext* context, gint x, gint y, GtkSelectionData* selectionData, guint info, guint time)
650 {
651     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
652     OwnPtr<DragData> dragData(webViewBase->priv->dragAndDropHelper.handleDragDataReceived(context, selectionData, info));
653     if (!dragData)
654         return;
655
656     webViewBase->priv->pageProxy->resetDragOperation();
657     webViewBase->priv->pageProxy->dragEntered(dragData.get());
658     DragOperation operation = webViewBase->priv->pageProxy->dragSession().operation;
659     gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time);
660 }
661
662 static AtkObject* webkitWebViewBaseGetAccessible(GtkWidget* widget)
663 {
664     // If the socket has already been created and embedded a plug ID, return it.
665     WebKitWebViewBasePrivate* priv = WEBKIT_WEB_VIEW_BASE(widget)->priv;
666     if (priv->accessible && atk_socket_is_occupied(ATK_SOCKET(priv->accessible.get())))
667         return priv->accessible.get();
668
669     // Create the accessible object and associate it to the widget.
670     if (!priv->accessible) {
671         priv->accessible = adoptGRef(ATK_OBJECT(webkitWebViewBaseAccessibleNew(widget)));
672
673         // Set the parent not to break bottom-up navigation.
674         GtkWidget* parentWidget = gtk_widget_get_parent(widget);
675         AtkObject* axParent = parentWidget ? gtk_widget_get_accessible(parentWidget) : 0;
676         if (axParent)
677             atk_object_set_parent(priv->accessible.get(), axParent);
678     }
679
680     // Try to embed the plug in the socket, if posssible.
681     String plugID = priv->pageProxy->accessibilityPlugID();
682     if (plugID.isNull())
683         return priv->accessible.get();
684
685     atk_socket_embed(ATK_SOCKET(priv->accessible.get()), const_cast<gchar*>(plugID.utf8().data()));
686
687     return priv->accessible.get();
688 }
689
690 static gboolean webkitWebViewBaseDragMotion(GtkWidget* widget, GdkDragContext* context, gint x, gint y, guint time)
691 {
692     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
693     OwnPtr<DragData> dragData(webViewBase->priv->dragAndDropHelper.handleDragMotion(context, IntPoint(x, y), time));
694     if (!dragData)
695         return TRUE;
696
697     webViewBase->priv->pageProxy->dragUpdated(dragData.get());
698     DragOperation operation = webViewBase->priv->pageProxy->dragSession().operation;
699     gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time);
700     return TRUE;
701 }
702
703 static void dragExitedCallback(GtkWidget* widget, DragData* dragData, bool dropHappened)
704 {
705     // Don't call dragExited if we have just received a drag-drop signal. This
706     // happens in the case of a successful drop onto the view.
707     if (dropHappened)
708         return;
709
710     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
711     webViewBase->priv->pageProxy->dragExited(dragData);
712     webViewBase->priv->pageProxy->resetDragOperation();
713 }
714
715 static void webkitWebViewBaseDragLeave(GtkWidget* widget, GdkDragContext* context, guint time)
716 {
717     WEBKIT_WEB_VIEW_BASE(widget)->priv->dragAndDropHelper.handleDragLeave(context, dragExitedCallback);
718 }
719
720 static gboolean webkitWebViewBaseDragDrop(GtkWidget* widget, GdkDragContext* context, gint x, gint y, guint time)
721 {
722     WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(widget);
723     OwnPtr<DragData> dragData(webViewBase->priv->dragAndDropHelper.handleDragDrop(context, IntPoint(x, y)));
724     if (!dragData)
725         return FALSE;
726
727     SandboxExtension::Handle handle;
728     SandboxExtension::HandleArray sandboxExtensionForUpload;
729     webViewBase->priv->pageProxy->performDrag(dragData.get(), String(), handle, sandboxExtensionForUpload);
730     gtk_drag_finish(context, TRUE, FALSE, time);
731     return TRUE;
732 }
733
734 static void webkitWebViewBaseParentSet(GtkWidget* widget, GtkWidget* oldParent)
735 {
736     if (!gtk_widget_get_parent(widget))
737         webkitWebViewBaseSetToplevelOnScreenWindow(WEBKIT_WEB_VIEW_BASE(widget), 0);
738
739 }
740
741 static void webkit_web_view_base_class_init(WebKitWebViewBaseClass* webkitWebViewBaseClass)
742 {
743     GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(webkitWebViewBaseClass);
744     widgetClass->realize = webkitWebViewBaseRealize;
745     widgetClass->draw = webkitWebViewBaseDraw;
746     widgetClass->size_allocate = webkitWebViewBaseSizeAllocate;
747     widgetClass->map = webkitWebViewBaseMap;
748     widgetClass->unmap = webkitWebViewBaseUnmap;
749     widgetClass->focus_in_event = webkitWebViewBaseFocusInEvent;
750     widgetClass->focus_out_event = webkitWebViewBaseFocusOutEvent;
751     widgetClass->key_press_event = webkitWebViewBaseKeyPressEvent;
752     widgetClass->key_release_event = webkitWebViewBaseKeyReleaseEvent;
753     widgetClass->button_press_event = webkitWebViewBaseButtonPressEvent;
754     widgetClass->button_release_event = webkitWebViewBaseButtonReleaseEvent;
755     widgetClass->scroll_event = webkitWebViewBaseScrollEvent;
756     widgetClass->motion_notify_event = webkitWebViewBaseMotionNotifyEvent;
757     widgetClass->query_tooltip = webkitWebViewBaseQueryTooltip;
758     widgetClass->drag_end = webkitWebViewBaseDragEnd;
759     widgetClass->drag_data_get = webkitWebViewBaseDragDataGet;
760     widgetClass->drag_motion = webkitWebViewBaseDragMotion;
761     widgetClass->drag_leave = webkitWebViewBaseDragLeave;
762     widgetClass->drag_drop = webkitWebViewBaseDragDrop;
763     widgetClass->drag_data_received = webkitWebViewBaseDragDataReceived;
764     widgetClass->get_accessible = webkitWebViewBaseGetAccessible;
765     widgetClass->parent_set = webkitWebViewBaseParentSet;
766
767     GObjectClass* gobjectClass = G_OBJECT_CLASS(webkitWebViewBaseClass);
768     gobjectClass->finalize = webkitWebViewBaseFinalize;
769
770     GtkContainerClass* containerClass = GTK_CONTAINER_CLASS(webkitWebViewBaseClass);
771     containerClass->add = webkitWebViewBaseContainerAdd;
772     containerClass->remove = webkitWebViewBaseContainerRemove;
773     containerClass->forall = webkitWebViewBaseContainerForall;
774
775     g_type_class_add_private(webkitWebViewBaseClass, sizeof(WebKitWebViewBasePrivate));
776 }
777
778 WebKitWebViewBase* webkitWebViewBaseCreate(WebContext* context, WebPageGroup* pageGroup)
779 {
780     WebKitWebViewBase* webkitWebViewBase = WEBKIT_WEB_VIEW_BASE(g_object_new(WEBKIT_TYPE_WEB_VIEW_BASE, NULL));
781     webkitWebViewBaseCreateWebPage(webkitWebViewBase, context, pageGroup);
782     return webkitWebViewBase;
783 }
784
785 GtkIMContext* webkitWebViewBaseGetIMContext(WebKitWebViewBase* webkitWebViewBase)
786 {
787     return webkitWebViewBase->priv->imContext.get();
788 }
789
790 WebPageProxy* webkitWebViewBaseGetPage(WebKitWebViewBase* webkitWebViewBase)
791 {
792     return webkitWebViewBase->priv->pageProxy.get();
793 }
794
795 void webkitWebViewBaseCreateWebPage(WebKitWebViewBase* webkitWebViewBase, WebContext* context, WebPageGroup* pageGroup)
796 {
797     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
798
799     priv->pageProxy = context->createWebPage(priv->pageClient.get(), pageGroup);
800     priv->pageProxy->initializeWebPage();
801
802 #if ENABLE(FULLSCREEN_API)
803     priv->pageProxy->fullScreenManager()->setWebView(webkitWebViewBase);
804 #endif
805
806 #if USE(TEXTURE_MAPPER_GL)
807     if (priv->redirectedWindow)
808         priv->pageProxy->setAcceleratedCompositingWindowId(priv->redirectedWindow->windowId());
809 #endif
810 }
811
812 void webkitWebViewBaseSetTooltipText(WebKitWebViewBase* webViewBase, const char* tooltip)
813 {
814     WebKitWebViewBasePrivate* priv = webViewBase->priv;
815     if (tooltip && tooltip[0] != '\0') {
816         priv->tooltipText = tooltip;
817         gtk_widget_set_has_tooltip(GTK_WIDGET(webViewBase), TRUE);
818     } else {
819         priv->tooltipText = "";
820         gtk_widget_set_has_tooltip(GTK_WIDGET(webViewBase), FALSE);
821     }
822
823     gtk_widget_trigger_tooltip_query(GTK_WIDGET(webViewBase));
824 }
825
826 void webkitWebViewBaseSetTooltipArea(WebKitWebViewBase* webViewBase, const IntRect& tooltipArea)
827 {
828     webViewBase->priv->tooltipArea = tooltipArea;
829 }
830
831 void webkitWebViewBaseStartDrag(WebKitWebViewBase* webViewBase, const DragData& dragData, PassRefPtr<ShareableBitmap> dragImage)
832 {
833     WebKitWebViewBasePrivate* priv = webViewBase->priv;
834
835     RefPtr<DataObjectGtk> dataObject = adoptRef(dragData.platformData());
836     GRefPtr<GtkTargetList> targetList = adoptGRef(PasteboardHelper::defaultPasteboardHelper()->targetListForDataObject(dataObject.get()));
837     GOwnPtr<GdkEvent> currentEvent(gtk_get_current_event());
838     GdkDragContext* context = gtk_drag_begin(GTK_WIDGET(webViewBase),
839                                              targetList.get(),
840                                              dragOperationToGdkDragActions(dragData.draggingSourceOperationMask()),
841                                              1, /* button */
842                                              currentEvent.get());
843     priv->dragAndDropHelper.startedDrag(context, dataObject.get());
844
845
846     // A drag starting should prevent a double-click from happening. This might
847     // happen if a drag is followed very quickly by another click (like in the DRT).
848     priv->clickCounter.reset();
849
850     if (dragImage) {
851         RefPtr<cairo_surface_t> image(dragImage->createCairoSurface());
852         priv->dragIcon.setImage(image.get());
853         priv->dragIcon.useForDrag(context);
854     } else
855         gtk_drag_set_icon_default(context);
856 }
857
858 void webkitWebViewBaseForwardNextKeyEvent(WebKitWebViewBase* webkitWebViewBase)
859 {
860     webkitWebViewBase->priv->shouldForwardNextKeyEvent = TRUE;
861 }
862
863 void webkitWebViewBaseEnterFullScreen(WebKitWebViewBase* webkitWebViewBase)
864 {
865 #if ENABLE(FULLSCREEN_API)
866     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
867     if (priv->fullScreenModeActive)
868         return;
869
870     if (!priv->fullScreenClient.willEnterFullScreen())
871         return;
872
873     WebFullScreenManagerProxy* fullScreenManagerProxy = priv->pageProxy->fullScreenManager();
874     fullScreenManagerProxy->willEnterFullScreen();
875
876     GtkWidget* topLevelWindow = gtk_widget_get_toplevel(GTK_WIDGET(webkitWebViewBase));
877     if (gtk_widget_is_toplevel(topLevelWindow))
878         gtk_window_fullscreen(GTK_WINDOW(topLevelWindow));
879     fullScreenManagerProxy->didEnterFullScreen();
880     priv->fullScreenModeActive = true;
881 #endif
882 }
883
884 void webkitWebViewBaseExitFullScreen(WebKitWebViewBase* webkitWebViewBase)
885 {
886 #if ENABLE(FULLSCREEN_API)
887     WebKitWebViewBasePrivate* priv = webkitWebViewBase->priv;
888     if (!priv->fullScreenModeActive)
889         return;
890
891     if (!priv->fullScreenClient.willExitFullScreen())
892         return;
893
894     WebFullScreenManagerProxy* fullScreenManagerProxy = priv->pageProxy->fullScreenManager();
895     fullScreenManagerProxy->willExitFullScreen();
896
897     GtkWidget* topLevelWindow = gtk_widget_get_toplevel(GTK_WIDGET(webkitWebViewBase));
898     if (gtk_widget_is_toplevel(topLevelWindow))
899         gtk_window_unfullscreen(GTK_WINDOW(topLevelWindow));
900     fullScreenManagerProxy->didExitFullScreen();
901     priv->fullScreenModeActive = false;
902 #endif
903 }
904
905 void webkitWebViewBaseInitializeFullScreenClient(WebKitWebViewBase* webkitWebViewBase, const WKFullScreenClientGtk* wkClient)
906 {
907     webkitWebViewBase->priv->fullScreenClient.initialize(wkClient);
908 }
909
910 void webkitWebViewBaseSetInspectorViewHeight(WebKitWebViewBase* webkitWebViewBase, unsigned height)
911 {
912     if (webkitWebViewBase->priv->inspectorViewHeight == height)
913         return;
914     webkitWebViewBase->priv->inspectorViewHeight = height;
915     if (webkitWebViewBase->priv->inspectorView)
916         gtk_widget_queue_resize_no_redraw(GTK_WIDGET(webkitWebViewBase));
917 }
918
919 void webkitWebViewBaseSetActiveContextMenuProxy(WebKitWebViewBase* webkitWebViewBase, WebContextMenuProxyGtk* contextMenuProxy)
920 {
921     webkitWebViewBase->priv->activeContextMenuProxy = contextMenuProxy;
922 }
923
924 WebContextMenuProxyGtk* webkitWebViewBaseGetActiveContextMenuProxy(WebKitWebViewBase* webkitWebViewBase)
925 {
926     return webkitWebViewBase->priv->activeContextMenuProxy;
927 }
928
929 GdkEvent* webkitWebViewBaseTakeContextMenuEvent(WebKitWebViewBase* webkitWebViewBase)
930 {
931     return webkitWebViewBase->priv->contextMenuEvent.release();
932 }
933
934 #if USE(TEXTURE_MAPPER_GL)
935 void redirectedWindowDamagedCallback(void* data)
936 {
937     gtk_widget_queue_draw(GTK_WIDGET(data));
938 }
939 #endif
940
941 void webkitWebViewBaseSetFocus(WebKitWebViewBase* webViewBase, bool focused)
942 {
943     WebKitWebViewBasePrivate* priv = webViewBase->priv;
944     if (priv->isFocused == focused)
945         return;
946
947     unsigned viewStateFlags = WebPageProxy::ViewIsFocused;
948     priv->isFocused = focused;
949
950     // If the view has received the focus and the window is not active
951     // mark the current window as active now. This can happen if the
952     // toplevel window is a GTK_WINDOW_POPUP and the focus has been
953     // set programatically like WebKitTestRunner does, because POPUP
954     // can't be focused.
955     if (priv->isFocused && !priv->isInWindowActive) {
956         priv->isInWindowActive = true;
957         viewStateFlags |= WebPageProxy::ViewWindowIsActive;
958     }
959     priv->pageProxy->viewStateDidChange(viewStateFlags);
960 }
961
962 bool webkitWebViewBaseIsInWindowActive(WebKitWebViewBase* webViewBase)
963 {
964     return webViewBase->priv->isInWindowActive;
965 }
966
967 bool webkitWebViewBaseIsFocused(WebKitWebViewBase* webViewBase)
968 {
969     return webViewBase->priv->isFocused;
970 }
971
972 bool webkitWebViewBaseIsVisible(WebKitWebViewBase* webViewBase)
973 {
974     return webViewBase->priv->isVisible;
975 }
976
977 bool webkitWebViewBaseIsInWindow(WebKitWebViewBase* webViewBase)
978 {
979     return webViewBase->priv->toplevelOnScreenWindow;
980 }