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