~mmach/netext73/webkit2gtk

« back to all changes in this revision

Viewing changes to Tools/TestWebKitAPI/Tests/WebKitGLib/TestInputMethodContext.cpp

  • Committer: mmach
  • Date: 2023-06-16 17:21:37 UTC
  • Revision ID: netbit73@gmail.com-20230616172137-2rqx6yr96ga9g3kp
1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2017 Igalia S.L.
 
3
 *
 
4
 * This library is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU Lesser General Public
 
6
 * License as published by the Free Software Foundation; either
 
7
 * version 2,1 of the License, or (at your option) any later version.
 
8
 *
 
9
 * This library is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * Library General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Library General Public License
 
15
 * along with this library; see the file COPYING.LIB.  If not, write to
 
16
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
17
 * Boston, MA 02110-1301, USA.
 
18
 */
 
19
 
 
20
#include "config.h"
 
21
 
 
22
#include "TestMain.h"
 
23
#include "WebViewTest.h"
 
24
#include <wtf/glib/GUniquePtr.h>
 
25
 
 
26
#if PLATFORM(GTK)
 
27
using PlatformEventKey = GdkEventKey;
 
28
#define KEY(x) GDK_KEY_##x
 
29
#define CONTROL_MASK GDK_CONTROL_MASK
 
30
#define SHIFT_MASK GDK_SHIFT_MASK
 
31
#elif PLATFORM(WPE)
 
32
using PlatformEventKey = struct wpe_input_keyboard_event;
 
33
#define KEY(x) WPE_KEY_##x
 
34
#define CONTROL_MASK wpe_input_keyboard_modifier_control
 
35
#define SHIFT_MASK wpe_input_keyboard_modifier_shift
 
36
#endif
 
37
 
 
38
typedef struct _WebKitInputMethodContextMock {
 
39
    WebKitInputMethodContext parent;
 
40
 
 
41
    bool enabled;
 
42
    GString* preedit;
 
43
    bool commitNextCharacter;
 
44
    char* surroundingText;
 
45
    unsigned surroundingCursorIndex;
 
46
    unsigned surroundingSelectionIndex;
 
47
} WebKitInputMethodContextMock;
 
48
 
 
49
typedef struct _WebKitInputMethodContextMockClass {
 
50
    WebKitInputMethodContextClass parent;
 
51
} WebKitInputMethodContextMockClass;
 
52
 
 
53
G_DEFINE_TYPE(WebKitInputMethodContextMock, webkit_input_method_context_mock, WEBKIT_TYPE_INPUT_METHOD_CONTEXT)
 
54
 
 
55
static const char* testHTML = "<html><body><textarea id='editable' rows='3', cols='50' onkeydown='logKeyDown()' onkeyup='logKeyUp()' onkeypress='logKeyPress()'></textarea><script>"
 
56
    "var input = document.getElementById('editable');"
 
57
    "input.addEventListener('compositionstart', logCompositionEvent);"
 
58
    "input.addEventListener('compositionupdate', logCompositionEvent);"
 
59
    "input.addEventListener('compositionend', logCompositionEvent);"
 
60
    "function logCompositionEvent(event) { window.webkit.messageHandlers.imEvent.postMessage({ 'type' : event.type, 'data' : event.data }) }"
 
61
    "function logKeyDown() { window.webkit.messageHandlers.imEvent.postMessage({ 'type' : 'keyDown', 'keyCode' : event.keyCode, 'key' : event.key, 'isComposing' : event.isComposing }) }"
 
62
    "function logKeyUp() { window.webkit.messageHandlers.imEvent.postMessage({ 'type' : 'keyUp', 'keyCode' : event.keyCode, 'key' : event.key, 'isComposing' : event.isComposing }) }"
 
63
    "function logKeyPress() { window.webkit.messageHandlers.imEvent.postMessage({ 'type' : 'keyPress', 'keyCode' : event.keyCode }) }"
 
64
    "</script></body></html>";
 
65
 
 
66
static void webkitInputMethodContextMockFinalize(GObject* object)
 
67
{
 
68
    auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(object);
 
69
    if (mock->preedit) {
 
70
        g_string_free(mock->preedit, TRUE);
 
71
        mock->preedit = nullptr;
 
72
    }
 
73
    g_clear_pointer(&mock->surroundingText, g_free);
 
74
    G_OBJECT_CLASS(webkit_input_method_context_mock_parent_class)->finalize(object);
 
75
}
 
76
 
 
77
static void webkitInputMethodContextMockGetPreedit(WebKitInputMethodContext* context, char** text, GList** underlines, guint* cursorOffset)
 
78
{
 
79
    auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(context);
 
80
    if (text)
 
81
        *text = mock->preedit ? g_strdup(mock->preedit->str) : g_strdup("");
 
82
    if (underlines)
 
83
        *underlines = mock->preedit ? g_list_prepend(*underlines, webkit_input_method_underline_new(0, mock->preedit->len)) : nullptr;
 
84
    if (cursorOffset)
 
85
        *cursorOffset = mock->preedit ? mock->preedit->len : 0;
 
86
}
 
87
 
 
88
static gboolean webkitInputMethodContextMockFilterKeyEvent(WebKitInputMethodContext* context, PlatformEventKey* keyEvent)
 
