chromeos: Support IME system tray menus in mash
For go/mustash the ash system UI does not run in the chrome browser
process. Convert browser to ash communication for IMEs to use mojo.
Use mojo for both classic ash and mash so that we exercise the code
before mash ships.
Introduce ash ImeController mojo interface. Use ImeControllerClient
in chrome browser to observe InputMethodManager for IME changes and
push the data down to ash. This allows ash to synchronously query
the current and available IMEs when opening UI menus.
Declare the interface for ash ImeControllerClient, which will be used
in the next CL to handle IME switching via keyboard accelerators.
Bug: 724305
Test: added to ash_unittests and chrome unit_tests
Change-Id: I00e21078a519a706e2fe0ee8164b154f724abab2
Reviewed-on: https://chromium-review.googlesource.com/540015
Commit-Queue: James Cook <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Xiyuan Xia <[email protected]>
Cr-Commit-Position: refs/heads/master@{#480836}
diff --git a/ash/ime/ime_controller.cc b/ash/ime/ime_controller.cc
index 03c820f..35d2e91 100644
--- a/ash/ime/ime_controller.cc
+++ b/ash/ime/ime_controller.cc
@@ -13,13 +13,54 @@
ImeController::~ImeController() = default;
-void ImeController::RefreshIme(
- const mojom::ImeInfo& current_ime,
- const std::vector<mojom::ImeInfo>& available_imes,
- const std::vector<mojom::ImeMenuItem>& menu_items) {
- current_ime_ = current_ime;
- available_imes_ = available_imes;
- current_ime_menu_items_ = menu_items;
+void ImeController::BindRequest(mojom::ImeControllerRequest request) {
+ bindings_.AddBinding(this, std::move(request));
+}
+
+void ImeController::SetClient(mojom::ImeControllerClientPtr client) {
+ client_ = std::move(client);
+}
+
+bool ImeController::CanSwitchIme() const {
+ NOTIMPLEMENTED();
+ return true;
+}
+
+void ImeController::SwitchToNextIme() {
+ NOTIMPLEMENTED();
+}
+
+void ImeController::SwitchToPreviousIme() {
+ NOTIMPLEMENTED();
+}
+
+bool ImeController::CanSwitchImeWithAccelerator(
+ const ui::Accelerator& accelerator) const {
+ NOTIMPLEMENTED();
+ return true;
+}
+
+void ImeController::SwitchImeWithAccelerator(
+ const ui::Accelerator& accelerator) {
+ NOTIMPLEMENTED();
+}
+
+// mojom::ImeController:
+void ImeController::RefreshIme(mojom::ImeInfoPtr current_ime,
+ std::vector<mojom::ImeInfoPtr> available_imes,
+ std::vector<mojom::ImeMenuItemPtr> menu_items) {
+ current_ime_ = *current_ime;
+
+ available_imes_.clear();
+ available_imes_.reserve(available_imes.size());
+ for (const auto& ime : available_imes)
+ available_imes_.push_back(*ime);
+
+ current_ime_menu_items_.clear();
+ current_ime_menu_items_.reserve(menu_items.size());
+ for (const auto& item : menu_items)
+ current_ime_menu_items_.push_back(*item);
+
Shell::Get()->system_tray_notifier()->NotifyRefreshIME();
}
diff --git a/ash/ime/ime_controller.h b/ash/ime/ime_controller.h
index 96183636..744a543e 100644
--- a/ash/ime/ime_controller.h
+++ b/ash/ime/ime_controller.h
@@ -8,18 +8,24 @@
#include <vector>
#include "ash/ash_export.h"
+#include "ash/public/interfaces/ime_controller.mojom.h"
#include "ash/public/interfaces/ime_info.mojom.h"
#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+
+namespace ui {
+class Accelerator;
+}
namespace ash {
// Connects ash IME users (e.g. the system tray) to the IME implementation,
// which might live in Chrome browser or in a separate mojo service.
-// TODO(jamescook): Convert to use mojo IME interface to Chrome browser.
-class ASH_EXPORT ImeController {
+class ASH_EXPORT ImeController
+ : public NON_EXPORTED_BASE(mojom::ImeController) {
public:
ImeController();
- ~ImeController();
+ ~ImeController() override;
const mojom::ImeInfo& current_ime() const { return current_ime_; }
@@ -33,18 +39,31 @@
return current_ime_menu_items_;
}
- // Updates the cached IME information and refreshes the UI.
- // TODO(jamescook): This should take an ID for current_ime.
- void RefreshIme(const mojom::ImeInfo& current_ime,
- const std::vector<mojom::ImeInfo>& available_imes,
- const std::vector<mojom::ImeMenuItem>& menu_items);
+ // Binds the mojo interface to this object.
+ void BindRequest(mojom::ImeControllerRequest request);
- void SetImesManagedByPolicy(bool managed);
+ // TODO(jamescook): Implement these. http://crbug.com/724305
+ bool CanSwitchIme() const;
+ void SwitchToNextIme();
+ void SwitchToPreviousIme();
+ bool CanSwitchImeWithAccelerator(const ui::Accelerator& accelerator) const;
+ void SwitchImeWithAccelerator(const ui::Accelerator& accelerator);
- // Shows the IME menu on the shelf instead of inside the system tray menu.
- void ShowImeMenuOnShelf(bool show);
+ // mojom::ImeController:
+ void SetClient(mojom::ImeControllerClientPtr client) override;
+ void RefreshIme(mojom::ImeInfoPtr current_ime,
+ std::vector<mojom::ImeInfoPtr> available_imes,
+ std::vector<mojom::ImeMenuItemPtr> menu_items) override;
+ void SetImesManagedByPolicy(bool managed) override;
+ void ShowImeMenuOnShelf(bool show) override;
private:
+ // Bindings for users of the mojo interface.
+ mojo::BindingSet<mojom::ImeController> bindings_;
+
+ // Client interface back to IME code in chrome.
+ mojom::ImeControllerClientPtr client_;
+
mojom::ImeInfo current_ime_;
// "Available" IMEs are both installed and enabled by the user in settings.
diff --git a/ash/ime/ime_controller_unittest.cc b/ash/ime/ime_controller_unittest.cc
index e235e6a4..19ef8af0 100644
--- a/ash/ime/ime_controller_unittest.cc
+++ b/ash/ime/ime_controller_unittest.cc
@@ -21,7 +21,7 @@
~TestImeObserver() override = default;
// IMEObserver:
- void OnIMERefresh() override { refresh_count_++; }
+ void OnIMERefresh() override { ++refresh_count_; }
void OnIMEMenuActivationChanged(bool is_active) override {
ime_menu_active_ = is_active;
}
@@ -37,15 +37,23 @@
TestImeObserver observer;
Shell::Get()->system_tray_notifier()->AddIMEObserver(&observer);
- mojom::ImeInfo ime1;
- ime1.id = "ime1";
- mojom::ImeInfo ime2;
- ime2.id = "ime2";
+ mojom::ImeInfoPtr ime1 = mojom::ImeInfo::New();
+ ime1->id = "ime1";
+ mojom::ImeInfoPtr ime2 = mojom::ImeInfo::New();
+ ime2->id = "ime2";
- mojom::ImeMenuItem item1;
- item1.key = "key1";
+ std::vector<mojom::ImeInfoPtr> available_imes;
+ available_imes.push_back(ime1.Clone());
+ available_imes.push_back(ime2.Clone());
- controller->RefreshIme(ime1, {ime1, ime2}, {item1});
+ mojom::ImeMenuItemPtr item1 = mojom::ImeMenuItem::New();
+ item1->key = "key1";
+
+ std::vector<mojom::ImeMenuItemPtr> menu_items;
+ menu_items.push_back(item1.Clone());
+
+ controller->RefreshIme(std::move(ime1), std::move(available_imes),
+ std::move(menu_items));
// Cached data was updated.
EXPECT_EQ("ime1", controller->current_ime().id);
diff --git a/ash/mojo_interface_factory.cc b/ash/mojo_interface_factory.cc
index b11a3f8..d7bd8295 100644
--- a/ash/mojo_interface_factory.cc
+++ b/ash/mojo_interface_factory.cc
@@ -9,6 +9,7 @@
#include "ash/accelerators/accelerator_controller.h"
#include "ash/cast_config_controller.h"
#include "ash/display/ash_display_controller.h"
+#include "ash/ime/ime_controller.h"
#include "ash/login/lock_screen_controller.h"
#include "ash/media_controller.h"
#include "ash/new_window_controller.h"
@@ -57,6 +58,12 @@
Shell::Get()->cast_config()->BindRequest(std::move(request));
}
+void BindImeControllerRequestOnMainThread(
+ const service_manager::BindSourceInfo& source_info,
+ mojom::ImeControllerRequest request) {
+ Shell::Get()->ime_controller()->BindRequest(std::move(request));
+}
+
void BindLocaleNotificationControllerOnMainThread(
const service_manager::BindSourceInfo& source_info,
mojom::LocaleNotificationControllerRequest request) {
@@ -148,6 +155,8 @@
main_thread_task_runner);
registry->AddInterface(base::Bind(&BindAppListRequestOnMainThread),
main_thread_task_runner);
+ registry->AddInterface(base::Bind(&BindImeControllerRequestOnMainThread),
+ main_thread_task_runner);
registry->AddInterface(
base::Bind(&BindAshDisplayControllerRequestOnMainThread),
main_thread_task_runner);
diff --git a/ash/mus/manifest.json b/ash/mus/manifest.json
index 32e927d5..d05c528 100644
--- a/ash/mus/manifest.json
+++ b/ash/mus/manifest.json
@@ -10,6 +10,7 @@
"app_list::mojom::AppList",
"ash::mojom::AcceleratorController",
"ash::mojom::CastConfig",
+ "ash::mojom::ImeController",
"ash::mojom::LocaleNotificationController",
"ash::mojom::LockScreen",
"ash::mojom::MediaController",
diff --git a/ash/mus/standalone/manifest.json b/ash/mus/standalone/manifest.json
index 04ba7fe..89d5106 100644
--- a/ash/mus/standalone/manifest.json
+++ b/ash/mus/standalone/manifest.json
@@ -8,6 +8,7 @@
"app_list::mojom::AppList",
"ash::mojom::AcceleratorController",
"ash::mojom::CastConfig",
+ "ash::mojom::ImeController",
"ash::mojom::LocaleNotificationController",
"ash::mojom::MediaController",
"ash::mojom::NewWindowController",
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index 623b242..1d8da270 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -17,6 +17,7 @@
"cast_config.mojom",
"constants.mojom",
"event_properties.mojom",
+ "ime_controller.mojom",
"ime_info.mojom",
"locale.mojom",
"lock_screen.mojom",
diff --git a/ash/public/interfaces/ime_controller.mojom b/ash/public/interfaces/ime_controller.mojom
new file mode 100644
index 0000000..65dd3b9
--- /dev/null
+++ b/ash/public/interfaces/ime_controller.mojom
@@ -0,0 +1,50 @@
+// 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.
+
+module ash.mojom;
+
+import "ash/public/interfaces/ime_info.mojom";
+
+// Interface for ash client (e.g. Chrome) to send input method info to ash.
+interface ImeController {
+ // Sets the client interface.
+ SetClient(ImeControllerClient client);
+
+ // Updates the cached IME information and refreshes the IME menus.
+ // |current_ime| has an empty ID when there is no active IME yet.
+ // TODO(jamescook): Convert |current_ime| to an ID instead of an IMEInfo.
+ RefreshIme(ImeInfo current_ime,
+ array<ImeInfo> available_imes,
+ array<ImeMenuItem> menu_items);
+
+ // Shows an icon in the IME menu indicating that IMEs are controlled by device
+ // policy.
+ SetImesManagedByPolicy(bool managed);
+
+ // Shows the IME menu on the shelf instead of inside the system tray menu.
+ // Users with multiple IMEs that have multiple configurable properties (e.g.
+ // some Chinese IMEs) prefer this to keeping the IME menu under the primary
+ // system tray menu.
+ ShowImeMenuOnShelf(bool show);
+};
+
+// Interface for ash to send input method requests to its client (e.g. Chrome).
+interface ImeControllerClient {
+ // Switches to the next input method. Does nothing if only one input method
+ // is installed.
+ SwitchToNextIme();
+
+ // Switches to the previous input method. Does nothing if only one input
+ // method is installed.
+ SwitchToPreviousIme();
+
+ // Switches to an input method by |id| (for example, "xkb:jp::jpn"). Does
+ // nothing if the input method is not installed.
+ SwitchImeById(string id);
+
+ // Activates an input method property. These are settings that are provided by
+ // IME extensions that appear in the IME menu. Does nothing if the |key| does
+ // not exist.
+ ActivateImeProperty(string key);
+};
diff --git a/ash/public/interfaces/ime_info.mojom b/ash/public/interfaces/ime_info.mojom
index b471e4e..3d8ca018 100644
--- a/ash/public/interfaces/ime_info.mojom
+++ b/ash/public/interfaces/ime_info.mojom
@@ -9,6 +9,7 @@
// Metadata about an installed input method.
struct ImeInfo {
// True if the IME is the current IME.
+ // TODO(jamescook): Remove this and use ImeController::current_ime().
bool selected;
// True if the IME is a third-party extension.
diff --git a/ash/system/ime/tray_ime_chromeos_unittest.cc b/ash/system/ime/tray_ime_chromeos_unittest.cc
index 4496c08..ffc570c 100644
--- a/ash/system/ime/tray_ime_chromeos_unittest.cc
+++ b/ash/system/ime/tray_ime_chromeos_unittest.cc
@@ -55,6 +55,9 @@
void TearDown() override;
private:
+ // Updates the ImeController with the latest IME test data.
+ void RefreshImeController();
+
std::unique_ptr<TrayIME> tray_;
std::unique_ptr<views::View> default_view_;
std::unique_ptr<views::View> detailed_view_;
@@ -81,8 +84,7 @@
void TrayIMETest::SetActiveImeCount(int count) {
available_imes_.resize(count);
- Shell::Get()->ime_controller()->RefreshIme(current_ime_, available_imes_,
- menu_items_);
+ RefreshImeController();
}
views::View* TrayIMETest::GetToggleView() const {
@@ -101,8 +103,7 @@
void TrayIMETest::SetCurrentImeMenuItems(
const std::vector<mojom::ImeMenuItem>& items) {
menu_items_ = items;
- Shell::Get()->ime_controller()->RefreshIme(current_ime_, available_imes_,
- menu_items_);
+ RefreshImeController();
}
void TrayIMETest::SuppressKeyboard() {
@@ -142,6 +143,22 @@
detailed_view_.reset(tray_->CreateDetailedView(LoginStatus::USER));
}
+void TrayIMETest::RefreshImeController() {
+ mojom::ImeInfoPtr current_ime_ptr = current_ime_.Clone();
+
+ std::vector<mojom::ImeInfoPtr> available_ime_ptrs;
+ for (const auto& ime : available_imes_)
+ available_ime_ptrs.push_back(ime.Clone());
+
+ std::vector<mojom::ImeMenuItemPtr> menu_item_ptrs;
+ for (const auto& item : menu_items_)
+ menu_item_ptrs.push_back(item.Clone());
+
+ Shell::Get()->ime_controller()->RefreshIme(std::move(current_ime_ptr),
+ std::move(available_ime_ptrs),
+ std::move(menu_item_ptrs));
+}
+
void TrayIMETest::TearDown() {
if (keyboard_suppressed_)
RestoreKeyboard();
diff --git a/ash/system/ime_menu/ime_menu_tray_unittest.cc b/ash/system/ime_menu/ime_menu_tray_unittest.cc
index 7602ac3..818e629 100644
--- a/ash/system/ime_menu/ime_menu_tray_unittest.cc
+++ b/ash/system/ime_menu/ime_menu_tray_unittest.cc
@@ -34,8 +34,13 @@
void SetCurrentIme(mojom::ImeInfo current_ime,
const std::vector<mojom::ImeInfo>& available_imes) {
- Shell::Get()->ime_controller()->RefreshIme(current_ime, available_imes,
- std::vector<mojom::ImeMenuItem>());
+ mojom::ImeInfoPtr current_ime_ptr = current_ime.Clone();
+ std::vector<mojom::ImeInfoPtr> available_ime_ptrs;
+ for (const auto& ime : available_imes)
+ available_ime_ptrs.push_back(ime.Clone());
+ Shell::Get()->ime_controller()->RefreshIme(
+ std::move(current_ime_ptr), std::move(available_ime_ptrs),
+ std::vector<mojom::ImeMenuItemPtr>());
}
} // namespace
diff --git a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
index 3ce9aec..be2d234 100644
--- a/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
+++ b/chrome/browser/chromeos/input_method/input_method_manager_impl_unittest.cc
@@ -69,13 +69,37 @@
return extension_ime_util::GetInputMethodIDByEngineID(id);
}
-std::string GetCurrentImeIdFromAsh() {
- return ash::Shell::Get()->ime_controller()->current_ime().id;
-}
+class TestImeController : ash::mojom::ImeController {
+ public:
+ TestImeController() : binding_(this) {}
+ ~TestImeController() override = default;
-size_t GetAvailableImeCountFromAsh() {
- return ash::Shell::Get()->ime_controller()->available_imes().size();
-}
+ // Returns a mojo interface pointer bound to this object.
+ ash::mojom::ImeControllerPtr CreateInterfacePtr() {
+ ash::mojom::ImeControllerPtr ptr;
+ binding_.Bind(mojo::MakeRequest(&ptr));
+ return ptr;
+ }
+
+ // ash::mojom::ImeController:
+ void SetClient(ash::mojom::ImeControllerClientPtr client) override {}
+ void RefreshIme(ash::mojom::ImeInfoPtr current_ime,
+ std::vector<ash::mojom::ImeInfoPtr> available_imes,
+ std::vector<ash::mojom::ImeMenuItemPtr> menu_items) override {
+ current_ime_ = std::move(current_ime);
+ available_imes_ = std::move(available_imes);
+ }
+ void SetImesManagedByPolicy(bool managed) override {}
+ void ShowImeMenuOnShelf(bool show) override {}
+
+ ash::mojom::ImeInfoPtr current_ime_;
+ std::vector<ash::mojom::ImeInfoPtr> available_imes_;
+
+ private:
+ mojo::Binding<ash::mojom::ImeController> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestImeController);
+};
class TestObserver : public InputMethodManager::Observer,
public ui::ime::InputMethodMenuManager::Observer {
@@ -1574,41 +1598,51 @@
// Verifies that the combination of InputMethodManagerImpl and
// ImeControllerClient sends the correct data to ash.
TEST_F(InputMethodManagerImplTest, IntegrationWithAsh) {
+ TestImeController ime_controller; // Mojo interface to ash.
ImeControllerClient ime_controller_client(manager_.get());
+ ime_controller_client.InitForTesting(ime_controller.CreateInterfacePtr());
+ // Setup 3 IMEs.
InitComponentExtension();
manager_->SetUISessionState(InputMethodManager::STATE_BROWSER_SCREEN);
std::vector<std::string> ids;
ids.push_back(ImeIdFromEngineId("xkb:us:dvorak:eng"));
ids.push_back(ImeIdFromEngineId(kExt2Engine2Id));
ids.push_back(ImeIdFromEngineId(kExt2Engine1Id));
- EXPECT_TRUE(manager_->GetActiveIMEState()->ReplaceEnabledInputMethods(ids));
+ manager_->GetActiveIMEState()->ReplaceEnabledInputMethods(ids);
+ ime_controller_client.FlushMojoForTesting();
- EXPECT_EQ(3u, GetAvailableImeCountFromAsh());
- EXPECT_EQ(ImeIdFromEngineId(ids[0]), GetCurrentImeIdFromAsh());
+ // Ash received the IMEs.
+ ASSERT_EQ(3u, ime_controller.available_imes_.size());
+ EXPECT_EQ(ImeIdFromEngineId(ids[0]), ime_controller.current_ime_->id);
// Switch to Mozc.
manager_->GetActiveIMEState()->SwitchToNextInputMethod();
- EXPECT_EQ(ImeIdFromEngineId(ids[1]), GetCurrentImeIdFromAsh());
+ ime_controller_client.FlushMojoForTesting();
+ EXPECT_EQ(ImeIdFromEngineId(ids[1]), ime_controller.current_ime_->id);
- // Lock screen
+ // Lock the screen.
scoped_refptr<input_method::InputMethodManager::State> saved_ime_state =
manager_->GetActiveIMEState();
manager_->SetState(saved_ime_state->Clone());
manager_->GetActiveIMEState()->EnableLockScreenLayouts();
manager_->SetUISessionState(InputMethodManager::STATE_LOCK_SCREEN);
- EXPECT_EQ(2u, GetAvailableImeCountFromAsh()); // Qwerty+Dvorak.
- EXPECT_EQ(ImeIdFromEngineId("xkb:us:dvorak:eng"), GetCurrentImeIdFromAsh());
+ ime_controller_client.FlushMojoForTesting();
+ EXPECT_EQ(2u, ime_controller.available_imes_.size()); // Qwerty+Dvorak.
+ EXPECT_EQ(ImeIdFromEngineId("xkb:us:dvorak:eng"),
+ ime_controller.current_ime_->id);
manager_->GetActiveIMEState()->SwitchToNextInputMethod();
+ ime_controller_client.FlushMojoForTesting();
EXPECT_EQ(ImeIdFromEngineId("xkb:us::eng"), // The hardware keyboard layout.
- GetCurrentImeIdFromAsh());
+ ime_controller.current_ime_->id);
// Unlock screen. The original state, pinyin-dv, is restored.
manager_->SetState(saved_ime_state);
manager_->SetUISessionState(InputMethodManager::STATE_BROWSER_SCREEN);
- EXPECT_EQ(3u, GetAvailableImeCountFromAsh()); // Dvorak and 2 IMEs.
- EXPECT_EQ(ImeIdFromEngineId(ids[1]), GetCurrentImeIdFromAsh());
+ ime_controller_client.FlushMojoForTesting();
+ ASSERT_EQ(3u, ime_controller.available_imes_.size()); // Dvorak and 2 IMEs.
+ EXPECT_EQ(ImeIdFromEngineId(ids[1]), ime_controller.current_ime_->id);
}
} // namespace input_method
diff --git a/chrome/browser/chromeos/login/lock_screen_utils.cc b/chrome/browser/chromeos/login/lock_screen_utils.cc
index c9d856f..cd395fc18 100644
--- a/chrome/browser/chromeos/login/lock_screen_utils.cc
+++ b/chrome/browser/chromeos/login/lock_screen_utils.cc
@@ -115,7 +115,8 @@
chromeos::input_method::InputMethodManager* imm =
chromeos::input_method::InputMethodManager::Get();
imm->GetActiveIMEState()->SetAllowedInputMethods(allowed_input_methods);
- ImeControllerClient::Get()->SetImesManagedByPolicy(true);
+ if (ImeControllerClient::Get()) // Can be null in tests.
+ ImeControllerClient::Get()->SetImesManagedByPolicy(true);
}
void StopEnforcingPolicyInputMethods() {
@@ -124,7 +125,8 @@
chromeos::input_method::InputMethodManager* imm =
chromeos::input_method::InputMethodManager::Get();
imm->GetActiveIMEState()->SetAllowedInputMethods(allowed_input_methods);
- ImeControllerClient::Get()->SetImesManagedByPolicy(false);
+ if (ImeControllerClient::Get()) // Can be null in tests.
+ ImeControllerClient::Get()->SetImesManagedByPolicy(false);
}
void SetKeyboardSettings(const AccountId& account_id) {
diff --git a/chrome/browser/ui/ash/ime_controller_client.cc b/chrome/browser/ui/ash/ime_controller_client.cc
index 54d6929e..16385ce 100644
--- a/chrome/browser/ui/ash/ime_controller_client.cc
+++ b/chrome/browser/ui/ash/ime_controller_client.cc
@@ -7,12 +7,11 @@
#include <memory>
#include <vector>
-#include "ash/ime/ime_controller.h"
-#include "ash/public/interfaces/ime_info.mojom.h"
-#include "ash/shell.h"
-#include "ash/system/tray/system_tray_notifier.h"
+#include "ash/public/interfaces/constants.mojom.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "ui/base/ime/chromeos/extension_ime_util.h"
#include "ui/base/ime/chromeos/input_method_descriptor.h"
#include "ui/base/ime/chromeos/input_method_util.h"
@@ -29,13 +28,13 @@
} // namespace
ImeControllerClient::ImeControllerClient(InputMethodManager* manager)
- : input_method_manager_(manager) {
+ : input_method_manager_(manager), binding_(this) {
DCHECK(input_method_manager_);
input_method_manager_->AddObserver(this);
input_method_manager_->AddImeMenuObserver(this);
InputMethodMenuManager::GetInstance()->AddObserver(this);
- // This does not need send the initial state to ash because that happens
+ // This does not need to send the initial state to ash because that happens
// via observers when the InputMethodManager initializes its list of IMEs.
DCHECK(!g_instance);
@@ -51,13 +50,44 @@
input_method_manager_->RemoveObserver(this);
}
+void ImeControllerClient::Init() {
+ // Connect to the controller in ash.
+ content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->BindInterface(ash::mojom::kServiceName, &ime_controller_ptr_);
+ BindAndSetClient();
+}
+
+void ImeControllerClient::InitForTesting(
+ ash::mojom::ImeControllerPtr controller) {
+ ime_controller_ptr_ = std::move(controller);
+ BindAndSetClient();
+}
+
// static
ImeControllerClient* ImeControllerClient::Get() {
return g_instance;
}
void ImeControllerClient::SetImesManagedByPolicy(bool managed) {
- ash::Shell::Get()->ime_controller()->SetImesManagedByPolicy(managed);
+ ime_controller_ptr_->SetImesManagedByPolicy(managed);
+}
+
+// ash::mojom::ImeControllerClient:
+void ImeControllerClient::SwitchToNextIme() {
+ NOTIMPLEMENTED();
+}
+
+void ImeControllerClient::SwitchToPreviousIme() {
+ NOTIMPLEMENTED();
+}
+
+void ImeControllerClient::SwitchImeById(const std::string& id) {
+ NOTIMPLEMENTED();
+}
+
+void ImeControllerClient::ActivateImeProperty(const std::string& key) {
+ NOTIMPLEMENTED();
}
// chromeos::input_method::InputMethodManager::Observer:
@@ -69,7 +99,7 @@
// chromeos::input_method::InputMethodManager::ImeMenuObserver:
void ImeControllerClient::ImeMenuActivationChanged(bool is_active) {
- ash::Shell::Get()->ime_controller()->ShowImeMenuOnShelf(is_active);
+ ime_controller_ptr_->ShowImeMenuOnShelf(is_active);
}
void ImeControllerClient::ImeMenuListChanged() {
@@ -86,53 +116,63 @@
RefreshIme();
}
-ash::mojom::ImeInfo ImeControllerClient::GetAshImeInfo(
+void ImeControllerClient::FlushMojoForTesting() {
+ ime_controller_ptr_.FlushForTesting();
+}
+
+void ImeControllerClient::BindAndSetClient() {
+ ash::mojom::ImeControllerClientPtr client;
+ binding_.Bind(mojo::MakeRequest(&client));
+ ime_controller_ptr_->SetClient(std::move(client));
+}
+
+ash::mojom::ImeInfoPtr ImeControllerClient::GetAshImeInfo(
const InputMethodDescriptor& ime) const {
InputMethodUtil* util = input_method_manager_->GetInputMethodUtil();
- ash::mojom::ImeInfo info;
- info.id = ime.id();
- info.name = util->GetInputMethodLongName(ime);
- info.medium_name = util->GetInputMethodMediumName(ime);
- info.short_name = util->GetInputMethodShortName(ime);
- info.third_party = chromeos::extension_ime_util::IsExtensionIME(ime.id());
+ ash::mojom::ImeInfoPtr info = ash::mojom::ImeInfo::New();
+ info->id = ime.id();
+ info->name = util->GetInputMethodLongName(ime);
+ info->medium_name = util->GetInputMethodMediumName(ime);
+ info->short_name = util->GetInputMethodShortName(ime);
+ info->third_party = chromeos::extension_ime_util::IsExtensionIME(ime.id());
return info;
}
void ImeControllerClient::RefreshIme() {
- ash::ImeController* ime_controller = ash::Shell::Get()->ime_controller();
InputMethodManager::State* state =
input_method_manager_->GetActiveIMEState().get();
if (!state) {
- ime_controller->RefreshIme(ash::mojom::ImeInfo(),
- std::vector<ash::mojom::ImeInfo>(),
- std::vector<ash::mojom::ImeMenuItem>());
+ ime_controller_ptr_->RefreshIme(ash::mojom::ImeInfo::New(),
+ std::vector<ash::mojom::ImeInfoPtr>(),
+ std::vector<ash::mojom::ImeMenuItemPtr>());
return;
}
InputMethodDescriptor current_descriptor = state->GetCurrentInputMethod();
- ash::mojom::ImeInfo current_ime = GetAshImeInfo(current_descriptor);
- current_ime.selected = true;
+ ash::mojom::ImeInfoPtr current_ime = GetAshImeInfo(current_descriptor);
+ current_ime->selected = true;
- std::vector<ash::mojom::ImeInfo> available_imes;
+ std::vector<ash::mojom::ImeInfoPtr> available_imes;
std::unique_ptr<std::vector<InputMethodDescriptor>>
available_ime_descriptors = state->GetActiveInputMethods();
for (const InputMethodDescriptor& descriptor : *available_ime_descriptors) {
- ash::mojom::ImeInfo info = GetAshImeInfo(descriptor);
- info.selected = descriptor.id() == current_ime.id;
- available_imes.push_back(info);
+ ash::mojom::ImeInfoPtr info = GetAshImeInfo(descriptor);
+ info->selected = descriptor.id() == current_ime->id;
+ available_imes.push_back(std::move(info));
}
- std::vector<ash::mojom::ImeMenuItem> ash_menu_items;
+ std::vector<ash::mojom::ImeMenuItemPtr> ash_menu_items;
ui::ime::InputMethodMenuItemList menu_list =
ui::ime::InputMethodMenuManager::GetInstance()
->GetCurrentInputMethodMenuItemList();
for (const ui::ime::InputMethodMenuItem& menu_item : menu_list) {
- ash::mojom::ImeMenuItem ash_item;
- ash_item.key = menu_item.key;
- ash_item.label = base::UTF8ToUTF16(menu_item.label);
- ash_item.checked = menu_item.is_selection_item_checked;
- ash_menu_items.push_back(ash_item);
+ ash::mojom::ImeMenuItemPtr ash_item = ash::mojom::ImeMenuItem::New();
+ ash_item->key = menu_item.key;
+ ash_item->label = base::UTF8ToUTF16(menu_item.label);
+ ash_item->checked = menu_item.is_selection_item_checked;
+ ash_menu_items.push_back(std::move(ash_item));
}
- ash::Shell::Get()->ime_controller()->RefreshIme(current_ime, available_imes,
- ash_menu_items);
+ ime_controller_ptr_->RefreshIme(std::move(current_ime),
+ std::move(available_imes),
+ std::move(ash_menu_items));
}
diff --git a/chrome/browser/ui/ash/ime_controller_client.h b/chrome/browser/ui/ash/ime_controller_client.h
index e7f947a..e83f379 100644
--- a/chrome/browser/ui/ash/ime_controller_client.h
+++ b/chrome/browser/ui/ash/ime_controller_client.h
@@ -5,20 +5,17 @@
#ifndef CHROME_BROWSER_UI_ASH_IME_CONTROLLER_CLIENT_H_
#define CHROME_BROWSER_UI_ASH_IME_CONTROLLER_CLIENT_H_
+#include "ash/public/interfaces/ime_controller.mojom.h"
+#include "ash/public/interfaces/ime_info.mojom.h"
#include "base/macros.h"
+#include "mojo/public/cpp/bindings/binding.h"
#include "ui/base/ime/chromeos/input_method_manager.h"
#include "ui/chromeos/ime/input_method_menu_manager.h"
-namespace ash {
-namespace mojom {
-class ImeInfo;
-}
-}
-
// Connects the ImeController in ash to the InputMethodManagerImpl in chrome.
-// TODO(jamescook): Convert to using mojo interfaces.
class ImeControllerClient
- : public chromeos::input_method::InputMethodManager::Observer,
+ : public ash::mojom::ImeControllerClient,
+ public chromeos::input_method::InputMethodManager::Observer,
public chromeos::input_method::InputMethodManager::ImeMenuObserver,
public ui::ime::InputMethodMenuManager::Observer {
public:
@@ -26,11 +23,23 @@
chromeos::input_method::InputMethodManager* manager);
~ImeControllerClient() override;
+ // Initializes and connects to ash.
+ void Init();
+
+ // Tests can shim in a mock mojo interface for the ash controller.
+ void InitForTesting(ash::mojom::ImeControllerPtr controller);
+
static ImeControllerClient* Get();
// Sets whether the list of IMEs is managed by device policy.
void SetImesManagedByPolicy(bool managed);
+ // ash::mojom::ImeControllerClient:
+ void SwitchToNextIme() override;
+ void SwitchToPreviousIme() override;
+ void SwitchImeById(const std::string& id) override;
+ void ActivateImeProperty(const std::string& key) override;
+
// chromeos::input_method::InputMethodManager::Observer:
void InputMethodChanged(chromeos::input_method::InputMethodManager* manager,
Profile* profile,
@@ -48,9 +57,14 @@
void InputMethodMenuItemChanged(
ui::ime::InputMethodMenuManager* manager) override;
+ void FlushMojoForTesting();
+
private:
+ // Binds this object to its mojo interface and sets it as the ash client.
+ void BindAndSetClient();
+
// Converts IME information from |descriptor| into the ash mojo format.
- ash::mojom::ImeInfo GetAshImeInfo(
+ ash::mojom::ImeInfoPtr GetAshImeInfo(
const chromeos::input_method::InputMethodDescriptor& descriptor) const;
// Sends information about current and available IMEs to ash.
@@ -58,6 +72,12 @@
chromeos::input_method::InputMethodManager* const input_method_manager_;
+ // Binds this object to the mojo interface.
+ mojo::Binding<ash::mojom::ImeControllerClient> binding_;
+
+ // ImeController interface in ash.
+ ash::mojom::ImeControllerPtr ime_controller_ptr_;
+
DISALLOW_COPY_AND_ASSIGN(ImeControllerClient);
};
diff --git a/chrome/browser/ui/ash/ime_controller_client_unittest.cc b/chrome/browser/ui/ash/ime_controller_client_unittest.cc
index 76317c51..dc101460 100644
--- a/chrome/browser/ui/ash/ime_controller_client_unittest.cc
+++ b/chrome/browser/ui/ash/ime_controller_client_unittest.cc
@@ -5,26 +5,267 @@
#include "chrome/browser/ui/ash/ime_controller_client.h"
#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+#include "ash/public/interfaces/ime_controller.mojom.h"
+#include "base/macros.h"
#include "base/memory/ptr_util.h"
-#include "chrome/browser/chromeos/input_method/mock_input_method_manager_impl.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_environment.h"
+#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/ime/chromeos/fake_input_method_delegate.h"
+#include "ui/base/ime/chromeos/input_method_descriptor.h"
+#include "ui/base/ime/chromeos/input_method_util.h"
+#include "ui/base/ime/chromeos/mock_input_method_manager.h"
+#include "ui/chromeos/ime/input_method_menu_item.h"
+#include "ui/chromeos/ime/input_method_menu_manager.h"
-using ImeControllerClientTest = testing::Test;
+using chromeos::input_method::FakeInputMethodDelegate;
+using chromeos::input_method::InputMethodDescriptor;
+using chromeos::input_method::InputMethodManager;
+using chromeos::input_method::InputMethodUtil;
+using chromeos::input_method::MockInputMethodManager;
+using ui::ime::InputMethodMenuItem;
+using ui::ime::InputMethodMenuManager;
-TEST_F(ImeControllerClientTest, Basics) {
- chromeos::input_method::MockInputMethodManagerImpl input_method_manager;
+namespace {
+
+// Used to look up IME names.
+base::string16 GetLocalizedString(int resource_id) {
+ return base::ASCIIToUTF16("localized string");
+}
+
+// InputMethodManager with available IMEs.
+class TestInputMethodManager : public MockInputMethodManager {
+ public:
+ class TestState : public MockInputMethodManager::State {
+ public:
+ TestState() {
+ // Set up two input methods.
+ std::vector<std::string> layouts({"us"});
+ std::vector<std::string> languages({"en-US"});
+ InputMethodDescriptor ime1("id1", "name1", "indicator1", layouts,
+ languages, true /* is_login_keyboard */,
+ GURL(), GURL());
+ InputMethodDescriptor ime2("id2", "name2", "indicator2", layouts,
+ languages, false /* is_login_keyboard */,
+ GURL(), GURL());
+ current_ime_id_ = ime1.id();
+ input_methods_ = {ime1, ime2};
+ }
+
+ // MockInputMethodManager::State:
+ std::unique_ptr<std::vector<InputMethodDescriptor>> GetActiveInputMethods()
+ const override {
+ return base::MakeUnique<std::vector<InputMethodDescriptor>>(
+ input_methods_);
+ }
+ const InputMethodDescriptor* GetInputMethodFromId(
+ const std::string& input_method_id) const override {
+ for (const InputMethodDescriptor& descriptor : input_methods_) {
+ if (input_method_id == descriptor.id())
+ return &descriptor;
+ }
+ return nullptr;
+ }
+ InputMethodDescriptor GetCurrentInputMethod() const override {
+ for (const InputMethodDescriptor& descriptor : input_methods_) {
+ if (current_ime_id_ == descriptor.id())
+ return descriptor;
+ }
+ return InputMethodUtil::GetFallbackInputMethodDescriptor();
+ }
+
+ std::string current_ime_id_;
+ std::vector<InputMethodDescriptor> input_methods_;
+
+ protected:
+ friend base::RefCounted<InputMethodManager::State>;
+ ~TestState() override {}
+
+ DISALLOW_COPY_AND_ASSIGN(TestState);
+ };
+
+ TestInputMethodManager() : state_(new TestState), util_(&delegate_) {}
+ ~TestInputMethodManager() override = default;
+
+ // MockInputMethodManager:
+ void AddObserver(InputMethodManager::Observer* observer) override {
+ ++add_observer_count_;
+ }
+ void AddImeMenuObserver(ImeMenuObserver* observer) override {
+ ++add_menu_observer_count_;
+ }
+ void RemoveObserver(InputMethodManager::Observer* observer) override {
+ ++remove_observer_count_;
+ }
+ void RemoveImeMenuObserver(ImeMenuObserver* observer) override {
+ ++remove_menu_observer_count_;
+ }
+ InputMethodUtil* GetInputMethodUtil() override { return &util_; }
+ scoped_refptr<InputMethodManager::State> GetActiveIMEState() override {
+ return state_;
+ }
+
+ scoped_refptr<TestState> state_;
+ int add_observer_count_ = 0;
+ int remove_observer_count_ = 0;
+ int add_menu_observer_count_ = 0;
+ int remove_menu_observer_count_ = 0;
+ FakeInputMethodDelegate delegate_;
+ InputMethodUtil util_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestInputMethodManager);
+};
+
+class TestImeController : ash::mojom::ImeController {
+ public:
+ TestImeController() : binding_(this) {}
+ ~TestImeController() override = default;
+
+ // Returns a mojo interface pointer bound to this object.
+ ash::mojom::ImeControllerPtr CreateInterfacePtr() {
+ ash::mojom::ImeControllerPtr ptr;
+ binding_.Bind(mojo::MakeRequest(&ptr));
+ return ptr;
+ }
+
+ // ash::mojom::ImeController:
+ void SetClient(ash::mojom::ImeControllerClientPtr client) override {}
+ void RefreshIme(ash::mojom::ImeInfoPtr current_ime,
+ std::vector<ash::mojom::ImeInfoPtr> available_imes,
+ std::vector<ash::mojom::ImeMenuItemPtr> menu_items) override {
+ current_ime_ = std::move(current_ime);
+ available_imes_ = std::move(available_imes);
+ menu_items_ = std::move(menu_items);
+ }
+ void SetImesManagedByPolicy(bool managed) override {
+ managed_by_policy_ = managed;
+ }
+ void ShowImeMenuOnShelf(bool show) override {
+ show_ime_menu_on_shelf_ = show;
+ }
+
+ // The most recent values received via mojo.
+ ash::mojom::ImeInfoPtr current_ime_;
+ std::vector<ash::mojom::ImeInfoPtr> available_imes_;
+ std::vector<ash::mojom::ImeMenuItemPtr> menu_items_;
+ bool managed_by_policy_ = false;
+ bool show_ime_menu_on_shelf_ = false;
+
+ private:
+ mojo::Binding<ash::mojom::ImeController> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestImeController);
+};
+
+class ImeControllerClientTest : public testing::Test {
+ public:
+ ImeControllerClientTest() = default;
+ ~ImeControllerClientTest() override = default;
+
+ private:
+ base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImeControllerClientTest);
+};
+
+TEST_F(ImeControllerClientTest, Construction) {
+ TestInputMethodManager input_method_manager;
+ TestImeController ime_controller;
std::unique_ptr<ImeControllerClient> client =
base::MakeUnique<ImeControllerClient>(&input_method_manager);
- EXPECT_EQ(1, input_method_manager.add_observer_count());
- EXPECT_EQ(1, input_method_manager.add_menu_observer_count());
+ client->InitForTesting(ime_controller.CreateInterfacePtr());
+ EXPECT_EQ(1, input_method_manager.add_observer_count_);
+ EXPECT_EQ(1, input_method_manager.add_menu_observer_count_);
client.reset();
- EXPECT_EQ(1, input_method_manager.remove_observer_count());
- EXPECT_EQ(1, input_method_manager.remove_menu_observer_count());
+ EXPECT_EQ(1, input_method_manager.remove_observer_count_);
+ EXPECT_EQ(1, input_method_manager.remove_menu_observer_count_);
}
-// TODO(jamescook): When ImeControllerClient switches to using mojo add
-// additional tests that the correct mojo interface methods are called to send
-// data to ash.
+TEST_F(ImeControllerClientTest, SetImesManagedByPolicy) {
+ TestInputMethodManager input_method_manager;
+ TestImeController ime_controller;
+
+ ImeControllerClient client(&input_method_manager);
+ client.InitForTesting(ime_controller.CreateInterfacePtr());
+
+ client.SetImesManagedByPolicy(true);
+ client.FlushMojoForTesting();
+ EXPECT_TRUE(ime_controller.managed_by_policy_);
+}
+
+TEST_F(ImeControllerClientTest, ShowImeMenuOnShelf) {
+ TestInputMethodManager input_method_manager;
+ TestImeController ime_controller;
+
+ ImeControllerClient client(&input_method_manager);
+ client.InitForTesting(ime_controller.CreateInterfacePtr());
+
+ client.ImeMenuActivationChanged(true);
+ client.FlushMojoForTesting();
+ EXPECT_TRUE(ime_controller.show_ime_menu_on_shelf_);
+}
+
+TEST_F(ImeControllerClientTest, InputMethodChanged) {
+ TestInputMethodManager input_method_manager;
+ input_method_manager.delegate_.set_get_localized_string_callback(
+ base::Bind(&GetLocalizedString));
+ TestImeController ime_controller;
+
+ ImeControllerClient client(&input_method_manager);
+ client.InitForTesting(ime_controller.CreateInterfacePtr());
+
+ // Simulate a switch to IME 2.
+ input_method_manager.state_->current_ime_id_ = "id2";
+ client.InputMethodChanged(&input_method_manager, nullptr /* profile */,
+ false /* show_message */);
+ client.FlushMojoForTesting();
+
+ // IME controller received the change and the list of available IMEs.
+ EXPECT_EQ("id2", ime_controller.current_ime_->id);
+ ASSERT_EQ(2u, ime_controller.available_imes_.size());
+ EXPECT_EQ("id1", ime_controller.available_imes_[0]->id);
+ EXPECT_EQ(base::ASCIIToUTF16("name1"),
+ ime_controller.available_imes_[0]->name);
+ EXPECT_EQ("id2", ime_controller.available_imes_[1]->id);
+ EXPECT_EQ(base::ASCIIToUTF16("name2"),
+ ime_controller.available_imes_[1]->name);
+}
+
+TEST_F(ImeControllerClientTest, MenuItemChanged) {
+ TestInputMethodManager input_method_manager;
+ input_method_manager.delegate_.set_get_localized_string_callback(
+ base::Bind(&GetLocalizedString));
+ TestImeController ime_controller;
+
+ ImeControllerClient client(&input_method_manager);
+ client.InitForTesting(ime_controller.CreateInterfacePtr());
+
+ const bool is_selection_item = true;
+ InputMethodMenuItem item1("key1", "label1", is_selection_item,
+ true /* checked */);
+ InputMethodMenuItem item2("key2", "label2", is_selection_item,
+ false /* checked */);
+
+ // Setting the list triggers the InputMethodMenuItemChanged event.
+ InputMethodMenuManager::GetInstance()->SetCurrentInputMethodMenuItemList(
+ {item1, item2});
+ client.FlushMojoForTesting();
+
+ // IME controller received the menu items.
+ ASSERT_EQ(2u, ime_controller.menu_items_.size());
+ EXPECT_EQ("key1", ime_controller.menu_items_[0]->key);
+ EXPECT_TRUE(ime_controller.menu_items_[0]->checked);
+ EXPECT_EQ("key2", ime_controller.menu_items_[1]->key);
+ EXPECT_FALSE(ime_controller.menu_items_[1]->checked);
+}
+
+} // namespace
diff --git a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
index 9e6572e8f..ac2ba6d 100644
--- a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
@@ -88,11 +88,9 @@
// Must be available at login screen, so initialize before profile.
system_tray_client_ = base::MakeUnique<SystemTrayClient>();
- // TODO(jamescook): Enable in mash once converted to mojo.
- if (!ash_util::IsRunningInMash()) {
- ime_controller_client_ = base::MakeUnique<ImeControllerClient>(
- chromeos::input_method::InputMethodManager::Get());
- }
+ ime_controller_client_ = base::MakeUnique<ImeControllerClient>(
+ chromeos::input_method::InputMethodManager::Get());
+ ime_controller_client_->Init();
new_window_client_ = base::MakeUnique<ChromeNewWindowClient>();
volume_controller_ = base::MakeUnique<VolumeController>();
vpn_list_forwarder_ = base::MakeUnique<VpnListForwarder>();