[SCM] WebKit Debian packaging branch, webkit-1.2, updated. upstream/1.1.90-6072-g9a69373

mrobinson at webkit.org mrobinson at webkit.org
Thu Apr 8 02:23:55 UTC 2010


The following commit has been merged in the webkit-1.2 branch:
commit 14a3dc3801b495632521c557be1091e79b996b03
Author: mrobinson at webkit.org <mrobinson at webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Tue Mar 16 17:41:07 2010 +0000

    2010-03-16  Martin Robinson  <mrobinson at webkit.org>
    
            Reviewed by Xan Lopez.
    
            can't input korean into lower all input box except adress input box in webkit gtk launcher
            https://bugs.webkit.org/show_bug.cgi?id=32290
    
            Make the GTK+ EditorClient properly handle different types of input module
            behavior such as commit and preedit signals that happen outside of key event
            filtering and multiple times in a row. Filter keyup events as well as keydown
            events and call gtk_im_context_focus_{in/out} when the WebView focus changes.
    
            Added tests for this behavior to the GTK+ unit tests.
    
            * WebCoreSupport/EditorClientGtk.cpp:
            (WebKit::imContextCommitted): Handle this signal properly when it happens outside of
            key event filtering.
            (WebKit::imContextPreeditChanged): Immediately update the preedit state and do not reject
            empty preedits, so that cancellation works properly.
            (WebKit::EditorClient::updatePendingComposition): Add this method, which handles the
            situation where a commit signal happens when there is still a pending commit.
            (WebKit::EditorClient::shouldBeginEditing): Clear pending composition state before editing
            starts.
            (WebKit::EditorClient::shouldEndEditing): Clear pending composition state before editing ends.
            (WebKit::EditorClient::handleKeyboardEvent): No longer special case preedits which happen during
            key event filtering. When confirming a pending composition use insertText instead of confirmComposition.
            (WebKit::EditorClient::handleInputMethodKeydown):
            * WebCoreSupport/EditorClientGtk.h: Make pendingComposition a member, so that multiple WebViews
            do not share state.
            (WebKit::EditorClient::webView): Added.
            (WebKit::EditorClient::treatContextCommitAsKeyEvent): Added.
            (WebKit::EditorClient::clearPendingComposition): Added.
            * tests/testkeyevents.c:
            (test_keypress_events_load_status_cb):
            (map_event_cb):
            (setup_keyevent_test):
            (test_keypress_events):
            (element_text_equal_to):
            (test_ime_load_status_cb):
            (test_ime):
            (main):
            * webkit/webkitwebview.cpp:
            (webkit_web_view_key_release_event):
            (webkit_web_view_focus_in_event):
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@56072 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/WebKit/gtk/ChangeLog b/WebKit/gtk/ChangeLog
index 964362c..ad21d91 100644
--- a/WebKit/gtk/ChangeLog
+++ b/WebKit/gtk/ChangeLog
@@ -1,3 +1,48 @@
+2010-03-16  Martin Robinson  <mrobinson at webkit.org>
+
+        Reviewed by Xan Lopez.
+
+        can't input korean into lower all input box except adress input box in webkit gtk launcher
+        https://bugs.webkit.org/show_bug.cgi?id=32290
+
+        Make the GTK+ EditorClient properly handle different types of input module
+        behavior such as commit and preedit signals that happen outside of key event
+        filtering and multiple times in a row. Filter keyup events as well as keydown
+        events and call gtk_im_context_focus_{in/out} when the WebView focus changes.
+
+        Added tests for this behavior to the GTK+ unit tests.
+
+        * WebCoreSupport/EditorClientGtk.cpp:
+        (WebKit::imContextCommitted): Handle this signal properly when it happens outside of
+        key event filtering.
+        (WebKit::imContextPreeditChanged): Immediately update the preedit state and do not reject
+        empty preedits, so that cancellation works properly.
+        (WebKit::EditorClient::updatePendingComposition): Add this method, which handles the
+        situation where a commit signal happens when there is still a pending commit.
+        (WebKit::EditorClient::shouldBeginEditing): Clear pending composition state before editing
+        starts.
+        (WebKit::EditorClient::shouldEndEditing): Clear pending composition state before editing ends.
+        (WebKit::EditorClient::handleKeyboardEvent): No longer special case preedits which happen during
+        key event filtering. When confirming a pending composition use insertText instead of confirmComposition.
+        (WebKit::EditorClient::handleInputMethodKeydown):
+        * WebCoreSupport/EditorClientGtk.h: Make pendingComposition a member, so that multiple WebViews
+        do not share state.
+        (WebKit::EditorClient::webView): Added.
+        (WebKit::EditorClient::treatContextCommitAsKeyEvent): Added.
+        (WebKit::EditorClient::clearPendingComposition): Added.
+        * tests/testkeyevents.c:
+        (test_keypress_events_load_status_cb):
+        (map_event_cb):
+        (setup_keyevent_test):
+        (test_keypress_events):
+        (element_text_equal_to):
+        (test_ime_load_status_cb):
+        (test_ime):
+        (main):
+        * webkit/webkitwebview.cpp:
+        (webkit_web_view_key_release_event):
+        (webkit_web_view_focus_in_event):
+
 2010-03-16  Yury Semikhatsky <yurys at chromium.org>
 
         Reviewed by Pavel Feldman.