89
{
 
90
    auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(context);
 
91
    if (!mock->enabled)
 
92
        return FALSE;
 
93
 
 
94
#if PLATFORM(GTK)
 
95
    GdkModifierType state;
 
96
    guint keyval;
 
97
    if (!gdk_event_get_state(reinterpret_cast<GdkEvent*>(keyEvent), &state) || !gdk_event_get_keyval(reinterpret_cast<GdkEvent*>(keyEvent), &keyval))
 
98
        return FALSE;
 
99
    bool isKeyPress = gdk_event_get_event_type(reinterpret_cast<GdkEvent*>(keyEvent)) == GDK_KEY_PRESS;
 
100
    gunichar character = gdk_keyval_to_unicode(keyval);
 
101
#elif PLATFORM(WPE)
 
102
    uint32_t state = keyEvent->modifiers;
 
103
    uint32_t keyval = keyEvent->key_code;
 
104
    bool isKeyPress = keyEvent->pressed;
 
105
    gunichar character = wpe_key_code_to_unicode(keyval);
 
106
#endif
 
107
    bool isControl = state & CONTROL_MASK;
 
108
    bool isShift = state & SHIFT_MASK;
 
109
    bool isComposeEnd = (keyval == KEY(space) || keyval == KEY(Return) || keyval == KEY(ISO_Enter));
 
110
 
 
111
    if (isKeyPress && mock->commitNextCharacter) {
 
112
        char buffer[6];
 
113
        auto length = g_unichar_to_utf8(character, buffer);
 
114
        buffer[length] = '\0';
 
115
        g_signal_emit_by_name(context, "committed", buffer, nullptr);
 
116
        mock->commitNextCharacter = false;
 
117
 
 
118
        return TRUE;
 
119
    }
 
120
 
 
121
    if (!mock->preedit) {
 
122
        if (isKeyPress && isControl && isShift && keyval == KEY(w)) {
 
123
            mock->preedit = g_string_new("w");
 
124
            g_signal_emit_by_name(context, "preedit-started", nullptr);
 
125
            g_signal_emit_by_name(context, "preedit-changed", nullptr);
 
126
 
 
127
            return TRUE;
 
128
        }
 
129
 
 
130
        return FALSE;
 
131
    }
 
132
 
 
133
    if (keyval == KEY(Escape)) {
 
134
        g_string_free(mock->preedit, TRUE);
 
135
        mock->preedit = nullptr;
 
136
        g_signal_emit_by_name(context, "preedit-changed", nullptr);
 
137
        g_signal_emit_by_name(context, "preedit-finished", nullptr);
 
138
 
 
139
        return TRUE;
 
140
    }
 
141
 
 
142
    if (isComposeEnd) {
 
143
        if (!g_strcmp0(mock->preedit->str, "wgtk"))
 
144
            g_signal_emit_by_name(context, "committed", "WebKitGTK", nullptr);
 
145
        else if (!g_strcmp0(mock->preedit->str, "wwpe"))
 
146
            g_signal_emit_by_name(context, "committed", "WPEWebKit", nullptr);
 
147
        else
 
148
            g_signal_emit_by_name(context, "committed", mock->preedit->str + 1, nullptr);
 
149
 
 
150
        g_string_free(mock->preedit, TRUE);
 
151
        mock->preedit = nullptr;
 
152
        g_signal_emit_by_name(context, "preedit-changed", nullptr);
 
153
        g_signal_emit_by_name(context, "preedit-finished", nullptr);
 
154
 
 
155
        return TRUE;
 
156
    }
 
157
 
 
158
    if (isKeyPress) {
 
159
        g_string_append_unichar(mock->preedit, character);
 
160
        g_signal_emit_by_name(context, "preedit-changed", nullptr);
 
161
 
 
162
        return TRUE;
 
163
    }
 
164
 
 
165
    return FALSE;
 
166
}
 
167
 
 
168
static void webkitInputMethodContextMockNotifyFocusIn(WebKitInputMethodContext* context)
 
169
{
 
170
    reinterpret_cast<WebKitInputMethodContextMock*>(context)->enabled = true;
 
171
}
 
172
 
 
173
static void webkitInputMethodContextMockNotifyFocusOut(WebKitInputMethodContext* context)
 
174
{
 
175
    reinterpret_cast<WebKitInputMethodContextMock*>(context)->enabled = false;
 
176
}
 
177
 
 
178
static void webkitInputMethodContextMockNotifySurrounding(WebKitInputMethodContext* context, const gchar *text, unsigned length, unsigned cursorIndex, unsigned selectionIndex)
 
179
{
 
180
    auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(context);
 
181
    g_clear_pointer(&mock->surroundingText, g_free);
 
182
 
 
183
    if (!mock->preedit && cursorIndex >= 3 && text[cursorIndex - 3] == ':' && text[cursorIndex - 2] == '-' && text[cursorIndex - 1] == ')') {
 
184
        g_signal_emit_by_name(context, "delete-surrounding", -3, 3, nullptr);
 
185
        g_signal_emit_by_name(context, "committed", "😀️", nullptr);
 
186
    }
 
187
    mock->surroundingText = g_strndup(text, length);
 
188
    mock->surroundingCursorIndex = cursorIndex;
 
189
    mock->surroundingSelectionIndex = selectionIndex;
 
190
}
 
191
 
 
192
static void webkitInputMethodContextMockReset(WebKitInputMethodContext* context)
 
193
{
 
194
    auto* mock = reinterpret_cast<WebKitInputMethodContextMock*>(context);
 
195
    if (!mock->preedit)
 
196
        return;
 
197
 
 
198
    g_string_free(mock->preedit, TRUE);
 
199
    mock->preedit = nullptr;
 
200
    g_clear_pointer(&mock->surroundingText, g_free);
 
201
    mock->surroundingCursorIndex = 0;
 
202
    mock->surroundingSelectionIndex = 0;
 
203
 
 
204
    g_signal_emit_by_name(context, "preedit-changed", nullptr);
 
205
    g_signal_emit_by_name(context, "preedit-finished", nullptr);
 
206
}
 
207
 
 
208
static void webkit_input_method_context_mock_class_init(WebKitInputMethodContextMockClass* klass)
 
209
{
 
210
    GObjectClass* objectClass = G_OBJECT_CLASS(klass);
 
211
    objectClass->finalize = webkitInputMethodContextMockFinalize;
 
212
 
 
213
    auto* imClass = WEBKIT_INPUT_METHOD_CONTEXT_CLASS(klass);
 
214
    imClass->get_preedit = webkitInputMethodContextMockGetPreedit;
 
215
    imClass->filter_key_event = webkitInputMethodContextMockFilterKeyEvent;
 
216
    imClass->notify_focus_in = webkitInputMethodContextMockNotifyFocusIn;
 
217
    imClass->notify_focus_out = webkitInputMethodContextMockNotifyFocusOut;
 
218
    imClass->notify_surrounding = webkitInputMethodContextMockNotifySurrounding;
 
219
    imClass->reset = webkitInputMethodContextMockReset;
 
220
}
 
221
 
 
222
static void webkit_input_method_context_mock_init(WebKitInputMethodContextMock*)
 
223
{
 
224
}
 
