| /* |
| * 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; |
| } |