diff --git a/WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp b/WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp
index 0700939..8f2ecfa 100644
--- a/WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp
+++ b/WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp
@@ -4,6 +4,7 @@
  *  Copyright (C) 2009 Diego Escalante Urrelo <diegoe at gnome.org>
  *  Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
  *  Copyright (C) 2009, Igalia S.L.
+ *  Copyright (C) 2010, Martin Robinson <mrobinson at webkit.org>
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -51,40 +52,48 @@ using namespace WebCore;
 
 namespace WebKit {
 
-static gchar* pendingComposition = 0;
-static gchar* pendingPreedit = 0;
-
-static void setPendingComposition(gchar* newComposition)
+static void imContextCommitted(GtkIMContext* context, const gchar* compositionString, EditorClient* client)
 {
-    g_free(pendingComposition);
-    pendingComposition = newComposition;
-}
+    Frame* frame = core(client->webView())->focusController()->focusedOrMainFrame();
+    if (!frame || !frame->editor()->canEdit())
+        return;
 
-static void setPendingPreedit(gchar* newPreedit)
-{
-    g_free(pendingPreedit);
-    pendingPreedit = newPreedit;
-}
+    // If this signal fires during a keydown event when we are not in the middle
+    // of a composition, then treat this 'commit' as a normal key event and just
+    // change the editable area right before the keypress event.
+    if (client->treatContextCommitAsKeyEvent()) {
+        client->updatePendingComposition(compositionString);
+        return;
+    }
 
-static void clearPendingIMData()
-{
-    setPendingComposition(0);
-    setPendingPreedit(0);
-}
-static void imContextCommitted(GtkIMContext* context, const gchar* str, EditorClient* client)
-{
-    // This signal will fire during a keydown event. We want the contents of the
-    // field to change right before the keyup event, so we wait until then to actually
-    // commit this composition.
-    setPendingComposition(g_strdup(str));
+    frame->editor()->confirmComposition(String::fromUTF8(compositionString));
+    client->clearPendingComposition();
 }
 
 static void imContextPreeditChanged(GtkIMContext* context, EditorClient* client)
 {
+    Frame* frame = core(client->webView())->focusController()->focusedOrMainFrame();
+    if (!frame || !frame->editor()->canEdit())
+        return;
+
     // We ignore the provided PangoAttrList for now.
-    gchar* newPreedit = 0;
-    gtk_im_context_get_preedit_string(context, &newPreedit, NULL, NULL);
-    setPendingPreedit(newPreedit);
+    GOwnPtr<gchar> newPreedit(0);
+    gtk_im_context_get_preedit_string(context, &newPreedit.outPtr(), 0, 0);
+
+    String preeditString = String::fromUTF8(newPreedit.get());
+    Vector<CompositionUnderline> underlines;
+    underlines.append(CompositionUnderline(0, preeditString.length(), Color(0, 0, 0), false));
+    frame->editor()->setComposition(preeditString, underlines, 0, 0);
+}
+
+void EditorClient::updatePendingComposition(const gchar* newComposition)
+{
+    // The IMContext may signal more than one completed composition in a row,
+    // in which case we want to append them, rather than overwrite the old one.
+    if (!m_pendingComposition)
+        m_pendingComposition.set(g_strdup(newComposition));
+    else
+        m_pendingComposition.set(g_strconcat(m_pendingComposition.get(), newComposition, NULL));
 }
 
 void EditorClient::setInputMethodState(bool active)
@@ -139,7 +148,7 @@ int EditorClient::spellCheckerDocumentTag()
 
 bool EditorClient::shouldBeginEditing(WebCore::Range*)
 {
-    clearPendingIMData();
+    clearPendingComposition();
 
     notImplemented();
     return true;
@@ -147,7 +156,7 @@ bool EditorClient::shouldBeginEditing(WebCore::Range*)
 
 bool EditorClient::shouldEndEditing(WebCore::Range*)
 {
-    clearPendingIMData();
+    clearPendingComposition();
 
     notImplemented();
     return true;
@@ -466,8 +475,12 @@ void EditorClient::handleKeyboardEvent(KeyboardEvent* event)
             if (!command.isTextInsertion() && command.execute(event))
                 event->setDefaultHandled();
 
+            clearPendingComposition();
             return;
-        } else if (command.execute(event)) {
+        }
+
+        if (command.execute(event)) {
+            clearPendingComposition();
             event->setDefaultHandled();
             return;
         }
@@ -478,25 +491,14 @@ void EditorClient::handleKeyboardEvent(KeyboardEvent* event)
     // be reflected in the contents of the field until the keyup DOM event.
     if (event->type() == eventNames().keypressEvent) {
 
-        if (pendingComposition) {
-            String compositionString = String::fromUTF8(pendingComposition);
-            frame->editor()->confirmComposition(compositionString);
-
-            clearPendingIMData();
-            event->setDefaultHandled();
-
-        } else if (pendingPreedit) {
-            String preeditString = String::fromUTF8(pendingPreedit);
-
-            // Don't use an empty preedit as it will destroy the current
-            // selection, even if the composition is cancelled or fails later on.
-            if (!preeditString.isEmpty()) {
-                Vector<CompositionUnderline> underlines;
-                underlines.append(CompositionUnderline(0, preeditString.length(), Color(0, 0, 0), false));
-                frame->editor()->setComposition(preeditString, underlines, 0, 0);
-            }
-
-            clearPendingIMData();
+        // If we have a pending composition at this point, it happened while
+        // filtering a keypress, so we treat it as a normal text insertion.
+        // This will also ensure that if the keypress event handler changed the
+        // currently focused node, the text is still inserted into the original
+        // node (insertText() has this logic, but confirmComposition() does not).
+        if (m_pendingComposition) {
+            frame->editor()->insertText(String::fromUTF8(m_pendingComposition.get()), event);
+            clearPendingComposition();
             event->setDefaultHandled();
 
         } else {
@@ -520,15 +522,53 @@ void EditorClient::handleInputMethodKeydown(KeyboardEvent* event)
     if (!targetFrame || !targetFrame->editor()->canEdit())
         return;
 
-    // TODO: We need to decide which filtered keystrokes should be treated as IM
-    // events and which should not.
     WebKitWebViewPrivate* priv = m_webView->priv;
-    gtk_im_context_filter_keypress(priv->imContext, event->keyEvent()->gdkEventKey());
+
+
+    // Some IM contexts (e.g. 'simple') will act as if they filter every
+    // keystroke and just issue a 'commit' signal during handling. In situations
+    // where the 'commit' signal happens during filtering and there is no active
+    // composition, act as if the keystroke was not filtered. The one exception to
+    // this is when the keyval parameter of the GdkKeyEvent is 0, which is often
+    // a key event sent by the IM context for committing the current composition.
+
+    // Here is a typical sequence of events for the 'simple' context:
+    // 1. GDK key press event -> webkit_web_view_key_press_event
+    // 2. Keydown event -> EditorClient::handleInputMethodKeydown
+    //     gtk_im_context_filter_keypress returns true, but there is a pending
+    //     composition so event->preventDefault is not called (below).
+    // 3. Keydown event bubbles through the DOM
+    // 4. Keydown event -> EditorClient::handleKeyboardEvent
+    //     No action taken.
+    // 4. GDK key release event -> webkit_web_view_key_release_event
+    // 5. gtk_im_context_filter_keypress is called on the release event.
+    //     Simple does not filter most key releases, so the event continues.
+    // 6. Keypress event bubbles through the DOM.
+    // 7. Keypress event -> EditorClient::handleKeyboardEvent
+    //     pending composition is inserted.
+    // 8. Keyup event bubbles through the DOM.
+    // 9. Keyup event -> EditorClient::handleKeyboardEvent
+    //     No action taken.
+
+    // There are two situations where we do filter the keystroke:
+    // 1. The IMContext instructed us to filter and we have no pending composition.
+    // 2. The IMContext did not instruct us to filter, but the keystroke caused a
+    //    composition in progress to finish. It seems that sometimes SCIM will finish
+    //    a composition and not mark the keystroke as filtered.
+    m_treatContextCommitAsKeyEvent = (!targetFrame->editor()->hasComposition())
+         && event->keyEvent()->gdkEventKey()->keyval;
+    clearPendingComposition();
+    if ((gtk_im_context_filter_keypress(priv->imContext, event->keyEvent()->gdkEventKey()) && !m_pendingComposition)
+        || (!m_treatContextCommitAsKeyEvent && !targetFrame->editor()->hasComposition()))
+        event->preventDefault();
+
+    m_treatContextCommitAsKeyEvent = false;
 }
 
 EditorClient::EditorClient(WebKitWebView* webView)
     : m_isInRedo(false)
     , m_webView(webView)
+    , m_treatContextCommitAsKeyEvent(false)
 {
     WebKitWebViewPrivate* priv = m_webView->priv;
     g_signal_connect(priv->imContext, "commit", G_CALLBACK(imContextCommitted), this);
diff --git a/WebKit/gtk/WebCoreSupport/EditorClientGtk.h b/WebKit/gtk/WebCoreSupport/EditorClientGtk.h
index 9292651..825c146 100644
--- a/WebKit/gtk/WebCoreSupport/EditorClientGtk.h
+++ b/WebKit/gtk/WebCoreSupport/EditorClientGtk.h
@@ -2,6 +2,7 @@
  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann at kde.org>
  * Copyright (C) 2006 Zack Rusin <zack at kde.org>
  * Copyright (C) 2006 Apple Computer, Inc.
+ * Copyright (C) 2010 Martin Robinson <mrobinson at webkit.org>
  *
  * All rights reserved.
  *
@@ -34,8 +35,10 @@
 
 #include <wtf/Deque.h>
 #include <wtf/Forward.h>
+#include <wtf/gobject/GOwnPtr.h>
 
 typedef struct _WebKitWebView WebKitWebView;
+typedef char gchar;
 
 namespace WebCore {
     class Page;
@@ -53,6 +56,11 @@ namespace WebKit {
     public:
         EditorClient(WebKitWebView*);
         ~EditorClient();
+        WebKitWebView* webView() { return m_webView; }
+        bool treatContextCommitAsKeyEvent() { return m_treatContextCommitAsKeyEvent; }
+        void clearPendingComposition() { m_pendingComposition.set(0); }
+        bool hasPendingComposition() { return m_pendingComposition; }
+        void updatePendingComposition(const char*);
 
         // from EditorClient
         virtual void pageDestroyed();
@@ -120,6 +128,8 @@ namespace WebKit {
 
     private:
         WebKitWebView* m_webView;
+        bool m_treatContextCommitAsKeyEvent;
+        GOwnPtr<gchar> m_pendingComposition;
     };
 }
 
diff --git a/WebKit/gtk/tests/testkeyevents.c b/WebKit/gtk/tests/testkeyevents.c
index 3c9e198..b41a032 100644
--- a/WebKit/gtk/tests/testkeyevents.c
+++ b/WebKit/gtk/tests/testkeyevents.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Martin Robinson
+ * Copyright (C) 2009, 2010 Martin Robinson <mrobinson at webkit.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -22,12 +22,16 @@
 #include <string.h>
 #include <glib/gstdio.h>
 #include <webkit/webkit.h>
+#include <JavaScriptCore/JSStringRef.h>
+#include <JavaScriptCore/JSContextRef.h>
+
 
 #if GTK_CHECK_VERSION(2, 14, 0)
 
 typedef struct {
-  char* page;
-  gboolean shouldBeHandled;
+    char* page;
+    char* text;
+    gboolean shouldBeHandled;
 } TestInfo;
 
 typedef struct {
@@ -45,6 +49,7 @@ test_info_new(const char* page, gboolean shouldBeHandled)
     info = g_slice_new(TestInfo);
     info->page = g_strdup(page);
     info->shouldBeHandled = shouldBeHandled;
+    info->text = 0;
 
     return info;
 }
@@ -53,6 +58,7 @@ void
 test_info_destroy(TestInfo* info)
 {
     g_free(info->page);
+    g_free(info->text);
     g_slice_free(TestInfo, info);
 }
 
@@ -82,7 +88,6 @@ static gboolean key_press_event_cb(WebKitWebView* webView, GdkEvent* event, gpoi
     return FALSE;
 }
 
-
 static gboolean key_release_event_cb(WebKitWebView* webView, GdkEvent* event, gpointer data)
 {
     // WebCore never seems to mark keyup events as handled.
@@ -95,11 +100,15 @@ static gboolean key_release_event_cb(WebKitWebView* webView, GdkEvent* event, gp
     return FALSE;
 }
 
-static void load_status_cb(WebKitWebView* webView, GParamSpec* spec, gpointer data)
+static void test_keypress_events_load_status_cb(WebKitWebView* webView, GParamSpec* spec, gpointer data)
 {
     KeyEventFixture* fixture = (KeyEventFixture*)data;
     WebKitLoadStatus status = webkit_web_view_get_load_status(webView);
     if (status == WEBKIT_LOAD_FINISHED) {
+        g_signal_connect(fixture->webView, "key-press-event",
+                         G_CALLBACK(key_press_event_cb), fixture);
+        g_signal_connect(fixture->webView, "key-release-event",
+                         G_CALLBACK(key_release_event_cb), fixture);
         if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
                                       gdk_unicode_to_keyval('a'), 0))
             g_assert_not_reached();
@@ -111,25 +120,14 @@ gboolean map_event_cb(GtkWidget *widget, GdkEvent* event, gpointer data)
 {
     gtk_widget_grab_focus(widget);
     KeyEventFixture* fixture = (KeyEventFixture*)data;
-
-    g_signal_connect(fixture->webView, "key-press-event",
-                     G_CALLBACK(key_press_event_cb), fixture);
-    g_signal_connect(fixture->webView, "key-release-event",
-                     G_CALLBACK(key_release_event_cb), fixture);
-
-    g_signal_connect(fixture->webView, "notify::load-status",
-                     G_CALLBACK(load_status_cb), fixture);
-
     webkit_web_view_load_string(fixture->webView, fixture->info->page,
                                 "text/html", "utf-8", "file://");
-
     return FALSE;
 }
 
-static void test_keypress(KeyEventFixture* fixture, gconstpointer data)
+static void setup_keyevent_test(KeyEventFixture* fixture, gconstpointer data, GCallback load_event_callback)
 {
     fixture->info = (TestInfo*)data;
-
     g_signal_connect(fixture->window, "map-event",
                      G_CALLBACK(map_event_cb), fixture);
 
@@ -137,10 +135,172 @@ static void test_keypress(KeyEventFixture* fixture, gconstpointer data)
     gtk_widget_show(GTK_WIDGET(fixture->webView));
     gtk_window_present(GTK_WINDOW(fixture->window));
 
+    g_signal_connect(fixture->webView, "notify::load-status",
+                     load_event_callback, fixture);
+
     g_main_loop_run(fixture->loop);
+}
+
+static void test_keypress_events(KeyEventFixture* fixture, gconstpointer data)
+{
+    setup_keyevent_test(fixture, data, G_CALLBACK(test_keypress_events_load_status_cb));
+}
+
+static gboolean element_text_equal_to(JSContextRef context, const gchar* text)
+{
+    JSStringRef scriptString = JSStringCreateWithUTF8CString(
+      "window.document.getElementById(\"in\").value;");
+    JSValueRef value = JSEvaluateScript(context, scriptString, 0, 0, 0, 0);
+    JSStringRelease(scriptString);
+
+    // If the value isn't a string, the element is probably a div
+    // so grab the innerText instead.
+    if (!JSValueIsString(context, value)) {
+        JSStringRef scriptString = JSStringCreateWithUTF8CString(
+          "window.document.getElementById(\"in\").innerText;");
+        value = JSEvaluateScript(context, scriptString, 0, 0, 0, 0);
+        JSStringRelease(scriptString);
+    }
+
+    g_assert(JSValueIsString(context, value));
+    JSStringRef inputString = JSValueToStringCopy(context, value, 0);
+    g_assert(inputString);
+
+    gint size = JSStringGetMaximumUTF8CStringSize(inputString);
+    gchar* cString = g_malloc(size);
+    JSStringGetUTF8CString(inputString, cString, size);
+    JSStringRelease(inputString);
+
+    gboolean result = g_utf8_collate(cString, text) == 0;
+    g_free(cString);
+    return result;
+}
+
+static void test_ime_load_status_cb(WebKitWebView* webView, GParamSpec* spec, gpointer data)
+{
+    KeyEventFixture* fixture = (KeyEventFixture*)data;
+    WebKitLoadStatus status = webkit_web_view_get_load_status(webView);
+    if (status != WEBKIT_LOAD_FINISHED)
+        return;
+
+    JSGlobalContextRef context = webkit_web_frame_get_global_context(
+        webkit_web_view_get_main_frame(webView));
+    g_assert(context);
+
+    GtkIMContext* imContext = 0;
+    g_object_get(webView, "im-context", &imContext, NULL);
+    g_assert(imContext);
+
+    // Test that commits that happen outside of key events
+    // change the text field immediately. This closely replicates
+    // the behavior of SCIM.
+    g_assert(element_text_equal_to(context, ""));
+    g_signal_emit_by_name(imContext, "commit", "a");
+    g_assert(element_text_equal_to(context, "a"));
+    g_signal_emit_by_name(imContext, "commit", "b");
+    g_assert(element_text_equal_to(context, "ab"));
+    g_signal_emit_by_name(imContext, "commit", "c");
+    g_assert(element_text_equal_to(context, "abc"));
+
+    g_object_unref(imContext);
+    g_main_loop_quit(fixture->loop);
+}
+
+static void test_ime(KeyEventFixture* fixture, gconstpointer data)
+{
+    setup_keyevent_test(fixture, data, G_CALLBACK(test_ime_load_status_cb));
+}
+
+static gboolean verify_contents(gpointer data)
+{
+    KeyEventFixture* fixture = (KeyEventFixture*)data;
+    JSGlobalContextRef context = webkit_web_frame_get_global_context(
+        webkit_web_view_get_main_frame(fixture->webView));
+    g_assert(context);
+
+    g_assert(element_text_equal_to(context, fixture->info->text));
+    g_main_loop_quit(fixture->loop);
+    return FALSE;
+}
+
+static void test_blocking_load_status_cb(WebKitWebView* webView, GParamSpec* spec, gpointer data)
+{
+    KeyEventFixture* fixture = (KeyEventFixture*)data;
+    WebKitLoadStatus status = webkit_web_view_get_load_status(webView);
+    if (status != WEBKIT_LOAD_FINISHED)
+        return;
+
+    // The first keypress event should not modify the field.
+    fixture->info->text = g_strdup("bc");
+    if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
+                                 gdk_unicode_to_keyval('a'), 0))
+        g_assert_not_reached();
+    if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
+                                  gdk_unicode_to_keyval('b'), 0))
+        g_assert_not_reached();
+    if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
+                                  gdk_unicode_to_keyval('c'), 0))
+        g_assert_not_reached();
+
+    g_idle_add(verify_contents, fixture);
+}
+
+static void test_blocking(KeyEventFixture* fixture, gconstpointer data)
+{
+    setup_keyevent_test(fixture, data, G_CALLBACK(test_blocking_load_status_cb));
+}
 
+#if defined(GDK_WINDOWING_X11) && GTK_CHECK_VERSION(2, 16, 0)
+static void test_xim_load_status_cb(WebKitWebView* webView, GParamSpec* spec, gpointer data)
+{
+    KeyEventFixture* fixture = (KeyEventFixture*)data;
+    WebKitLoadStatus status = webkit_web_view_get_load_status(webView);
+    if (status != WEBKIT_LOAD_FINISHED)
+        return;
+
+    GtkIMContext* imContext = 0;
+    g_object_get(webView, "im-context", &imContext, NULL);
+    g_assert(imContext);
+
+    gchar* originalId = g_strdup(gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(imContext)));
+    gtk_im_multicontext_set_context_id(GTK_IM_MULTICONTEXT(imContext), "xim");
+
+    // Test that commits that happen outside of key events
+    // change the text field immediately. This closely replicates
+    // the behavior of SCIM.
+    fixture->info->text = g_strdup("debian");
+    if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
+                                 gdk_unicode_to_keyval('d'), 0))
+        g_assert_not_reached();
+    if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
+                             gdk_unicode_to_keyval('e'), 0))
+        g_assert_not_reached();
+    if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
+                             gdk_unicode_to_keyval('b'), 0))
+        g_assert_not_reached();
+    if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
+                             gdk_unicode_to_keyval('i'), 0))
+        g_assert_not_reached();
+    if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
+                             gdk_unicode_to_keyval('a'), 0))
+        g_assert_not_reached();
+    if (!gtk_test_widget_send_key(GTK_WIDGET(fixture->webView),
+                             gdk_unicode_to_keyval('n'), 0))
+        g_assert_not_reached();
+
+    gtk_im_multicontext_set_context_id(GTK_IM_MULTICONTEXT(imContext), originalId);
+    g_free(originalId);
+    g_object_unref(imContext);
+
+    g_idle_add(verify_contents, fixture);
 }
 