225
 
 
226
class InputMethodTest: public WebViewTest {
 
227
public:
 
228
    MAKE_GLIB_TEST_FIXTURE(InputMethodTest);
 
229
 
 
230
    struct Event {
 
231
        enum class Type { KeyDown, KeyPress, KeyUp, CompositionStart, CompositionUpdate, CompositionEnd };
 
232
 
 
233
        explicit Event(Type type)
 
234
            : type(type)
 
235
        {
 
236
        }
 
237
 
 
238
        Type type;
 
239
        CString data;
 
240
        unsigned keyCode;
 
241
        CString key;
 
242
        bool isComposing;
 
243
    };
 
244
 
 
245
    static void imEventCallback(WebKitUserContentManager*, WebKitJavascriptResult* javascriptResult, InputMethodTest* test)
 
246
    {
 
247
        test->imEvent(javascriptResult);
 
248
    }
 
249
 
 
250
    InputMethodTest()
 
251
        : m_context(adoptGRef(static_cast<WebKitInputMethodContextMock*>(g_object_new(webkit_input_method_context_mock_get_type(), nullptr))))
 
252
    {
 
253
#if PLATFORM(GTK)
 
254
        WebViewTest::showInWindowAndWaitUntilMapped(GTK_WINDOW_TOPLEVEL);
 
255
        auto* defaultContext = webkit_web_view_get_input_method_context(m_webView);
 
256
        g_assert_true(WEBKIT_IS_INPUT_METHOD_CONTEXT(defaultContext));
 
257
        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(defaultContext));
 
258
#elif PLATFORM(WPE)
 
259
        WebViewTest::showInWindow();
 
260
        g_assert_null(webkit_web_view_get_input_method_context(m_webView));
 
261
#endif
 
262
        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_context.get()));
 
263
        webkit_web_view_set_input_method_context(m_webView, WEBKIT_INPUT_METHOD_CONTEXT(m_context.get()));
 
264
        g_assert_true(webkit_web_view_get_input_method_context(m_webView) == WEBKIT_INPUT_METHOD_CONTEXT(m_context.get()));
 
265
 
 
266
        webkit_user_content_manager_register_script_message_handler(m_userContentManager.get(), "imEvent");
 
267
        g_signal_connect(m_userContentManager.get(), "script-message-received::imEvent", G_CALLBACK(imEventCallback), this);
 
268
    }
 
269
 
 
270
    ~InputMethodTest()
 
271
    {
 
272
        webkit_user_content_manager_unregister_script_message_handler(m_userContentManager.get(), "imEvent");
 
273
        g_signal_handlers_disconnect_by_data(m_userContentManager.get(), this);
 
274
    }
 
275
 
 
276
    void imEvent(WebKitJavascriptResult* result)
 
277
    {
 
278
        auto* jsEvent = webkit_javascript_result_get_js_value(result);
 
279
        g_assert_true(jsc_value_is_object(jsEvent));
 
280
 
 
281
        GRefPtr<JSCValue> value = adoptGRef(jsc_value_object_get_property(jsEvent, "type"));
 
282
        g_assert_true(jsc_value_is_string(value.get()));
 
283
        GUniquePtr<char> strValue(jsc_value_to_string(value.get()));
 
284
        if (!g_strcmp0(strValue.get(), "keyDown")) {
 
285
            InputMethodTest::Event event(InputMethodTest::Event::Type::KeyDown);
 
286
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "keyCode"));
 
287
            g_assert_true(jsc_value_is_number(value.get()));
 
288
            event.keyCode = jsc_value_to_int32(value.get());
 
289
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "key"));
 
290
            g_assert_true(jsc_value_is_string(value.get()));
 
291
            strValue.reset(jsc_value_to_string(value.get()));
 
292
            event.key = strValue.get();
 
293
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "isComposing"));
 
294
            g_assert_true(jsc_value_is_boolean(value.get()));
 
295
            event.isComposing = jsc_value_to_boolean(value.get());
 
296
            m_events.append(WTFMove(event));
 
297
        } else if (!g_strcmp0(strValue.get(), "keyPress")) {
 
298
            InputMethodTest::Event event(InputMethodTest::Event::Type::KeyPress);
 
299
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "keyCode"));
 
300
            g_assert_true(jsc_value_is_number(value.get()));
 
301
            event.keyCode = jsc_value_to_int32(value.get());
 
302
            m_events.append(WTFMove(event));
 
303
        } else if (!g_strcmp0(strValue.get(), "keyUp")) {
 
304
            InputMethodTest::Event event(InputMethodTest::Event::Type::KeyUp);
 
305
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "keyCode"));
 
306
            g_assert_true(jsc_value_is_number(value.get()));
 
307
            event.keyCode = jsc_value_to_int32(value.get());
 
308
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "key"));
 
309
            g_assert_true(jsc_value_is_string(value.get()));
 
310
            strValue.reset(jsc_value_to_string(value.get()));
 
311
            event.key = strValue.get();
 
312
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "isComposing"));
 
313
            g_assert_true(jsc_value_is_boolean(value.get()));
 
314
            event.isComposing = jsc_value_to_boolean(value.get());
 
315
            m_events.append(WTFMove(event));
 
316
        } else if (!g_strcmp0(strValue.get(), "compositionstart")) {
 
317
            InputMethodTest::Event event(InputMethodTest::Event::Type::CompositionStart);
 
318
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "data"));
 
319
            g_assert_true(jsc_value_is_string(value.get()));
 
320
            strValue.reset(jsc_value_to_string(value.get()));
 
321
            event.data = strValue.get();
 
322
            m_events.append(WTFMove(event));
 
323
        } else if (!g_strcmp0(strValue.get(), "compositionupdate")) {
 
324
            InputMethodTest::Event event(InputMethodTest::Event::Type::CompositionUpdate);
 
325
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "data"));
 
326
            g_assert_true(jsc_value_is_string(value.get()));
 
327
            strValue.reset(jsc_value_to_string(value.get()));
 
328
            event.data = strValue.get();
 
329
            m_events.append(WTFMove(event));
 
