[SCM] WebKit Debian packaging branch, webkit-1.1, updated. upstream/1.1.22-985-g3c00f00
mrobinson at webkit.org
mrobinson at webkit.org
Wed Mar 17 18:44:02 UTC 2010
The following commit has been merged in the webkit-1.1 branch:
commit eb98b0a1dc01fffa8a707b54dae81c472658e128
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