+static void test_xim(KeyEventFixture* fixture, gconstpointer data)
+{
+    setup_keyevent_test(fixture, data, G_CALLBACK(test_xim_load_status_cb));
+}
+#endif
+
 int main(int argc, char** argv)
 {
     g_thread_init(NULL);
@@ -148,30 +308,85 @@ int main(int argc, char** argv)
 
     g_test_bug_base("https://bugs.webkit.org/");
 
-    g_test_add("/webkit/keyevent/textfield", KeyEventFixture,
-               test_info_new("<html><body><input id=\"in\" type=\"text\">"
-                             "<script>document.getElementById('in').focus();"
-                             "</script></body></html>", TRUE),
+
+    // We'll test input on a slew of different node types. Key events to
+    // text inputs and editable divs should be marked as handled. Key events
+    // to buttons and links should not.
+    const char* textinput_html = "<html><body><input id=\"in\" type=\"text\">"
+        "<script>document.getElementById('in').focus();</script></body></html>";
+    const char* button_html = "<html><body><input id=\"in\" type=\"button\">"
+        "<script>document.getElementById('in').focus();</script></body></html>";
+    const char* link_html = "<html><body><a href=\"http://www.gnome.org\" id=\"in\">"
+        "LINKY MCLINKERSON</a><script>document.getElementById('in').focus();</script>"
+        "</body></html>";
+    const char* div_html = "<html><body><div id=\"in\" contenteditable=\"true\">"
+        "<script>document.getElementById('in').focus();</script></body></html>";
+
+    // These are similar to the blocks above, but they should block the first
+    // keypress modifying the editable node.
+    const char* textinput_html_blocking = "<html><body>"
+        "<input id=\"in\" type=\"text\" "
+        "onkeypress=\"if (first) {event.preventDefault();first=false;}\">"
+        "<script>first = true;\ndocument.getElementById('in').focus();</script>\n"
+        "</script></body></html>";
+    const char* div_html_blocking = "<html><body>"
+        "<div id=\"in\" contenteditable=\"true\" "
+        "onkeypress=\"if (first) {event.preventDefault();first=false;}\">"
+        "<script>first = true; document.getElementById('in').focus();</script>\n"
+        "</script></body></html>";
+
+    g_test_add("/webkit/keyevents/event-textinput", KeyEventFixture,
+               test_info_new(textinput_html, TRUE),
                key_event_fixture_setup,
-               test_keypress,
+               test_keypress_events,
                key_event_fixture_teardown);
-
-    g_test_add("/webkit/keyevent/buttons", KeyEventFixture,
-               test_info_new("<html><body><input id=\"in\" type=\"button\">"
-                             "<script>document.getElementById('in').focus();"
-                             "</script></body></html>", FALSE),
+    g_test_add("/webkit/keyevents/event-buttons", KeyEventFixture,
+               test_info_new(button_html, FALSE),
                key_event_fixture_setup,
-               test_keypress,
+               test_keypress_events,
                key_event_fixture_teardown);
-
-    g_test_add("/webkit/keyevent/link", KeyEventFixture,
-               test_info_new("<html><body><a href=\"http://www.gnome.org\" id=\"in\">"
-                             "LINKY MCLINKERSON</a><script>"
-                             "document.getElementById('in').focus();</script>"
-                             "</body></html>", FALSE),
+    g_test_add("/webkit/keyevents/event-link", KeyEventFixture,
+               test_info_new(link_html, FALSE),
                key_event_fixture_setup,
-               test_keypress,
+               test_keypress_events,
                key_event_fixture_teardown);
+    g_test_add("/webkit/keyevent/event-div", KeyEventFixture,
+               test_info_new(div_html, TRUE),
+               key_event_fixture_setup,
+               test_keypress_events,
+               key_event_fixture_teardown);
+    g_test_add("/webkit/keyevent/ime-textinput", KeyEventFixture,
+               test_info_new(textinput_html, TRUE),
+               key_event_fixture_setup,
+               test_ime,
+               key_event_fixture_teardown);
+    g_test_add("/webkit/keyevent/ime-div", KeyEventFixture,
+               test_info_new(div_html, TRUE),
+               key_event_fixture_setup,
+               test_ime,
+               key_event_fixture_teardown);
+    g_test_add("/webkit/keyevent/block-textinput", KeyEventFixture,
+               test_info_new(textinput_html_blocking, TRUE),
+               key_event_fixture_setup,
+               test_blocking,
+               key_event_fixture_teardown);
+    g_test_add("/webkit/keyevent/block-div", KeyEventFixture,
+               test_info_new(div_html_blocking, TRUE),
+               key_event_fixture_setup,
+               test_blocking,
+               key_event_fixture_teardown);
+#if defined(GDK_WINDOWING_X11) && GTK_CHECK_VERSION(2, 16, 0)
+    g_test_add("/webkit/keyevent/xim-textinput", KeyEventFixture,
+               test_info_new(textinput_html, TRUE),
+               key_event_fixture_setup,
+               test_xim,
+               key_event_fixture_teardown);
+    g_test_add("/webkit/keyevent/xim-div", KeyEventFixture,
+               test_info_new(div_html, TRUE),
+               key_event_fixture_setup,
+               test_xim,
+               key_event_fixture_teardown);
+#endif
 
     return g_test_run();
 }
diff --git a/WebKit/gtk/webkit/webkitwebview.cpp b/WebKit/gtk/webkit/webkitwebview.cpp
index 81ce502..b70d97d 100644
--- a/WebKit/gtk/webkit/webkitwebview.cpp
+++ b/WebKit/gtk/webkit/webkitwebview.cpp
@@ -562,12 +562,19 @@ static gboolean webkit_web_view_key_release_event(GtkWidget* widget, GdkEventKey
 {
     WebKitWebView* webView = WEBKIT_WEB_VIEW(widget);
 
+    // GTK+ IM contexts often require us to filter key release events, which
+    // WebCore does not do by default, so we filter the event here. We only block
+    // the event if we don't have a pending composition, because that means we
+    // are using a context like 'simple' which marks every keystroke as filtered.
+    WebKit::EditorClient* client = static_cast<WebKit::EditorClient*>(core(webView)->editorClient());
+    if (gtk_im_context_filter_keypress(webView->priv->imContext, event) && !client->hasPendingComposition())
+        return TRUE;
+
     Frame* frame = core(webView)->focusController()->focusedOrMainFrame();
     if (!frame->view())
         return FALSE;
 
     PlatformKeyboardEvent keyboardEvent(event);
-
     if (frame->eventHandler()->keyEvent(keyboardEvent))
         return TRUE;
 
@@ -727,6 +734,8 @@ static gboolean webkit_web_view_focus_in_event(GtkWidget* widget, GdkEventFocus*
             focusController->setFocused(true);
         else
             focusController->setFocusedFrame(core(webView)->mainFrame());
+
+        gtk_im_context_focus_in(webView->priv->imContext);
     }
     return GTK_WIDGET_CLASS(webkit_web_view_parent_class)->focus_in_event(widget, event);
 }
@@ -743,6 +752,9 @@ static gboolean webkit_web_view_focus_out_event(GtkWidget* widget, GdkEventFocus
         page->focusController()->setFocused(false);
     }
 
+    if (webView->priv->imContext)
+        gtk_im_context_focus_out(webView->priv->imContext);
+
     return GTK_WIDGET_CLASS(webkit_web_view_parent_class)->focus_out_event(widget, event);
 }
 

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list