330
        } else if (!g_strcmp0(strValue.get(), "compositionend")) {
 
331
            InputMethodTest::Event event(InputMethodTest::Event::Type::CompositionEnd);
 
332
            value = adoptGRef(jsc_value_object_get_property(jsEvent, "data"));
 
333
            g_assert_true(jsc_value_is_string(value.get()));
 
334
            strValue.reset(jsc_value_to_string(value.get()));
 
335
            event.data = strValue.get();
 
336
            m_events.append(WTFMove(event));
 
337
        }
 
338
 
 
339
        if (m_events.size() == m_eventsExpected)
 
340
            g_main_loop_quit(m_mainLoop);
 
341
    }
 
342
 
 
343
    void focusEditableAndWaitUntilInputMethodEnabled()
 
344
    {
 
345
        g_assert_false(m_context->enabled);
 
346
        runJavaScriptAndWaitUntilFinished("document.getElementById('editable').focus()", nullptr);
 
347
        if (m_context->enabled)
 
348
            return;
 
349
 
 
350
        g_idle_add([](gpointer userData) -> gboolean {
 
351
            auto* test = static_cast<InputMethodTest*>(userData);
 
352
            if (test->m_context->enabled) {
 
353
                test->quitMainLoop();
 
354
                return FALSE;
 
355
            }
 
356
 
 
357
            return TRUE;
 
358
        }, this);
 
359
        g_main_loop_run(m_mainLoop);
 
360
        g_assert_true(m_context->enabled);
 
361
    }
 
362
 
 
363
    void unfocusEditableAndWaitUntilInputMethodDisabled()
 
364
    {
 
365
        g_assert_true(m_context->enabled);
 
366
        runJavaScriptAndWaitUntilFinished("document.getElementById('editable').blur()", nullptr);
 
367
        if (!m_context->enabled)
 
368
            return;
 
369
 
 
370
        g_idle_add([](gpointer userData) -> gboolean {
 
371
            auto* test = static_cast<InputMethodTest*>(userData);
 
372
            if (!test->m_context->enabled) {
 
373
                test->quitMainLoop();
 
374
                return FALSE;
 
375
            }
 
376
 
 
377
            return TRUE;
 
378
        }, this);
 
379
        g_main_loop_run(m_mainLoop);
 
380
        g_assert_false(m_context->enabled);
 
381
    }
 
382
 
 
383
    void resetEditable()
 
384
    {
 
385
        runJavaScriptAndWaitUntilFinished("document.getElementById('editable').value = ''", nullptr);
 
386
        m_events.clear();
 
387
    }
 
388
 
 
389
    GUniquePtr<char> editableValue()
 
390
    {
 
391
        auto* jsResult = runJavaScriptAndWaitUntilFinished("document.getElementById('editable').value", nullptr);
 
392
        return GUniquePtr<char>(WebViewTest::javascriptResultToCString(jsResult));
 
393
    }
 
394
 
 
395
    void keyStrokeAndWaitForEvents(unsigned keyval, unsigned eventsCount, unsigned modifiers = 0)
 
396
    {
 
397
        m_eventsExpected = eventsCount;
 
398
        keyStroke(keyval, modifiers);
 
399
        g_main_loop_run(m_mainLoop);
 
400
        m_eventsExpected = 0;
 
401
    }
 
402
 
 
403
    void keyStrokeHandledByInputMethodAndWaitForEvents(unsigned keyval, unsigned eventsCount)
 
404
    {
 
405
        m_context->commitNextCharacter = true;
 
406
        keyStrokeAndWaitForEvents(keyval, eventsCount);
 
407
        m_context->commitNextCharacter = false;
 
408
    }
 
409
 
 
410
    void clickAndWaitForEvents(unsigned eventsCount)
 
411
    {
 
412
        m_eventsExpected = eventsCount;
 
413
        clickMouseButton(0, 0, 1);
 
414
        g_main_loop_run(m_mainLoop);
 
415
        m_eventsExpected = 0;
 
416
    }
 
417
 
 
418
    WebKitInputPurpose purpose() const
 
419
    {
 
420
        return webkit_input_method_context_get_input_purpose(WEBKIT_INPUT_METHOD_CONTEXT(m_context.get()));
 
421
    }
 
422
 
 
423
    WebKitInputHints hints() const
 
424
    {
 
425
        return webkit_input_method_context_get_input_hints(WEBKIT_INPUT_METHOD_CONTEXT(m_context.get()));
 
426
    }
 
427
 
 
428
    const char* surroundingText() const
 
429
    {
 
430
        return m_context->surroundingText;
 
431
    }
 
432
 
 
433
    unsigned surroundingCursorIndex() const
 
434
    {
 
435
        return m_context->surroundingCursorIndex;
 
436
    }
 
437
 
 
438
    unsigned surroundingSelectionIndex() const
 
439
    {
 
440
        return m_context->surroundingSelectionIndex;
 
441
    }
 
442
 
 
443
    void waitForSurroundingText(const char* text)
 
444
    {
 
445
        m_expectedSurroundingText = text;
 
446
        g_idle_add([](gpointer userData) -> gboolean {
 
447
            auto* test = static_cast<InputMethodTest*>(userData);
 
448
            if (!g_strcmp0(test->m_context->surroundingText, test->m_expectedSurroundingText.data())) {
 
449
                test->quitMainLoop();
 
450
                return FALSE;
 
451
            }
 
452
 
 
453
            return TRUE;
 
454
        }, this);
 
455
        g_main_loop_run(m_mainLoop);
 
456
        m_expectedSurroundingText = { };
 
457
    }
 
458
 
 
459
    GRefPtr<WebKitInputMethodContextMock> m_context;
 
460
    Vector<Event> m_events;
 
461
    unsigned m_eventsExpected { 0 };
 
462
    CString m_expectedSurroundingText;
 
463
};
 
464
 
 
465
static void testWebKitInputMethodContextSimple(InputMethodTest* test, gconstpointer)
 
466
{
 
467
    test->loadHtml(testHTML, nullptr);
 
468
    test->waitUntilLoadFinished();
 
469
 
 
470
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
471
 
 
472
    // Send a normal character not handled by IM.
 
473
    test->keyStrokeAndWaitForEvents(KEY(a), 3);
 
474
    g_assert_cmpuint(test->m_events.size(), ==, 3);
 
475
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
476
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 65);
 
477
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "a");
 
478
    g_assert_false(test->m_events[0].isComposing);
 
479
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::KeyPress);
 
