diff options
Diffstat (limited to 'Source/WebKit2/UIProcess/gtk/WebViewWidget.cpp')
-rw-r--r-- | Source/WebKit2/UIProcess/gtk/WebViewWidget.cpp | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/gtk/WebViewWidget.cpp b/Source/WebKit2/UIProcess/gtk/WebViewWidget.cpp new file mode 100644 index 0000000..dfdd3b2 --- /dev/null +++ b/Source/WebKit2/UIProcess/gtk/WebViewWidget.cpp @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * Portions Copyright (c) 2010 Motorola Mobility, Inc. All rights reserved. + * Copyright (C) 2011 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebViewWidget.h" + +#include "GOwnPtrGtk.h" +#include "GtkVersioning.h" +#include "NotImplemented.h" +#include "RefPtrCairo.h" + +using namespace WebKit; +using namespace WebCore; + +static gpointer webViewWidgetParentClass = 0; + +struct _WebViewWidgetPrivate { + WebView* webViewInstance; + GtkIMContext* imContext; + gint currentClickCount; + IntPoint previousClickPoint; + guint previousClickButton; + guint32 previousClickTime; +}; + +static void webViewWidgetRealize(GtkWidget* widget) +{ + gtk_widget_set_realized(widget, TRUE); + + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + + GdkWindowAttr attributes; + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes.width = allocation.width; + attributes.height = allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual(widget); +#ifdef GTK_API_VERSION_2 + attributes.colormap = gtk_widget_get_colormap(widget); +#endif + attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK + | GDK_EXPOSURE_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_POINTER_MOTION_MASK + | GDK_KEY_PRESS_MASK + | GDK_KEY_RELEASE_MASK + | GDK_BUTTON_MOTION_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON2_MOTION_MASK + | GDK_BUTTON3_MOTION_MASK; + + gint attributesMask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; +#ifdef GTK_API_VERSION_2 + attributesMask |= GDK_WA_COLORMAP; +#endif + GdkWindow* window = gdk_window_new(gtk_widget_get_parent_window(widget), &attributes, attributesMask); + gtk_widget_set_window(widget, window); + gdk_window_set_user_data(window, widget); + +#ifdef GTK_API_VERSION_2 +#if GTK_CHECK_VERSION(2, 20, 0) + gtk_widget_style_attach(widget); +#else + widget->style = gtk_style_attach(gtk_widget_get_style(widget), window); +#endif + gtk_style_set_background(gtk_widget_get_style(widget), window, GTK_STATE_NORMAL); +#else + gtk_style_context_set_background(gtk_widget_get_style_context(widget), window); +#endif + + WebViewWidget* webView = WEB_VIEW_WIDGET(widget); + WebViewWidgetPrivate* priv = webView->priv; + gtk_im_context_set_client_window(priv->imContext, window); +} + +static void webViewWidgetContainerAdd(GtkContainer* container, GtkWidget* widget) +{ + gtk_widget_set_parent(widget, GTK_WIDGET(container)); +} + +static void webViewWidgetDispose(GObject* gobject) +{ + WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(gobject); + WebViewWidgetPrivate* priv = webViewWidget->priv; + + if (priv->imContext) { + g_object_unref(priv->imContext); + priv->imContext = 0; + } + + G_OBJECT_CLASS(webViewWidgetParentClass)->dispose(gobject); +} + +static void webViewWidgetInit(WebViewWidget* webViewWidget) +{ + WebViewWidgetPrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(webViewWidget, WEB_VIEW_TYPE_WIDGET, WebViewWidgetPrivate); + webViewWidget->priv = priv; + + gtk_widget_set_can_focus(GTK_WIDGET(webViewWidget), TRUE); + priv->imContext = gtk_im_multicontext_new(); + + priv->currentClickCount = 0; + priv->previousClickButton = 0; + priv->previousClickTime = 0; +} + +#ifdef GTK_API_VERSION_2 +static gboolean webViewExpose(GtkWidget* widget, GdkEventExpose* event) +{ + WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget)); + GdkRectangle clipRect; + gdk_region_get_clipbox(event->region, &clipRect); + + GdkWindow* window = gtk_widget_get_window(widget); + RefPtr<cairo_t> cr = adoptRef(gdk_cairo_create(window)); + + webView->paint(widget, clipRect, cr.get()); + + return FALSE; +} +#else +static gboolean webViewDraw(GtkWidget* widget, cairo_t* cr) +{ + WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget)); + GdkRectangle clipRect; + + if (!gdk_cairo_get_clip_rectangle(cr, &clipRect)) + return FALSE; + + webView->paint(widget, clipRect, cr); + + return FALSE; +} +#endif + +static void webViewSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) +{ + WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget)); + GTK_WIDGET_CLASS(webViewWidgetParentClass)->size_allocate(widget, allocation); + webView->setSize(widget, IntSize(allocation->width, allocation->height)); +} + +static gboolean webViewFocusInEvent(GtkWidget* widget, GdkEventFocus* event) +{ + WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(widget); + WebView* webView = webViewWidgetGetWebViewInstance(webViewWidget); + + GtkWidget* toplevel = gtk_widget_get_toplevel(widget); + if (gtk_widget_is_toplevel(toplevel) && gtk_window_has_toplevel_focus(GTK_WINDOW(toplevel))) { + gtk_im_context_focus_in(webViewWidgetGetIMContext(webViewWidget)); + webView->handleFocusInEvent(widget); + } + + return GTK_WIDGET_CLASS(webViewWidgetParentClass)->focus_in_event(widget, event); +} + +static gboolean webViewFocusOutEvent(GtkWidget* widget, GdkEventFocus* event) +{ + WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(widget); + WebView* webView = webViewWidgetGetWebViewInstance(webViewWidget); + + webView->handleFocusOutEvent(widget); + GtkIMContext* imContext = webViewWidgetGetIMContext(webViewWidget); + if (imContext) + gtk_im_context_focus_out(imContext); + + return GTK_WIDGET_CLASS(webViewWidgetParentClass)->focus_out_event(widget, event); +} + +static gboolean webViewKeyPressEvent(GtkWidget* widget, GdkEventKey* event) +{ + WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget)); + webView->handleKeyboardEvent(event); + + return GTK_WIDGET_CLASS(webViewWidgetParentClass)->key_press_event(widget, event); +} + +static gboolean webViewKeyReleaseEvent(GtkWidget* widget, GdkEventKey* event) +{ + WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(widget); + WebView* webView = webViewWidgetGetWebViewInstance(webViewWidget); + + if (gtk_im_context_filter_keypress(webViewWidgetGetIMContext(webViewWidget), event)) + return TRUE; + + webView->handleKeyboardEvent(event); + + return GTK_WIDGET_CLASS(webViewWidgetParentClass)->key_release_event(widget, event); +} + +// Copied from webkitwebview.cpp +static guint32 getEventTime(GdkEvent* event) +{ + guint32 time = gdk_event_get_time(event); + if (time) + return time; + + // Real events always have a non-zero time, but events synthesized + // by the DRT do not and we must calculate a time manually. This time + // is not calculated in the DRT, because GTK+ does not work well with + // anything other than GDK_CURRENT_TIME on synthesized events. + GTimeVal timeValue; + g_get_current_time(&timeValue); + return (timeValue.tv_sec * 1000) + (timeValue.tv_usec / 1000); +} + +static gboolean webViewButtonPressEvent(GtkWidget* widget, GdkEventButton* buttonEvent) +{ + if (buttonEvent->button == 3) { + // FIXME: [GTK] Add context menu support for Webkit2. + // https://bugs.webkit.org/show_bug.cgi?id=54827 + notImplemented(); + return FALSE; + } + + gtk_widget_grab_focus(widget); + + // For double and triple clicks GDK sends both a normal button press event + // and a specific type (like GDK_2BUTTON_PRESS). If we detect a special press + // coming up, ignore this event as it certainly generated the double or triple + // click. The consequence of not eating this event is two DOM button press events + // are generated. + GOwnPtr<GdkEvent> nextEvent(gdk_event_peek()); + if (nextEvent && (nextEvent->any.type == GDK_2BUTTON_PRESS || nextEvent->any.type == GDK_3BUTTON_PRESS)) + return TRUE; + + gint doubleClickDistance = 250; + gint doubleClickTime = 5; + GtkSettings* settings = gtk_settings_get_for_screen(gtk_widget_get_screen(widget)); + g_object_get(settings, + "gtk-double-click-distance", &doubleClickDistance, + "gtk-double-click-time", &doubleClickTime, NULL); + + // GTK+ only counts up to triple clicks, but WebCore wants to know about + // quadruple clicks, quintuple clicks, ad infinitum. Here, we replicate the + // GDK logic for counting clicks. + GdkEvent* event(reinterpret_cast<GdkEvent*>(buttonEvent)); + guint32 eventTime = getEventTime(event); + WebViewWidget* webViewWidget = WEB_VIEW_WIDGET(widget); + WebViewWidgetPrivate* priv = webViewWidget->priv; + if ((event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) + || ((abs(buttonEvent->x - priv->previousClickPoint.x()) < doubleClickDistance) + && (abs(buttonEvent->y - priv->previousClickPoint.y()) < doubleClickDistance) + && (eventTime - priv->previousClickTime < static_cast<guint>(doubleClickTime)) + && (buttonEvent->button == priv->previousClickButton))) + priv->currentClickCount++; + else + priv->currentClickCount = 1; + + WebView* webView = webViewWidgetGetWebViewInstance(webViewWidget); + webView->handleMouseEvent(event, priv->currentClickCount); + + gdouble x, y; + gdk_event_get_coords(event, &x, &y); + priv->previousClickPoint = IntPoint(x, y); + priv->previousClickButton = buttonEvent->button; + priv->previousClickTime = eventTime; + + return FALSE; +} + +static gboolean webViewButtonReleaseEvent(GtkWidget* widget, GdkEventButton* event) +{ + WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget)); + gtk_widget_grab_focus(widget); + webView->handleMouseEvent(reinterpret_cast<GdkEvent*>(event), 0 /* currentClickCount */); + + return FALSE; +} + +static gboolean webViewScrollEvent(GtkWidget* widget, GdkEventScroll* event) +{ + WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget)); + webView->handleWheelEvent(event); + + return FALSE; +} + +static gboolean webViewMotionNotifyEvent(GtkWidget* widget, GdkEventMotion* event) +{ + WebView* webView = webViewWidgetGetWebViewInstance(WEB_VIEW_WIDGET(widget)); + webView->handleMouseEvent(reinterpret_cast<GdkEvent*>(event), 0 /* currentClickCount */); + + return FALSE; +} + +static void webViewWidgetClassInit(WebViewWidgetClass* webViewWidgetClass) +{ + webViewWidgetParentClass = g_type_class_peek_parent(webViewWidgetClass); + + GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(webViewWidgetClass); + widgetClass->realize = webViewWidgetRealize; +#ifdef GTK_API_VERSION_2 + widgetClass->expose_event = webViewExpose; +#else + widgetClass->draw = webViewDraw; +#endif + widgetClass->size_allocate = webViewSizeAllocate; + widgetClass->focus_in_event = webViewFocusInEvent; + widgetClass->focus_out_event = webViewFocusOutEvent; + widgetClass->key_press_event = webViewKeyPressEvent; + widgetClass->key_release_event = webViewKeyReleaseEvent; + widgetClass->button_press_event = webViewButtonPressEvent; + widgetClass->button_release_event = webViewButtonReleaseEvent; + widgetClass->scroll_event = webViewScrollEvent; + widgetClass->motion_notify_event = webViewMotionNotifyEvent; + + GObjectClass* gobjectClass = G_OBJECT_CLASS(webViewWidgetClass); + gobjectClass->dispose = webViewWidgetDispose; + + GtkContainerClass* containerClass = GTK_CONTAINER_CLASS(webViewWidgetClass); + containerClass->add = webViewWidgetContainerAdd; + + g_type_class_add_private(webViewWidgetClass, sizeof(WebViewWidgetPrivate)); +} + +GType webViewWidgetGetType() +{ + static volatile gsize gDefineTypeIdVolatile = 0; + + if (!g_once_init_enter(&gDefineTypeIdVolatile)) + return gDefineTypeIdVolatile; + + GType gDefineTypeId = g_type_register_static_simple(GTK_TYPE_CONTAINER, + g_intern_static_string("WebViewWidget"), + sizeof(WebViewWidgetClass), + reinterpret_cast<GClassInitFunc>(webViewWidgetClassInit), + sizeof(WebViewWidget), + reinterpret_cast<GInstanceInitFunc>(webViewWidgetInit), + static_cast<GTypeFlags>(0)); + g_once_init_leave(&gDefineTypeIdVolatile, gDefineTypeId); + + return gDefineTypeIdVolatile; +} + +WebView* webViewWidgetGetWebViewInstance(WebViewWidget* webViewWidget) +{ + return webViewWidget->priv->webViewInstance; +} + +void webViewWidgetSetWebViewInstance(WebViewWidget* webViewWidget, WebView* webViewInstance) +{ + webViewWidget->priv->webViewInstance = webViewInstance; +} + +GtkIMContext* webViewWidgetGetIMContext(WebViewWidget* webViewWidget) +{ + return webViewWidget->priv->imContext; +} |