123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/ime/ime_controller_impl.h"
- #include <vector>
- #include "ash/ime/mode_indicator_observer.h"
- #include "ash/ime/test_ime_controller_client.h"
- #include "ash/public/cpp/ime_info.h"
- #include "ash/shell.h"
- #include "ash/system/ime/ime_observer.h"
- #include "ash/system/tray/system_tray_notifier.h"
- #include "ash/test/ash_test_base.h"
- #include "base/strings/utf_string_conversions.h"
- #include "ui/base/accelerators/accelerator.h"
- #include "ui/base/ime/ash/extension_ime_util.h"
- #include "ui/display/manager/display_manager.h"
- #include "ui/events/keycodes/keyboard_codes.h"
- #include "ui/views/widget/widget.h"
- namespace ash {
- namespace {
- // 43 is the designed size of the inner contents.
- // This value corresponds with kMinSize defined in
- // mode_indicator_delegate_view.cc.
- const int kInnerSize = 43;
- // Refreshes the IME list with fake IMEs and fake menu items.
- void RefreshImesWithMenuItems(const std::string& current_ime_id,
- const std::vector<std::string>& ime_ids,
- const std::vector<std::string>& menu_item_keys) {
- std::vector<ImeInfo> available_imes;
- for (const std::string& ime_id : ime_ids) {
- ImeInfo ime;
- ime.id = ime_id;
- available_imes.push_back(std::move(ime));
- }
- std::vector<ImeMenuItem> menu_items;
- for (const std::string& menu_item_key : menu_item_keys) {
- ImeMenuItem item;
- item.key = menu_item_key;
- menu_items.push_back(std::move(item));
- }
- Shell::Get()->ime_controller()->RefreshIme(
- current_ime_id, std::move(available_imes), std::move(menu_items));
- }
- // Refreshes the IME list without adding any menu items.
- void RefreshImes(const std::string& current_ime_id,
- const std::vector<std::string>& ime_ids) {
- const std::vector<std::string> empty_menu_items;
- RefreshImesWithMenuItems(current_ime_id, ime_ids, empty_menu_items);
- }
- class TestImeObserver : public IMEObserver {
- public:
- TestImeObserver() = default;
- ~TestImeObserver() override = default;
- // IMEObserver:
- void OnIMERefresh() override { ++refresh_count_; }
- void OnIMEMenuActivationChanged(bool is_active) override {
- ime_menu_active_ = is_active;
- }
- int refresh_count_ = 0;
- bool ime_menu_active_ = false;
- };
- class TestImeControllerObserver : public ImeControllerImpl::Observer {
- public:
- TestImeControllerObserver() = default;
- TestImeControllerObserver(const TestImeControllerObserver&) = delete;
- TestImeControllerObserver& operator=(const TestImeControllerObserver&) =
- delete;
- // IMEController::Observer:
- void OnCapsLockChanged(bool enabled) override { ++caps_lock_count_; }
- void OnKeyboardLayoutNameChanged(const std::string& layout_name) override {
- last_keyboard_layout_name_ = layout_name;
- }
- const std::string& last_keyboard_layout_name() const {
- return last_keyboard_layout_name_;
- }
- private:
- int caps_lock_count_ = 0;
- std::string last_keyboard_layout_name_;
- };
- using ImeControllerImplTest = AshTestBase;
- TEST_F(ImeControllerImplTest, RefreshIme) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- TestImeObserver observer;
- Shell::Get()->system_tray_notifier()->AddIMEObserver(&observer);
- RefreshImesWithMenuItems("ime1", {"ime1", "ime2"}, {"menu1"});
- // Cached data was updated.
- EXPECT_EQ("ime1", controller->current_ime().id);
- ASSERT_EQ(2u, controller->GetVisibleImes().size());
- EXPECT_EQ("ime1", controller->GetVisibleImes()[0].id);
- EXPECT_EQ("ime2", controller->GetVisibleImes()[1].id);
- ASSERT_EQ(1u, controller->current_ime_menu_items().size());
- EXPECT_EQ("menu1", controller->current_ime_menu_items()[0].key);
- // Observer was notified.
- EXPECT_EQ(1, observer.refresh_count_);
- }
- TEST_F(ImeControllerImplTest, NoCurrentIme) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- // Set up a single IME.
- RefreshImes("ime1", {"ime1"});
- EXPECT_EQ("ime1", controller->current_ime().id);
- EXPECT_TRUE(controller->IsCurrentImeVisible());
- // When there is no current IME the cached current IME is empty.
- const std::string empty_ime_id;
- RefreshImes(empty_ime_id, {"ime1"});
- EXPECT_TRUE(controller->current_ime().id.empty());
- }
- TEST_F(ImeControllerImplTest, CurrentImeNotVisible) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- // Add only Dictation.
- std::string dictation_id =
- "_ext_ime_egfdjlfmgnehecnclamagfafdccgfndpdictation";
- RefreshImes(dictation_id, {dictation_id});
- EXPECT_EQ(dictation_id, controller->current_ime().id);
- EXPECT_FALSE(controller->IsCurrentImeVisible());
- EXPECT_EQ(0u, controller->GetVisibleImes().size());
- // Add something else too, but Dictation is active.
- RefreshImes(dictation_id, {dictation_id, "ime1"});
- EXPECT_EQ(dictation_id, controller->current_ime().id);
- EXPECT_FALSE(controller->IsCurrentImeVisible());
- EXPECT_EQ(1u, controller->GetVisibleImes().size());
- // Inactivate the other IME, leave Dictation in the list.
- RefreshImes("ime1", {dictation_id, "ime1"});
- EXPECT_EQ("ime1", controller->current_ime().id);
- EXPECT_TRUE(controller->IsCurrentImeVisible());
- EXPECT_EQ(1u, controller->GetVisibleImes().size());
- }
- TEST_F(ImeControllerImplTest, SetImesManagedByPolicy) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- TestImeObserver observer;
- Shell::Get()->system_tray_notifier()->AddIMEObserver(&observer);
- // Defaults to false.
- EXPECT_FALSE(controller->managed_by_policy());
- // Setting the value notifies observers.
- controller->SetImesManagedByPolicy(true);
- EXPECT_TRUE(controller->managed_by_policy());
- EXPECT_EQ(1, observer.refresh_count_);
- }
- TEST_F(ImeControllerImplTest, ShowImeMenuOnShelf) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- TestImeObserver observer;
- Shell::Get()->system_tray_notifier()->AddIMEObserver(&observer);
- controller->ShowImeMenuOnShelf(true);
- EXPECT_TRUE(observer.ime_menu_active_);
- }
- TEST_F(ImeControllerImplTest, CanSwitchIme) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- // Can't switch IMEs when none are available.
- ASSERT_EQ(0u, controller->GetVisibleImes().size());
- EXPECT_FALSE(controller->CanSwitchIme());
- // Can't switch with only 1 IME.
- RefreshImes("ime1", {"ime1"});
- EXPECT_FALSE(controller->CanSwitchIme());
- // Can switch with more than 1 IME.
- RefreshImes("ime1", {"ime1", "ime2"});
- EXPECT_TRUE(controller->CanSwitchIme());
- }
- TEST_F(ImeControllerImplTest, SwitchIme) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- TestImeControllerClient client;
- // Can't switch IME before the client is set.
- controller->SwitchToNextIme();
- EXPECT_EQ(0, client.next_ime_count_);
- controller->SwitchToLastUsedIme();
- EXPECT_EQ(0, client.last_used_ime_count_);
- controller->SwitchImeById("ime1", true /* show_message */);
- EXPECT_EQ(0, client.switch_ime_count_);
- // After setting the client the requests are forwarded.
- controller->SetClient(&client);
- controller->SwitchToNextIme();
- EXPECT_EQ(1, client.next_ime_count_);
- controller->SwitchToLastUsedIme();
- EXPECT_EQ(1, client.last_used_ime_count_);
- controller->SwitchImeById("ime1", true /* show_message */);
- EXPECT_EQ(1, client.switch_ime_count_);
- EXPECT_EQ("ime1", client.last_switch_ime_id_);
- EXPECT_TRUE(client.last_show_message_);
- controller->SwitchImeById("ime2", false /* show_message */);
- EXPECT_EQ(2, client.switch_ime_count_);
- EXPECT_EQ("ime2", client.last_switch_ime_id_);
- EXPECT_FALSE(client.last_show_message_);
- }
- TEST_F(ImeControllerImplTest, SwitchImeWithAccelerator) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- TestImeControllerClient client;
- controller->SetClient(&client);
- const ui::Accelerator convert(ui::VKEY_CONVERT, ui::EF_NONE);
- const ui::Accelerator non_convert(ui::VKEY_NONCONVERT, ui::EF_NONE);
- const ui::Accelerator wide_half_1(ui::VKEY_DBE_SBCSCHAR, ui::EF_NONE);
- const ui::Accelerator wide_half_2(ui::VKEY_DBE_DBCSCHAR, ui::EF_NONE);
- // When there are no IMEs available switching by accelerator does not work.
- ASSERT_EQ(0u, controller->GetVisibleImes().size());
- EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(convert));
- EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(non_convert));
- EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(wide_half_1));
- EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(wide_half_2));
- // With only test IMEs (and no Japanese IMEs) the special keys do not work.
- RefreshImes("ime1", {"ime1", "ime2"});
- EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(convert));
- EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(non_convert));
- EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(wide_half_1));
- EXPECT_FALSE(controller->CanSwitchImeWithAccelerator(wide_half_2));
- // Install both a test IME and Japanese IMEs.
- using extension_ime_util::GetInputMethodIDByEngineID;
- const std::string nacl_mozc_jp = GetInputMethodIDByEngineID("nacl_mozc_jp");
- const std::string xkb_jp_jpn = GetInputMethodIDByEngineID("xkb:jp::jpn");
- RefreshImes("ime1", {"ime1", nacl_mozc_jp, xkb_jp_jpn});
- // Accelerator based switching now works.
- EXPECT_TRUE(controller->CanSwitchImeWithAccelerator(convert));
- EXPECT_TRUE(controller->CanSwitchImeWithAccelerator(non_convert));
- EXPECT_TRUE(controller->CanSwitchImeWithAccelerator(wide_half_1));
- EXPECT_TRUE(controller->CanSwitchImeWithAccelerator(wide_half_2));
- // Convert keys jump directly to the requested IME.
- controller->SwitchImeWithAccelerator(convert);
- EXPECT_EQ(1, client.switch_ime_count_);
- EXPECT_EQ(nacl_mozc_jp, client.last_switch_ime_id_);
- controller->SwitchImeWithAccelerator(non_convert);
- EXPECT_EQ(2, client.switch_ime_count_);
- EXPECT_EQ(xkb_jp_jpn, client.last_switch_ime_id_);
- // Switch from nacl_mozc_jp to xkb_jp_jpn.
- RefreshImes(nacl_mozc_jp, {"ime1", nacl_mozc_jp, xkb_jp_jpn});
- controller->SwitchImeWithAccelerator(wide_half_1);
- EXPECT_EQ(3, client.switch_ime_count_);
- EXPECT_EQ(xkb_jp_jpn, client.last_switch_ime_id_);
- // Switch from xkb_jp_jpn to nacl_mozc_jp.
- RefreshImes(xkb_jp_jpn, {"ime1", nacl_mozc_jp, xkb_jp_jpn});
- controller->SwitchImeWithAccelerator(wide_half_2);
- EXPECT_EQ(4, client.switch_ime_count_);
- EXPECT_EQ(nacl_mozc_jp, client.last_switch_ime_id_);
- }
- TEST_F(ImeControllerImplTest, SetCapsLock) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- TestImeControllerClient client;
- EXPECT_EQ(0, client.set_caps_lock_count_);
- controller->SetCapsLockEnabled(true);
- EXPECT_EQ(0, client.set_caps_lock_count_);
- controller->SetClient(&client);
- controller->SetCapsLockEnabled(true);
- EXPECT_EQ(1, client.set_caps_lock_count_);
- // Does not no-op when the state is the same. Should send all notifications.
- controller->SetCapsLockEnabled(true);
- EXPECT_EQ(2, client.set_caps_lock_count_);
- controller->SetCapsLockEnabled(false);
- EXPECT_EQ(3, client.set_caps_lock_count_);
- controller->SetCapsLockEnabled(false);
- EXPECT_EQ(4, client.set_caps_lock_count_);
- EXPECT_FALSE(controller->IsCapsLockEnabled());
- controller->UpdateCapsLockState(true);
- EXPECT_TRUE(controller->IsCapsLockEnabled());
- controller->UpdateCapsLockState(false);
- EXPECT_FALSE(controller->IsCapsLockEnabled());
- }
- TEST_F(ImeControllerImplTest, OnKeyboardLayoutNameChanged) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- EXPECT_TRUE(controller->keyboard_layout_name().empty());
- TestImeControllerObserver observer;
- controller->AddObserver(&observer);
- controller->OnKeyboardLayoutNameChanged("us(dvorak)");
- EXPECT_EQ("us(dvorak)", controller->keyboard_layout_name());
- EXPECT_EQ("us(dvorak)", observer.last_keyboard_layout_name());
- }
- TEST_F(ImeControllerImplTest, ShowModeIndicator) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- std::u16string text = u"US";
- gfx::Rect cursor1_bounds(100, 100, 1, 20);
- controller->ShowModeIndicator(cursor1_bounds, text);
- views::Widget* widget1 =
- controller->mode_indicator_observer()->active_widget();
- EXPECT_TRUE(widget1);
- // The widget bounds should be bigger than the inner size.
- gfx::Rect bounds1 = widget1->GetWindowBoundsInScreen();
- EXPECT_LE(kInnerSize, bounds1.width());
- EXPECT_LE(kInnerSize, bounds1.height());
- gfx::Rect cursor2_bounds(50, 200, 1, 20);
- controller->ShowModeIndicator(cursor2_bounds, text);
- views::Widget* widget2 =
- controller->mode_indicator_observer()->active_widget();
- EXPECT_TRUE(widget2);
- EXPECT_NE(widget1, widget2);
- // Check if the location of the mode indicator corresponds to the cursor
- // bounds.
- gfx::Rect bounds2 = widget2->GetWindowBoundsInScreen();
- EXPECT_EQ(cursor1_bounds.x() - cursor2_bounds.x(), bounds1.x() - bounds2.x());
- EXPECT_EQ(cursor1_bounds.y() - cursor2_bounds.y(), bounds1.y() - bounds2.y());
- EXPECT_EQ(bounds1.width(), bounds2.width());
- EXPECT_EQ(bounds1.height(), bounds2.height());
- const gfx::Rect screen_bounds = display::Screen::GetScreen()
- ->GetDisplayMatching(cursor1_bounds)
- .work_area();
- const gfx::Rect cursor3_bounds(100, screen_bounds.bottom() - 25, 1, 20);
- controller->ShowModeIndicator(cursor3_bounds, text);
- views::Widget* widget3 =
- controller->mode_indicator_observer()->active_widget();
- EXPECT_TRUE(widget3);
- EXPECT_NE(widget2, widget3);
- // Check if the location of the mode indicator is considered with the screen
- // size.
- gfx::Rect bounds3 = widget3->GetWindowBoundsInScreen();
- EXPECT_LT(bounds3.bottom(), screen_bounds.bottom());
- }
- TEST_F(ImeControllerImplTest, MirroringChanged) {
- UpdateDisplay("600x500,600x500");
- // The controller is already an observer of the display_manager
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- TestImeControllerClient client;
- controller->SetClient(&client);
- display::DisplayManager* display_manager = Shell::Get()->display_manager();
- display_manager->SetMultiDisplayMode(display::DisplayManager::MIRRORING);
- display_manager->UpdateDisplays();
- EXPECT_TRUE(client.is_mirroring_);
- UpdateDisplay("600x500");
- EXPECT_FALSE(client.is_mirroring_);
- UpdateDisplay("600x500,600x500");
- EXPECT_TRUE(client.is_mirroring_);
- }
- TEST_F(ImeControllerImplTest, MeetMirroringChanged) {
- ImeControllerImpl* controller = Shell::Get()->ime_controller();
- TestImeControllerClient client;
- controller->SetClient(&client);
- EXPECT_FALSE(client.is_casting_);
- Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStart(
- base::DoNothing(), base::DoNothing(), u"");
- EXPECT_TRUE(client.is_casting_);
- Shell::Get()->system_tray_notifier()->NotifyScreenCaptureStop();
- EXPECT_FALSE(client.is_casting_);
- }
- } // namespace
- } // namespace ash
|