480
    g_assert_cmpuint(test->m_events[1].keyCode, ==, 97);
 
481
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
482
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 65);
 
483
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "a");
 
484
    g_assert_false(test->m_events[2].isComposing);
 
485
    {
 
486
        auto editableValue = test->editableValue();
 
487
        g_assert_cmpstr(editableValue.get(), ==, "a");
 
488
    }
 
489
    test->resetEditable();
 
490
 
 
491
    // Send a normal character handled by IM.
 
492
    test->keyStrokeHandledByInputMethodAndWaitForEvents(KEY(a), 3);
 
493
    g_assert_cmpuint(test->m_events.size(), ==, 3);
 
494
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
495
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 65);
 
496
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "a");
 
497
    g_assert_false(test->m_events[0].isComposing);
 
498
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::KeyPress);
 
499
    g_assert_cmpuint(test->m_events[1].keyCode, ==, 97);
 
500
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
501
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 65);
 
502
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "a");
 
503
    g_assert_false(test->m_events[2].isComposing);
 
504
    {
 
505
        auto editableValue = test->editableValue();
 
506
        g_assert_cmpstr(editableValue.get(), ==, "a");
 
507
    }
 
508
    test->resetEditable();
 
509
}
 
510
 
 
511
static void testWebKitInputMethodContextSequence(InputMethodTest* test, gconstpointer)
 
512
{
 
513
    test->loadHtml(testHTML, nullptr);
 
514
    test->waitUntilLoadFinished();
 
515
 
 
516
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
517
 
 
518
    // Compose w + gtk + Enter.
 
519
    test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK);
 
520
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
521
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
522
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
523
    g_assert_false(test->m_events[0].isComposing);
 
524
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart);
 
525
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "");
 
526
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate);
 
527
    g_assert_cmpstr(test->m_events[2].data.data(), ==, "w");
 
528
    g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp);
 
529
    g_assert_cmpuint(test->m_events[3].keyCode, ==, 87);
 
530
    g_assert_cmpstr(test->m_events[3].key.data(), ==, "w");
 
531
    g_assert_true(test->m_events[3].isComposing);
 
532
    test->m_events.clear();
 
533
    test->keyStrokeAndWaitForEvents(KEY(g), 3);
 
534
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
535
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
536
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
537
    g_assert_true(test->m_events[0].isComposing);
 
538
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate);
 
539
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "wg");
 
540
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
541
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 71);
 
542
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "g");
 
543
    g_assert_true(test->m_events[2].isComposing);
 
544
    test->m_events.clear();
 
545
    test->keyStrokeAndWaitForEvents(KEY(t), 3);
 
546
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
547
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
548
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
549
    g_assert_true(test->m_events[0].isComposing);
 
550
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate);
 
551
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "wgt");
 
552
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
553
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 84);
 
554
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "t");
 
555
    g_assert_true(test->m_events[2].isComposing);
 
556
    test->m_events.clear();
 
557
    test->keyStrokeAndWaitForEvents(KEY(k), 3);
 
558
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
559
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
560
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
561
    g_assert_true(test->m_events[0].isComposing);
 
562
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate);
 
563
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "wgtk");
 
564
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
565
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 75);
 
566
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "k");
 
567
    g_assert_true(test->m_events[2].isComposing);
 
568
    test->m_events.clear();
 
569
    test->keyStrokeAndWaitForEvents(KEY(ISO_Enter), 3);
 
570
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
571
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
572
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
573
    g_assert_true(test->m_events[0].isComposing);
 
574
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionEnd);
 
575
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "WebKitGTK");
 
576
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
577
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 13);
 
578
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "Enter");
 
579
    g_assert_false(test->m_events[2].isComposing);
 
580
    {
 
581
        auto editableValue = test->editableValue();
 
582
        g_assert_cmpstr(editableValue.get(), ==, "WebKitGTK");
 
583
    }
 
584
    test->resetEditable();
 
585
 
 
586
    // Compose w + wpe + Space.
 
587
    test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK);
 
588
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
589
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
590
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
591
    g_assert_false(test->m_events[0].isComposing);
 
592
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart);
 
593
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "");
 
594
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate);
 
595
    g_assert_cmpstr(test->m_events[2].data.data(), ==, "w");
 
596
    g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp);
 
597
    g_assert_cmpuint(test->m_events[3].keyCode, ==, 87);
 
598
    g_assert_cmpstr(test->m_events[3].key.data(), ==, "w");
 
599
    g_assert_true(test->m_events[3].isComposing);
 
600
    test->m_events.clear();
 
601
    test->keyStrokeAndWaitForEvents(KEY(w), 3);
 
602
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
603
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
604
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
605
    g_assert_true(test->m_events[0].isComposing);
 
606
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate);
 
607
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "ww");
 
608
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
609
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 87);
 
610
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "w");
 
611
    g_assert_true(test->m_events[2].isComposing);
 
612
    test->m_events.clear();
 
613
    test->keyStrokeAndWaitForEvents(KEY(p), 3);
 
614
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
615
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
616
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
617
    g_assert_true(test->m_events[0].isComposing);
 
618
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate);
 
619
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "wwp");
 
620
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
621
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 80);
 
622
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "p");
 
623
    g_assert_true(test->m_events[2].isComposing);
 
624
    test->m_events.clear();
 
625
    test->keyStrokeAndWaitForEvents(KEY(e), 3);
 
626
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
627
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
628
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
629
    g_assert_true(test->m_events[0].isComposing);
 
630
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate);
 
631
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "wwpe");
 
632
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
633
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 69);
 
634
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "e");
 
635
    g_assert_true(test->m_events[2].isComposing);
 
636
    test->m_events.clear();
 
637
    test->keyStrokeAndWaitForEvents(KEY(space), 3);
 
638
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
639
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
640
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
641
    g_assert_true(test->m_events[0].isComposing);
 
642
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionEnd);
 
643
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "WPEWebKit");
 
644
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
645
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 32);
 
646
    g_assert_cmpstr(test->m_events[2].key.data(), ==, " ");
 
647
    g_assert_false(test->m_events[2].isComposing);
 
648
    {
 
649
        auto editableValue = test->editableValue();
 
650
        g_assert_cmpstr(editableValue.get(), ==, "WPEWebKit");
 
651
    }
 
