Home | History | Annotate | Download | only in gtk
      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