blob: 6e4c1dd2f1d9737eee94179007ebc7aa3ce174c0 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/base/ime/linux/input_method_auralinux.h"
#include <stddef.h>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/singleton.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/base/ime/dummy_text_input_client.h"
#include "ui/base/ime/ime_key_event_dispatcher.h"
#include "ui/base/ime/init/input_method_initializer.h"
#include "ui/base/ime/linux/linux_input_method_context_factory.h"
#include "ui/base/ime/virtual_keyboard_controller_stub.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"
namespace ui {
namespace {
const char16_t kActionCommit = L'C';
const char16_t kActionCompositionStart = L'S';
const char16_t kActionCompositionUpdate = L'U';
const char16_t kActionCompositionEnd = L'E';
class TestResult {
public:
static TestResult* GetInstance() {
return base::Singleton<TestResult>::get();
}
void RecordAction(const std::u16string& action) {
recorded_actions_.push_back(action);
}
void ExpectAction(const std::string& action) {
expected_actions_.push_back(base::ASCIIToUTF16(action));
}
void Verify() {
size_t len = recorded_actions_.size();
size_t len_exp = expected_actions_.size();
EXPECT_EQ(len_exp, len);
for (size_t i = 0; i < len; i++)
EXPECT_EQ(expected_actions_[i], recorded_actions_[i]);
recorded_actions_.clear();
expected_actions_.clear();
}
private:
std::vector<std::u16string> recorded_actions_;
std::vector<std::u16string> expected_actions_;
};
class LinuxInputMethodContextForTesting : public LinuxInputMethodContext {
public:
explicit LinuxInputMethodContextForTesting(
LinuxInputMethodContextDelegate* delegate)
: delegate_(delegate), is_sync_mode_(false), eat_key_(false) {}
LinuxInputMethodContextForTesting(const LinuxInputMethodContextForTesting&) =
delete;
LinuxInputMethodContextForTesting& operator=(
const LinuxInputMethodContextForTesting&) = delete;
void SetSyncMode(bool is_sync_mode) { is_sync_mode_ = is_sync_mode; }
void SetEatKey(bool eat_key) { eat_key_ = eat_key; }
void AddCommitAction(const std::string& text) {
actions_.push_back(u"C:" + base::ASCIIToUTF16(text));
}
void AddCompositionUpdateAction(const std::string& text) {
actions_.push_back(u"U:" + base::ASCIIToUTF16(text));
}
void AddCompositionStartAction() { actions_.push_back(u"S"); }
void AddCompositionEndAction() { actions_.push_back(u"E"); }
VirtualKeyboardController* GetVirtualKeyboardController() override {
return &virtual_keyboard_controller_;
}
TextInputType input_type() const { return input_type_; }
TextInputMode input_mode() const { return input_mode_; }
uint32_t input_flags() const { return input_flags_; }
bool should_do_learning() const { return should_do_learning_; }
protected:
bool DispatchKeyEvent(const ui::KeyEvent& key_event) override {
if (!is_sync_mode_) {
actions_.clear();
return eat_key_;
}
for (const auto& action : actions_) {
std::vector<std::u16string> parts =
base::SplitString(action, std::u16string(1, ':'),
base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
char16_t id = parts[0][0];
std::u16string param;
if (parts.size() > 1)
param = parts[1];
if (id == kActionCommit) {
delegate_->OnCommit(param);
} else if (id == kActionCompositionStart) {
delegate_->OnPreeditStart();
} else if (id == kActionCompositionUpdate) {
CompositionText comp;
comp.text = param;
delegate_->OnPreeditChanged(comp);
} else if (id == kActionCompositionEnd) {
delegate_->OnPreeditEnd();
}
}
actions_.clear();
return eat_key_;
}
bool IsPeekKeyEvent(const ui::KeyEvent& key_event) override {
const auto* properties = key_event.properties();
// For the purposes of tests if kPropertyKeyboardImeFlag is not
// explicitly set assume the event is not a key event.
if (!properties)
return true;
auto it = properties->find(kPropertyKeyboardImeFlag);
if (it == properties->end())
return true;
return !(it->second[0] & kPropertyKeyboardImeIgnoredFlag);
}
void Reset() override {}
void UpdateFocus(bool has_client,
TextInputType old_type,
TextInputType new_type) override {}
void SetCursorLocation(const gfx::Rect& rect) override {
cursor_position_ = rect;
}
void SetSurroundingText(const std::u16string& text,
const gfx::Range& selection_range) override {
TestResult::GetInstance()->RecordAction(u"surroundingtext:" + text);
std::stringstream rs;
rs << "selectionrangestart:" << selection_range.start();
std::stringstream re;
re << "selectionrangeend:" << selection_range.end();
TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(rs.str()));
TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(re.str()));
}
void SetContentType(TextInputType type,
TextInputMode mode,
uint32_t flags,
bool should_do_learning) override {
input_type_ = type;
input_mode_ = mode;
input_flags_ = flags;
should_do_learning_ = should_do_learning;
}
void SetGrammarFragmentAtCursor(
const ui::GrammarFragment& fragment) override {}
void SetAutocorrectInfo(const gfx::Range& autocorrect_range,
const gfx::Rect& autocorrect_bounds) override {}
private:
raw_ptr<LinuxInputMethodContextDelegate> delegate_;
VirtualKeyboardControllerStub virtual_keyboard_controller_;
std::vector<std::u16string> actions_;
bool is_sync_mode_;
bool eat_key_;
gfx::Rect cursor_position_;
TextInputType input_type_;
TextInputMode input_mode_;
uint32_t input_flags_;
bool should_do_learning_;
};
class InputMethodDelegateForTesting : public ImeKeyEventDispatcher {
public:
InputMethodDelegateForTesting() {}
InputMethodDelegateForTesting(const InputMethodDelegateForTesting&) = delete;
InputMethodDelegateForTesting& operator=(
const InputMethodDelegateForTesting&) = delete;
~InputMethodDelegateForTesting() override {}
ui::EventDispatchDetails DispatchKeyEventPostIME(
ui::KeyEvent* key_event) override {
std::string action;
switch (key_event->type()) {
case ET_KEY_PRESSED:
action = "keydown:";
break;
case ET_KEY_RELEASED:
action = "keyup:";
break;
default:
break;
}
std::stringstream ss;
ss << key_event->key_code();
action += std::string(ss.str());
TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(action));
return ui::EventDispatchDetails();
}
};
class TextInputClientForTesting : public DummyTextInputClient {
public:
explicit TextInputClientForTesting(TextInputType text_input_type)
: DummyTextInputClient(text_input_type) {}
std::u16string composition_text;
gfx::Range text_range;
gfx::Range selection_range;
std::u16string surrounding_text;
absl::optional<gfx::Rect> caret_not_in_rect;
protected:
void SetCompositionText(const CompositionText& composition) override {
composition_text = composition.text;
TestResult::GetInstance()->RecordAction(u"compositionstart");
TestResult::GetInstance()->RecordAction(u"compositionupdate:" +
composition.text);
}
bool HasCompositionText() const override { return !composition_text.empty(); }
size_t ConfirmCompositionText(bool keep_selection) override {
// TODO(b/134473433) Modify this function so that when keep_selection is
// true, the selection is not changed when text committed
if (keep_selection) {
NOTIMPLEMENTED_LOG_ONCE();
}
TestResult::GetInstance()->RecordAction(u"compositionend");
TestResult::GetInstance()->RecordAction(u"textinput:" + composition_text);
const size_t composition_text_length = composition_text.length();
composition_text.clear();
return composition_text_length;
}
void ClearCompositionText() override {
TestResult::GetInstance()->RecordAction(u"compositionend");
composition_text.clear();
}
void InsertText(
const std::u16string& text,
TextInputClient::InsertTextCursorBehavior cursor_behavior) override {
if (HasCompositionText()) {
TestResult::GetInstance()->RecordAction(u"compositionend");
}
TestResult::GetInstance()->RecordAction(u"textinput:" + text);
composition_text.clear();
}
void InsertChar(const ui::KeyEvent& event) override {
std::stringstream ss;
ss << static_cast<uint16_t>(event.GetCharacter());
TestResult::GetInstance()->RecordAction(u"keypress:" +
base::ASCIIToUTF16(ss.str()));
}
bool GetTextRange(gfx::Range* range) const override {
*range = text_range;
return true;
}
bool GetEditableSelectionRange(gfx::Range* range) const override {
*range = selection_range;
return true;
}
bool GetTextFromRange(const gfx::Range& range,
std::u16string* text) const override {
if (surrounding_text.empty())
return false;
*text = surrounding_text.substr(range.GetMin(), range.length());
return true;
}
void EnsureCaretNotInRect(const gfx::Rect& rect) override {
caret_not_in_rect = rect;
}
};
class InputMethodAuraLinuxTest : public testing::Test {
public:
InputMethodAuraLinuxTest(const InputMethodAuraLinuxTest&) = delete;
InputMethodAuraLinuxTest& operator=(const InputMethodAuraLinuxTest&) = delete;
protected:
InputMethodAuraLinuxTest()
: input_method_auralinux_(nullptr),
delegate_(nullptr),
context_(nullptr) {
GetInputMethodContextFactoryForTest() =
base::BindRepeating([](LinuxInputMethodContextDelegate* delegate)
-> std::unique_ptr<LinuxInputMethodContext> {
return std::make_unique<LinuxInputMethodContextForTesting>(delegate);
});
test_result_ = TestResult::GetInstance();
}
~InputMethodAuraLinuxTest() override {
ShutdownInputMethodForTesting();
test_result_ = nullptr;
}
void SetUp() override {
delegate_ = new InputMethodDelegateForTesting();
input_method_auralinux_ = new InputMethodAuraLinux(delegate_);
input_method_auralinux_->OnFocus();
context_ = static_cast<LinuxInputMethodContextForTesting*>(
input_method_auralinux_->GetContextForTesting());
}
void TearDown() override {
context_->SetSyncMode(false);
context_->SetEatKey(false);
context_ = nullptr;
delete input_method_auralinux_;
input_method_auralinux_ = nullptr;
delete delegate_;
delegate_ = nullptr;
}
raw_ptr<InputMethodAuraLinux> input_method_auralinux_;
raw_ptr<InputMethodDelegateForTesting> delegate_;
raw_ptr<LinuxInputMethodContextForTesting> context_;
raw_ptr<TestResult> test_result_;
};
TEST_F(InputMethodAuraLinuxTest, BasicSyncModeTest) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCommitAction("a");
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:65");
test_result_->ExpectAction("keypress:97");
test_result_->Verify();
input_method_auralinux_->DetachTextInputClient(client.get());
client =
std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_PASSWORD);
context_->SetEatKey(false);
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:65");
test_result_->ExpectAction("keypress:97");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, BasicAsyncModeTest) {
context_->SetSyncMode(false);
context_->SetEatKey(true);
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
input_method_auralinux_->OnCommit(u"a");
test_result_->ExpectAction("keydown:65");
test_result_->ExpectAction("keypress:97");
test_result_->Verify();
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
input_method_auralinux_->OnCommit(u"foo");
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("textinput:foo");
test_result_->Verify();
input_method_auralinux_->DetachTextInputClient(client.get());
client =
std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_PASSWORD);
context_->SetEatKey(false);
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:65");
test_result_->ExpectAction("keypress:97");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, IBusUSTest) {
context_->SetSyncMode(false);
context_->SetEatKey(true);
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
// IBus mutes the key down.
test_result_->Verify();
// IBus simulates a faked key down and handle it in sync mode.
context_->SetSyncMode(true);
context_->AddCommitAction("a");
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:65");
test_result_->ExpectAction("keypress:97");
test_result_->Verify();
// IBus does NOT handle the key up.
context_->SetEatKey(false);
KeyEvent key_up(ET_KEY_RELEASED, VKEY_A, 0);
input_method_auralinux_->DispatchKeyEvent(&key_up);
test_result_->ExpectAction("keyup:65");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, IBusPinyinTest) {
context_->SetSyncMode(false);
context_->SetEatKey(true);
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
key.set_character(L'a');
input_method_auralinux_->DispatchKeyEvent(&key);
// IBus issues a standalone set_composition action.
input_method_auralinux_->OnPreeditStart();
CompositionText comp;
comp.text = u"a";
input_method_auralinux_->OnPreeditChanged(comp);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
// IBus issues a commit text with composition after muting the space key down.
KeyEvent key_up(ET_KEY_RELEASED, VKEY_SPACE, 0);
input_method_auralinux_->DispatchKeyEvent(&key_up);
input_method_auralinux_->OnPreeditEnd();
input_method_auralinux_->OnCommit(u"A");
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:A");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, JapaneseCommit) {
context_->SetSyncMode(false);
context_->SetEatKey(true);
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
key.set_character(L'a');
input_method_auralinux_->DispatchKeyEvent(&key);
// IBus issues a standalone set_composition action.
input_method_auralinux_->OnPreeditStart();
CompositionText comp;
comp.text = u"a";
input_method_auralinux_->OnPreeditChanged(comp);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
// IBus issues a commit text with composition after muting the space key down.
// Typing return issues a commit, followed by preedit change (to make
// composition empty), then preedit end.
KeyEvent key_up(ET_KEY_PRESSED, VKEY_RETURN, 0);
input_method_auralinux_->DispatchKeyEvent(&key_up);
input_method_auralinux_->OnCommit(u"a");
comp.text = u"";
input_method_auralinux_->OnPreeditChanged(comp);
input_method_auralinux_->OnPreeditEnd();
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:a");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, EmptyCommit) {
context_->SetSyncMode(false);
context_->SetEatKey(true);
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
key.set_character(L'a');
input_method_auralinux_->DispatchKeyEvent(&key);
input_method_auralinux_->OnPreeditStart();
CompositionText comp;
comp.text = u"a";
input_method_auralinux_->OnPreeditChanged(comp);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
input_method_auralinux_->OnCommit(u"");
comp.text = u"";
input_method_auralinux_->OnPreeditChanged(comp);
input_method_auralinux_->OnPreeditEnd();
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:");
test_result_->Verify();
}
// crbug.com/463491
void DeadKeyTest(TextInputType text_input_type,
InputMethodAuraLinux* input_method_auralinux,
LinuxInputMethodContextForTesting* context,
TestResult* test_result) {
context->SetSyncMode(true);
context->SetEatKey(true);
auto client = std::make_unique<TextInputClientForTesting>(text_input_type);
input_method_auralinux->SetFocusedTextInputClient(client.get());
input_method_auralinux->OnTextInputTypeChanged(client.get());
constexpr int32_t kCombiningGraveAccent = 0x0300;
{
KeyEvent dead_key(
ET_KEY_PRESSED, VKEY_OEM_4, ui::DomCode::BRACKET_LEFT,
/* flags= */ 0,
DomKey::DeadKeyFromCombiningCharacter(kCombiningGraveAccent),
base::TimeTicks());
input_method_auralinux->DispatchKeyEvent(&dead_key);
}
// Do not filter release key event.
context->SetEatKey(false);
{
KeyEvent dead_key(
ET_KEY_RELEASED, VKEY_OEM_4, ui::DomCode::BRACKET_LEFT,
/* flags= */ 0,
DomKey::DeadKeyFromCombiningCharacter(kCombiningGraveAccent),
base::TimeTicks());
input_method_auralinux->DispatchKeyEvent(&dead_key);
}
// The single quote key is muted.
test_result->ExpectAction("keydown:219");
test_result->ExpectAction("keyup:219");
test_result->Verify();
// Reset to filter press key again.
context->SetEatKey(true);
context->AddCommitAction("X");
KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
key.set_character(L'a');
input_method_auralinux->DispatchKeyEvent(&key);
// The following A key generates the accent key: á.
test_result->ExpectAction("keydown:65");
test_result->ExpectAction("keypress:88");
test_result->Verify();
}
TEST_F(InputMethodAuraLinuxTest, DeadKeyTest) {
DeadKeyTest(TEXT_INPUT_TYPE_TEXT, input_method_auralinux_, context_,
test_result_);
}
TEST_F(InputMethodAuraLinuxTest, DeadKeyTestTypeNone) {
DeadKeyTest(TEXT_INPUT_TYPE_NONE, input_method_auralinux_, context_,
test_result_);
}
// Wayland may send both a peek key event and a key event for key events not
// consumed by IME. In that case, the peek key should not be dispatched.
TEST_F(InputMethodAuraLinuxTest, MockWaylandEventsTest) {
KeyEvent peek_key(ET_KEY_PRESSED, VKEY_TAB, 0);
input_method_auralinux_->DispatchKeyEvent(&peek_key);
// No expected action for peek key events.
test_result_->Verify();
KeyEvent key(ET_KEY_PRESSED, VKEY_TAB, 0);
ui::Event::Properties properties;
properties[ui::kPropertyKeyboardImeFlag] =
std::vector<uint8_t>({ui::kPropertyKeyboardImeIgnoredFlag});
key.SetProperties(properties);
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:9");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, MultiCommitsTest) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCommitAction("a");
context_->AddCommitAction("b");
context_->AddCommitAction("c");
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key(ET_KEY_PRESSED, VKEY_A, 0);
key.set_character(L'a');
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("textinput:abc");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, MixedCompositionAndCommitTest) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCommitAction("a");
context_->AddCompositionStartAction();
context_->AddCompositionUpdateAction("b");
context_->AddCommitAction("c");
context_->AddCompositionUpdateAction("d");
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("textinput:ac");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:d");
test_result_->Verify();
context_->AddCommitAction("e");
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:e");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, CompositionEndWithoutCommitTest) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCompositionStartAction();
context_->AddCompositionUpdateAction("a");
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
context_->AddCompositionEndAction();
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionend");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, CompositionEndWithEmptyCommitTest) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCompositionStartAction();
context_->AddCompositionUpdateAction("a");
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
context_->AddCompositionEndAction();
context_->AddCommitAction("");
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, CompositionEndWithCommitTest) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCompositionStartAction();
context_->AddCompositionUpdateAction("a");
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
context_->AddCompositionEndAction();
context_->AddCommitAction("b");
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
// Verifies single char commit under composition mode will call InsertText
// intead of InsertChar.
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:b");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, CompositionUpdateWithCommitTest) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCompositionStartAction();
context_->AddCompositionUpdateAction("a");
context_->AddCommitAction("b");
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("textinput:b");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
// crbug.com/513124.
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCommitAction("c");
context_->AddCompositionUpdateAction("");
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:c");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, MixedAsyncAndSyncTest) {
context_->SetSyncMode(false);
context_->SetEatKey(true);
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
CompositionText comp;
comp.text = u"a";
input_method_auralinux_->OnPreeditChanged(comp);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
context_->SetSyncMode(true);
context_->AddCompositionEndAction();
context_->AddCommitAction("b");
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:b");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, MixedSyncAndAsyncTest) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCompositionStartAction();
context_->AddCompositionUpdateAction("a");
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'a');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
context_->SetSyncMode(false);
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
input_method_auralinux_->OnCommit(u"b");
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:b");
test_result_->Verify();
context_->SetSyncMode(true);
context_->AddCommitAction("c");
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:65");
test_result_->ExpectAction("keypress:99");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, ReleaseKeyTest) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
context_->AddCompositionUpdateAction("a");
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
KeyEvent key_new(ET_KEY_PRESSED, VKEY_A, 0);
key_new.set_character(L'A');
KeyEvent key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keydown:229");
test_result_->ExpectAction("compositionstart");
test_result_->ExpectAction("compositionupdate:a");
test_result_->Verify();
context_->SetEatKey(false);
context_->AddCommitAction("b");
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("compositionend");
test_result_->ExpectAction("textinput:b");
test_result_->ExpectAction("keydown:65");
test_result_->ExpectAction("keypress:65");
test_result_->Verify();
context_->AddCommitAction("c");
key = key_new;
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("textinput:c");
test_result_->ExpectAction("keydown:65");
test_result_->ExpectAction("keypress:65");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, ReleaseKeyTest_PeekKey) {
context_->SetSyncMode(true);
context_->SetEatKey(true);
KeyEvent key(ET_KEY_RELEASED, VKEY_A, 0);
key.set_character(L'A');
input_method_auralinux_->DispatchKeyEvent(&key);
test_result_->ExpectAction("keyup:65");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, SurroundingText_NoSelectionTest) {
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
client->surrounding_text = u"abcdef";
client->text_range = gfx::Range(0, 6);
client->selection_range = gfx::Range(3, 3);
input_method_auralinux_->OnCaretBoundsChanged(client.get());
test_result_->ExpectAction("surroundingtext:abcdef");
test_result_->ExpectAction("selectionrangestart:3");
test_result_->ExpectAction("selectionrangeend:3");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, SurroundingText_SelectionTest) {
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
client->surrounding_text = u"abcdef";
client->text_range = gfx::Range(0, 6);
client->selection_range = gfx::Range(2, 5);
input_method_auralinux_->OnCaretBoundsChanged(client.get());
test_result_->ExpectAction("surroundingtext:abcdef");
test_result_->ExpectAction("selectionrangestart:2");
test_result_->ExpectAction("selectionrangeend:5");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, SurroundingText_PartialText) {
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
client->surrounding_text = u"abcdefghij";
client->text_range = gfx::Range(5, 10);
client->selection_range = gfx::Range(7, 9);
input_method_auralinux_->OnCaretBoundsChanged(client.get());
test_result_->ExpectAction("surroundingtext:fghij");
test_result_->ExpectAction("selectionrangestart:7");
test_result_->ExpectAction("selectionrangeend:9");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, SetPreeditRegionSingleCharTest) {
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
client->surrounding_text = u"a";
client->text_range = gfx::Range(0, 1);
client->selection_range = gfx::Range(1, 1);
input_method_auralinux_->OnCaretBoundsChanged(client.get());
input_method_auralinux_->OnSetPreeditRegion(client->text_range,
std::vector<ImeTextSpan>());
test_result_->ExpectAction("surroundingtext:a");
test_result_->ExpectAction("selectionrangestart:1");
test_result_->ExpectAction("selectionrangeend:1");
input_method_auralinux_->OnCommit(u"a");
// Verifies single char commit under composition mode will call InsertText
// instead of InsertChar.
test_result_->ExpectAction("textinput:a");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, SetPreeditRegionCompositionEndTest) {
std::unique_ptr<TextInputClientForTesting> client(
new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT));
input_method_auralinux_->SetFocusedTextInputClient(client.get());
input_method_auralinux_->OnTextInputTypeChanged(client.get());
input_method_auralinux_->OnCommit(u"a");
test_result_->ExpectAction("keypress:97");
client->surrounding_text = u"a";
client->text_range = gfx::Range(0, 1);
client->selection_range = gfx::Range(1, 1);
input_method_auralinux_->OnCaretBoundsChanged(client.get());
input_method_auralinux_->OnSetPreeditRegion(client->text_range,
std::vector<ImeTextSpan>());
test_result_->ExpectAction("surroundingtext:a");
test_result_->ExpectAction("selectionrangestart:1");
test_result_->ExpectAction("selectionrangeend:1");
CompositionText comp;
comp.text = u"";
input_method_auralinux_->OnPreeditChanged(comp);
test_result_->ExpectAction("compositionend");
test_result_->Verify();
}
TEST_F(InputMethodAuraLinuxTest, OnSetVirtualKeyboardOccludedBounds) {
auto client =
std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_TEXT);
input_method_auralinux_->SetFocusedTextInputClient(client.get());
constexpr gfx::Rect kBounds(10, 20, 300, 400);
input_method_auralinux_->OnSetVirtualKeyboardOccludedBounds(kBounds);
EXPECT_EQ(client->caret_not_in_rect, kBounds);
}
TEST_F(InputMethodAuraLinuxTest, GetVirtualKeyboardController) {
EXPECT_EQ(input_method_auralinux_->GetVirtualKeyboardController(),
context_->GetVirtualKeyboardController());
}
TEST_F(InputMethodAuraLinuxTest, SetContentTypeWithUpdateFocus) {
auto client1 =
std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_TEXT);
auto client2 =
std::make_unique<TextInputClientForTesting>(TEXT_INPUT_TYPE_URL);
input_method_auralinux_->SetFocusedTextInputClient(client1.get());
EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, context_->input_type());
input_method_auralinux_->SetFocusedTextInputClient(client2.get());
EXPECT_EQ(TEXT_INPUT_TYPE_URL, context_->input_type());
input_method_auralinux_->SetFocusedTextInputClient(client1.get());
EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, context_->input_type());
}
} // namespace
} // namespace ui