652
    test->resetEditable();
 
653
}
 
654
 
 
655
static void testWebKitInputMethodContextInvalidSequence(InputMethodTest* test, gconstpointer)
 
656
{
 
657
    test->loadHtml(testHTML, nullptr);
 
658
    test->waitUntilLoadFinished();
 
659
 
 
660
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
661
 
 
662
    // Compose w + w + Space -> invalid sequence.
 
663
    test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK);
 
664
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
665
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
666
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
667
    g_assert_false(test->m_events[0].isComposing);
 
668
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart);
 
669
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "");
 
670
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate);
 
671
    g_assert_cmpstr(test->m_events[2].data.data(), ==, "w");
 
672
    g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp);
 
673
    g_assert_cmpuint(test->m_events[3].keyCode, ==, 87);
 
674
    g_assert_cmpstr(test->m_events[3].key.data(), ==, "w");
 
675
    g_assert_true(test->m_events[3].isComposing);
 
676
    test->m_events.clear();
 
677
    test->keyStrokeAndWaitForEvents(KEY(w), 3);
 
678
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
679
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
680
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
681
    g_assert_true(test->m_events[0].isComposing);
 
682
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionUpdate);
 
683
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "ww");
 
684
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
685
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 87);
 
686
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "w");
 
687
    g_assert_true(test->m_events[2].isComposing);
 
688
    test->m_events.clear();
 
689
    test->keyStrokeAndWaitForEvents(KEY(space), 3);
 
690
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
691
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
692
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
693
    g_assert_true(test->m_events[0].isComposing);
 
694
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionEnd);
 
695
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "w");
 
696
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
697
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 32);
 
698
    g_assert_cmpstr(test->m_events[2].key.data(), ==, " ");
 
699
    g_assert_false(test->m_events[2].isComposing);
 
700
    {
 
701
        auto editableValue = test->editableValue();
 
702
        g_assert_cmpstr(editableValue.get(), ==, "w");
 
703
    }
 
704
    test->resetEditable();
 
705
}
 
706
 
 
707
static void testWebKitInputMethodContextCancelSequence(InputMethodTest* test, gconstpointer)
 
708
{
 
709
    test->loadHtml(testHTML, nullptr);
 
710
    test->waitUntilLoadFinished();
 
711
 
 
712
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
713
 
 
714
    // Compose w + w + Escape -> cancel sequence.
 
715
    test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK);
 
716
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
717
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
718
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
719
    g_assert_false(test->m_events[0].isComposing);
 
720
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart);
 
721
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "");
 
722
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate);
 
723
    g_assert_cmpstr(test->m_events[2].data.data(), ==, "w");
 
724
    g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp);
 
725
    g_assert_cmpuint(test->m_events[3].keyCode, ==, 87);
 
726
    g_assert_cmpstr(test->m_events[3].key.data(), ==, "w");
 
727
    g_assert_true(test->m_events[3].isComposing);
 
728
    test->m_events.clear();
 
729
    test->keyStrokeAndWaitForEvents(KEY(Escape), 3);
 
730
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
731
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
732
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
733
    g_assert_true(test->m_events[0].isComposing);
 
734
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionEnd);
 
735
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "");
 
736
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::KeyUp);
 
737
    g_assert_cmpuint(test->m_events[2].keyCode, ==, 27);
 
738
    g_assert_cmpstr(test->m_events[2].key.data(), ==, "Escape");
 
739
    g_assert_false(test->m_events[2].isComposing);
 
740
    {
 
741
        auto editableValue = test->editableValue();
 
742
        g_assert_cmpstr(editableValue.get(), ==, "");
 
743
    }
 
744
    test->resetEditable();
 
745
}
 
746
 
 
747
static void testWebKitInputMethodContextSurrounding(InputMethodTest* test, gconstpointer)
 
748
{
 
749
    test->loadHtml(testHTML, nullptr);
 
750
    test->waitUntilLoadFinished();
 
751
 
 
752
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
753
 
 
754
    g_assert_null(test->surroundingText());
 
755
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0);
 
756
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
757
 
 
758
    test->keyStrokeAndWaitForEvents(KEY(a), 3);
 
759
    test->keyStrokeAndWaitForEvents(KEY(b), 6);
 
760
    test->keyStrokeAndWaitForEvents(KEY(c), 9);
 
761
    g_assert_cmpstr(test->surroundingText(), ==, "abc");
 
762
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 3);
 
763
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
764
    test->m_events.clear();
 
765
 
 
766
    // Check preedit string is not included in surrounding.
 
767
    // 1. Preedit string at the beginning of context.
 
768
    test->keyStrokeAndWaitForEvents(KEY(Left), 2);
 
769
    test->keyStrokeAndWaitForEvents(KEY(Left), 4);
 
770
    test->keyStrokeAndWaitForEvents(KEY(Left), 6);
 
771
    g_assert_cmpstr(test->surroundingText(), ==, "abc");
 
772
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0);
 
773
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
774
    test->m_events.clear();
 
775
    test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK);
 
776
    g_assert_cmpstr(test->surroundingText(), ==, "abc");
 
777
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0);
 
778
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
779
    test->keyStrokeAndWaitForEvents(KEY(g), 7);
 
780
    test->keyStrokeAndWaitForEvents(KEY(t), 10);
 
781
    test->keyStrokeAndWaitForEvents(KEY(k), 13);
 
782
    g_assert_cmpstr(test->surroundingText(), ==, "abc");
 
783
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0);
 
784
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
785
    test->keyStrokeAndWaitForEvents(KEY(ISO_Enter), 16);
 
786
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKabc");
 
787
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 9);
 
788
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
789
    test->m_events.clear();
 
790
    // 2. Preedit string in the middle of context.
 
791
    test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK);
 
792
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKabc");
 
793
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 9);
 
794
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
795
    test->keyStrokeAndWaitForEvents(KEY(w), 7);
 
796
    test->keyStrokeAndWaitForEvents(KEY(p), 10);
 
797
    test->keyStrokeAndWaitForEvents(KEY(e), 13);
 
798
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKabc");
 
799
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 9);
 
800
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
801
    test->keyStrokeAndWaitForEvents(KEY(space), 16);
 
