<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[175046] trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/175046">175046</a></dd>
<dt>Author</dt> <dd>carlosgc@webkit.org</dd>
<dt>Date</dt> <dd>2014-10-22 06:00:12 -0700 (Wed, 22 Oct 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>[GTK] Move GtkInputMethodFilter from Platform to WebKit2
https://bugs.webkit.org/show_bug.cgi?id=137884
Reviewed by Gustavo Noronha Silva.
Source/WebCore:
Remove GtkInputMethodFilter.
* PlatformGTK.cmake:
* platform/gtk/GtkInputMethodFilter.cpp: Removed.
* platform/gtk/GtkInputMethodFilter.h: Removed.
Source/WebKit2:
Merge WebViewBaseInputMethodFilter and GtkInputMethodFilter into a
single class InputMethodFilter. The code is mostly the same, but
instead of having a setWebView method only to get the WebPageProxy,
it has a setPage method that passes the WebPageProxy. The GtkIMContext
client window is set by the WebView when it's realized/unrealized.
* PlatformGTK.cmake:
* Shared/NativeWebKeyboardEvent.h:
* Shared/gtk/NativeWebKeyboardEventGtk.cpp:
(WebKit::NativeWebKeyboardEvent::NativeWebKeyboardEvent):
* UIProcess/API/gtk/WebKitWebViewBase.cpp:
(webkitWebViewBaseRealize):
(webkitWebViewBaseUnrealize):
(webkit_web_view_base_class_init):
(webkitWebViewBaseCreateWebPage):
(webkitWebViewBaseEnterFullScreen): Deleted.
* UIProcess/API/gtk/WebViewBaseInputMethodFilter.cpp: Removed.
* UIProcess/API/gtk/WebViewBaseInputMethodFilter.h: Removed.
* UIProcess/gtk/InputMethodFilter.cpp: Added.
(WebKit::InputMethodFilter::handleCommitCallback):
(WebKit::InputMethodFilter::handlePreeditStartCallback):
(WebKit::InputMethodFilter::handlePreeditChangedCallback):
(WebKit::InputMethodFilter::handlePreeditEndCallback):
(WebKit::InputMethodFilter::InputMethodFilter):
(WebKit::InputMethodFilter::~InputMethodFilter):
(WebKit::InputMethodFilter::setEnabled):
(WebKit::InputMethodFilter::setCursorRect):
(WebKit::InputMethodFilter::handleKeyboardEvent):
(WebKit::InputMethodFilter::handleKeyboardEventWithCompositionResults):
(WebKit::InputMethodFilter::filterKeyEvent):
(WebKit::InputMethodFilter::confirmComposition):
(WebKit::InputMethodFilter::updatePreedit):
(WebKit::InputMethodFilter::notifyFocusedIn):
(WebKit::InputMethodFilter::notifyFocusedOut):
(WebKit::InputMethodFilter::notifyMouseButtonPress):
(WebKit::InputMethodFilter::confirmCurrentComposition):
(WebKit::InputMethodFilter::cancelContextComposition):
(WebKit::InputMethodFilter::sendCompositionAndPreeditWithFakeKeyEvents):
(WebKit::InputMethodFilter::handleCommit):
(WebKit::InputMethodFilter::handlePreeditStart):
(WebKit::InputMethodFilter::handlePreeditChanged):
(WebKit::InputMethodFilter::handlePreeditEnd):
(WebKit::InputMethodFilter::logHandleKeyboardEventForTesting):
(WebKit::InputMethodFilter::logHandleKeyboardEventWithCompositionResultsForTesting):
(WebKit::InputMethodFilter::logConfirmCompositionForTesting):
(WebKit::InputMethodFilter::logSetPreeditForTesting):
* UIProcess/gtk/InputMethodFilter.h: Added.
(WebKit::InputMethodFilter::context):
(WebKit::InputMethodFilter::setPage):
(WebKit::InputMethodFilter::setTestingMode):
(WebKit::InputMethodFilter::events):
Tools:
Move InputMethodFilter test from WebCore tests to WebKit2 tests
and adapt it to use the new InputMethodFilter WebKit class. Instead
of having virtual methods just for testing, it has a testing mode
that logs the events.
* TestWebKitAPI/PlatformGTK.cmake:
* TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp: Renamed from Tools/TestWebKitAPI/Tests/WebCore/gtk/InputMethodFilter.cpp.
(TestWebKitAPI::TestInputMethodFilter::TestInputMethodFilter):
(TestWebKitAPI::TestInputMethodFilter::~TestInputMethodFilter):
(TestWebKitAPI::TestInputMethodFilter::sendKeyEventToFilter):
(TestWebKitAPI::TestInputMethodFilter::sendPressAndReleaseKeyEventPairToFilter):
(TestWebKitAPI::TEST):
(TestWebKitAPI::temporaryGetPreeditStringOverride):
(TestWebKitAPI::temporaryResetOverride):
(TestWebKitAPI::verifyCanceledComposition):</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorePlatformGTKcmake">trunk/Source/WebCore/PlatformGTK.cmake</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2PlatformGTKcmake">trunk/Source/WebKit2/PlatformGTK.cmake</a></li>
<li><a href="#trunkSourceWebKit2SharedNativeWebKeyboardEventh">trunk/Source/WebKit2/Shared/NativeWebKeyboardEvent.h</a></li>
<li><a href="#trunkSourceWebKit2SharedgtkNativeWebKeyboardEventGtkcpp">trunk/Source/WebKit2/Shared/gtk/NativeWebKeyboardEventGtk.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPIgtkWebKitWebViewBasecpp">trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewBase.cpp</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPIPlatformGTKcmake">trunk/Tools/TestWebKitAPI/PlatformGTK.cmake</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebKit2UIProcessgtkInputMethodFiltercpp">trunk/Source/WebKit2/UIProcess/gtk/InputMethodFilter.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessgtkInputMethodFilterh">trunk/Source/WebKit2/UIProcess/gtk/InputMethodFilter.h</a></li>
<li>trunk/Tools/TestWebKitAPI/Tests/WebKit2/gtk/</li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKit2gtkInputMethodFiltercpp">trunk/Tools/TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp</a></li>
</ul>
<h3>Removed Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreplatformgtkGtkInputMethodFiltercpp">trunk/Source/WebCore/platform/gtk/GtkInputMethodFilter.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgtkGtkInputMethodFilterh">trunk/Source/WebCore/platform/gtk/GtkInputMethodFilter.h</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPIgtkWebViewBaseInputMethodFiltercpp">trunk/Source/WebKit2/UIProcess/API/gtk/WebViewBaseInputMethodFilter.cpp</a></li>
<li><a href="#trunkSourceWebKit2UIProcessAPIgtkWebViewBaseInputMethodFilterh">trunk/Source/WebKit2/UIProcess/API/gtk/WebViewBaseInputMethodFilter.h</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebCoregtkInputMethodFiltercpp">trunk/Tools/TestWebKitAPI/Tests/WebCore/gtk/InputMethodFilter.cpp</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebCore/ChangeLog        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2014-10-22 Carlos Garcia Campos <cgarcia@igalia.com>
+
+ [GTK] Move GtkInputMethodFilter from Platform to WebKit2
+ https://bugs.webkit.org/show_bug.cgi?id=137884
+
+ Reviewed by Gustavo Noronha Silva.
+
+ Remove GtkInputMethodFilter.
+
+ * PlatformGTK.cmake:
+ * platform/gtk/GtkInputMethodFilter.cpp: Removed.
+ * platform/gtk/GtkInputMethodFilter.h: Removed.
+
</ins><span class="cx"> 2014-10-22 Tibor Meszaros <tmeszaros.u-szeged@partner.samsung.com>
</span><span class="cx">
</span><span class="cx"> Match spec for font-weight: bolder|lighter
</span></span></pre></div>
<a id="trunkSourceWebCorePlatformGTKcmake"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/PlatformGTK.cmake (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/PlatformGTK.cmake        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebCore/PlatformGTK.cmake        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -222,7 +222,6 @@
</span><span class="cx"> platform/gtk/DragImageGtk.cpp
</span><span class="cx"> platform/gtk/GRefPtrGtk.cpp
</span><span class="cx"> platform/gtk/GtkClickCounter.cpp
</span><del>- platform/gtk/GtkInputMethodFilter.cpp
</del><span class="cx"> platform/gtk/GtkUtilities.cpp
</span><span class="cx"> platform/gtk/GtkVersioning.c
</span><span class="cx"> platform/gtk/KeyBindingTranslator.cpp
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgtkGtkInputMethodFiltercpp"></a>
<div class="delfile"><h4>Deleted: trunk/Source/WebCore/platform/gtk/GtkInputMethodFilter.cpp (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/gtk/GtkInputMethodFilter.cpp        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebCore/platform/gtk/GtkInputMethodFilter.cpp        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -1,353 +0,0 @@
</span><del>-/*
- * Copyright (C) 2012 Igalia S.L.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include "config.h"
-#include "GtkInputMethodFilter.h"
-
-#include "GUniquePtrGtk.h"
-#include "GtkVersioning.h"
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtk.h>
-#include <wtf/MathExtras.h>
-#include <wtf/gobject/GUniquePtr.h>
-
-// The Windows composition key event code is 299 or VK_PROCESSKEY. We need to
-// emit this code for web compatibility reasons when key events trigger
-// composition results. GDK doesn't have an equivalent, so we send VoidSymbol
-// here to WebCore. PlatformKeyEvent knows to convert this code into
-// VK_PROCESSKEY.
-const int gCompositionEventKeyCode = GDK_KEY_VoidSymbol;
-
-namespace WebCore {
-
-static void handleCommitCallback(GtkIMContext*, const char* compositionString, GtkInputMethodFilter* filter)
-{
- filter->handleCommit(compositionString);
-}
-
-static void handlePreeditStartCallback(GtkIMContext*, GtkInputMethodFilter* filter)
-{
- filter->handlePreeditStart();
-}
-
-static void handlePreeditChangedCallback(GtkIMContext*, GtkInputMethodFilter* filter)
-{
- filter->handlePreeditChanged();
-}
-
-static void handlePreeditEndCallback(GtkIMContext*, GtkInputMethodFilter* filter)
-{
- filter->handlePreeditEnd();
-}
-
-static void handleWidgetRealize(GtkWidget* widget, GtkInputMethodFilter* filter)
-{
- GdkWindow* window = gtk_widget_get_window(widget);
- ASSERT(window);
- gtk_im_context_set_client_window(filter->context(), window);
-}
-
-void GtkInputMethodFilter::setWidget(GtkWidget* widget)
-{
- ASSERT(!m_widget);
-
- m_widget = widget;
- if (gtk_widget_get_window(m_widget))
- handleWidgetRealize(m_widget, this);
- else
- g_signal_connect_after(widget, "realize", G_CALLBACK(handleWidgetRealize), this);
-}
-
-void GtkInputMethodFilter::setCursorRect(const IntRect& cursorRect)
-{
- // Don't move the window unless the cursor actually moves more than 10
- // pixels. This prevents us from making the window flash during minor
- // cursor adjustments.
- static const int windowMovementThreshold = 10 * 10;
- if (cursorRect.location().distanceSquaredToPoint(m_lastCareLocation) < windowMovementThreshold)
- return;
-
- m_lastCareLocation = cursorRect.location();
- IntRect translatedRect = cursorRect;
-
- ASSERT(m_widget);
- GtkAllocation allocation;
- gtk_widget_get_allocation(m_widget, &allocation);
- translatedRect.move(allocation.x, allocation.y);
-
- GdkRectangle gdkCursorRect = { cursorRect.x(), cursorRect.y(), cursorRect.width(), cursorRect.height() };
- gtk_im_context_set_cursor_location(m_context.get(), &gdkCursorRect);
-}
-
-GtkInputMethodFilter::GtkInputMethodFilter()
- : m_cursorOffset(0)
- , m_context(adoptGRef(gtk_im_multicontext_new()))
- , m_widget(0)
- , m_enabled(false)
- , m_composingTextCurrently(false)
- , m_filteringKeyEvent(false)
- , m_preeditChanged(false)
- , m_preventNextCommit(false)
- , m_justSentFakeKeyUp(false)
- , m_lastFilteredKeyPressCodeWithNoResults(GDK_KEY_VoidSymbol)
-{
- g_signal_connect(m_context.get(), "commit", G_CALLBACK(handleCommitCallback), this);
- g_signal_connect(m_context.get(), "preedit-start", G_CALLBACK(handlePreeditStartCallback), this);
- g_signal_connect(m_context.get(), "preedit-changed", G_CALLBACK(handlePreeditChangedCallback), this);
- g_signal_connect(m_context.get(), "preedit-end", G_CALLBACK(handlePreeditEndCallback), this);
-}
-
-GtkInputMethodFilter::~GtkInputMethodFilter()
-{
- g_signal_handlers_disconnect_by_func(m_context.get(), reinterpret_cast<void*>(handleCommitCallback), this);
- g_signal_handlers_disconnect_by_func(m_context.get(), reinterpret_cast<void*>(handlePreeditStartCallback), this);
- g_signal_handlers_disconnect_by_func(m_context.get(), reinterpret_cast<void*>(handlePreeditChangedCallback), this);
- g_signal_handlers_disconnect_by_func(m_context.get(), reinterpret_cast<void*>(handlePreeditEndCallback), this);
- g_signal_handlers_disconnect_by_func(m_widget, reinterpret_cast<void*>(handleWidgetRealize), this);
-}
-
-void GtkInputMethodFilter::setEnabled(bool enabled)
-{
- m_enabled = enabled;
- if (enabled)
- gtk_im_context_focus_in(m_context.get());
- else
- gtk_im_context_focus_out(m_context.get());
-}
-
-bool GtkInputMethodFilter::filterKeyEvent(GdkEventKey* event)
-{
- if (!canEdit() || !m_enabled)
- return sendSimpleKeyEvent(event);
-
- m_preeditChanged = false;
- m_filteringKeyEvent = true;
-
- unsigned int lastFilteredKeyPressCodeWithNoResults = m_lastFilteredKeyPressCodeWithNoResults;
- m_lastFilteredKeyPressCodeWithNoResults = GDK_KEY_VoidSymbol;
-
- bool filtered = gtk_im_context_filter_keypress(m_context.get(), event);
- m_filteringKeyEvent = false;
-
- bool justSentFakeKeyUp = m_justSentFakeKeyUp;
- m_justSentFakeKeyUp = false;
- if (justSentFakeKeyUp && event->type == GDK_KEY_RELEASE)
- return true;
-
- // Simple input methods work such that even normal keystrokes fire the
- // commit signal. We detect those situations and treat them as normal
- // key events, supplying the commit string as the key character.
- if (filtered && !m_composingTextCurrently && !m_preeditChanged && m_confirmedComposition.length() == 1) {
- bool result = sendSimpleKeyEvent(event, m_confirmedComposition);
- m_confirmedComposition = String();
- return result;
- }
-
- if (filtered && event->type == GDK_KEY_PRESS) {
- if (!m_preeditChanged && m_confirmedComposition.isNull()) {
- m_composingTextCurrently = true;
- m_lastFilteredKeyPressCodeWithNoResults = event->keyval;
- return true;
- }
-
- bool result = sendKeyEventWithCompositionResults(event);
- if (!m_confirmedComposition.isEmpty()) {
- m_composingTextCurrently = false;
- m_confirmedComposition = String();
- }
- return result;
- }
-
- // If we previously filtered a key press event and it yielded no results. Suppress
- // the corresponding key release event to avoid confusing the web content.
- if (event->type == GDK_KEY_RELEASE && lastFilteredKeyPressCodeWithNoResults == event->keyval)
- return true;
-
- // At this point a keystroke was either:
- // 1. Unfiltered
- // 2. A filtered keyup event. As the IME code in EditorClient.h doesn't
- // ever look at keyup events, we send any composition results before
- // the key event.
- // Both might have composition results or not.
- //
- // It's important to send the composition results before the event
- // because some IM modules operate that way. For example (taken from
- // the Chromium source), the latin-post input method gives this sequence
- // when you press 'a' and then backspace:
- // 1. keydown 'a' (filtered)
- // 2. preedit changed to "a"
- // 3. keyup 'a' (unfiltered)
- // 4. keydown Backspace (unfiltered)
- // 5. commit "a"
- // 6. preedit end
- if (!m_confirmedComposition.isEmpty())
- confirmComposition();
- if (m_preeditChanged)
- updatePreedit();
- return sendSimpleKeyEvent(event);
-}
-
-void GtkInputMethodFilter::notifyMouseButtonPress()
-{
- // Confirming the composition may trigger a selection change, which
- // might trigger further unwanted actions on the context, so we prevent
- // that by setting m_composingTextCurrently to false.
- if (m_composingTextCurrently)
- confirmCurrentComposition();
- m_composingTextCurrently = false;
- cancelContextComposition();
-}
-
-void GtkInputMethodFilter::resetContext()
-{
-
- // We always cancel the current WebCore composition here, in case the
- // composition was set outside the GTK+ IME path (via a script, for
- // instance) and we aren't tracking it.
- cancelCurrentComposition();
-
- if (!m_composingTextCurrently)
- return;
- m_composingTextCurrently = false;
- cancelContextComposition();
-}
-
-void GtkInputMethodFilter::cancelContextComposition()
-{
- m_preventNextCommit = !m_preedit.isEmpty();
-
- gtk_im_context_reset(m_context.get());
-
- m_composingTextCurrently = false;
- m_justSentFakeKeyUp = false;
- m_preedit = String();
- m_confirmedComposition = String();
-}
-
-void GtkInputMethodFilter::notifyFocusedIn()
-{
- m_enabled = true;
- gtk_im_context_focus_in(m_context.get());
-}
-
-void GtkInputMethodFilter::notifyFocusedOut()
-{
- if (!m_enabled)
- return;
-
- if (m_composingTextCurrently)
- confirmCurrentComposition();
- cancelContextComposition();
- gtk_im_context_focus_out(m_context.get());
- m_enabled = false;
-}
-
-void GtkInputMethodFilter::confirmComposition()
-{
- confirmCompositionText(m_confirmedComposition);
- m_confirmedComposition = String();
-}
-
-void GtkInputMethodFilter::updatePreedit()
-{
- setPreedit(m_preedit, m_cursorOffset);
- m_preeditChanged = false;
-}
-
-void GtkInputMethodFilter::sendCompositionAndPreeditWithFakeKeyEvents(ResultsToSend resultsToSend)
-{
- GUniquePtr<GdkEvent> event(gdk_event_new(GDK_KEY_PRESS));
- event->key.time = GDK_CURRENT_TIME;
- event->key.keyval = gCompositionEventKeyCode;
- sendKeyEventWithCompositionResults(&event->key, resultsToSend, EventFaked);
-
- m_confirmedComposition = String();
- if (resultsToSend & Composition)
- m_composingTextCurrently = false;
-
- event->type = GDK_KEY_RELEASE;
- sendSimpleKeyEvent(&event->key, String(), EventFaked);
- m_justSentFakeKeyUp = true;
-}
-
-void GtkInputMethodFilter::handleCommit(const char* compositionString)
-{
- if (m_preventNextCommit) {
- m_preventNextCommit = false;
- return;
- }
-
- if (!m_enabled)
- return;
-
- m_confirmedComposition.append(String::fromUTF8(compositionString));
-
- // If the commit was triggered outside of a key event, just send
- // the IME event now. If we are handling a key event, we'll decide
- // later how to handle this.
- if (!m_filteringKeyEvent)
- sendCompositionAndPreeditWithFakeKeyEvents(Composition);
-}
-
-void GtkInputMethodFilter::handlePreeditStart()
-{
- if (m_preventNextCommit || !m_enabled)
- return;
- m_preeditChanged = true;
- m_preedit = "";
-}
-
-void GtkInputMethodFilter::handlePreeditChanged()
-{
- if (!m_enabled)
- return;
-
- GUniqueOutPtr<gchar> newPreedit;
- gtk_im_context_get_preedit_string(m_context.get(), &newPreedit.outPtr(), 0, &m_cursorOffset);
-
- if (m_preventNextCommit) {
- if (strlen(newPreedit.get()) > 0)
- m_preventNextCommit = false;
- else
- return;
- }
-
- m_preedit = String::fromUTF8(newPreedit.get());
- m_cursorOffset = std::min(std::max(m_cursorOffset, 0), static_cast<int>(m_preedit.length()));
-
- m_composingTextCurrently = !m_preedit.isEmpty();
- m_preeditChanged = true;
-
- if (!m_filteringKeyEvent)
- sendCompositionAndPreeditWithFakeKeyEvents(Preedit);
-}
-
-void GtkInputMethodFilter::handlePreeditEnd()
-{
- if (m_preventNextCommit || !m_enabled)
- return;
-
- m_preedit = String();
- m_cursorOffset = 0;
- m_preeditChanged = true;
-
- if (!m_filteringKeyEvent)
- updatePreedit();
-}
-
-}
</del></span></pre></div>
<a id="trunkSourceWebCoreplatformgtkGtkInputMethodFilterh"></a>
<div class="delfile"><h4>Deleted: trunk/Source/WebCore/platform/gtk/GtkInputMethodFilter.h (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/gtk/GtkInputMethodFilter.h        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebCore/platform/gtk/GtkInputMethodFilter.h        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -1,99 +0,0 @@
</span><del>-/*
- * Copyright (C) 2012 Igalia S.L.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef GtkInputMethodFilter_h
-#define GtkInputMethodFilter_h
-
-#include "GRefPtrGtk.h"
-#include "IntRect.h"
-#include <gdk/gdk.h>
-#include <wtf/text/WTFString.h>
-
-typedef struct _GtkIMContext GtkIMContext;
-typedef struct _GtkWidget GtkWidget;
-
-namespace WebCore {
-
-class GtkInputMethodFilter {
-public:
- GtkInputMethodFilter();
- ~GtkInputMethodFilter();
-
- bool filterKeyEvent(GdkEventKey*);
- void notifyMouseButtonPress();
- void notifyFocusedIn();
- void notifyFocusedOut();
- void resetContext();
- void setEnabled(bool);
-
- void handleCommit(const char* compositionString);
- void handlePreeditChanged();
- void handlePreeditStart();
- void handlePreeditEnd();
-
- void confirmComposition();
- void cancelContextComposition();
- void updatePreedit();
- void setCursorRect(const IntRect& location);
-
- GtkIMContext* context() { return m_context.get(); }
-
- enum EventFakedForComposition {
- EventFaked,
- EventNotFaked
- };
-
-protected:
- enum ResultsToSend {
- Preedit = 1 << 1,
- Composition = 1 << 2,
- PreeditAndComposition = Preedit + Composition
- };
-
- void setWidget(GtkWidget*);
- virtual bool canEdit() = 0;
- virtual bool sendSimpleKeyEvent(GdkEventKey*, WTF::String eventString = String(), EventFakedForComposition = EventNotFaked) = 0;
- virtual bool sendKeyEventWithCompositionResults(GdkEventKey*, ResultsToSend = PreeditAndComposition, EventFakedForComposition = EventNotFaked) = 0;
- virtual void confirmCompositionText(String composition) = 0;
- virtual void confirmCurrentComposition() = 0;
- virtual void cancelCurrentComposition() = 0;
- virtual void setPreedit(String, int cursorOffset) = 0;
-
- WTF::String m_confirmedComposition;
- WTF::String m_preedit;
- int m_cursorOffset;
-
-private:
- void sendCompositionAndPreeditWithFakeKeyEvents(ResultsToSend);
-
- GRefPtr<GtkIMContext> m_context;
- GtkWidget* m_widget;
- bool m_enabled;
- bool m_composingTextCurrently;
- bool m_filteringKeyEvent;
- bool m_preeditChanged;
- bool m_preventNextCommit;
- bool m_justSentFakeKeyUp;
- unsigned int m_lastFilteredKeyPressCodeWithNoResults;
- IntPoint m_lastCareLocation;
-};
-
-} // namespace WebCore
-
-#endif // GtkInputMethodFilter_h
</del></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebKit2/ChangeLog        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -1,3 +1,62 @@
</span><ins>+2014-10-22 Carlos Garcia Campos <cgarcia@igalia.com>
+
+ [GTK] Move GtkInputMethodFilter from Platform to WebKit2
+ https://bugs.webkit.org/show_bug.cgi?id=137884
+
+ Reviewed by Gustavo Noronha Silva.
+
+ Merge WebViewBaseInputMethodFilter and GtkInputMethodFilter into a
+ single class InputMethodFilter. The code is mostly the same, but
+ instead of having a setWebView method only to get the WebPageProxy,
+ it has a setPage method that passes the WebPageProxy. The GtkIMContext
+ client window is set by the WebView when it's realized/unrealized.
+
+ * PlatformGTK.cmake:
+ * Shared/NativeWebKeyboardEvent.h:
+ * Shared/gtk/NativeWebKeyboardEventGtk.cpp:
+ (WebKit::NativeWebKeyboardEvent::NativeWebKeyboardEvent):
+ * UIProcess/API/gtk/WebKitWebViewBase.cpp:
+ (webkitWebViewBaseRealize):
+ (webkitWebViewBaseUnrealize):
+ (webkit_web_view_base_class_init):
+ (webkitWebViewBaseCreateWebPage):
+ (webkitWebViewBaseEnterFullScreen): Deleted.
+ * UIProcess/API/gtk/WebViewBaseInputMethodFilter.cpp: Removed.
+ * UIProcess/API/gtk/WebViewBaseInputMethodFilter.h: Removed.
+ * UIProcess/gtk/InputMethodFilter.cpp: Added.
+ (WebKit::InputMethodFilter::handleCommitCallback):
+ (WebKit::InputMethodFilter::handlePreeditStartCallback):
+ (WebKit::InputMethodFilter::handlePreeditChangedCallback):
+ (WebKit::InputMethodFilter::handlePreeditEndCallback):
+ (WebKit::InputMethodFilter::InputMethodFilter):
+ (WebKit::InputMethodFilter::~InputMethodFilter):
+ (WebKit::InputMethodFilter::setEnabled):
+ (WebKit::InputMethodFilter::setCursorRect):
+ (WebKit::InputMethodFilter::handleKeyboardEvent):
+ (WebKit::InputMethodFilter::handleKeyboardEventWithCompositionResults):
+ (WebKit::InputMethodFilter::filterKeyEvent):
+ (WebKit::InputMethodFilter::confirmComposition):
+ (WebKit::InputMethodFilter::updatePreedit):
+ (WebKit::InputMethodFilter::notifyFocusedIn):
+ (WebKit::InputMethodFilter::notifyFocusedOut):
+ (WebKit::InputMethodFilter::notifyMouseButtonPress):
+ (WebKit::InputMethodFilter::confirmCurrentComposition):
+ (WebKit::InputMethodFilter::cancelContextComposition):
+ (WebKit::InputMethodFilter::sendCompositionAndPreeditWithFakeKeyEvents):
+ (WebKit::InputMethodFilter::handleCommit):
+ (WebKit::InputMethodFilter::handlePreeditStart):
+ (WebKit::InputMethodFilter::handlePreeditChanged):
+ (WebKit::InputMethodFilter::handlePreeditEnd):
+ (WebKit::InputMethodFilter::logHandleKeyboardEventForTesting):
+ (WebKit::InputMethodFilter::logHandleKeyboardEventWithCompositionResultsForTesting):
+ (WebKit::InputMethodFilter::logConfirmCompositionForTesting):
+ (WebKit::InputMethodFilter::logSetPreeditForTesting):
+ * UIProcess/gtk/InputMethodFilter.h: Added.
+ (WebKit::InputMethodFilter::context):
+ (WebKit::InputMethodFilter::setPage):
+ (WebKit::InputMethodFilter::setTestingMode):
+ (WebKit::InputMethodFilter::events):
+
</ins><span class="cx"> 2014-10-21 Tim Horton <timothy_horton@apple.com>
</span><span class="cx">
</span><span class="cx"> Quick Look preview bubble has unnecessary controls
</span></span></pre></div>
<a id="trunkSourceWebKit2PlatformGTKcmake"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/PlatformGTK.cmake (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/PlatformGTK.cmake        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebKit2/PlatformGTK.cmake        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -228,8 +228,6 @@
</span><span class="cx"> UIProcess/API/gtk/WebKitWindowProperties.cpp
</span><span class="cx"> UIProcess/API/gtk/WebKitWindowProperties.h
</span><span class="cx"> UIProcess/API/gtk/WebKitWindowPropertiesPrivate.h
</span><del>- UIProcess/API/gtk/WebViewBaseInputMethodFilter.cpp
- UIProcess/API/gtk/WebViewBaseInputMethodFilter.h
</del><span class="cx"> UIProcess/API/gtk/webkit2.h
</span><span class="cx">
</span><span class="cx"> UIProcess/InspectorServer/gtk/WebInspectorServerGtk.cpp
</span><span class="lines">@@ -254,6 +252,7 @@
</span><span class="cx"> UIProcess/gtk/DragAndDropHandler.cpp
</span><span class="cx"> UIProcess/gtk/ExperimentalFeatures.cpp
</span><span class="cx"> UIProcess/gtk/GestureController.cpp
</span><ins>+ UIProcess/gtk/InputMethodFilter.cpp
</ins><span class="cx"> UIProcess/gtk/TextCheckerGtk.cpp
</span><span class="cx"> UIProcess/gtk/WebContextGtk.cpp
</span><span class="cx"> UIProcess/gtk/WebContextMenuProxyGtk.cpp
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedNativeWebKeyboardEventh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/NativeWebKeyboardEvent.h (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/NativeWebKeyboardEvent.h        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebKit2/Shared/NativeWebKeyboardEvent.h        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -44,9 +44,9 @@
</span><span class="cx"> #endif
</span><span class="cx">
</span><span class="cx"> #if PLATFORM(GTK)
</span><ins>+#include "InputMethodFilter.h"
</ins><span class="cx"> #include <WebCore/CompositionResults.h>
</span><span class="cx"> #include <WebCore/GUniquePtrGtk.h>
</span><del>-#include <WebCore/GtkInputMethodFilter.h>
</del><span class="cx"> typedef union _GdkEvent GdkEvent;
</span><span class="cx"> #endif
</span><span class="cx">
</span><span class="lines">@@ -63,7 +63,7 @@
</span><span class="cx"> NativeWebKeyboardEvent(NSEvent *, bool handledByInputMethod, const Vector<WebCore::KeypressCommand>&);
</span><span class="cx"> #elif PLATFORM(GTK)
</span><span class="cx"> NativeWebKeyboardEvent(const NativeWebKeyboardEvent&);
</span><del>- NativeWebKeyboardEvent(GdkEvent*, const WebCore::CompositionResults&, WebCore::GtkInputMethodFilter::EventFakedForComposition);
</del><ins>+ NativeWebKeyboardEvent(GdkEvent*, const WebCore::CompositionResults&, InputMethodFilter::EventFakedForComposition);
</ins><span class="cx"> #elif PLATFORM(EFL)
</span><span class="cx"> NativeWebKeyboardEvent(const Evas_Event_Key_Down*, bool);
</span><span class="cx"> NativeWebKeyboardEvent(const Evas_Event_Key_Up*);
</span></span></pre></div>
<a id="trunkSourceWebKit2SharedgtkNativeWebKeyboardEventGtkcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/Shared/gtk/NativeWebKeyboardEventGtk.cpp (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/Shared/gtk/NativeWebKeyboardEventGtk.cpp        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebKit2/Shared/gtk/NativeWebKeyboardEventGtk.cpp        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -35,11 +35,11 @@
</span><span class="cx">
</span><span class="cx"> namespace WebKit {
</span><span class="cx">
</span><del>-NativeWebKeyboardEvent::NativeWebKeyboardEvent(GdkEvent* event, const WebCore::CompositionResults& compositionResults, GtkInputMethodFilter::EventFakedForComposition faked)
</del><ins>+NativeWebKeyboardEvent::NativeWebKeyboardEvent(GdkEvent* event, const WebCore::CompositionResults& compositionResults, InputMethodFilter::EventFakedForComposition faked)
</ins><span class="cx"> : WebKeyboardEvent(WebEventFactory::createWebKeyboardEvent(event, compositionResults))
</span><span class="cx"> , m_nativeEvent(gdk_event_copy(event))
</span><span class="cx"> , m_compositionResults(compositionResults)
</span><del>- , m_fakeEventForComposition(faked == GtkInputMethodFilter::EventFaked)
</del><ins>+ , m_fakeEventForComposition(faked == InputMethodFilter::EventFaked)
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPIgtkWebKitWebViewBasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewBase.cpp (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewBase.cpp        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebKitWebViewBase.cpp        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> #include "WebKitWebViewBase.h"
</span><span class="cx">
</span><span class="cx"> #include "DrawingAreaProxyImpl.h"
</span><ins>+#include "InputMethodFilter.h"
</ins><span class="cx"> #include "NativeWebMouseEvent.h"
</span><span class="cx"> #include "NativeWebWheelEvent.h"
</span><span class="cx"> #include "PageClientImpl.h"
</span><span class="lines">@@ -46,7 +47,6 @@
</span><span class="cx"> #include "WebPageProxy.h"
</span><span class="cx"> #include "WebPreferences.h"
</span><span class="cx"> #include "WebUserContentControllerProxy.h"
</span><del>-#include "WebViewBaseInputMethodFilter.h"
</del><span class="cx"> #include <WebCore/CairoUtilities.h>
</span><span class="cx"> #include <WebCore/GUniquePtrGtk.h>
</span><span class="cx"> #include <WebCore/GtkClickCounter.h>
</span><span class="lines">@@ -107,7 +107,7 @@
</span><span class="cx"> unsigned inspectorViewSize;
</span><span class="cx"> GUniquePtr<GdkEvent> contextMenuEvent;
</span><span class="cx"> WebContextMenuProxyGtk* activeContextMenuProxy;
</span><del>- WebViewBaseInputMethodFilter inputMethodFilter;
</del><ins>+ InputMethodFilter inputMethodFilter;
</ins><span class="cx"> TouchEventsMap touchEvents;
</span><span class="cx">
</span><span class="cx"> GtkWindow* toplevelOnScreenWindow;
</span><span class="lines">@@ -299,11 +299,21 @@
</span><span class="cx"> gtk_style_context_set_background(gtk_widget_get_style_context(widget), window);
</span><span class="cx">
</span><span class="cx"> WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(widget);
</span><ins>+ gtk_im_context_set_client_window(webView->priv->inputMethodFilter.context(), window);
+
</ins><span class="cx"> GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
</span><span class="cx"> if (widgetIsOnscreenToplevelWindow(toplevel))
</span><span class="cx"> webkitWebViewBaseSetToplevelOnScreenWindow(webView, GTK_WINDOW(toplevel));
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+static void webkitWebViewBaseUnrealize(GtkWidget* widget)
+{
+ WebKitWebViewBase* webView = WEBKIT_WEB_VIEW_BASE(widget);
+ gtk_im_context_set_client_window(webView->priv->inputMethodFilter.context(), nullptr);
+
+ GTK_WIDGET_CLASS(webkit_web_view_base_parent_class)->unrealize(widget);
+}
+
</ins><span class="cx"> static bool webkitWebViewChildIsInternalWidget(WebKitWebViewBase* webViewBase, GtkWidget* widget)
</span><span class="cx"> {
</span><span class="cx"> WebKitWebViewBasePrivate* priv = webViewBase->priv;
</span><span class="lines">@@ -965,6 +975,7 @@
</span><span class="cx"> {
</span><span class="cx"> GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(webkitWebViewBaseClass);
</span><span class="cx"> widgetClass->realize = webkitWebViewBaseRealize;
</span><ins>+ widgetClass->unrealize = webkitWebViewBaseUnrealize;
</ins><span class="cx"> widgetClass->draw = webkitWebViewBaseDraw;
</span><span class="cx"> widgetClass->size_allocate = webkitWebViewBaseSizeAllocate;
</span><span class="cx"> widgetClass->map = webkitWebViewBaseMap;
</span><span class="lines">@@ -1050,6 +1061,8 @@
</span><span class="cx"> priv->pageProxy = context->createWebPage(*priv->pageClient, WTF::move(webPageConfiguration));
</span><span class="cx"> priv->pageProxy->initializeWebPage();
</span><span class="cx">
</span><ins>+ priv->inputMethodFilter.setPage(priv->pageProxy.get());
+
</ins><span class="cx"> #if USE(TEXTURE_MAPPER_GL) && PLATFORM(X11)
</span><span class="cx"> if (priv->redirectedWindow)
</span><span class="cx"> priv->pageProxy->setAcceleratedCompositingWindowId(priv->redirectedWindow->windowId());
</span><span class="lines">@@ -1062,10 +1075,6 @@
</span><span class="cx"> #endif
</span><span class="cx">
</span><span class="cx"> webkitWebViewBaseUpdatePreferences(webkitWebViewBase);
</span><del>-
- // This must happen here instead of the instance initializer, because the input method
- // filter must have access to the page.
- priv->inputMethodFilter.setWebView(webkitWebViewBase);
</del><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void webkitWebViewBaseSetTooltipText(WebKitWebViewBase* webViewBase, const char* tooltip)
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPIgtkWebViewBaseInputMethodFiltercpp"></a>
<div class="delfile"><h4>Deleted: trunk/Source/WebKit2/UIProcess/API/gtk/WebViewBaseInputMethodFilter.cpp (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/gtk/WebViewBaseInputMethodFilter.cpp        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebViewBaseInputMethodFilter.cpp        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -1,95 +0,0 @@
</span><del>-/*
- * Copyright (C) 2012 Igalia S.L.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#include "config.h"
-#include "WebViewBaseInputMethodFilter.h"
-
-#include "NativeWebKeyboardEvent.h"
-#include "WebKitWebViewBasePrivate.h"
-#include "WebPageProxy.h"
-#include <WebCore/Color.h>
-#include <WebCore/CompositionResults.h>
-#include <WebCore/Editor.h>
-
-using namespace WebCore;
-
-namespace WebKit {
-
-void WebViewBaseInputMethodFilter::setWebView(WebKitWebViewBase* webView)
-{
- GtkInputMethodFilter::setWidget(GTK_WIDGET(webView));
-
- m_webPageProxy = webkitWebViewBaseGetPage(webView);
- ASSERT(m_webPageProxy);
-}
-
-bool WebViewBaseInputMethodFilter::canEdit()
-{
- return true;
-}
-
-bool WebViewBaseInputMethodFilter::sendSimpleKeyEvent(GdkEventKey* event, WTF::String simpleString, EventFakedForComposition faked)
-{
- ASSERT(m_webPageProxy);
- m_webPageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(reinterpret_cast<GdkEvent*>(event),
- CompositionResults(simpleString), faked));
- return true;
-}
-
-bool WebViewBaseInputMethodFilter::sendKeyEventWithCompositionResults(GdkEventKey* event, ResultsToSend resultsToSend, EventFakedForComposition faked)
-{
- ASSERT(m_webPageProxy);
- m_webPageProxy->handleKeyboardEvent(NativeWebKeyboardEvent(reinterpret_cast<GdkEvent*>(event),
- CompositionResults(CompositionResults::WillSendCompositionResultsSoon),
- faked));
-
- if (resultsToSend & Composition && !m_confirmedComposition.isNull())
- confirmCompositionText(m_confirmedComposition);
- if (resultsToSend & Preedit && !m_preedit.isNull())
- setPreedit(m_preedit, m_cursorOffset);
- return true;
-}
-
-void WebViewBaseInputMethodFilter::confirmCompositionText(String text)
-{
- ASSERT(m_webPageProxy);
- m_webPageProxy->confirmComposition(text, -1, 0);
-}
-
-void WebViewBaseInputMethodFilter::confirmCurrentComposition()
-{
- ASSERT(m_webPageProxy);
- m_webPageProxy->confirmComposition(String(), -1, 0);
-}
-
-void WebViewBaseInputMethodFilter::cancelCurrentComposition()
-{
- ASSERT(m_webPageProxy);
- m_webPageProxy->cancelComposition();
-}
-
-void WebViewBaseInputMethodFilter::setPreedit(String newPreedit, int /* cursorOffset */)
-{
- // TODO: We should parse the PangoAttrList that we get from the IM context here.
- ASSERT(m_webPageProxy);
- m_webPageProxy->setComposition(newPreedit, Vector<CompositionUnderline>{ CompositionUnderline(0, newPreedit.length(), Color(1, 1, 1), false) },
- m_cursorOffset, m_cursorOffset, 0 /* replacement start */, 0 /* replacement end */);
-}
-
-} // namespace WebKit
</del></span></pre></div>
<a id="trunkSourceWebKit2UIProcessAPIgtkWebViewBaseInputMethodFilterh"></a>
<div class="delfile"><h4>Deleted: trunk/Source/WebKit2/UIProcess/API/gtk/WebViewBaseInputMethodFilter.h (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/API/gtk/WebViewBaseInputMethodFilter.h        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Source/WebKit2/UIProcess/API/gtk/WebViewBaseInputMethodFilter.h        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -1,49 +0,0 @@
</span><del>-/*
- * Copyright (C) 2012 Igalia S.L.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public License
- * along with this library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
- * Boston, MA 02110-1301, USA.
- */
-
-#ifndef WebViewBaseInputMethodFilter_h
-#define WebViewBaseInputMethodFilter_h
-
-#include "GtkInputMethodFilter.h"
-#include "WebPageProxy.h"
-
-typedef struct _WebKitWebViewBase WebKitWebViewBase;
-
-namespace WebKit {
-
-class WebViewBaseInputMethodFilter : public WebCore::GtkInputMethodFilter {
-public:
- void setWebView(WebKitWebViewBase*);
-
-protected:
- virtual bool sendSimpleKeyEvent(GdkEventKey*, WTF::String eventString, EventFakedForComposition);
- virtual bool sendKeyEventWithCompositionResults(GdkEventKey*, ResultsToSend, EventFakedForComposition);
- virtual bool canEdit();
- virtual void confirmCompositionText(String);
- virtual void confirmCurrentComposition();
- virtual void cancelCurrentComposition();
- virtual void setPreedit(String, int cursorOffset);
-
-private:
- WebPageProxy* m_webPageProxy;
-};
-
-} // namespace WebKit
-
-#endif // WebViewBaseInputMethodFilter_h
</del></span></pre></div>
<a id="trunkSourceWebKit2UIProcessgtkInputMethodFiltercpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebKit2/UIProcess/gtk/InputMethodFilter.cpp (0 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/gtk/InputMethodFilter.cpp         (rev 0)
+++ trunk/Source/WebKit2/UIProcess/gtk/InputMethodFilter.cpp        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -0,0 +1,441 @@
</span><ins>+/*
+ * Copyright (C) 2012, 2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "InputMethodFilter.h"
+
+#include "NativeWebKeyboardEvent.h"
+#include "WebPageProxy.h"
+#include <WebCore/Color.h>
+#include <WebCore/CompositionResults.h>
+#include <WebCore/Editor.h>
+#include <WebCore/GUniquePtrGtk.h>
+#include <WebCore/IntRect.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <wtf/Vector.h>
+#include <wtf/gobject/GUniquePtr.h>
+
+using namespace WebCore;
+
+namespace WebKit {
+
+void InputMethodFilter::handleCommitCallback(InputMethodFilter* filter, const char* compositionString)
+{
+ filter->handleCommit(compositionString);
+}
+
+void InputMethodFilter::handlePreeditStartCallback(InputMethodFilter* filter)
+{
+ filter->handlePreeditStart();
+}
+
+void InputMethodFilter::handlePreeditChangedCallback(InputMethodFilter* filter)
+{
+ filter->handlePreeditChanged();
+}
+
+void InputMethodFilter::handlePreeditEndCallback(InputMethodFilter* filter)
+{
+ filter->handlePreeditEnd();
+}
+
+InputMethodFilter::InputMethodFilter()
+ : m_context(adoptGRef(gtk_im_multicontext_new()))
+ , m_page(nullptr)
+ , m_enabled(false)
+ , m_composingTextCurrently(false)
+ , m_filteringKeyEvent(false)
+ , m_preeditChanged(false)
+ , m_preventNextCommit(false)
+ , m_justSentFakeKeyUp(false)
+ , m_cursorOffset(0)
+ , m_lastFilteredKeyPressCodeWithNoResults(GDK_KEY_VoidSymbol)
+#if ENABLE(API_TESTS)
+ , m_testingMode(false)
+#endif
+{
+ g_signal_connect_swapped(m_context.get(), "commit", G_CALLBACK(handleCommitCallback), this);
+ g_signal_connect_swapped(m_context.get(), "preedit-start", G_CALLBACK(handlePreeditStartCallback), this);
+ g_signal_connect_swapped(m_context.get(), "preedit-changed", G_CALLBACK(handlePreeditChangedCallback), this);
+ g_signal_connect_swapped(m_context.get(), "preedit-end", G_CALLBACK(handlePreeditEndCallback), this);
+}
+
+InputMethodFilter::~InputMethodFilter()
+{
+ g_signal_handlers_disconnect_matched(m_context.get(), G_SIGNAL_MATCH_DATA, 0, 0, nullptr, nullptr, this);
+}
+
+void InputMethodFilter::setEnabled(bool enabled)
+{
+ ASSERT(m_page);
+
+ m_enabled = enabled;
+ if (enabled)
+ gtk_im_context_focus_in(m_context.get());
+ else
+ gtk_im_context_focus_out(m_context.get());
+}
+
+void InputMethodFilter::setCursorRect(const IntRect& cursorRect)
+{
+ ASSERT(m_page);
+ // Don't move the window unless the cursor actually moves more than 10
+ // pixels. This prevents us from making the window flash during minor
+ // cursor adjustments.
+ static const int windowMovementThreshold = 10 * 10;
+ if (cursorRect.location().distanceSquaredToPoint(m_lastCareLocation) < windowMovementThreshold)
+ return;
+
+ m_lastCareLocation = cursorRect.location();
+ IntRect translatedRect = cursorRect;
+
+ ASSERT(m_widget);
+ GtkAllocation allocation;
+ gtk_widget_get_allocation(m_page->viewWidget(), &allocation);
+ translatedRect.move(allocation.x, allocation.y);
+
+ GdkRectangle gdkCursorRect = translatedRect;
+ gtk_im_context_set_cursor_location(m_context.get(), &gdkCursorRect);
+}
+
+void InputMethodFilter::handleKeyboardEvent(GdkEventKey* event, const String& simpleString, EventFakedForComposition faked)
+{
+#if ENABLE(API_TESTS)
+ if (m_testingMode) {
+ logHandleKeyboardEventForTesting(event, simpleString, faked);
+ return;
+ }
+#endif
+ m_page->handleKeyboardEvent(NativeWebKeyboardEvent(reinterpret_cast<GdkEvent*>(event), CompositionResults(simpleString), faked));
+}
+
+void InputMethodFilter::handleKeyboardEventWithCompositionResults(GdkEventKey* event, ResultsToSend resultsToSend, EventFakedForComposition faked)
+{
+#if ENABLE(API_TESTS)
+ if (m_testingMode) {
+ logHandleKeyboardEventWithCompositionResultsForTesting(event, resultsToSend, faked);
+ return;
+ }
+#endif
+ m_page->handleKeyboardEvent(NativeWebKeyboardEvent(reinterpret_cast<GdkEvent*>(event), CompositionResults(CompositionResults::WillSendCompositionResultsSoon), faked));
+
+ if (resultsToSend & Composition && !m_confirmedComposition.isNull())
+ m_page->confirmComposition(m_confirmedComposition, -1, 0);
+
+ if (resultsToSend & Preedit && !m_preedit.isNull()) {
+ m_page->setComposition(m_preedit, Vector<CompositionUnderline>{ CompositionUnderline(0, m_preedit.length(), Color(1, 1, 1), false) },
+ m_cursorOffset, m_cursorOffset, 0 /* replacement start */, 0 /* replacement end */);
+ }
+}
+
+void InputMethodFilter::filterKeyEvent(GdkEventKey* event)
+{
+#if ENABLE(API_TESTS)
+ ASSERT(m_page || m_testingMode);
+#else
+ ASSERT(m_page);
+#endif
+ if (!m_enabled) {
+ handleKeyboardEvent(event);
+ return;
+ }
+
+ m_preeditChanged = false;
+ m_filteringKeyEvent = true;
+
+ unsigned lastFilteredKeyPressCodeWithNoResults = m_lastFilteredKeyPressCodeWithNoResults;
+ m_lastFilteredKeyPressCodeWithNoResults = GDK_KEY_VoidSymbol;
+
+ bool filtered = gtk_im_context_filter_keypress(m_context.get(), event);
+ m_filteringKeyEvent = false;
+
+ bool justSentFakeKeyUp = m_justSentFakeKeyUp;
+ m_justSentFakeKeyUp = false;
+ if (justSentFakeKeyUp && event->type == GDK_KEY_RELEASE)
+ return;
+
+ // Simple input methods work such that even normal keystrokes fire the
+ // commit signal. We detect those situations and treat them as normal
+ // key events, supplying the commit string as the key character.
+ if (filtered && !m_composingTextCurrently && !m_preeditChanged && m_confirmedComposition.length() == 1) {
+ handleKeyboardEvent(event, m_confirmedComposition);
+ m_confirmedComposition = String();
+ return;
+ }
+
+ if (filtered && event->type == GDK_KEY_PRESS) {
+ if (!m_preeditChanged && m_confirmedComposition.isNull()) {
+ m_composingTextCurrently = true;
+ m_lastFilteredKeyPressCodeWithNoResults = event->keyval;
+ return;
+ }
+
+ handleKeyboardEventWithCompositionResults(event);
+ if (!m_confirmedComposition.isEmpty()) {
+ m_composingTextCurrently = false;
+ m_confirmedComposition = String();
+ }
+ return;
+ }
+
+ // If we previously filtered a key press event and it yielded no results. Suppress
+ // the corresponding key release event to avoid confusing the web content.
+ if (event->type == GDK_KEY_RELEASE && lastFilteredKeyPressCodeWithNoResults == event->keyval)
+ return;
+
+ // At this point a keystroke was either:
+ // 1. Unfiltered
+ // 2. A filtered keyup event. As the IME code in EditorClient.h doesn't
+ // ever look at keyup events, we send any composition results before
+ // the key event.
+ // Both might have composition results or not.
+ //
+ // It's important to send the composition results before the event
+ // because some IM modules operate that way. For example (taken from
+ // the Chromium source), the latin-post input method gives this sequence
+ // when you press 'a' and then backspace:
+ // 1. keydown 'a' (filtered)
+ // 2. preedit changed to "a"
+ // 3. keyup 'a' (unfiltered)
+ // 4. keydown Backspace (unfiltered)
+ // 5. commit "a"
+ // 6. preedit end
+ if (!m_confirmedComposition.isEmpty())
+ confirmComposition();
+ if (m_preeditChanged)
+ updatePreedit();
+ handleKeyboardEvent(event);
+}
+
+void InputMethodFilter::confirmComposition()
+{
+#if ENABLE(API_TESTS)
+ if (m_testingMode) {
+ logConfirmCompositionForTesting();
+ m_confirmedComposition = String();
+ return;
+ }
+#endif
+ m_page->confirmComposition(m_confirmedComposition, -1, 0);
+ m_confirmedComposition = String();
+}
+
+void InputMethodFilter::updatePreedit()
+{
+#if ENABLE(API_TESTS)
+ if (m_testingMode) {
+ logSetPreeditForTesting();
+ return;
+ }
+#endif
+ // FIXME: We should parse the PangoAttrList that we get from the IM context here.
+ m_page->setComposition(m_preedit, Vector<CompositionUnderline>{ CompositionUnderline(0, m_preedit.length(), Color(1, 1, 1), false) },
+ m_cursorOffset, m_cursorOffset, 0 /* replacement start */, 0 /* replacement end */);
+ m_preeditChanged = false;
+}
+
+void InputMethodFilter::notifyFocusedIn()
+{
+#if ENABLE(API_TESTS)
+ ASSERT(m_page || m_testingMode);
+#else
+ ASSERT(m_page);
+#endif
+ m_enabled = true;
+ gtk_im_context_focus_in(m_context.get());
+}
+
+void InputMethodFilter::notifyFocusedOut()
+{
+#if ENABLE(API_TESTS)
+ ASSERT(m_page || m_testingMode);
+#else
+ ASSERT(m_page);
+#endif
+ if (!m_enabled)
+ return;
+
+ confirmCurrentComposition();
+ cancelContextComposition();
+ gtk_im_context_focus_out(m_context.get());
+ m_enabled = false;
+}
+
+void InputMethodFilter::notifyMouseButtonPress()
+{
+#if ENABLE(API_TESTS)
+ ASSERT(m_page || m_testingMode);
+#else
+ ASSERT(m_page);
+#endif
+
+ // Confirming the composition may trigger a selection change, which
+ // might trigger further unwanted actions on the context, so we prevent
+ // that by setting m_composingTextCurrently to false.
+ confirmCurrentComposition();
+ cancelContextComposition();
+}
+
+void InputMethodFilter::confirmCurrentComposition()
+{
+ if (!m_composingTextCurrently)
+ return;
+ m_page->confirmComposition(String(), -1, 0);
+ m_composingTextCurrently = false;
+}
+
+void InputMethodFilter::cancelContextComposition()
+{
+ m_preventNextCommit = !m_preedit.isEmpty();
+
+ gtk_im_context_reset(m_context.get());
+
+ m_composingTextCurrently = false;
+ m_justSentFakeKeyUp = false;
+ m_preedit = String();
+ m_confirmedComposition = String();
+}
+
+void InputMethodFilter::sendCompositionAndPreeditWithFakeKeyEvents(ResultsToSend resultsToSend)
+{
+ // The Windows composition key event code is 299 or VK_PROCESSKEY. We need to
+ // emit this code for web compatibility reasons when key events trigger
+ // composition results. GDK doesn't have an equivalent, so we send VoidSymbol
+ // here to WebCore. PlatformKeyEvent knows to convert this code into
+ // VK_PROCESSKEY.
+ static const int compositionEventKeyCode = GDK_KEY_VoidSymbol;
+
+ GUniquePtr<GdkEvent> event(gdk_event_new(GDK_KEY_PRESS));
+ event->key.time = GDK_CURRENT_TIME;
+ event->key.keyval = compositionEventKeyCode;
+ handleKeyboardEventWithCompositionResults(&event->key, resultsToSend, EventFaked);
+
+ m_confirmedComposition = String();
+ if (resultsToSend & Composition)
+ m_composingTextCurrently = false;
+
+ event->type = GDK_KEY_RELEASE;
+ handleKeyboardEvent(&event->key, String(), EventFaked);
+ m_justSentFakeKeyUp = true;
+}
+
+void InputMethodFilter::handleCommit(const char* compositionString)
+{
+ if (m_preventNextCommit) {
+ m_preventNextCommit = false;
+ return;
+ }
+
+ if (!m_enabled)
+ return;
+
+ m_confirmedComposition.append(String::fromUTF8(compositionString));
+
+ // If the commit was triggered outside of a key event, just send
+ // the IME event now. If we are handling a key event, we'll decide
+ // later how to handle this.
+ if (!m_filteringKeyEvent)
+ sendCompositionAndPreeditWithFakeKeyEvents(Composition);
+}
+
+void InputMethodFilter::handlePreeditStart()
+{
+ if (m_preventNextCommit || !m_enabled)
+ return;
+ m_preeditChanged = true;
+ m_preedit = emptyString();
+}
+
+void InputMethodFilter::handlePreeditChanged()
+{
+ if (!m_enabled)
+ return;
+
+ GUniqueOutPtr<gchar> newPreedit;
+ gtk_im_context_get_preedit_string(m_context.get(), &newPreedit.outPtr(), nullptr, &m_cursorOffset);
+
+ if (m_preventNextCommit) {
+ if (strlen(newPreedit.get()) > 0)
+ m_preventNextCommit = false;
+ else
+ return;
+ }
+
+ m_preedit = String::fromUTF8(newPreedit.get());
+ m_cursorOffset = std::min(std::max(m_cursorOffset, 0), static_cast<int>(m_preedit.length()));
+
+ m_composingTextCurrently = !m_preedit.isEmpty();
+ m_preeditChanged = true;
+
+ if (!m_filteringKeyEvent)
+ sendCompositionAndPreeditWithFakeKeyEvents(Preedit);
+}
+
+void InputMethodFilter::handlePreeditEnd()
+{
+ if (m_preventNextCommit || !m_enabled)
+ return;
+
+ m_preedit = String();
+ m_cursorOffset = 0;
+ m_preeditChanged = true;
+
+ if (!m_filteringKeyEvent)
+ updatePreedit();
+}
+
+#if ENABLE(API_TESTS)
+void InputMethodFilter::logHandleKeyboardEventForTesting(GdkEventKey* event, const String& eventString, EventFakedForComposition faked)
+{
+ const char* eventType = event->type == GDK_KEY_RELEASE ? "release" : "press";
+ const char* fakedString = faked == EventFaked ? " (faked)" : "";
+ if (!eventString.isNull())
+ m_events.append(String::format("sendSimpleKeyEvent type=%s keycode=%x text='%s'%s", eventType, event->keyval, eventString.utf8().data(), fakedString));
+ else
+ m_events.append(String::format("sendSimpleKeyEvent type=%s keycode=%x%s", eventType, event->keyval, fakedString));
+}
+
+void InputMethodFilter::logHandleKeyboardEventWithCompositionResultsForTesting(GdkEventKey* event, ResultsToSend resultsToSend, EventFakedForComposition faked)
+{
+ const char* eventType = event->type == GDK_KEY_RELEASE ? "release" : "press";
+ const char* fakedString = faked == EventFaked ? " (faked)" : "";
+ m_events.append(String::format("sendKeyEventWithCompositionResults type=%s keycode=%u%s", eventType, event->keyval, fakedString));
+
+ if (resultsToSend & Composition && !m_confirmedComposition.isNull())
+ logConfirmCompositionForTesting();
+ if (resultsToSend & Preedit && !m_preedit.isNull())
+ logSetPreeditForTesting();
+}
+
+void InputMethodFilter::logConfirmCompositionForTesting()
+{
+ if (m_confirmedComposition.isEmpty())
+ m_events.append(String("confirmCurrentcomposition"));
+ else
+ m_events.append(String::format("confirmComposition '%s'", m_confirmedComposition.utf8().data()));
+}
+
+void InputMethodFilter::logSetPreeditForTesting()
+{
+ m_events.append(String::format("setPreedit text='%s' cursorOffset=%i", m_preedit.utf8().data(), m_cursorOffset));
+}
+#endif // ENABLE(API_TESTS)
+
+} // namespace WebKit
</ins></span></pre></div>
<a id="trunkSourceWebKit2UIProcessgtkInputMethodFilterh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebKit2/UIProcess/gtk/InputMethodFilter.h (0 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/gtk/InputMethodFilter.h         (rev 0)
+++ trunk/Source/WebKit2/UIProcess/gtk/InputMethodFilter.h        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -0,0 +1,122 @@
</span><ins>+/*
+ * Copyright (C) 2012, 2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef InputMethodFilter_h
+#define InputMethodFilter_h
+
+#include <WebCore/IntPoint.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/gobject/GRefPtr.h>
+#include <wtf/text/WTFString.h>
+
+typedef struct _GdkEventKey GdkEventKey;
+typedef struct _GtkIMContext GtkIMContext;
+
+namespace WebCore {
+class IntRect;
+}
+
+namespace WebKit {
+
+class WebPageProxy;
+
+class InputMethodFilter {
+ WTF_MAKE_NONCOPYABLE(InputMethodFilter);
+public:
+ enum EventFakedForComposition {
+ EventFaked,
+ EventNotFaked
+ };
+
+ InputMethodFilter();
+ ~InputMethodFilter();
+
+ GtkIMContext* context() const { return m_context.get(); }
+
+ void setPage(WebPageProxy* page) { m_page = page; }
+
+ void setEnabled(bool);
+ void setCursorRect(const WebCore::IntRect&);
+
+ void filterKeyEvent(GdkEventKey*);
+ void notifyFocusedIn();
+ void notifyFocusedOut();
+ void notifyMouseButtonPress();
+
+#if ENABLE(API_TESTS)
+ void setTestingMode(bool enable) { m_testingMode = enable; }
+ const Vector<String>& events() const { return m_events; }
+#endif
+
+private:
+ enum ResultsToSend {
+ Preedit = 1 << 1,
+ Composition = 1 << 2,
+ PreeditAndComposition = Preedit | Composition
+ };
+
+ static void handleCommitCallback(InputMethodFilter*, const char* compositionString);
+ static void handlePreeditStartCallback(InputMethodFilter*);
+ static void handlePreeditChangedCallback(InputMethodFilter*);
+ static void handlePreeditEndCallback(InputMethodFilter*);
+
+ void handleCommit(const char* compositionString);
+ void handlePreeditChanged();
+ void handlePreeditStart();
+ void handlePreeditEnd();
+
+ void handleKeyboardEvent(GdkEventKey*, const String& eventString = String(), EventFakedForComposition = EventNotFaked);
+ void handleKeyboardEventWithCompositionResults(GdkEventKey*, ResultsToSend = PreeditAndComposition, EventFakedForComposition = EventNotFaked);
+
+ void sendCompositionAndPreeditWithFakeKeyEvents(ResultsToSend);
+ void confirmComposition();
+ void updatePreedit();
+ void confirmCurrentComposition();
+ void cancelContextComposition();
+
+#if ENABLE(API_TESTS)
+ void logHandleKeyboardEventForTesting(GdkEventKey*, const String&, EventFakedForComposition);
+ void logHandleKeyboardEventWithCompositionResultsForTesting(GdkEventKey*, ResultsToSend, EventFakedForComposition);
+ void logConfirmCompositionForTesting();
+ void logSetPreeditForTesting();
+#endif
+
+ GRefPtr<GtkIMContext> m_context;
+ WebPageProxy* m_page;
+ unsigned m_enabled : 1;
+ unsigned m_composingTextCurrently : 1;
+ unsigned m_filteringKeyEvent : 1;
+ unsigned m_preeditChanged : 1;
+ unsigned m_preventNextCommit : 1;
+ unsigned m_justSentFakeKeyUp : 1;
+ int m_cursorOffset;
+ unsigned m_lastFilteredKeyPressCodeWithNoResults;
+ WebCore::IntPoint m_lastCareLocation;
+ String m_confirmedComposition;
+ String m_preedit;
+
+#if ENABLE(API_TESTS)
+ bool m_testingMode;
+ Vector<String> m_events;
+#endif
+};
+
+} // namespace WebKit
+
+#endif // InputMethodFilter_h
</ins></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Tools/ChangeLog        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -1,3 +1,26 @@
</span><ins>+2014-10-22 Carlos Garcia Campos <cgarcia@igalia.com>
+
+ [GTK] Move GtkInputMethodFilter from Platform to WebKit2
+ https://bugs.webkit.org/show_bug.cgi?id=137884
+
+ Reviewed by Gustavo Noronha Silva.
+
+ Move InputMethodFilter test from WebCore tests to WebKit2 tests
+ and adapt it to use the new InputMethodFilter WebKit class. Instead
+ of having virtual methods just for testing, it has a testing mode
+ that logs the events.
+
+ * TestWebKitAPI/PlatformGTK.cmake:
+ * TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp: Renamed from Tools/TestWebKitAPI/Tests/WebCore/gtk/InputMethodFilter.cpp.
+ (TestWebKitAPI::TestInputMethodFilter::TestInputMethodFilter):
+ (TestWebKitAPI::TestInputMethodFilter::~TestInputMethodFilter):
+ (TestWebKitAPI::TestInputMethodFilter::sendKeyEventToFilter):
+ (TestWebKitAPI::TestInputMethodFilter::sendPressAndReleaseKeyEventPairToFilter):
+ (TestWebKitAPI::TEST):
+ (TestWebKitAPI::temporaryGetPreeditStringOverride):
+ (TestWebKitAPI::temporaryResetOverride):
+ (TestWebKitAPI::verifyCanceledComposition):
+
</ins><span class="cx"> 2014-10-21 Alexey Proskuryakov <ap@apple.com>
</span><span class="cx">
</span><span class="cx"> build.webkit.org/dashboard: Cannot click on green tester bubbles
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPIPlatformGTKcmake"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/PlatformGTK.cmake (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/PlatformGTK.cmake        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Tools/TestWebKitAPI/PlatformGTK.cmake        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -98,6 +98,7 @@
</span><span class="cx"> ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WKString.cpp
</span><span class="cx"> ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WKStringJSString.cpp
</span><span class="cx"> ${TESTWEBKITAPI_DIR}/Tests/WebKit2/WKURL.cpp
</span><ins>+ ${TESTWEBKITAPI_DIR}/Tests/WebKit2/gtk/InputMethodFilter.cpp
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> target_link_libraries(TestWebKit2 ${test_webkit2_api_LIBRARIES})
</span><span class="lines">@@ -106,12 +107,6 @@
</span><span class="cx"> set_target_properties(TestWebKit2 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TESTWEBKITAPI_RUNTIME_OUTPUT_DIRECTORY}/WebKit2)
</span><span class="cx">
</span><span class="cx"> set(TestWebCoreGtk_SOURCES
</span><del>- ${WEBCORE_DIR}/platform/graphics/IntPoint.cpp
- ${WEBCORE_DIR}/platform/graphics/IntRect.cpp
- ${WEBCORE_DIR}/platform/graphics/IntSize.cpp
- ${WEBCORE_DIR}/platform/graphics/cairo/IntRectCairo.cpp
- ${WEBCORE_DIR}/platform/gtk/GtkInputMethodFilter.cpp
- ${TESTWEBKITAPI_DIR}/Tests/WebCore/gtk/InputMethodFilter.cpp
</del><span class="cx"> ${TESTWEBKITAPI_DIR}/Tests/WebCore/gtk/UserAgentQuirks.cpp
</span><span class="cx"> )
</span><span class="cx">
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoregtkInputMethodFiltercpp"></a>
<div class="delfile"><h4>Deleted: trunk/Tools/TestWebKitAPI/Tests/WebCore/gtk/InputMethodFilter.cpp (175045 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/gtk/InputMethodFilter.cpp        2014-10-22 12:27:44 UTC (rev 175045)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/gtk/InputMethodFilter.cpp        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -1,310 +0,0 @@
</span><del>-/*
- * Copyright (C) 2012 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 "WTFStringUtilities.h"
-#include <WebCore/GtkInputMethodFilter.h>
-#include <gdk/gdkkeysyms.h>
-#include <gtk/gtk.h>
-#include <wtf/gobject/GUniquePtr.h>
-#include <wtf/gobject/GRefPtr.h>
-#include <wtf/text/CString.h>
-
-using namespace WebCore;
-
-namespace TestWebKitAPI {
-
-class TestInputMethodFilter : public GtkInputMethodFilter {
-public:
- TestInputMethodFilter()
- : m_testWindow(gtk_window_new(GTK_WINDOW_POPUP))
- {
- gtk_widget_show(m_testWindow.get());
- setWidget(m_testWindow.get());
-
- // Focus in is necessary to activate the default input method in the multicontext.
- notifyFocusedIn();
- }
-
- Vector<String>& events() { return m_events; }
-
- void sendKeyEventToFilter(unsigned gdkKeyValue, GdkEventType type, unsigned modifiers = 0)
- {
- GdkEvent* event = gdk_event_new(type);
- event->key.keyval = gdkKeyValue;
- event->key.state = modifiers;
- event->key.window = gtk_widget_get_window(m_testWindow.get());
- event->key.time = GDK_CURRENT_TIME;
- g_object_ref(event->key.window);
-
-#ifndef GTK_API_VERSION_2
- gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())));
-#endif
-
- GUniqueOutPtr<GdkKeymapKey> keys;
- gint nKeys;
- if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeyValue, &keys.outPtr(), &nKeys))
- event->key.hardware_keycode = keys.get()[0].keycode;
-
- filterKeyEvent(&event->key);
- gdk_event_free(event);
- }
-
- void sendPressAndReleaseKeyEventPairToFilter(unsigned gdkKeyValue, unsigned modifiers = 0)
- {
- sendKeyEventToFilter(gdkKeyValue, GDK_KEY_PRESS, modifiers);
- sendKeyEventToFilter(gdkKeyValue, GDK_KEY_RELEASE, modifiers);
- }
-
-protected:
- virtual bool sendSimpleKeyEvent(GdkEventKey* event, WTF::String eventString, EventFakedForComposition faked)
- {
- const char* eventType = event->type == GDK_KEY_RELEASE ? "release" : "press";
- const char* fakedString = faked == EventFaked ? " (faked)" : "";
- if (!eventString.isNull())
- m_events.append(String::format("sendSimpleKeyEvent type=%s keycode=%x text='%s'%s", eventType, event->keyval, eventString.utf8().data(), fakedString));
- else
- m_events.append(String::format("sendSimpleKeyEvent type=%s keycode=%x%s", eventType, event->keyval, fakedString));
-
- return true;
- }
-
- virtual bool sendKeyEventWithCompositionResults(GdkEventKey* event, ResultsToSend resultsToSend, EventFakedForComposition faked)
- {
- const char* eventType = event->type == GDK_KEY_RELEASE ? "release" : "press";
- const char* fakedString = faked == EventFaked ? " (faked)" : "";
- m_events.append(String::format("sendKeyEventWithCompositionResults type=%s keycode=%u%s", eventType, event->keyval, fakedString));
-
- if (resultsToSend & Composition && !m_confirmedComposition.isNull())
- confirmCompositionText(m_confirmedComposition);
- if (resultsToSend & Preedit && !m_preedit.isNull())
- setPreedit(m_preedit, m_cursorOffset);
-
- return true;
- }
-
- virtual bool canEdit()
- {
- return true;
- }
-
- virtual void confirmCompositionText(String text)
- {
- m_events.append(String::format("confirmComposition '%s'", text.utf8().data()));
- }
-
- virtual void confirmCurrentComposition()
- {
- m_events.append(String("confirmCurrentcomposition"));
- }
-
- virtual void cancelCurrentComposition()
- {
- m_events.append(String("cancelCurrentComposition"));
- }
-
- virtual void setPreedit(String preedit, int cursorOffset)
- {
- m_events.append(String::format("setPreedit text='%s' cursorOffset=%i", preedit.utf8().data(), cursorOffset));
- }
-
-private:
- GRefPtr<GtkWidget> m_testWindow;
- Vector<String> m_events;
-};
-
-TEST(GTK, GtkInputMethodFilterSimple)
-{
- TestInputMethodFilter inputMethodFilter;
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_g);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_t);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_k);
-
- const Vector<String>& events = inputMethodFilter.events();
-
- ASSERT_EQ(6, events.size());
- ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=67 text='g'"), events[0]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=67"), events[1]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=74 text='t'"), events[2]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=74"), events[3]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=6b text='k'"), events[4]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=6b"), events[5]);
-}
-
-TEST(GTK, GtkInputMethodFilterUnicodeSequence)
-{
- TestInputMethodFilter inputMethodFilter;
-
- // This is simple unicode hex entry of the characters, u, 0, 0, f, 4 pressed with
- // the shift and controls keys held down. In reality, these values are not typical
- // of an actual hex entry, because they'd be transformed by the shift modifier according
- // to the keyboard layout. For instance, on a US keyboard a 0 with the shift key pressed
- // is a right parenthesis. Using these values prevents having to work out what the
- // transformed characters are based on the current keyboard layout.
- inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Control_L, GDK_KEY_PRESS);
- inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Shift_L, GDK_KEY_PRESS, GDK_CONTROL_MASK);
-
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_U, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_0, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_0, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_F, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_4, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
-
- inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Shift_L, GDK_KEY_RELEASE, GDK_CONTROL_MASK | GDK_SHIFT_MASK);
- inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Control_L, GDK_KEY_RELEASE, GDK_CONTROL_MASK);
-
- const Vector<String>& events = inputMethodFilter.events();
- ASSERT_EQ(21, events.size());
- ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=ffe3"), events[0]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=ffe1"), events[1]);
- ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=85"), events[2]);
- ASSERT_EQ(String("setPreedit text='u' cursorOffset=1"), events[3]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=55"), events[4]);
- ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=48"), events[5]);
- ASSERT_EQ(String("setPreedit text='u0' cursorOffset=2"), events[6]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=30"), events[7]);
- ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=48"), events[8]);
- ASSERT_EQ(String("setPreedit text='u00' cursorOffset=3"), events[9]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=30"), events[10]);
- ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=70"), events[11]);
- ASSERT_EQ(String("setPreedit text='u00F' cursorOffset=4"), events[12]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=46"), events[13]);
- ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=52"), events[14]);
- ASSERT_EQ(String("setPreedit text='u00F4' cursorOffset=5"), events[15]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=34"), events[16]);
- ASSERT_EQ(String("confirmComposition 'ô'"), events[17]);
- ASSERT_EQ(String("setPreedit text='' cursorOffset=0"), events[18]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffe1"), events[19]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffe3"), events[20]);
-}
-
-TEST(GTK, GtkInputMethodFilterComposeKey)
-{
- TestInputMethodFilter inputMethodFilter;
-
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_Multi_key);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_apostrophe);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_o);
-
- const Vector<String>& events = inputMethodFilter.events();
- ASSERT_EQ(5, events.size());
- ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=39"), events[0]);
- ASSERT_EQ(String("setPreedit text='' cursorOffset=0"), events[1]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=27"), events[2]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=6f text='ó'"), events[3]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=6f"), events[4]);
-}
-
-typedef void (*GetPreeditStringCallback) (GtkIMContext*, gchar**, PangoAttrList**, int*);
-static void temporaryGetPreeditStringOverride(GtkIMContext*, char** string, PangoAttrList** attrs, int* cursorPosition)
-{
- *string = g_strdup("preedit of doom, bringer of cheese");
- *cursorPosition = 3;
-}
-
-TEST(GTK, GtkInputMethodFilterContextEventsWithoutKeyEvents)
-{
- TestInputMethodFilter inputMethodFilter;
-
- // This is a bit of a hack to avoid mocking out the entire InputMethodContext, by
- // simply replacing the get_preedit_string virtual method for the length of this test.
- GtkIMContext* context = inputMethodFilter.context();
- GtkIMContextClass* contextClass = GTK_IM_CONTEXT_GET_CLASS(context);
- GetPreeditStringCallback previousCallback = contextClass->get_preedit_string;
- contextClass->get_preedit_string = temporaryGetPreeditStringOverride;
-
- g_signal_emit_by_name(context, "preedit-changed");
- g_signal_emit_by_name(context, "commit", "commit text");
-
- contextClass->get_preedit_string = previousCallback;
-
- const Vector<String>& events = inputMethodFilter.events();
- ASSERT_EQ(6, events.size());
- ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=16777215 (faked)"), events[0]);
- ASSERT_EQ(String("setPreedit text='preedit of doom, bringer of cheese' cursorOffset=3"), events[1]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffffff (faked)"), events[2]);
- ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=16777215 (faked)"), events[3]);
- ASSERT_EQ(String("confirmComposition 'commit text'"), events[4]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffffff (faked)"), events[5]);
-}
-
-static bool gSawContextReset = false;
-typedef void (*ResetCallback) (GtkIMContext*);
-static void temporaryResetOverride(GtkIMContext*)
-{
- gSawContextReset = true;
-}
-
-static void verifyCanceledComposition(const Vector<String>& events)
-{
- ASSERT_EQ(3, events.size());
- ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=39"), events[0]);
- ASSERT_EQ(String("setPreedit text='' cursorOffset=0"), events[1]);
- ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=27"), events[2]);
- ASSERT(gSawContextReset);
-}
-
-TEST(GTK, GtkInputMethodFilterContextFocusOutDuringOngoingComposition)
-{
- TestInputMethodFilter inputMethodFilter;
-
- // See comment above about this technique.
- GtkIMContext* context = inputMethodFilter.context();
- GtkIMContextClass* contextClass = GTK_IM_CONTEXT_GET_CLASS(context);
- ResetCallback previousCallback = contextClass->reset;
- contextClass->reset = temporaryResetOverride;
-
- gSawContextReset = false;
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_Multi_key);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_apostrophe);
- inputMethodFilter.notifyFocusedOut();
-
- verifyCanceledComposition(inputMethodFilter.events());
-
- contextClass->reset = previousCallback;
-}
-
-TEST(GTK, GtkInputMethodFilterContextMouseClickDuringOngoingComposition)
-{
- TestInputMethodFilter inputMethodFilter;
-
- // See comment above about this technique.
- GtkIMContext* context = inputMethodFilter.context();
- GtkIMContextClass* contextClass = GTK_IM_CONTEXT_GET_CLASS(context);
- ResetCallback previousCallback = contextClass->reset;
- contextClass->reset = temporaryResetOverride;
-
- gSawContextReset = false;
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_Multi_key);
- inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_apostrophe);
- inputMethodFilter.notifyMouseButtonPress();
-
- verifyCanceledComposition(inputMethodFilter.events());
-
- contextClass->reset = previousCallback;
-}
-
-} // namespace TestWebKitAPI
</del></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKit2gtkInputMethodFiltercppfromrev175043trunkToolsTestWebKitAPITestsWebCoregtkInputMethodFiltercpp"></a>
<div class="copfile"><h4>Copied: trunk/Tools/TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp (from rev 175043, trunk/Tools/TestWebKitAPI/Tests/WebCore/gtk/InputMethodFilter.cpp) (0 => 175046)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp         (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2/gtk/InputMethodFilter.cpp        2014-10-22 13:00:12 UTC (rev 175046)
</span><span class="lines">@@ -0,0 +1,259 @@
</span><ins>+/*
+ * Copyright (C) 2012 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 "WTFStringUtilities.h"
+#include <WebKit/InputMethodFilter.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <wtf/gobject/GUniquePtr.h>
+#include <wtf/gobject/GRefPtr.h>
+#include <wtf/text/CString.h>
+
+using namespace WebKit;
+
+namespace TestWebKitAPI {
+
+class TestInputMethodFilter : public InputMethodFilter {
+public:
+ TestInputMethodFilter()
+ : m_testWindow(gtk_window_new(GTK_WINDOW_POPUP))
+ {
+ setTestingMode(true);
+
+ gtk_widget_show(m_testWindow);
+ gtk_im_context_set_client_window(context(), gtk_widget_get_window(m_testWindow));
+
+ // Focus in is necessary to activate the default input method in the multicontext.
+ notifyFocusedIn();
+ }
+
+ ~TestInputMethodFilter()
+ {
+ gtk_widget_destroy(m_testWindow);
+ }
+
+ void sendKeyEventToFilter(unsigned gdkKeyValue, GdkEventType type, unsigned modifiers = 0)
+ {
+ GdkEvent* event = gdk_event_new(type);
+ event->key.keyval = gdkKeyValue;
+ event->key.state = modifiers;
+ event->key.window = gtk_widget_get_window(m_testWindow);
+ event->key.time = GDK_CURRENT_TIME;
+ g_object_ref(event->key.window);
+ gdk_event_set_device(event, gdk_device_manager_get_client_pointer(gdk_display_get_device_manager(gdk_display_get_default())));
+
+ GUniqueOutPtr<GdkKeymapKey> keys;
+ gint nKeys;
+ if (gdk_keymap_get_entries_for_keyval(gdk_keymap_get_default(), gdkKeyValue, &keys.outPtr(), &nKeys))
+ event->key.hardware_keycode = keys.get()[0].keycode;
+
+ filterKeyEvent(&event->key);
+ gdk_event_free(event);
+ }
+
+ void sendPressAndReleaseKeyEventPairToFilter(unsigned gdkKeyValue, unsigned modifiers = 0)
+ {
+ sendKeyEventToFilter(gdkKeyValue, GDK_KEY_PRESS, modifiers);
+ sendKeyEventToFilter(gdkKeyValue, GDK_KEY_RELEASE, modifiers);
+ }
+
+private:
+ GtkWidget* m_testWindow;
+};
+
+TEST(WebKit2, InputMethodFilterSimple)
+{
+ TestInputMethodFilter inputMethodFilter;
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_g);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_t);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_k);
+
+ const Vector<String>& events = inputMethodFilter.events();
+
+ ASSERT_EQ(6, events.size());
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=67 text='g'"), events[0]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=67"), events[1]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=74 text='t'"), events[2]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=74"), events[3]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=6b text='k'"), events[4]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=6b"), events[5]);
+}
+
+TEST(WebKit2, InputMethodFilterUnicodeSequence)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ // This is simple unicode hex entry of the characters, u, 0, 0, f, 4 pressed with
+ // the shift and controls keys held down. In reality, these values are not typical
+ // of an actual hex entry, because they'd be transformed by the shift modifier according
+ // to the keyboard layout. For instance, on a US keyboard a 0 with the shift key pressed
+ // is a right parenthesis. Using these values prevents having to work out what the
+ // transformed characters are based on the current keyboard layout.
+ inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Control_L, GDK_KEY_PRESS);
+ inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Shift_L, GDK_KEY_PRESS, GDK_CONTROL_MASK);
+
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_U, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_0, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_0, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_F, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_4, GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+
+ inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Shift_L, GDK_KEY_RELEASE, GDK_CONTROL_MASK | GDK_SHIFT_MASK);
+ inputMethodFilter.sendKeyEventToFilter(GDK_KEY_Control_L, GDK_KEY_RELEASE, GDK_CONTROL_MASK);
+
+ const Vector<String>& events = inputMethodFilter.events();
+ ASSERT_EQ(21, events.size());
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=ffe3"), events[0]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=ffe1"), events[1]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=85"), events[2]);
+ ASSERT_EQ(String("setPreedit text='u' cursorOffset=1"), events[3]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=55"), events[4]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=48"), events[5]);
+ ASSERT_EQ(String("setPreedit text='u0' cursorOffset=2"), events[6]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=30"), events[7]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=48"), events[8]);
+ ASSERT_EQ(String("setPreedit text='u00' cursorOffset=3"), events[9]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=30"), events[10]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=70"), events[11]);
+ ASSERT_EQ(String("setPreedit text='u00F' cursorOffset=4"), events[12]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=46"), events[13]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=52"), events[14]);
+ ASSERT_EQ(String("setPreedit text='u00F4' cursorOffset=5"), events[15]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=34"), events[16]);
+ ASSERT_EQ(String("confirmComposition 'ô'"), events[17]);
+ ASSERT_EQ(String("setPreedit text='' cursorOffset=0"), events[18]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffe1"), events[19]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffe3"), events[20]);
+}
+
+TEST(WebKit2, InputMethodFilterComposeKey)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_Multi_key);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_apostrophe);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_o);
+
+ const Vector<String>& events = inputMethodFilter.events();
+ ASSERT_EQ(5, events.size());
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=39"), events[0]);
+ ASSERT_EQ(String("setPreedit text='' cursorOffset=0"), events[1]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=27"), events[2]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=press keycode=6f text='ó'"), events[3]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=6f"), events[4]);
+}
+
+typedef void (*GetPreeditStringCallback) (GtkIMContext*, gchar**, PangoAttrList**, int*);
+static void temporaryGetPreeditStringOverride(GtkIMContext*, char** string, PangoAttrList** attrs, int* cursorPosition)
+{
+ *string = g_strdup("preedit of doom, bringer of cheese");
+ *cursorPosition = 3;
+}
+
+TEST(WebKit2, InputMethodFilterContextEventsWithoutKeyEvents)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ // This is a bit of a hack to avoid mocking out the entire InputMethodContext, by
+ // simply replacing the get_preedit_string virtual method for the length of this test.
+ GtkIMContext* context = inputMethodFilter.context();
+ GtkIMContextClass* contextClass = GTK_IM_CONTEXT_GET_CLASS(context);
+ GetPreeditStringCallback previousCallback = contextClass->get_preedit_string;
+ contextClass->get_preedit_string = temporaryGetPreeditStringOverride;
+
+ g_signal_emit_by_name(context, "preedit-changed");
+ g_signal_emit_by_name(context, "commit", "commit text");
+
+ contextClass->get_preedit_string = previousCallback;
+
+ const Vector<String>& events = inputMethodFilter.events();
+ ASSERT_EQ(6, events.size());
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=16777215 (faked)"), events[0]);
+ ASSERT_EQ(String("setPreedit text='preedit of doom, bringer of cheese' cursorOffset=3"), events[1]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffffff (faked)"), events[2]);
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=16777215 (faked)"), events[3]);
+ ASSERT_EQ(String("confirmComposition 'commit text'"), events[4]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=ffffff (faked)"), events[5]);
+}
+
+static bool gSawContextReset = false;
+typedef void (*ResetCallback) (GtkIMContext*);
+static void temporaryResetOverride(GtkIMContext*)
+{
+ gSawContextReset = true;
+}
+
+static void verifyCanceledComposition(const Vector<String>& events)
+{
+ ASSERT_EQ(3, events.size());
+ ASSERT_EQ(String("sendKeyEventWithCompositionResults type=press keycode=39"), events[0]);
+ ASSERT_EQ(String("setPreedit text='' cursorOffset=0"), events[1]);
+ ASSERT_EQ(String("sendSimpleKeyEvent type=release keycode=27"), events[2]);
+ ASSERT(gSawContextReset);
+}
+
+TEST(WebKit2, InputMethodFilterContextFocusOutDuringOngoingComposition)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ // See comment above about this technique.
+ GtkIMContext* context = inputMethodFilter.context();
+ GtkIMContextClass* contextClass = GTK_IM_CONTEXT_GET_CLASS(context);
+ ResetCallback previousCallback = contextClass->reset;
+ contextClass->reset = temporaryResetOverride;
+
+ gSawContextReset = false;
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_Multi_key);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_apostrophe);
+ inputMethodFilter.notifyFocusedOut();
+
+ verifyCanceledComposition(inputMethodFilter.events());
+
+ contextClass->reset = previousCallback;
+}
+
+TEST(WebKit2, InputMethodFilterContextMouseClickDuringOngoingComposition)
+{
+ TestInputMethodFilter inputMethodFilter;
+
+ // See comment above about this technique.
+ GtkIMContext* context = inputMethodFilter.context();
+ GtkIMContextClass* contextClass = GTK_IM_CONTEXT_GET_CLASS(context);
+ ResetCallback previousCallback = contextClass->reset;
+ contextClass->reset = temporaryResetOverride;
+
+ gSawContextReset = false;
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_Multi_key);
+ inputMethodFilter.sendPressAndReleaseKeyEventPairToFilter(GDK_KEY_apostrophe);
+ inputMethodFilter.notifyMouseButtonPress();
+
+ verifyCanceledComposition(inputMethodFilter.events());
+
+ contextClass->reset = previousCallback;
+}
+
+} // namespace TestWebKitAPI
</ins></span></pre>
</div>
</div>
</body>
</html>