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 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 104 static void webViewWidgetContainerAdd(GtkContainer* container, GtkWidget* widget) 105 { 106 gtk_widget_set_parent(widget, GTK_WIDGET(container)); 107 } 108 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 364 WebView* webViewWidgetGetWebViewInstance(WebViewWidget* webViewWidget) 365 { 366 return webViewWidget->priv->webViewInstance; 367 } 368 369 void webViewWidgetSetWebViewInstance(WebViewWidget* webViewWidget, WebView* webViewInstance) 370 { 371 webViewWidget->priv->webViewInstance = webViewInstance; 372 } 373 374 GtkIMContext* webViewWidgetGetIMContext(WebViewWidget* webViewWidget) 375 { 376 return webViewWidget->priv->imContext; 377 } 378