802
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabc");
 
803
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 18);
 
804
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
805
    test->m_events.clear();
 
806
    // 3. Preedit string at the end of context.
 
807
    test->keyStrokeAndWaitForEvents(KEY(Right), 2);
 
808
    test->keyStrokeAndWaitForEvents(KEY(Right), 4);
 
809
    test->keyStrokeAndWaitForEvents(KEY(Right), 6);
 
810
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabc");
 
811
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 21);
 
812
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
813
    test->m_events.clear();
 
814
    test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK);
 
815
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabc");
 
816
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 21);
 
817
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
818
    test->keyStrokeAndWaitForEvents(KEY(g), 7);
 
819
    test->keyStrokeAndWaitForEvents(KEY(t), 10);
 
820
    test->keyStrokeAndWaitForEvents(KEY(k), 13);
 
821
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabc");
 
822
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 21);
 
823
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
824
    test->keyStrokeAndWaitForEvents(KEY(ISO_Enter), 16);
 
825
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK");
 
826
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 30);
 
827
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
828
    test->m_events.clear();
 
829
 
 
830
    // Check selection cursor.
 
831
    test->keyStrokeAndWaitForEvents(KEY(Left), 2, SHIFT_MASK);
 
832
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK");
 
833
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 29);
 
834
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, 30);
 
835
    test->keyStrokeAndWaitForEvents(KEY(Home), 4, SHIFT_MASK);
 
836
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0);
 
837
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, 30);
 
838
    test->keyStrokeAndWaitForEvents(KEY(Left), 6);
 
839
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0);
 
840
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
841
    test->m_events.clear();
 
842
    test->keyStrokeAndWaitForEvents(KEY(Right), 2, SHIFT_MASK);
 
843
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK");
 
844
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0);
 
845
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, 1);
 
846
    test->keyStrokeAndWaitForEvents(KEY(End), 4, SHIFT_MASK);
 
847
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 0);
 
848
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, 30);
 
849
    test->keyStrokeAndWaitForEvents(KEY(Right), 6);
 
850
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK");
 
851
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 30);
 
852
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
853
    test->m_events.clear();
 
854
 
 
855
    // Check text replacements (get surrounding + delete surrounding).
 
856
    test->keyStrokeAndWaitForEvents(KEY(colon), 3);
 
857
    test->keyStrokeAndWaitForEvents(KEY(minus), 6);
 
858
    test->keyStrokeAndWaitForEvents(KEY(parenright), 9);
 
859
    test->waitForSurroundingText("WebKitGTKWPEWebKitabcWebKitGTK😀️");
 
860
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK😀️");
 
861
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 37);
 
862
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
863
    test->m_events.clear();
 
864
 
 
865
    // Check multiline context.
 
866
    test->keyStrokeAndWaitForEvents(KEY(Return), 3);
 
867
    test->keyStrokeAndWaitForEvents(KEY(a), 6);
 
868
    test->waitForSurroundingText("WebKitGTKWPEWebKitabcWebKitGTK😀️\na");
 
869
    g_assert_cmpstr(test->surroundingText(), ==, "WebKitGTKWPEWebKitabcWebKitGTK😀️\na");
 
870
    g_assert_cmpuint(test->surroundingCursorIndex(), ==, 39);
 
871
    g_assert_cmpuint(test->surroundingSelectionIndex(), ==, test->surroundingCursorIndex());
 
872
    test->m_events.clear();
 
873
}
 
874
 
 
875
static void testWebKitInputMethodContextReset(InputMethodTest* test, gconstpointer)
 
876
{
 
877
    test->loadHtml(testHTML, nullptr);
 
878
    test->waitUntilLoadFinished();
 
879
 
 
880
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
881
 
 
882
    // Compose w + w + click -> reset sequence.
 
883
    test->keyStrokeAndWaitForEvents(KEY(w), 4, CONTROL_MASK | SHIFT_MASK);
 
884
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::KeyDown);
 
885
    g_assert_cmpuint(test->m_events[0].keyCode, ==, 229);
 
886
    g_assert_cmpstr(test->m_events[0].key.data(), ==, "Unidentified");
 
887
    g_assert_false(test->m_events[0].isComposing);
 
888
    g_assert_true(test->m_events[1].type == InputMethodTest::Event::Type::CompositionStart);
 
889
    g_assert_cmpstr(test->m_events[1].data.data(), ==, "");
 
890
    g_assert_true(test->m_events[2].type == InputMethodTest::Event::Type::CompositionUpdate);
 
891
    g_assert_cmpstr(test->m_events[2].data.data(), ==, "w");
 
892
    g_assert_true(test->m_events[3].type == InputMethodTest::Event::Type::KeyUp);
 
893
    g_assert_cmpuint(test->m_events[3].keyCode, ==, 87);
 
894
    g_assert_cmpstr(test->m_events[3].key.data(), ==, "w");
 
895
    g_assert_true(test->m_events[3].isComposing);
 
896
    test->m_events.clear();
 
897
    test->clickAndWaitForEvents(1);
 
898
    g_assert_true(test->m_events[0].type == InputMethodTest::Event::Type::CompositionEnd);
 
899
    g_assert_cmpstr(test->m_events[0].data.data(), ==, "w");
 
900
    {
 
901
        auto editableValue = test->editableValue();
 
902
        g_assert_cmpstr(editableValue.get(), ==, "w");
 
903
    }
 
904
    test->resetEditable();
 
905
}
 
906
 
 
907
static void testWebKitInputMethodContextContentType(InputMethodTest* test, gconstpointer)
 
908
{
 
909
    test->loadHtml("<input id='editable' spellcheck='false'></input>", nullptr);
 
910
    test->waitUntilLoadFinished();
 
911
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
912
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM);
 
913
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
914
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
915
 
 
916
    test->loadHtml("<input id='editable' type='number' spellcheck='false'>", nullptr);
 
917
    test->waitUntilLoadFinished();
 
918
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
919
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_NUMBER);
 
920
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
921
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
922
 
 
923
    test->loadHtml("<input id='editable' type='number' spellcheck='false' pattern='[0-9]*'>", nullptr);
 
924
    test->waitUntilLoadFinished();
 
925
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
926
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_DIGITS);
 
