• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "WebViewWidget.h"
30 
31 #include "GOwnPtrGtk.h"
32 #include "GtkVersioning.h"
33 #include "NotImplemented.h"
34 #include "RefPtrCairo.h"
35 
36 using namespace WebKit;
37 using namespace WebCore;
38 
39 static gpointer webViewWidgetParentClass = 0;
40 
41 struct _WebViewWidgetPrivate {
42     WebView* webViewInstance;
43     GtkIMContext* imContext;
44     gint currentClickCount;
45     IntPoint previousClickPoint;
46     guint previousClickButton;
47     guint32 previousClickTime;
48 };
49 
webViewWidgetRealize(GtkWidget * widget)50 static void webViewWidgetRealize(GtkWidget* widget)
51 {
52     gtk_widget_set_realized(widget, TRUE);
53 
54     GtkAllocation allocation;
55     gtk_widget_get_allocation(widget, &allocation);
56 
57     GdkWindowAttr attributes;
58     attributes.window_type = GDK_WINDOW_CHILD;
59     attributes.x = allocation.x;
60     attributes.y = allocation.y;
61     attributes.width = allocation.width;
62     attributes.height = allocation.height;
63     attributes.wclass = GDK_INPUT_OUTPUT;
64     attributes.visual = gtk_widget_get_visual(widget);
65 #ifdef GTK_API_VERSION_2
66     attributes.colormap = gtk_widget_get_colormap(widget);
67 #endif
68     attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK
69         | GDK_EXPOSURE_MASK
70         | GDK_BUTTON_PRESS_MASK
71         | GDK_BUTTON_RELEASE_MASK
72         | GDK_POINTER_MOTION_MASK
73         | GDK_KEY_PRESS_MASK
74         | GDK_KEY_RELEASE_MASK
75         | GDK_BUTTON_MOTION_MASK
76         | GDK_BUTTON1_MOTION_MASK
77         | GDK_BUTTON2_MOTION_MASK
78         | GDK_BUTTON3_MOTION_MASK;
79 
80     gint attributesMask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
81 #ifdef GTK_API_VERSION_2
82     attributesMask |= GDK_WA_COLORMAP;
83 #endif
84     GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributesMask);
85     gtk_widget_set_window(widget, window);
86     gdk_window_set_user_data(window, widget);
87 
88 #ifdef GTK_API_VERSION_2
89 #if GTK_CHECK_VERSION(2, 20, 0)
90     gtk_widget_style_attach(widget);
91 #else
92     widget->style = gtk_style_attach(gtk_widget_get_style(widget), window);
93 #endif
94     gtk_style_set_background(gtk_widget_get_style(widget), window, GTK_STATE_NORMAL);
95 #else
96     gtk_style_context_set_background(gtk_widget_get_style_context(widget), window);
97 #endif
98 
99     WebViewWidget* webView = WEB_VIEW_WIDGET(widget);
100     WebViewWidgetPrivate* priv = webView->priv;
101     gtk_im_context_set_client_window(priv->imContext, window);
102 }
103 
webViewWidgetContainerAdd(GtkContainer * container,GtkWidget * widget)104 static void webViewWidgetContainerAdd(GtkContainer* container, GtkWidget* widget)
105 {
106     gtk_widget_set_parent(widget, GTK_WIDGET(container));
107 }
108 
webViewWidgetDispose(GObject * gobject)109 static void webViewWidgetDispose(GObject* gobject)
110 {
111     WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(gobject);
112     WebViewWidgetPrivate* priv = webViewWidget->priv;
113 
114     if (priv->imContext) {
115         g_object_unref(priv->imContext);
116         priv->imContext = 0;
117     }
118 
119     G_OBJECT_CLASS(webViewWidgetParentClass)->dispose(gobject);
120 }
121 
webViewWidgetInit(WebViewWidget * webViewWidget)122 static void webViewWidgetInit(WebViewWidget* webViewWidget)
123 {
124     WebViewWidgetPrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(webViewWidget, WEB_VIEW_TYPE_WIDGET, WebViewWidgetPrivate);
125     webViewWidget->priv = priv;
126 
127     gtk_widget_set_can_focus(GTK_WIDGET(webViewWidget), TRUE);
128     priv->imContext = gtk_im_multicontext_new();
129 
130     priv->currentClickCount = 0;
131     priv->previousClickButton = 0;
132     priv->previousClickTime = 0;
133 }
134 
135 #ifdef GTK_API_VERSION_2
webViewExpose(GtkWidget * widget,GdkEventExpose * event)136 static gboolean webViewExpose(GtkWidget* widget, GdkEventExpose* event)
137 {
138     WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget));
139     GdkRectangle clipRect;
140     gdk_region_get_clipbox(event->region, &clipRect);
141 
142     GdkWindow* window = gtk_widget_get_window(widget);
143     RefPtr<cairo_t> cr = adoptRef(gdk_cairo_create(window));
144 
145     webView->paint(widget, clipRect, cr.get());
146 
147     return FALSE;
148 }
149 #else
webViewDraw(GtkWidget * widget,cairo_t * cr)150 static gboolean webViewDraw(GtkWidget* widget, cairo_t* cr)
151 {
152     WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget));
153     GdkRectangle clipRect;
154 
155     if (!gdk_cairo_get_clip_rectangle(cr, &clipRect))
156         return FALSE;
157 
158     webView->paint(widget, clipRect, cr);
159 
160     return FALSE;
161 }
162 #endif
163 
webViewSizeAllocate(GtkWidget * widget,GtkAllocation * allocation)164 static void webViewSizeAllocate(GtkWidget* widget, GtkAllocation* allocation)
165 {
166     WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget));
167     GTK_WIDGET_CLASS(webViewWidgetParentClass)->size_allocate(widget, allocation);
168     webView->setSize(widget, IntSize(allocation->width, allocation->height));
169 }
170 
webViewFocusInEvent(GtkWidget * widget,GdkEventFocus * event)171 static gboolean webViewFocusInEvent(GtkWidget* widget, GdkEventFocus* event)
172 {
173     WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(widget);
174     WebView* webView = webViewWidgetGetWebViewInstance(webViewWidget);
175 
176     GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
177     if (gtk_widget_is_toplevel(toplevel) && gtk_window_has_toplevel_focus(GTK_WINDOW(toplevel))) {
178         gtk_im_context_focus_in(webViewWidgetGetIMContext(webViewWidget));
179         webView->handleFocusInEvent(widget);
180     }
181 
182     return GTK_WIDGET_CLASS(webViewWidgetParentClass)->focus_in_event(widget, event);
183 }
184 
webViewFocusOutEvent(GtkWidget * widget,GdkEventFocus * event)185 static gboolean webViewFocusOutEvent(GtkWidget* widget, GdkEventFocus* event)
186 {
187     WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(widget);
188     WebView* webView = webViewWidgetGetWebViewInstance(webViewWidget);
189 
190     webView->handleFocusOutEvent(widget);
191     GtkIMContext* imContext = webViewWidgetGetIMContext(webViewWidget);
192     if (imContext)
193         gtk_im_context_focus_out(imContext);
194 
195     return GTK_WIDGET_CLASS(webViewWidgetParentClass)->focus_out_event(widget, event);
196 }
197 
webViewKeyPressEvent(GtkWidget * widget,GdkEventKey * event)198 static gboolean webViewKeyPressEvent(GtkWidget* widget, GdkEventKey* event)
199 {
200     WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget));
201     webView->handleKeyboardEvent(event);
202 
203     return GTK_WIDGET_CLASS(webViewWidgetParentClass)->key_press_event(widget, event);
204 }
205 
webViewKeyReleaseEvent(GtkWidget * widget,GdkEventKey * event)206 static gboolean webViewKeyReleaseEvent(GtkWidget* widget, GdkEventKey* event)
207 {
208     WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(widget);
209     WebView* webView = webViewWidgetGetWebViewInstance(webViewWidget);
210 
211     if (gtk_im_context_filter_keypress(webViewWidgetGetIMContext(webViewWidget), event))
212         return TRUE;
213 
214     webView->handleKeyboardEvent(event);
215 
216     return GTK_WIDGET_CLASS(webViewWidgetParentClass)->key_release_event(widget, event);
217 }
218 
219 // Copied from webkitwebview.cpp
getEventTime(GdkEvent * event)220 static guint32 getEventTime(GdkEvent* event)
221 {
222     guint32 time = gdk_event_get_time(event);
223     if (time)
224         return time;
225 
226     // Real events always have a non-zero time, but events synthesized
227     // by the DRT do not and we must calculate a time manually. This time
228     // is not calculated in the DRT, because GTK+ does not work well with
229     // anything other than GDK_CURRENT_TIME on synthesized events.
230     GTimeVal timeValue;
231     g_get_current_time(&timeValue);
232     return (timeValue.tv_sec * 1000) + (timeValue.tv_usec / 1000);
233 }
234 
webViewButtonPressEvent(GtkWidget * widget,GdkEventButton * buttonEvent)235 static gboolean webViewButtonPressEvent(GtkWidget* widget, GdkEventButton* buttonEvent)
236 {
237     if (buttonEvent->button == 3) {
238         // FIXME: [GTK] Add context menu support for Webkit2.
239         // https://bugs.webkit.org/show_bug.cgi?id=54827
240         notImplemented();
241         return FALSE;
242     }
243 
244     gtk_widget_grab_focus(widget);
245 
246     // For double and triple clicks GDK sends both a normal button press event
247     // and a specific type (like GDK_2BUTTON_PRESS). If we detect a special press
248     // coming up, ignore this event as it certainly generated the double or triple
249     // click. The consequence of not eating this event is two DOM button press events
250     // are generated.
251     GOwnPtr<GdkEvent> nextEvent(gdk_event_peek());
252     if (nextEvent && (nextEvent->any.type == GDK_2BUTTON_PRESS || nextEvent->any.type == GDK_3BUTTON_PRESS))
253         return TRUE;
254 
255     gint doubleClickDistance = 250;
256     gint doubleClickTime = 5;
257     GtkSettings* settings = gtk_settings_get_for_screen(gtk_widget_get_screen(widget));
258     g_object_get(settings,
259                  "gtk-double-click-distance", &doubleClickDistance,
260                  "gtk-double-click-time", &doubleClickTime, NULL);
261 
262     // GTK+ only counts up to triple clicks, but WebCore wants to know about
263     // quadruple clicks, quintuple clicks, ad infinitum. Here, we replicate the
264     // GDK logic for counting clicks.
265     GdkEvent* event(reinterpret_cast<GdkEvent*>(buttonEvent));
266     guint32 eventTime = getEventTime(event);
267     WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(widget);
268     WebViewWidgetPrivate* priv = webViewWidget->priv;
269     if ((event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS)
270         || ((abs(buttonEvent->x - priv->previousClickPoint.x()) < doubleClickDistance)
271             && (abs(buttonEvent->y - priv->previousClickPoint.y()) < doubleClickDistance)
272             && (eventTime - priv->previousClickTime < static_cast<guint>(doubleClickTime))
273             && (buttonEvent->button == priv->previousClickButton)))
274         priv->currentClickCount++;
275     else
276         priv->currentClickCount = 1;
277 
278     WebView* webView = webViewWidgetGetWebViewInstance(webViewWidget);
279     webView->handleMouseEvent(event, priv->currentClickCount);
280 
281     gdouble x, y;
282     gdk_event_get_coords(event, &x, &y);
283     priv->previousClickPoint = IntPoint(x, y);
284     priv->previousClickButton = buttonEvent->button;
285     priv->previousClickTime = eventTime;
286 
287     return FALSE;
288 }
289 
webViewButtonReleaseEvent(GtkWidget * widget,GdkEventButton * event)290 static gboolean webViewButtonReleaseEvent(GtkWidget* widget, GdkEventButton* event)
291 {
292     WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget));
293     gtk_widget_grab_focus(widget);
294     webView->handleMouseEvent(reinterpret_cast<GdkEvent*>(event), 0 /* currentClickCount */);
295 
296     return FALSE;
297 }
298 
webViewScrollEvent(GtkWidget * widget,GdkEventScroll * event)299 static gboolean webViewScrollEvent(GtkWidget* widget, GdkEventScroll* event)
300 {
301     WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget));
302     webView->handleWheelEvent(event);
303 
304     return FALSE;
305 }
306 
webViewMotionNotifyEvent(GtkWidget * widget,GdkEventMotion * event)307 static gboolean webViewMotionNotifyEvent(GtkWidget* widget, GdkEventMotion* event)
308 {
309     WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget));
310     webView->handleMouseEvent(reinterpret_cast<GdkEvent*>(event), 0 /* currentClickCount */);
311 
312     return FALSE;
313 }
314 
webViewWidgetClassInit(WebViewWidgetClass * webViewWidgetClass)315 static void webViewWidgetClassInit(WebViewWidgetClass* webViewWidgetClass)
316 {
317     webViewWidgetParentClass = g_type_class_peek_parent(webViewWidgetClass);
318 
319     GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(webViewWidgetClass);
320     widgetClass->realize = webViewWidgetRealize;
321 #ifdef GTK_API_VERSION_2
322     widgetClass->expose_event = webViewExpose;
323 #else
324     widgetClass->draw = webViewDraw;
325 #endif
326     widgetClass->size_allocate = webViewSizeAllocate;
327     widgetClass->focus_in_event = webViewFocusInEvent;
328     widgetClass->focus_out_event = webViewFocusOutEvent;
329     widgetClass->key_press_event = webViewKeyPressEvent;
330     widgetClass->key_release_event = webViewKeyReleaseEvent;
331     widgetClass->button_press_event = webViewButtonPressEvent;
332     widgetClass->button_release_event = webViewButtonReleaseEvent;
333     widgetClass->scroll_event = webViewScrollEvent;
334     widgetClass->motion_notify_event = webViewMotionNotifyEvent;
335 
336     GObjectClass* gobjectClass = G_OBJECT_CLASS(webViewWidgetClass);
337     gobjectClass->dispose = webViewWidgetDispose;
338 
339     GtkContainerClass* containerClass = GTK_CONTAINER_CLASS(webViewWidgetClass);
340     containerClass->add = webViewWidgetContainerAdd;
341 
342     g_type_class_add_private(webViewWidgetClass, sizeof(WebViewWidgetPrivate));
343 }
344 
webViewWidgetGetType()345 GType webViewWidgetGetType()
346 {
347     static volatile gsize gDefineTypeIdVolatile = 0;
348 
349     if (!g_once_init_enter(&gDefineTypeIdVolatile))
350         return gDefineTypeIdVolatile;
351 
352     GType gDefineTypeId = g_type_register_static_simple(GTK_TYPE_CONTAINER,
353                                                         g_intern_static_string("WebViewWidget"),
354                                                         sizeof(WebViewWidgetClass),
355                                                         reinterpret_cast<GClassInitFunc>(webViewWidgetClassInit),
356                                                         sizeof(WebViewWidget),
357                                                         reinterpret_cast<GInstanceInitFunc>(webViewWidgetInit),
358                                                         static_cast<GTypeFlags>(0));
359     g_once_init_leave(&gDefineTypeIdVolatile, gDefineTypeId);
360 
361     return gDefineTypeIdVolatile;
362 }
363 
webViewWidgetGetWebViewInstance(WebViewWidget * webViewWidget)364 WebView* webViewWidgetGetWebViewInstance(WebViewWidget* webViewWidget)
365 {
366     return webViewWidget->priv->webViewInstance;
367 }
368 
webViewWidgetSetWebViewInstance(WebViewWidget * webViewWidget,WebView * webViewInstance)369 void webViewWidgetSetWebViewInstance(WebViewWidget* webViewWidget, WebView* webViewInstance)
370 {
371     webViewWidget->priv->webViewInstance = webViewInstance;
372 }
373 
webViewWidgetGetIMContext(WebViewWidget * webViewWidget)374 GtkIMContext* webViewWidgetGetIMContext(WebViewWidget* webViewWidget)
375 {
376     return webViewWidget->priv->imContext;
377 }
378