| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_piece.h" |
| #include "base/test/values_test_util.h" |
| #include "chrome/browser/lacros/browser_test_util.h" |
| #include "chrome/browser/ui/lacros/window_utility.h" |
| #include "chrome/browser/ui/views/frame/browser_view.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "chromeos/crosapi/cpp/input_method_test_interface_constants.h" |
| #include "chromeos/crosapi/mojom/test_controller.mojom-test-utils.h" |
| #include "chromeos/crosapi/mojom/test_controller.mojom.h" |
| #include "chromeos/lacros/lacros_service.h" |
| #include "content/public/test/browser_test.h" |
| #include "ui/aura/window.h" |
| #include "ui/events/event.h" |
| #include "ui/events/keycodes/dom/dom_code.h" |
| |
| namespace crosapi { |
| namespace { |
| |
| using ::crosapi::mojom::InputMethodTestInterface; |
| using ::crosapi::mojom::InputMethodTestInterfaceAsyncWaiter; |
| |
| bool IsInputMethodTestInterfaceAvailable() { |
| return chromeos::LacrosService::Get() |
| ->IsAvailable<crosapi::mojom::TestController>() && |
| chromeos::LacrosService::Get()->GetInterfaceVersion( |
| crosapi::mojom::TestController::Uuid_) >= |
| static_cast<int>( |
| crosapi::mojom::TestController::MethodMinVersions:: |
| kBindInputMethodTestInterfaceMinVersion); |
| } |
| |
| int GetInputMethodTestInterfaceVersion() { |
| return chromeos::LacrosService::Get()->GetInterfaceVersion( |
| crosapi::mojom::InputMethodTestInterface::Uuid_); |
| } |
| |
| // Binds an InputMethodTestInterface to Ash-Chrome, which allows these tests to |
| // execute IME operations from Ash-Chrome. |
| // `required_versions` are the `MethodMinVersion` values of all the test methods |
| // from InputMethodTestInterface that will be used by the test. |
| // `required_test_capabilities` is a list of all test-only capabilities that Ash |
| // needs to support. Returns an unbound remote if the current version of |
| // InputMethodTestInterface does not support the required test methods or |
| // capabilities. |
| mojo::Remote<InputMethodTestInterface> BindInputMethodTestInterface( |
| std::initializer_list<InputMethodTestInterface::MethodMinVersions> |
| required_versions, |
| const std::vector<base::StringPiece>& required_test_capabilities = {}) { |
| // TODO(b/238838841): Remove the `required_versions` check once all tested |
| // versions of Ash in skew tests support `HasCapabilities`. |
| if (!IsInputMethodTestInterfaceAvailable() || |
| GetInputMethodTestInterfaceVersion() < |
| static_cast<int>(std::max(required_versions))) { |
| return {}; |
| } |
| |
| // Bind an `InputMethodTestInterface`. |
| mojo::Remote<InputMethodTestInterface> remote; |
| crosapi::mojom::TestControllerAsyncWaiter test_controller_async_waiter( |
| chromeos::LacrosService::Get() |
| ->GetRemote<crosapi::mojom::TestController>() |
| .get()); |
| test_controller_async_waiter.BindInputMethodTestInterface( |
| remote.BindNewPipeAndPassReceiver()); |
| |
| if (required_test_capabilities.empty()) { |
| return remote; |
| } |
| |
| // Check if all the required test capabilities are satisfied. |
| if (GetInputMethodTestInterfaceVersion() < |
| static_cast<int>(InputMethodTestInterface::MethodMinVersions:: |
| kHasCapabilitiesMinVersion)) { |
| return {}; |
| } |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter(remote.get()); |
| bool has_capabilities; |
| input_method_async_waiter.HasCapabilities( |
| std::vector<std::string>(required_test_capabilities.begin(), |
| required_test_capabilities.end()), |
| &has_capabilities); |
| if (!has_capabilities) { |
| return {}; |
| } |
| return remote; |
| } |
| |
| // |browser| is a browser instance that will render `html`. |
| bool RenderHtmlInLacros(Browser* browser, const std::string& html) { |
| const GURL url(base::StrCat({"data:text/html,", html})); |
| if (!ui_test_utils::NavigateToURL(browser, url)) { |
| return false; |
| } |
| |
| std::string window_id = lacros_window_utility::GetRootWindowUniqueId( |
| BrowserView::GetBrowserViewForBrowser(browser) |
| ->frame() |
| ->GetNativeWindow() |
| ->GetRootWindow()); |
| EXPECT_TRUE(browser_test_util::WaitForWindowCreation(window_id)); |
| return true; |
| } |
| |
| // Renders a focused input field in `browser`. |
| // Returns the ID of the input field. |
| std::string RenderAutofocusedInputFieldInLacros(Browser* browser) { |
| if (!RenderHtmlInLacros( |
| browser, R"(<input type="text" id="test-input" autofocus/>)")) { |
| return ""; |
| } |
| return "test-input"; |
| } |
| |
| content::WebContents* GetActiveWebContents(Browser* browser) { |
| return browser->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| struct Modifiers { |
| bool alt; |
| bool control; |
| bool meta; |
| bool shift; |
| |
| ui::EventFlags ToFlags() const { |
| ui::EventFlags flags = ui::EF_NONE; |
| if (control) { |
| flags |= ui::EF_CONTROL_DOWN; |
| } |
| if (shift) { |
| flags |= ui::EF_SHIFT_DOWN; |
| } |
| if (meta) { |
| flags |= ui::EF_COMMAND_DOWN; |
| } |
| if (alt) { |
| flags |= ui::EF_ALT_DOWN; |
| } |
| return flags; |
| } |
| }; |
| |
| auto IsKeyboardEvent(const base::StringPiece type, |
| const base::StringPiece key, |
| const base::StringPiece code, |
| int key_code, |
| Modifiers modifiers = {}) { |
| return base::test::IsJson(content::JsReplace( |
| R"({ |
| "type": $1, |
| "key": $2, |
| "code": $3, |
| "keyCode": $4, |
| "altKey": $5, |
| "ctrlKey": $6, |
| "metaKey": $7, |
| "shiftKey": $8, |
| })", |
| type, key, code, key_code, modifiers.alt, modifiers.control, |
| modifiers.meta, modifiers.shift)); |
| } |
| |
| auto IsKeyDownEvent(const base::StringPiece key, |
| const base::StringPiece code, |
| int key_code, |
| Modifiers modifiers = {}) { |
| return IsKeyboardEvent("keydown", key, code, key_code, modifiers); |
| } |
| |
| auto IsKeyUpEvent(const base::StringPiece key, |
| const base::StringPiece code, |
| int key_code, |
| Modifiers modifiers = {}) { |
| return IsKeyboardEvent("keyup", key, code, key_code, modifiers); |
| } |
| |
| auto IsKeyPressEvent(const base::StringPiece key, |
| const base::StringPiece code, |
| int key_code, |
| Modifiers modifiers = {}) { |
| return IsKeyboardEvent("keypress", key, code, key_code, modifiers); |
| } |
| |
| auto IsCompositionEvent(const base::StringPiece type, |
| const base::StringPiece data) { |
| return base::test::IsJson(content::JsReplace( |
| R"({ |
| "type": $1, |
| "data": $2, |
| })", |
| type, data)); |
| } |
| |
| auto IsCompositionStartEvent() { |
| return IsCompositionEvent("compositionstart", ""); |
| } |
| |
| auto IsCompositionUpdateEvent(const base::StringPiece data) { |
| return IsCompositionEvent("compositionupdate", data); |
| } |
| |
| auto IsCompositionEndEvent() { |
| return IsCompositionEvent("compositionend", ""); |
| } |
| |
| enum class CompositionState { kComposing, kNotComposing }; |
| |
| auto IsInputEvent(const base::StringPiece type, |
| const base::StringPiece input_type, |
| const absl::optional<base::StringPiece> data, |
| CompositionState composition_state) { |
| const bool is_composing = composition_state == CompositionState::kComposing; |
| |
| if (!data.has_value()) { |
| return base::test::IsJson(content::JsReplace( |
| R"({ |
| "type": $1, |
| "inputType": $2, |
| "data": null, |
| "isComposing": $3, |
| })", |
| type, input_type, is_composing)); |
| } |
| |
| return base::test::IsJson(content::JsReplace( |
| R"({ |
| "type": $1, |
| "inputType": $2, |
| "data": $3, |
| "isComposing": $4, |
| })", |
| type, input_type, *data, is_composing)); |
| } |
| |
| auto IsBeforeInputEvent(const base::StringPiece input_type, |
| const absl::optional<base::StringPiece> data, |
| CompositionState composition_state) { |
| return IsInputEvent("beforeinput", input_type, data, composition_state); |
| } |
| |
| auto IsInputEvent(const base::StringPiece input_type, |
| const absl::optional<base::StringPiece> data, |
| CompositionState composition_state) { |
| return IsInputEvent("input", input_type, data, composition_state); |
| } |
| |
| class InputEventListener { |
| public: |
| explicit InputEventListener(content::WebContents* web_contents) |
| : messages_(web_contents) {} |
| |
| base::Value WaitForMessage() { |
| std::string event_message; |
| if (!messages_.WaitForMessage(&event_message)) { |
| return {}; |
| } |
| |
| return base::test::ParseJson(event_message); |
| } |
| |
| bool HasMessages() { return messages_.HasMessages(); } |
| |
| private: |
| content::DOMMessageQueue messages_; |
| }; |
| |
| // Listens for web input events from `element_id`. |
| InputEventListener ListenForInputEvents(content::WebContents* web_content, |
| base::StringPiece element_id) { |
| const std::string script = content::JsReplace( |
| R"(elem = document.getElementById($1); |
| function extractEventData(e) { |
| if (e instanceof CompositionEvent) { |
| return {type: e.type, data: e.data}; |
| } |
| if (e instanceof InputEvent) { |
| return { |
| type: e.type, |
| isComposing: e.isComposing, |
| inputType: e.inputType, |
| data: e.data |
| }; |
| } |
| if (e instanceof KeyboardEvent) { |
| return { |
| type: e.type, |
| key: e.key, |
| code: e.code, |
| keyCode: e.keyCode, |
| altKey: e.altKey, |
| ctrlKey: e.ctrlKey, |
| metaKey: e.metaKey, |
| shiftKey: e.shiftKey |
| }; |
| } |
| return {}; |
| } |
| function recordEvent(e) { |
| window.domAutomationController.send(extractEventData(e)); |
| } |
| elem.addEventListener('keydown', recordEvent); |
| elem.addEventListener('keypress', recordEvent); |
| elem.addEventListener('keyup', recordEvent); |
| elem.addEventListener('beforeinput', recordEvent); |
| elem.addEventListener('input', recordEvent); |
| elem.addEventListener('compositionstart', recordEvent); |
| elem.addEventListener('compositionupdate', recordEvent); |
| elem.addEventListener('compositionend', recordEvent);)", |
| element_id); |
| std::ignore = ExecJs(web_content, script); |
| return InputEventListener(web_content); |
| } |
| |
| // Waits for the contents of an input field with ID `element_id` to become |
| // `expected_text`, with the selection as `expected_selection`. |
| // For checking the text, this uses the DOM property `value`. |
| // For checking the selection, this uses the DOM properties |
| // `selectionStart` and `selectionEnd`. |
| // Returns true if the conditions are met within 3 seconds. |
| // Returns false otherwise. |
| bool WaitUntilInputFieldHasText(content::WebContents* web_content, |
| base::StringPiece element_id, |
| base::StringPiece expected_text, |
| const gfx::Range& expected_selection) { |
| const std::string script = content::JsReplace( |
| R"(new Promise((resolve) => { |
| let retriesLeft = 10; |
| elem = document.getElementById($1); |
| function checkValue() { |
| if (elem.value == $2 && |
| elem.selectionStart == $3 && |
| elem.selectionEnd == $4) { |
| return resolve(true); |
| } |
| if (retriesLeft == 0) return resolve(false); |
| retriesLeft--; |
| setTimeout(checkValue, 300); |
| } |
| checkValue(); |
| }))", |
| element_id, expected_text, static_cast<int>(expected_selection.start()), |
| static_cast<int>(expected_selection.end())); |
| return EvalJs(web_content, script).ExtractBool(); |
| } |
| |
| // Sets the contents of the input field with ID `element_id` to be `text`, with |
| // the text selection at `selection`. |
| bool SetInputFieldText(content::WebContents* web_content, |
| base::StringPiece element_id, |
| base::StringPiece text, |
| const gfx::Range& selection) { |
| const std::string script = content::JsReplace( |
| R"(elem = document.getElementById($1); |
| elem.value = $2; |
| elem.selectionStart = $3; |
| elem.selectionEnd = $4;)", |
| element_id, text, static_cast<int>(selection.start()), |
| static_cast<int>(selection.end())); |
| return ExecJs(web_content, script); |
| } |
| |
| mojom::KeyEventPtr CreateKeyPressEvent(ui::DomKey dom_key, |
| ui::DomCode dom_code, |
| ui::EventFlags flags = ui::EF_NONE) { |
| return mojom::KeyEvent::New( |
| mojom::KeyEventType::kKeyPress, static_cast<int>(dom_key), |
| static_cast<int>(dom_code), |
| static_cast<int>(ui::KeyboardCode::VKEY_UNKNOWN), flags); |
| } |
| |
| mojom::KeyEventPtr CreateKeyReleaseEvent(ui::DomKey dom_key, |
| ui::DomCode dom_code, |
| ui::EventFlags flags = ui::EF_NONE) { |
| return mojom::KeyEvent::New( |
| mojom::KeyEventType::kKeyRelease, static_cast<int>(dom_key), |
| static_cast<int>(dom_code), |
| static_cast<int>(ui::KeyboardCode::VKEY_UNKNOWN), flags); |
| } |
| |
| class KeySequenceBuilder { |
| public: |
| KeySequenceBuilder Press(ui::DomKey dom_key, ui::DomCode dom_code) && { |
| UpdateModifiersFromDomKey(dom_key, true); |
| key_events_.push_back( |
| CreateKeyPressEvent(dom_key, dom_code, active_modifiers_.ToFlags())); |
| return std::move(*this); |
| } |
| |
| KeySequenceBuilder Release(ui::DomKey dom_key, ui::DomCode dom_code) && { |
| UpdateModifiersFromDomKey(dom_key, false); |
| key_events_.push_back( |
| CreateKeyReleaseEvent(dom_key, dom_code, active_modifiers_.ToFlags())); |
| return std::move(*this); |
| } |
| |
| KeySequenceBuilder PressAndRelease(ui::DomKey dom_key, |
| ui::DomCode dom_code) && { |
| return std::move(*this).Press(dom_key, dom_code).Release(dom_key, dom_code); |
| } |
| |
| std::vector<mojom::KeyEventPtr> Build() && { return std::move(key_events_); } |
| |
| private: |
| void UpdateModifiersFromDomKey(ui::DomKey dom_key, bool pressed) { |
| if (dom_key == ui::DomKey::ALT) { |
| active_modifiers_.alt = pressed; |
| } |
| if (dom_key == ui::DomKey::CONTROL) { |
| active_modifiers_.control = pressed; |
| } |
| if (dom_key == ui::DomKey::META) { |
| active_modifiers_.meta = pressed; |
| } |
| if (dom_key == ui::DomKey::SHIFT) { |
| active_modifiers_.shift = pressed; |
| } |
| } |
| |
| std::vector<mojom::KeyEventPtr> key_events_; |
| Modifiers active_modifiers_; |
| }; |
| |
| // Sends the key events to the input method. The input method will not handle |
| // the given key events. |
| void SendKeyEventsSync( |
| InputMethodTestInterfaceAsyncWaiter& input_method_async_waiter, |
| std::vector<mojom::KeyEventPtr> key_events) { |
| uint64_t key_event_id; |
| for (mojom::KeyEventPtr& key_event : key_events) { |
| input_method_async_waiter.SendKeyEvent(std::move(key_event), &key_event_id); |
| input_method_async_waiter.KeyEventHandled(key_event_id, false); |
| } |
| } |
| |
| // Convenient version of `SendKeyEventsSync` for a single key event. |
| void SendKeyEventSync( |
| InputMethodTestInterfaceAsyncWaiter& input_method_async_waiter, |
| mojom::KeyEventPtr key_event) { |
| std::vector<mojom::KeyEventPtr> key_events; |
| key_events.push_back(std::move(key_event)); |
| SendKeyEventsSync(input_method_async_waiter, std::move(key_events)); |
| } |
| |
| // Sends the key event to the input method. The input method will handle the |
| // given key event by running `callback`. |
| void SendKeyEventAsync( |
| InputMethodTestInterfaceAsyncWaiter& input_method_async_waiter, |
| mojom::KeyEventPtr key_event, |
| base::OnceCallback<bool(InputMethodTestInterfaceAsyncWaiter&)> callback) { |
| uint64_t key_event_id; |
| input_method_async_waiter.SendKeyEvent(std::move(key_event), &key_event_id); |
| const bool handled = std::move(callback).Run(input_method_async_waiter); |
| input_method_async_waiter.KeyEventHandled(key_event_id, handled); |
| } |
| |
| using InputMethodLacrosBrowserTest = InProcessBrowserTest; |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| FocusingInputFieldSendsFocus) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions:: |
| kWaitForFocusMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| |
| input_method_async_waiter.WaitForFocus(); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| CommitTextInsertsTextInInputField) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions::kCommitTextMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| input_method_async_waiter.CommitText("hello"); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "hello", gfx::Range(5))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| CommitTextUpdatesSurroundingText) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions::kCommitTextMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kWaitForNextSurroundingTextChangeMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| input_method_async_waiter.CommitText("abc"); |
| std::string surrounding_text; |
| gfx::Range selection_range; |
| input_method_async_waiter.WaitForNextSurroundingTextChange(&surrounding_text, |
| &selection_range); |
| |
| EXPECT_EQ(surrounding_text, "abc"); |
| EXPECT_EQ(selection_range, gfx::Range(3)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| CommitTextReplacesCompositionText) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kSetCompositionMinVersion, |
| InputMethodTestInterface::MethodMinVersions::kCommitTextMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| ASSERT_TRUE(SetInputFieldText(GetActiveWebContents(browser()), id, "hello ", |
| gfx::Range(6))); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| input_method_async_waiter.SetComposition("world", 5); |
| ASSERT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "hello world", gfx::Range(11))); |
| |
| input_method_async_waiter.CommitText("abc"); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "hello abc", gfx::Range(9))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| CommitEmptyTextDeletesCompositionText) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kSetCompositionMinVersion, |
| InputMethodTestInterface::MethodMinVersions::kCommitTextMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| input_method_async_waiter.SetComposition("hello", 5); |
| ASSERT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "hello", gfx::Range(5))); |
| |
| input_method_async_waiter.CommitText(""); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "", gfx::Range(0))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| CommitTextReplacesSelection) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions::kCommitTextMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| ASSERT_TRUE(SetInputFieldText(GetActiveWebContents(browser()), id, "hello", |
| gfx::Range(1, 3))); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| input_method_async_waiter.CommitText("abc"); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "habclo", gfx::Range(4))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| CommitTextTriggersWebEvents) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kKeyEventHandledMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| InputEventListener event_listener = |
| ListenForInputEvents(GetActiveWebContents(browser()), id); |
| |
| input_method_async_waiter.CommitText("hello"); |
| input_method_async_waiter.CommitText(" world"); |
| |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsBeforeInputEvent("insertText", "hello", |
| CompositionState::kNotComposing)); |
| EXPECT_THAT( |
| event_listener.WaitForMessage(), |
| IsInputEvent("insertText", "hello", CompositionState::kNotComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsBeforeInputEvent("insertText", " world", |
| CompositionState::kNotComposing)); |
| EXPECT_THAT( |
| event_listener.WaitForMessage(), |
| IsInputEvent("insertText", " world", CompositionState::kNotComposing)); |
| EXPECT_FALSE(event_listener.HasMessages()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| CommitTextWhileHandlingKeyEventTriggersWebEvents) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kSetCompositionMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kKeyEventHandledMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| InputEventListener event_listener = |
| ListenForInputEvents(GetActiveWebContents(browser()), id); |
| |
| SendKeyEventAsync( |
| input_method_async_waiter, |
| CreateKeyPressEvent(ui::DomKey::FromCharacter('.'), ui::DomCode::PERIOD), |
| base::BindOnce( |
| [](InputMethodTestInterfaceAsyncWaiter& input_method_async_waiter) { |
| input_method_async_waiter.CommitText("。"); |
| return true; |
| })); |
| SendKeyEventSync(input_method_async_waiter, |
| CreateKeyReleaseEvent(ui::DomKey::FromCharacter('.'), |
| ui::DomCode::PERIOD)); |
| |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyDownEvent(".", "Period", 190)); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyPressEvent("。", "Period", 12290)); |
| EXPECT_THAT( |
| event_listener.WaitForMessage(), |
| IsBeforeInputEvent("insertText", "。", CompositionState::kNotComposing)); |
| EXPECT_THAT( |
| event_listener.WaitForMessage(), |
| IsInputEvent("insertText", "。", CompositionState::kNotComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyUpEvent(".", "Period", 190)); |
| EXPECT_FALSE(event_listener.HasMessages()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "。", gfx::Range(1))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SetCompositionInsertsCompositionInEmptyInputField) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kSetCompositionMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| input_method_async_waiter.SetComposition("hello", 3); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "hello", gfx::Range(3))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SetCompositionInsertsCompositionAtStartOfInputField) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kSetCompositionMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| ASSERT_TRUE(SetInputFieldText(GetActiveWebContents(browser()), id, " world", |
| gfx::Range(0))); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| input_method_async_waiter.SetComposition("hello", 5); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "hello world", gfx::Range(5))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SetCompositionInsertsCompositionAtEndOfInputField) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kSetCompositionMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| ASSERT_TRUE(SetInputFieldText(GetActiveWebContents(browser()), id, "hello ", |
| gfx::Range(6))); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| input_method_async_waiter.SetComposition("world", 5); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "hello world", gfx::Range(11))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SetCompositionInsertsCompositionInMiddleOfInputField) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kSetCompositionMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| ASSERT_TRUE(SetInputFieldText(GetActiveWebContents(browser()), id, "held", |
| gfx::Range(2))); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| input_method_async_waiter.SetComposition("llo wor", 3); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "hello world", gfx::Range(5))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SetCompositionReplacesCompositionInInputField) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kSetCompositionMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| input_method_async_waiter.SetComposition("hello", 4); |
| |
| input_method_async_waiter.SetComposition("abc", 2); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "abc", gfx::Range(2))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SetCompositionTriggersWebEvents) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kKeyEventHandledMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| InputEventListener event_listener = |
| ListenForInputEvents(GetActiveWebContents(browser()), id); |
| |
| input_method_async_waiter.SetComposition("hello", 4); |
| input_method_async_waiter.SetComposition("", 0); |
| |
| EXPECT_THAT(event_listener.WaitForMessage(), IsCompositionStartEvent()); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsBeforeInputEvent("insertCompositionText", "hello", |
| CompositionState::kComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsCompositionUpdateEvent("hello")); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsInputEvent("insertCompositionText", "hello", |
| CompositionState::kComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsBeforeInputEvent("insertCompositionText", "", |
| CompositionState::kComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), IsCompositionUpdateEvent("")); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsInputEvent("insertCompositionText", absl::nullopt, |
| CompositionState::kComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), IsCompositionEndEvent()); |
| EXPECT_FALSE(event_listener.HasMessages()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SetCompositionUpdatesSurroundingText) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions::kCommitTextMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kWaitForNextSurroundingTextChangeMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| input_method_async_waiter.SetComposition("abc", 3); |
| std::string surrounding_text; |
| gfx::Range selection_range; |
| input_method_async_waiter.WaitForNextSurroundingTextChange(&surrounding_text, |
| &selection_range); |
| |
| EXPECT_EQ(surrounding_text, "abc"); |
| EXPECT_EQ(selection_range, gfx::Range(3)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SendKeyEventNotHandledTypesInEmptyTextField) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kKeyEventHandledMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .PressAndRelease(ui::DomKey::FromCharacter('a'), ui::DomCode::US_A) |
| .PressAndRelease(ui::DomKey::FromCharacter('b'), ui::DomCode::US_B) |
| .PressAndRelease(ui::DomKey::FromCharacter('c'), ui::DomCode::US_C) |
| .Build()); |
| |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "abc", gfx::Range(3))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SendBackspaceDeletesNonEmptyTextField) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kKeyEventHandledMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| ASSERT_TRUE(SetInputFieldText(GetActiveWebContents(browser()), id, "hello", |
| gfx::Range(3))); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .PressAndRelease(ui::DomKey::BACKSPACE, ui::DomCode::BACKSPACE) |
| .Build()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "helo", gfx::Range(2))); |
| |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .PressAndRelease(ui::DomKey::BACKSPACE, ui::DomCode::BACKSPACE) |
| .Build()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "hlo", gfx::Range(1))); |
| |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .PressAndRelease(ui::DomKey::BACKSPACE, ui::DomCode::BACKSPACE) |
| .Build()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "lo", gfx::Range(0))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SendKeyEventShortcutsModifiesSelection) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kKeyEventHandledMinVersion}, |
| {kInputMethodTestCapabilitySendKeyModifiers}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| ASSERT_TRUE(SetInputFieldText(GetActiveWebContents(browser()), id, |
| "abc abc abc", gfx::Range(0))); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| |
| // Press Ctrl-A with: Control down, A down, A up, Control up |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .Press(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .PressAndRelease(ui::DomKey::FromCharacter('a'), ui::DomCode::US_A) |
| .Release(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Build()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "abc abc abc", gfx::Range(0, 11))); |
| |
| // Press Ctrl+Left. |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .Press(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .PressAndRelease(ui::DomKey::ARROW_LEFT, ui::DomCode::ARROW_LEFT) |
| .Release(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Build()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "abc abc abc", gfx::Range(8))); |
| |
| // Press Ctrl+Right with a different order. |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .Press(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Press(ui::DomKey::ARROW_RIGHT, ui::DomCode::ARROW_RIGHT) |
| .Release(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Release(ui::DomKey::ARROW_RIGHT, ui::DomCode::ARROW_RIGHT) |
| .Build()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "abc abc abc", gfx::Range(11))); |
| |
| // Press Shift+Left. |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .Press(ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT) |
| .PressAndRelease(ui::DomKey::ARROW_LEFT, ui::DomCode::ARROW_LEFT) |
| .Release(ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT) |
| .Build()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "abc abc abc", gfx::Range(10, 11))); |
| |
| // Press Ctrl+Shift+Left. |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .Press(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Press(ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT) |
| .PressAndRelease(ui::DomKey::ARROW_LEFT, ui::DomCode::ARROW_LEFT) |
| .Release(ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT) |
| .Release(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Build()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "abc abc abc", gfx::Range(8, 11))); |
| |
| // Press Ctrl+Shift+Right with a different order. |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder() |
| .Press(ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT) |
| .Press(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Press(ui::DomKey::ARROW_RIGHT, ui::DomCode::ARROW_RIGHT) |
| .Release(ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT) |
| .Release(ui::DomKey::ARROW_RIGHT, ui::DomCode::ARROW_RIGHT) |
| .Release(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Build()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "abc abc abc", gfx::Range(11, 11))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SetCompositionWhileHandlingKeyEventTriggersWebEvents) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kSetCompositionMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kKeyEventHandledMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| InputEventListener event_listener = |
| ListenForInputEvents(GetActiveWebContents(browser()), id); |
| |
| SendKeyEventAsync( |
| input_method_async_waiter, |
| CreateKeyPressEvent(ui::DomKey::FromCharacter('g'), ui::DomCode::US_G), |
| base::BindOnce( |
| [](InputMethodTestInterfaceAsyncWaiter& input_method_async_waiter) { |
| input_method_async_waiter.SetComposition("ㅎ", 1); |
| return true; |
| })); |
| SendKeyEventSync( |
| input_method_async_waiter, |
| CreateKeyReleaseEvent(ui::DomKey::FromCharacter('g'), ui::DomCode::US_G)); |
| |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyDownEvent("Process", "KeyG", 229)); |
| EXPECT_THAT(event_listener.WaitForMessage(), IsCompositionStartEvent()); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsBeforeInputEvent("insertCompositionText", "ㅎ", |
| CompositionState::kComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), IsCompositionUpdateEvent("ㅎ")); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsInputEvent("insertCompositionText", "ㅎ", |
| CompositionState::kComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), IsKeyUpEvent("g", "KeyG", 71)); |
| EXPECT_FALSE(event_listener.HasMessages()); |
| EXPECT_TRUE(WaitUntilInputFieldHasText(GetActiveWebContents(browser()), id, |
| "ㅎ", gfx::Range(1))); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SendKeyEventTriggersWebEvents) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kKeyEventHandledMinVersion}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| InputEventListener event_listener = |
| ListenForInputEvents(GetActiveWebContents(browser()), id); |
| |
| SendKeyEventsSync( |
| input_method_async_waiter, |
| KeySequenceBuilder().PressAndRelease('a', ui::DomCode::US_A).Build()); |
| |
| EXPECT_THAT(event_listener.WaitForMessage(), IsKeyDownEvent("a", "KeyA", 65)); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyPressEvent("a", "KeyA", 97)); |
| EXPECT_THAT( |
| event_listener.WaitForMessage(), |
| IsBeforeInputEvent("insertText", "a", CompositionState::kNotComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsInputEvent("insertText", "a", CompositionState::kNotComposing)); |
| EXPECT_THAT(event_listener.WaitForMessage(), IsKeyUpEvent("a", "KeyA", 65)); |
| EXPECT_FALSE(event_listener.HasMessages()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(InputMethodLacrosBrowserTest, |
| SendKeyEventModifiersTriggersWebEvents) { |
| mojo::Remote<InputMethodTestInterface> input_method = |
| BindInputMethodTestInterface( |
| {InputMethodTestInterface::MethodMinVersions::kWaitForFocusMinVersion, |
| InputMethodTestInterface::MethodMinVersions:: |
| kKeyEventHandledMinVersion}, |
| {kInputMethodTestCapabilitySendKeyModifiers}); |
| if (!input_method.is_bound()) { |
| GTEST_SKIP() << "Unsupported ash version"; |
| } |
| const std::string id = RenderAutofocusedInputFieldInLacros(browser()); |
| InputMethodTestInterfaceAsyncWaiter input_method_async_waiter( |
| input_method.get()); |
| input_method_async_waiter.WaitForFocus(); |
| InputEventListener event_listener = |
| ListenForInputEvents(GetActiveWebContents(browser()), id); |
| |
| // Press Control+Alt+Shift+Meta and release them in a different order. |
| SendKeyEventsSync(input_method_async_waiter, |
| KeySequenceBuilder() |
| .Press(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Press(ui::DomKey::ALT, ui::DomCode::ALT_RIGHT) |
| .Press(ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT) |
| .Press(ui::DomKey::META, ui::DomCode::META_LEFT) |
| .Release(ui::DomKey::SHIFT, ui::DomCode::SHIFT_LEFT) |
| .Release(ui::DomKey::ALT, ui::DomCode::ALT_RIGHT) |
| .Release(ui::DomKey::CONTROL, ui::DomCode::CONTROL_LEFT) |
| .Release(ui::DomKey::META, ui::DomCode::META_LEFT) |
| .Build()); |
| |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyDownEvent("Control", "ControlLeft", 17, {.control = true})); |
| EXPECT_THAT( |
| event_listener.WaitForMessage(), |
| IsKeyDownEvent("Alt", "AltRight", 18, {.alt = true, .control = true})); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyDownEvent("Shift", "ShiftLeft", 16, |
| {.alt = true, .control = true, .shift = true})); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyDownEvent( |
| "Meta", "MetaLeft", 91, |
| {.alt = true, .control = true, .meta = true, .shift = true})); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyUpEvent("Shift", "ShiftLeft", 16, |
| {.alt = true, .control = true, .meta = true})); |
| EXPECT_THAT( |
| event_listener.WaitForMessage(), |
| IsKeyUpEvent("Alt", "AltRight", 18, {.control = true, .meta = true})); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyUpEvent("Control", "ControlLeft", 17, {.meta = true})); |
| EXPECT_THAT(event_listener.WaitForMessage(), |
| IsKeyUpEvent("Meta", "MetaLeft", 91)); |
| EXPECT_FALSE(event_listener.HasMessages()); |
| } |
| |
| } // namespace |
| } // namespace crosapi |