927
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
928
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
929
 
 
930
    test->loadHtml("<input id='editable' type='text' spellcheck='false' pattern='\\d*'>", nullptr);
 
931
    test->waitUntilLoadFinished();
 
932
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
933
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_DIGITS);
 
934
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
935
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
936
 
 
937
    test->loadHtml("<input id='editable' type='tel' spellcheck='false'>", nullptr);
 
938
    test->waitUntilLoadFinished();
 
939
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
940
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_PHONE);
 
941
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
942
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
943
 
 
944
    test->loadHtml("<input id='editable' type='url' spellcheck='false'>", nullptr);
 
945
    test->waitUntilLoadFinished();
 
946
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
947
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_URL);
 
948
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
949
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
950
 
 
951
    test->loadHtml("<input id='editable' type='email' spellcheck='false'>", nullptr);
 
952
    test->waitUntilLoadFinished();
 
953
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
954
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_EMAIL);
 
955
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
956
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
957
 
 
958
    test->loadHtml("<input id='editable' type='password' spellcheck='false'>", nullptr);
 
959
    test->waitUntilLoadFinished();
 
960
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
961
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_PASSWORD);
 
962
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
963
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
964
 
 
965
    test->loadHtml("<div contenteditable id='editable' inputmode='text' spellcheck='false'></div>", nullptr);
 
966
    test->waitUntilLoadFinished();
 
967
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
968
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM);
 
969
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
970
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
971
 
 
972
    test->loadHtml("<div contenteditable id='editable' inputmode='decimal' spellcheck='false'></div>", nullptr);
 
973
    test->waitUntilLoadFinished();
 
974
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
975
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_NUMBER);
 
976
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
977
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
978
 
 
979
    test->loadHtml("<div contenteditable id='editable' inputmode='numeric' spellcheck='false'></div>", nullptr);
 
980
    test->waitUntilLoadFinished();
 
981
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
982
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_DIGITS);
 
983
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
984
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
985
 
 
986
    test->loadHtml("<div contenteditable id='editable' inputmode='tel' spellcheck='false'></div>", nullptr);
 
987
    test->waitUntilLoadFinished();
 
988
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
989
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_PHONE);
 
990
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
991
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
992
 
 
993
    test->loadHtml("<div contenteditable id='editable' inputmode='email' spellcheck='false'></div>", nullptr);
 
994
    test->waitUntilLoadFinished();
 
995
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
996
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_EMAIL);
 
997
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
998
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
999
 
 
1000
    test->loadHtml("<div contenteditable id='editable' inputmode='url' spellcheck='false'></div>", nullptr);
 
1001
    test->waitUntilLoadFinished();
 
1002
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
1003
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_URL);
 
1004
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
1005
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
1006
 
 
1007
    test->loadHtml("<div contenteditable id='editable' inputmode='search' spellcheck='false'></div>", nullptr);
 
1008
    test->waitUntilLoadFinished();
 
1009
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
1010
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM);
 
1011
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_NONE);
 
1012
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
1013
 
 
1014
    test->loadHtml("<div contenteditable id='editable' inputmode='none' spellcheck='false'></div>", nullptr);
 
1015
    test->waitUntilLoadFinished();
 
1016
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
1017
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM);
 
1018
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_INHIBIT_OSK);
 
1019
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
1020
 
 
1021
    test->loadHtml("<textarea id='editable'></textarea>", nullptr);
 
1022
    test->waitUntilLoadFinished();
 
1023
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
1024
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM);
 
1025
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK);
 
1026
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
1027
 
 
1028
    test->loadHtml("<textarea id='editable' autocapitalize='none'></textarea>", nullptr);
 
1029
    test->waitUntilLoadFinished();
 
1030
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
1031
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM);
 
1032
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK | WEBKIT_INPUT_HINT_LOWERCASE);
 
1033
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
1034
 
 
1035
    test->loadHtml("<textarea id='editable' autocapitalize='sentences'></textarea>", nullptr);
 
1036
    test->waitUntilLoadFinished();
 
1037
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
1038
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM);
 
1039
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK | WEBKIT_INPUT_HINT_UPPERCASE_SENTENCES);
 
1040
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
1041
 
 
1042
    test->loadHtml("<textarea id='editable' autocapitalize='words'></textarea>", nullptr);
 
1043
    test->waitUntilLoadFinished();
 
1044
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
1045
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM);
 
1046
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK | WEBKIT_INPUT_HINT_UPPERCASE_WORDS);
 
1047
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
1048
 
 
1049
    test->loadHtml("<textarea id='editable' autocapitalize='characters'></textarea>", nullptr);
 
1050
    test->waitUntilLoadFinished();
 
1051
    test->focusEditableAndWaitUntilInputMethodEnabled();
 
1052
    g_assert_cmpuint(test->purpose(), ==, WEBKIT_INPUT_PURPOSE_FREE_FORM);
 
1053
    g_assert_cmpuint(test->hints(), ==, WEBKIT_INPUT_HINT_SPELLCHECK | WEBKIT_INPUT_HINT_UPPERCASE_CHARS);
 
1054
    test->unfocusEditableAndWaitUntilInputMethodDisabled();
 
1055
}
 
1056
 
 
1057
void beforeAll()
 
1058
{
 
1059
    InputMethodTest::add("WebKitInputMethodContext", "simple", testWebKitInputMethodContextSimple);
 
1060
    InputMethodTest::add("WebKitInputMethodContext", "sequence", testWebKitInputMethodContextSequence);
 
1061
    InputMethodTest::add("WebKitInputMethodContext", "invalid-sequence", testWebKitInputMethodContextInvalidSequence);
 
1062
    InputMethodTest::add("WebKitInputMethodContext", "cancel-sequence", testWebKitInputMethodContextCancelSequence);
 
1063
    InputMethodTest::add("WebKitInputMethodContext", "surrounding", testWebKitInputMethodContextSurrounding);
 
1064
    InputMethodTest::add("WebKitInputMethodContext", "reset", testWebKitInputMethodContextReset);
 
1065
    InputMethodTest::add("WebKitInputMethodContext", "content-type", testWebKitInputMethodContextContentType);
 
1066
}
 
1067
 
 
1068
void afterAll()
 
1069
{
 
1070
 
 
1071
}