| /* |
| * Copyright (C) 2017 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,1 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 "TestMain.h" |
| #include "WebViewTest.h" |
| #include <wtf/glib/GUniquePtr.h> |
| |
| #if PLATFORM(GTK) |
| #include <WebCore/GtkVersioning.h> |
| #if USE(GTK4) |
| #include <webkit2/WebKitWebViewBaseInternal.h> |
| using PlatformEventKey = GdkEvent; |
| #else |
| using PlatformEventKey = GdkEventKey; |
| #endif |
| #define KEY(x) GDK_KEY_##x |
| #define CONTROL_MASK GDK_CONTROL_MASK |
| #define SHIFT_MASK GDK_SHIFT_MASK |
| #elif PLATFORM(WPE) |
| using PlatformEventKey = struct wpe_input_keyboard_event; |
| #define KEY(x) WPE_KEY_##x |
| #define CONTROL_MASK wpe_input_keyboard_modifier_control |
| #define SHIFT_MASK wpe_input_keyboard_modifier_shift |
| #endif |
| |
| typedef struct _WebKitInputMethodContextMock { |
| WebKitInputMethodContext parent; |
| |
| bool enabled; |
| GString* preedit; |
| bool commitNextCharacter; |
| char* surroundingText; |
| unsigned surroundingCursorIndex; |
| unsigned surroundingSelectionIndex; |
| } WebKitInputMethodContextMock; |
| |
| typedef struct _WebKitInputMethodContextMockClass { |
| WebKitInputMethodContextClass parent; |
| } WebKitInputMethodContextMockClass; |
| |
| G_DEFINE_TYPE(WebKitInputMethodContextMock, webkit_input_method_context_mock, WEBKIT_TYPE_INPUT_METHOD_CONTEXT) |
| |
| static const char* testHTML = "<html><body><textarea id='editable' rows='3', cols='50' onkeydown='logKeyDown()' onkeyup='logKeyUp()' onkeypress='logKeyPress()'></textarea><script>" |
| "var input = document.getElementById('editable');" |
| "input.addEventListener('compositionstart', logCompositionEvent);" |
| "input.addEventListener('compositionupdate', logCompositionEvent);" |
| "input.addEventListener('compositionend', logCompositionEvent);" |
| "function logCompositionEvent(event) { window.webkit.messageHandlers.imEvent.postMessage({ 'type' : event.type, 'data' : event.data }) }" |
| "function logKeyDown() { window.webkit.messageHandlers.imEvent.postMessage({ 'type' : 'keyDown', 'keyCode' : event.keyCode, 'key' : event.key, 'isComposing' : event.isComposing }) }" |
| "function logKeyUp() { window.webkit.messageHandlers.imEvent.postMessage({ 'type' : 'keyUp', 'keyCode' : event.keyCode, 'key' : event.key, 'isComposing' : event.isComposing }) }" |
| "function logKeyPress() { window.webkit.messageHandlers.imEvent.postMessage({ 'type' : 'keyPress', 'keyCode' : event.keyCode }) }" |
| "</script></body></html>"; |
| |
| static void webkitInputMethodContextMockFinalize(GObject* object) |
| { |
| auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(object); |
| if (mock->preedit) { |
| g_string_free(mock->preedit, TRUE); |
| mock->preedit = nullptr; |
| } |
| g_clear_pointer(&mock->surroundingText, g_free); |
| G_OBJECT_CLASS(webkit_input_method_context_mock_parent_class)->finalize(object); |
| } |
| |
| static void webkitInputMethodContextMockGetPreedit(WebKitInputMethodContext* context, char** text, GList** underlines, guint* cursorOffset) |
| { |
| auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(context); |
| if (text) |
| *text = mock->preedit ? g_strdup(mock->preedit->str) : g_strdup(""); |
| if (underlines) |
| *underlines = mock->preedit ? g_list_prepend(*underlines, webkit_input_method_underline_new(0, mock->preedit->len)) : nullptr; |
| if (cursorOffset) |
| *cursorOffset = mock->preedit ? mock->preedit->len : 0; |
| } |
| |
| static gboolean webkitInputMethodContextMockFilterKeyEvent(WebKitInputMethodContext* context, PlatformEventKey* keyEvent) |
| { |
| auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(context); |
| if (!mock->enabled) |
| return FALSE; |
| |
| #if PLATFORM(GTK) |
| #if USE(GTK4) |
| auto* event = reinterpret_cast<KeyEvent*>(keyEvent); |
| unsigned keyval = event->keyval; |
| auto state = static_cast<GdkModifierType>(event->modifiers); |
| bool isKeyPress = event->type == GDK_KEY_PRESS; |
| #else |
| GdkModifierType state; |
| guint keyval; |
| if (!gdk_event_get_state(reinterpret_cast<GdkEvent*>(keyEvent), &state) || !gdk_event_get_keyval(reinterpret_cast<GdkEvent*>(keyEvent), &keyval)) |
| return FALSE; |
| bool isKeyPress = gdk_event_get_event_type(reinterpret_cast<GdkEvent*>(keyEvent)) == GDK_KEY_PRESS; |
| #endif |
| gunichar character = gdk_keyval_to_unicode(keyval); |
| #elif PLATFORM(WPE) |
| uint32_t state = keyEvent->modifiers; |
| uint32_t keyval = keyEvent->key_code; |
| bool isKeyPress = keyEvent->pressed; |
| gunichar character = wpe_key_code_to_unicode(keyval); |
| #endif |
| bool isControl = state & CONTROL_MASK; |
| bool isShift = state & SHIFT_MASK; |
| bool isComposeEnd = (keyval == KEY(space) || keyval == KEY(Return) || keyval == KEY(ISO_Enter)); |
| |
| if (isKeyPress && mock->commitNextCharacter) { |
| char buffer[6]; |
| auto length = g_unichar_to_utf8(character, buffer); |
| buffer[length] = '\0'; |
| g_signal_emit_by_name(context, "committed", buffer, nullptr); |
| mock->commitNextCharacter = false; |
| |
| return TRUE; |
| } |
| |
| if (!mock->preedit) { |
| if (isKeyPress && isControl && isShift && keyval == KEY(w)) { |
| mock->preedit = g_string_new("w"); |
| g_signal_emit_by_name(context, "preedit-started", nullptr); |
| g_signal_emit_by_name(context, "preedit-changed", nullptr); |
| |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| if (keyval == KEY(Escape)) { |
| g_string_free(mock->preedit, TRUE); |
| mock->preedit = nullptr; |
| g_signal_emit_by_name(context, "preedit-changed", nullptr); |
| g_signal_emit_by_name(context, "preedit-finished", nullptr); |
| |
| return TRUE; |
| } |
| |
| if (isComposeEnd) { |
| if (!g_strcmp0(mock->preedit->str, "wgtk")) |
| g_signal_emit_by_name(context, "committed", "WebKitGTK", nullptr); |
| else if (!g_strcmp0(mock->preedit->str, "wwpe")) |
| g_signal_emit_by_name(context, "committed", "WPEWebKit", nullptr); |
| else |
| g_signal_emit_by_name(context, "committed", mock->preedit->str + 1, nullptr); |
| |
| g_string_free(mock->preedit, TRUE); |
| mock->preedit = nullptr; |
| g_signal_emit_by_name(context, "preedit-changed", nullptr); |
| g_signal_emit_by_name(context, "preedit-finished", nullptr); |
| |
| return TRUE; |
| } |
| |
| if (isKeyPress) { |
| g_string_append_unichar(mock->preedit, character); |
| g_signal_emit_by_name(context, "preedit-changed", nullptr); |
| |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| static void webkitInputMethodContextMockNotifyFocusIn(WebKitInputMethodContext* context) |
| { |
| reinterpret_cast<WebKitInputMethodContextMock*>(context)->enabled = true; |
| } |
| |
| static void webkitInputMethodContextMockNotifyFocusOut(WebKitInputMethodContext* context) |
| { |
| reinterpret_cast<WebKitInputMethodContextMock*>(context)->enabled = false; |
| } |
| |
| static void webkitInputMethodContextMockNotifySurrounding(WebKitInputMethodContext* context, const gchar *text, unsigned length, unsigned cursorIndex, unsigned selectionIndex) |
| { |
| auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(context); |
| g_clear_pointer(&mock->surroundingText, g_free); |
| |
| if (!mock->preedit && cursorIndex >= 3 && text[cursorIndex - 3] == ':' && text[cursorIndex - 2] == '-' && text[cursorIndex - 1] == ')') { |
| g_signal_emit_by_name(context, "delete-surrounding", -3, 3, nullptr); |
| g_signal_emit_by_name(context, "committed", "😀️", nullptr); |
| } |
| mock->surroundingText = g_strndup(text, length); |
| mock->surroundingCursorIndex = cursorIndex; |
| mock->surroundingSelectionIndex = selectionIndex; |
| } |
| |
| static void webkitInputMethodContextMockReset(WebKitInputMethodContext* context) |
| { |
| auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(context); |
| if (!mock->preedit) |
| return; |
| |
| g_string_free(mock->preedit, TRUE); |
| mock->preedit = nullptr; |
| g_clear_pointer(&mock->surroundingText, g_free); |
| mock->surroundingCursorIndex = 0; |
| mock->surroundingSelectionIndex = 0; |
| |
| g_signal_emit_by_name(context, "preedit-changed", nullptr); |
| g_signal_emit_by_name(context, "preedit-finished", nullptr); |
| } |
| |
| static void webkit_input_method_context_mock_class_init(WebKitInputMethodContextMockClass* klass) |
| { |
| GObjectClass* objectClass = G_OBJECT_CLASS(klass); |
| objectClass->finalize = webkitInputMethodContextMockFinalize; |
| |
| auto* imClass = WEBKIT_INPUT_METHOD_CONTEXT_CLASS(klass); |
| imClass->get_preedit = webkitInputMethodContextMockGetPreedit; |
| imClass->filter_key_event = webkitInputMethodContextMockFilterKeyEvent; |
| imClass->notify_focus_in = webkitInputMethodContextMockNotifyFocusIn; |
| imClass->notify_focus_out = webkitInputMethodContextMockNotifyFocusOut; |
| imClass->notify_surrounding = webkitInputMethodContextMockNotifySurrounding; |
| imClass->reset = webkitInputMethodContextMockReset; |
| } |
| |
| static void webkit_input_method_context_mock_init(WebKitInputMethodContextMock*) |
| { |
| } |
| |
| class InputMethodTest: public WebViewTest { |
| public: |
| MAKE_GLIB_TEST_FIXTURE(InputMethodTest); |
| |
| struct Event { |
| enum class Type { KeyDown, KeyPress, KeyUp, CompositionStart, CompositionUpdate, CompositionEnd }; |
| |
| explicit Event(Type type) |
| : type(type) |
| { |
| } |
| |
| Type type; |
| CString data; |
| unsigned keyCode; |
| CString key; |
| bool isComposing; |
| }; |
| |
| static void imEventCallback(WebKitUserContentManager*, WebKitJavascriptResult* javascriptResult, InputMethodTest* test) |
| { |
| test->imEvent(javascriptResult); |
| } |
| |
| InputMethodTest() |
| : m_context(adoptGRef(static_cast<WebKitInputMethodContextMock*>(g_object_new(webkit_input_method_context_mock_get_type(), nullptr)))) |
| { |
| WebViewTest::showInWindow(); |
| #if PLATFORM(GTK) |
| auto* defaultContext = webkit_web_view_get_input_method_context(m_webView); |
| g_assert_true(WEBKIT_IS_INPUT_METHOD_CONTEXT(defaultContext)); |
| assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultContext)); |
| #elif PLATFORM(WPE) |
| g_assert_null(webkit_web_view_get_input_method_context(m_webView)); |
| #endif |
| assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_context.get())); |
| webkit_web_view_set_input_method_context(m_webView, WEBKIT_INPUT_METHOD_CONTEXT(m_context.get())); |
| g_assert_true(webkit_web_view_get_input_method_context(m_webView) == WEBKIT_INPUT_METHOD_CONTEXT(m_context.get())); |
| |
| webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), "imEvent"); |
| g_signal_connect(m_userContentManager.get(), "script-message-received::imEvent", G_CALLBACK(imEventCallback), this); |
| } |
| |
| ~InputMethodTest() |
| { |
| webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), "imEvent"); |
| g_signal_handlers_disconnect_by_data(m_userContentManager.get(), this); |
| } |
| |
| void imEvent(WebKitJavascriptResult* result) |
| { |
| auto* jsEvent = webkit_javascript_result_get_js_value(result); |
| g_assert_true(jsc_value_is_object(jsEvent)); |
| |
| GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(jsEvent, "type")); |
| g_assert_true(jsc_value_is_string(value.get())); |
| GUniquePtr<char> strValue(jsc_value_to_string(value.get())); |
| if (!g_strcmp0(strValue.get(), "keyDown")) { |
| InputMethodTest::Event event(InputMethodTest::Event::Type::KeyDown); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "keyCode")); |
| g_assert_true(jsc_value_is_number(value.get())); |
| event.keyCode = jsc_value_to_int32(value.get()); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "key")); |
| g_assert_true(jsc_value_is_string(value.get())); |
| strValue.reset(jsc_value_to_string(value.get())); |
| event.key = strValue.get(); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "isComposing")); |
| g_assert_true(jsc_value_is_boolean(value.get())); |
| event.isComposing = jsc_value_to_boolean(value.get()); |
| m_events.append(WTFMove(event)); |
| } else if (!g_strcmp0(strValue.get(), "keyPress")) { |
| InputMethodTest::Event event(InputMethodTest::Event::Type::KeyPress); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "keyCode")); |
| g_assert_true(jsc_value_is_number(value.get())); |
| event.keyCode = jsc_value_to_int32(value.get()); |
| m_events.append(WTFMove(event)); |
| } else if (!g_strcmp0(strValue.get(), "keyUp")) { |
| InputMethodTest::Event event(InputMethodTest::Event::Type::KeyUp); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "keyCode")); |
| g_assert_true(jsc_value_is_number(value.get())); |
| event.keyCode = jsc_value_to_int32(value.get()); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "key")); |
| g_assert_true(jsc_value_is_string(value.get())); |
| strValue.reset(jsc_value_to_string(value.get())); |
| event.key = strValue.get(); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "isComposing")); |
| g_assert_true(jsc_value_is_boolean(value.get())); |
| event.isComposing = jsc_value_to_boolean(value.get()); |
| m_events.append(WTFMove(event)); |
| } else if (!g_strcmp0(strValue.get(), "compositionstart")) { |
| InputMethodTest::Event event(InputMethodTest::Event::Type::CompositionStart); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "data")); |
| g_assert_true(jsc_value_is_string(value.get())); |
| strValue.reset(jsc_value_to_string(value.get())); |
| event.data = strValue.get(); |
| m_events.append(WTFMove(event)); |
| } else if (!g_strcmp0(strValue.get(), "compositionupdate")) { |
| InputMethodTest::Event event(InputMethodTest::Event::Type::CompositionUpdate); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "data")); |
| g_assert_true(jsc_value_is_string(value.get())); |
| strValue.reset(jsc_value_to_string(value.get())); |
| event.data = strValue.get(); |
| m_events.append(WTFMove(event)); |
| } else if (!g_strcmp0(strValue.get(), "compositionend")) { |
| InputMethodTest::Event event(InputMethodTest::Event::Type::CompositionEnd); |
| value = adoptGRef(jsc_value_object_get_property(jsEvent, "data")); |
| g_assert_true(jsc_value_is_string(value.get())); |
| strValue.reset(jsc_value_to_string(value.get())); |
| event.data = strValue.get(); |
| m_events.append(WTFMove(event)); |
| } |
| |
| if (m_events.size() == m_eventsExpected) |
| g_main_loop_quit(m_mainLoop); |
| } |
| |
| void focusEditableAndWaitUntilInputMethodEnabled() |
| { |
| g_assert_false(m_context->enabled); |
| runJavaScriptAndWaitUntilFinished("document.getElementById('editable').focus()", nullptr); |
| if (m_context->enabled) |
| return; |
| |
| g_idle_add([](gpointer userData) -> gboolean { |
| auto* test = static_cast<InputMethodTest*>(userData); |
| if (test->m_context->enabled) { |
| test->quitMainLoop(); |
| return FALSE; |
| } |
| |
| return TRUE; |
| }, this); |
| g_main_loop_run(m_mainLoop); |
| g_assert_true(m_context->enabled); |
| } |
| |
| void unfocusEditableAndWaitUntilInputMethodDisabled() |
| { |
| g_assert_true(m_context->enabled); |
| runJavaScriptAndWaitUntilFinished("document.getElementById('editable').blur()", nullptr); |
| if (!m_context->enabled) |
| return; |
| |
| g_idle_add([](gpointer userData) -> gboolean { |
| auto* test = static_cast<InputMethodTest*>(userData); |
| if (!test->m_context->enabled) { |
| test->quitMainLoop(); |
| return FALSE; |
| } |
| |
| return TRUE; |
| }, this); |
| g_main_loop_run(m_mainLoop); |
| g_assert_false(m_context->enabled); |
| } |
| |
| void resetEditable() |
| { |
| runJavaScriptAndWaitUntilFinished("document.getElementById('editable').value = ''", nullptr); |
| m_events.clear(); |
| } |
| |
| GUniquePtr<char> editableValue() |
| { |
| auto* jsResult = runJavaScriptAndWaitUntilFinished("document.getElementById('editable').value", nullptr); |
| return GUniquePtr<char>(WebViewTest::javascriptResultToCString(jsResult)); |
| } |
| |
| void keyStrokeAndWaitForEvents(unsigned keyval, unsigned eventsCount, unsigned modifiers = 0) |
| { |
| m_eventsExpected = eventsCount; |
| keyStroke(keyval, modifiers); |
| g_main_loop_run(m_mainLoop); |
| m_eventsExpected = 0; |
| } |
| |
| void keyStrokeHandledByInputMethodAndWaitForEvents(unsigned keyval, unsigned eventsCount) |
| { |
| m_context->commitNextCharacter = true; |
| keyStrokeAndWaitForEvents(keyval, eventsCount); |
| m_context->commitNextCharacter = false; |
| } |
| |
| void clickAndWaitForEvents(unsigned eventsCount) |
| { |
| m_eventsExpected = eventsCount; |
| clickMouseButton(0, 0, 1); |
| g_main_loop_run(m_mainLoop); |
| m_eventsExpected = 0; |
| } |
| |
| WebKitInputPurpose purpose() const |
| { |
| return webkit_input_method_context_get_input_purpose(WEBKIT_INPUT_METHOD_CONTEXT(m_context.get())); |
| } |
| |
| WebKitInputHints hints() const |
| { |
| return webkit_input_method_context_get_input_hints(WEBKIT_INPUT_METHOD_CONTEXT(m_context.get())); |
| } |
| |
| const char* surroundingText() const |
| { |
| return m_context->surroundingText; |
| } |
| |
| unsigned surroundingCursorIndex() const |
| { |
| return m_context->surroundingCursorIndex; |
| } |
| |
| unsigned surroundingSelectionIndex() const |
| { |
| return m_context->surroundingSelectionIndex; |
| } |
| |
| void waitForSurroundingText(const char* text) |
| { |
| m_expectedSurroundingText = text; |
| g_idle_add([](gpointer userData) -> gboolean { |
| auto* test = static_cast<InputMethodTest*>(userData); |
| if (!g_strcmp0(test->m_context->surroundingText, test->m_expectedSurroundingText.data())) { |
| test->quitMainLoop(); |
| return FALSE; |
| } |
| |
| return TRUE; |
| }, this); |
| g_main_loop_run(m_mainLoop); |
| m_expectedSurroundingText = { }; |
| } |
| |
| GRefPtr<WebKitInputMethodContextMock> m_context; |
| Vector<Event> m_events; |
| unsigned m_eventsExpected { 0 }; |
| CString m_expectedSurroundingText; |
| }; |
| |
| static void testWebKitInputMethodContextSimple(InputMethodTest* test, gconstpointer) |
| { |
| test->loadHtml(testHTML, nullptr); |
| test->waitUntilLoadFinished(); |
| |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| |
| // Send a normal character not handled by IM. |
| test->keyStrokeAndWaitForEvents(KEY(a), 3); |
| g_assert_cmpuint(test->m_events.size(), ==, 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 65); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "a"); |
| g_assert_false(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::KeyPress); |
| g_assert_cmpuint(test->m_events[1].keyCode, ==, 97); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 65); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "a"); |
| g_assert_false(test->m_events[2].isComposing); |
| { |
| auto editableValue = test->editableValue(); |
| g_assert_cmpstr(editableValue.get(), ==, "a"); |
| } |
| test->resetEditable(); |
| |
| // Send a normal character handled by IM. |
| test->keyStrokeHandledByInputMethodAndWaitForEvents(KEY(a), 3); |
| g_assert_cmpuint(test->m_events.size(), ==, 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 65); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "a"); |
| g_assert_false(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::KeyPress); |
| g_assert_cmpuint(test->m_events[1].keyCode, ==, 97); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 65); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "a"); |
| g_assert_false(test->m_events[2].isComposing); |
| { |
| auto editableValue = test->editableValue(); |
| g_assert_cmpstr(editableValue.get(), ==, "a"); |
| } |
| test->resetEditable(); |
| } |
| |
| static void testWebKitInputMethodContextSequence(InputMethodTest* test, gconstpointer) |
| { |
| test->loadHtml(testHTML, nullptr); |
| test->waitUntilLoadFinished(); |
| |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| |
| // Compose w + gtk + Enter. |
| test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_false(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, ""); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[2].data.data(), ==, "w"); |
| g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[3].keyCode, ==, 87); |
| g_assert_cmpstr(test->m_events[3].key.data(), ==, "w"); |
| g_assert_true(test->m_events[3].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(g), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "wg"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 71); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "g"); |
| g_assert_true(test->m_events[2].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(t), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "wgt"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 84); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "t"); |
| g_assert_true(test->m_events[2].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(k), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "wgtk"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 75); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "k"); |
| g_assert_true(test->m_events[2].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(ISO_Enter), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionEnd); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "WebKitGTK"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 13); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "Enter"); |
| g_assert_false(test->m_events[2].isComposing); |
| { |
| auto editableValue = test->editableValue(); |
| g_assert_cmpstr(editableValue.get(), ==, "WebKitGTK"); |
| } |
| test->resetEditable(); |
| |
| // Compose w + wpe + Space. |
| test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_false(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, ""); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[2].data.data(), ==, "w"); |
| g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[3].keyCode, ==, 87); |
| g_assert_cmpstr(test->m_events[3].key.data(), ==, "w"); |
| g_assert_true(test->m_events[3].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(w), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "ww"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 87); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "w"); |
| g_assert_true(test->m_events[2].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(p), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "wwp"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 80); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "p"); |
| g_assert_true(test->m_events[2].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(e), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "wwpe"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 69); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "e"); |
| g_assert_true(test->m_events[2].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(space), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionEnd); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "WPEWebKit"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 32); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, " "); |
| g_assert_false(test->m_events[2].isComposing); |
| { |
| auto editableValue = test->editableValue(); |
| g_assert_cmpstr(editableValue.get(), ==, "WPEWebKit"); |
| } |
| test->resetEditable(); |
| } |
| |
| static void testWebKitInputMethodContextInvalidSequence(InputMethodTest* test, gconstpointer) |
| { |
| test->loadHtml(testHTML, nullptr); |
| test->waitUntilLoadFinished(); |
| |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| |
| // Compose w + w + Space -> invalid sequence. |
| test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_false(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, ""); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[2].data.data(), ==, "w"); |
| g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[3].keyCode, ==, 87); |
| g_assert_cmpstr(test->m_events[3].key.data(), ==, "w"); |
| g_assert_true(test->m_events[3].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(w), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "ww"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 87); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "w"); |
| g_assert_true(test->m_events[2].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(space), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionEnd); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, "w"); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 32); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, " "); |
| g_assert_false(test->m_events[2].isComposing); |
| { |
| auto editableValue = test->editableValue(); |
| g_assert_cmpstr(editableValue.get(), ==, "w"); |
| } |
| test->resetEditable(); |
| } |
| |
| static void testWebKitInputMethodContextCancelSequence(InputMethodTest* test, gconstpointer) |
| { |
| test->loadHtml(testHTML, nullptr); |
| test->waitUntilLoadFinished(); |
| |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| |
| // Compose w + w + Escape -> cancel sequence. |
| test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_false(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, ""); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[2].data.data(), ==, "w"); |
| g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[3].keyCode, ==, 87); |
| g_assert_cmpstr(test->m_events[3].key.data(), ==, "w"); |
| g_assert_true(test->m_events[3].isComposing); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(Escape), 3); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_true(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionEnd); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, ""); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[2].keyCode, ==, 27); |
| g_assert_cmpstr(test->m_events[2].key.data(), ==, "Escape"); |
| g_assert_false(test->m_events[2].isComposing); |
| { |
| auto editableValue = test->editableValue(); |
| g_assert_cmpstr(editableValue.get(), ==, ""); |
| } |
| test->resetEditable(); |
| } |
| |
| static void testWebKitInputMethodContextSurrounding(InputMethodTest* test, gconstpointer) |
| { |
| test->loadHtml(testHTML, nullptr); |
| test->waitUntilLoadFinished(); |
| |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| |
| g_assert_null(test->surroundingText()); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| |
| test->keyStrokeAndWaitForEvents(KEY(a), 3); |
| test->keyStrokeAndWaitForEvents(KEY(b), 6); |
| test->keyStrokeAndWaitForEvents(KEY(c), 9); |
| g_assert_cmpstr(test->surroundingText(), ==, "abc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 3); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| |
| // Check preedit string is not included in surrounding. |
| // 1. Preedit string at the beginning of context. |
| test->keyStrokeAndWaitForEvents(KEY(Left), 2); |
| test->keyStrokeAndWaitForEvents(KEY(Left), 4); |
| test->keyStrokeAndWaitForEvents(KEY(Left), 6); |
| g_assert_cmpstr(test->surroundingText(), ==, "abc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK); |
| g_assert_cmpstr(test->surroundingText(), ==, "abc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->keyStrokeAndWaitForEvents(KEY(g), 7); |
| test->keyStrokeAndWaitForEvents(KEY(t), 10); |
| test->keyStrokeAndWaitForEvents(KEY(k), 13); |
| g_assert_cmpstr(test->surroundingText(), ==, "abc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->keyStrokeAndWaitForEvents(KEY(ISO_Enter), 16); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKabc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 9); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| // 2. Preedit string in the middle of context. |
| test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKabc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 9); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->keyStrokeAndWaitForEvents(KEY(w), 7); |
| test->keyStrokeAndWaitForEvents(KEY(p), 10); |
| test->keyStrokeAndWaitForEvents(KEY(e), 13); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKabc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 9); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->keyStrokeAndWaitForEvents(KEY(space), 16); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 18); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| // 3. Preedit string at the end of context. |
| test->keyStrokeAndWaitForEvents(KEY(Right), 2); |
| test->keyStrokeAndWaitForEvents(KEY(Right), 4); |
| test->keyStrokeAndWaitForEvents(KEY(Right), 6); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 21); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 21); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->keyStrokeAndWaitForEvents(KEY(g), 7); |
| test->keyStrokeAndWaitForEvents(KEY(t), 10); |
| test->keyStrokeAndWaitForEvents(KEY(k), 13); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabc"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 21); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->keyStrokeAndWaitForEvents(KEY(ISO_Enter), 16); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 30); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| |
| // Check selection cursor. |
| test->keyStrokeAndWaitForEvents(KEY(Left), 2, SHIFT_MASK); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 29); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, 30); |
| test->keyStrokeAndWaitForEvents(KEY(Home), 4, SHIFT_MASK); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, 30); |
| test->keyStrokeAndWaitForEvents(KEY(Left), 6); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| test->keyStrokeAndWaitForEvents(KEY(Right), 2, SHIFT_MASK); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, 1); |
| test->keyStrokeAndWaitForEvents(KEY(End), 4, SHIFT_MASK); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, 30); |
| test->keyStrokeAndWaitForEvents(KEY(Right), 6); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 30); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| |
| // Check text replacements (get surrounding + delete surrounding). |
| test->keyStrokeAndWaitForEvents(KEY(colon), 3); |
| test->keyStrokeAndWaitForEvents(KEY(minus), 6); |
| test->keyStrokeAndWaitForEvents(KEY(parenright), 9); |
| test->waitForSurroundingText("WebKitGTKWPEWebKitabcWebKitGTK😀️"); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK😀️"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 37); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| |
| // Check multiline context. |
| test->keyStrokeAndWaitForEvents(KEY(Return), 3); |
| test->keyStrokeAndWaitForEvents(KEY(a), 6); |
| test->waitForSurroundingText("WebKitGTKWPEWebKitabcWebKitGTK😀️\na"); |
| g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK😀️\na"); |
| g_assert_cmpuint(test->surroundingCursorIndex(), ==, 39); |
| g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex()); |
| test->m_events.clear(); |
| } |
| |
| static void testWebKitInputMethodContextReset(InputMethodTest* test, gconstpointer) |
| { |
| test->loadHtml(testHTML, nullptr); |
| test->waitUntilLoadFinished(); |
| |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| |
| // Compose w + w + click -> reset sequence. |
| test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown); |
| g_assert_cmpuint(test->m_events[0].keyCode, ==, 229); |
| g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified"); |
| g_assert_false(test->m_events[0].isComposing); |
| g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart); |
| g_assert_cmpstr(test->m_events[1].data.data(), ==, ""); |
| g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate); |
| g_assert_cmpstr(test->m_events[2].data.data(), ==, "w"); |
| g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp); |
| g_assert_cmpuint(test->m_events[3].keyCode, ==, 87); |
| g_assert_cmpstr(test->m_events[3].key.data(), ==, "w"); |
| g_assert_true(test->m_events[3].isComposing); |
| test->m_events.clear(); |
| test->clickAndWaitForEvents(1); |
| g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::CompositionEnd); |
| g_assert_cmpstr(test->m_events[0].data.data(), ==, "w"); |
| { |
| auto editableValue = test->editableValue(); |
| g_assert_cmpstr(editableValue.get(), ==, "w"); |
| } |
| test->resetEditable(); |
| } |
| |
| static void testWebKitInputMethodContextContentType(InputMethodTest* test, gconstpointer) |
| { |
| test->loadHtml("<input id='editable' spellcheck='false'></input>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<input id='editable' type='number' spellcheck='false'>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_NUMBER); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<input id='editable' type='number' spellcheck='false' pattern='[0-9]*'>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_DIGITS); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<input id='editable' type='text' spellcheck='false' pattern='\\d*'>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_DIGITS); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<input id='editable' type='tel' spellcheck='false'>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_PHONE); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<input id='editable' type='url' spellcheck='false'>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_URL); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<input id='editable' type='email' spellcheck='false'>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_EMAIL); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<input id='editable' type='password' spellcheck='false'>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_PASSWORD); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<div contenteditable id='editable' inputmode='text' spellcheck='false'></div>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<div contenteditable id='editable' inputmode='decimal' spellcheck='false'></div>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_NUMBER); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<div contenteditable id='editable' inputmode='numeric' spellcheck='false'></div>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_DIGITS); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<div contenteditable id='editable' inputmode='tel' spellcheck='false'></div>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_PHONE); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<div contenteditable id='editable' inputmode='email' spellcheck='false'></div>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_EMAIL); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<div contenteditable id='editable' inputmode='url' spellcheck='false'></div>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_URL); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<div contenteditable id='editable' inputmode='search' spellcheck='false'></div>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<div contenteditable id='editable' inputmode='none' spellcheck='false'></div>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_INHIBIT_OSK); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<textarea id='editable'></textarea>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<textarea id='editable' autocapitalize='none'></textarea>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK | WEBKIT_INPUT_HINT_LOWERCASE); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<textarea id='editable' autocapitalize='sentences'></textarea>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK | WEBKIT_INPUT_HINT_UPPERCASE_SENTENCES); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<textarea id='editable' autocapitalize='words'></textarea>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK | WEBKIT_INPUT_HINT_UPPERCASE_WORDS); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| |
| test->loadHtml("<textarea id='editable' autocapitalize='characters'></textarea>", nullptr); |
| test->waitUntilLoadFinished(); |
| test->focusEditableAndWaitUntilInputMethodEnabled(); |
| g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM); |
| g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK | WEBKIT_INPUT_HINT_UPPERCASE_CHARS); |
| test->unfocusEditableAndWaitUntilInputMethodDisabled(); |
| } |
| |
| void beforeAll() |
| { |
| InputMethodTest::add("WebKitInputMethodContext", "simple", testWebKitInputMethodContextSimple); |
| InputMethodTest::add("WebKitInputMethodContext", "sequence", testWebKitInputMethodContextSequence); |
| InputMethodTest::add("WebKitInputMethodContext", "invalid-sequence", testWebKitInputMethodContextInvalidSequence); |
| InputMethodTest::add("WebKitInputMethodContext", "cancel-sequence", testWebKitInputMethodContextCancelSequence); |
| InputMethodTest::add("WebKitInputMethodContext", "surrounding", testWebKitInputMethodContextSurrounding); |
| InputMethodTest::add("WebKitInputMethodContext", "reset", testWebKitInputMethodContextReset); |
| InputMethodTest::add("WebKitInputMethodContext", "content-type", testWebKitInputMethodContextContentType); |
| } |
| |
| void afterAll() |
| { |
| |
| } |