summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkinaba <kinaba@chromium.org>2016-01-14 22:21:22 -0800
committerCommit bot <commit-bot@chromium.org>2016-01-15 06:22:28 +0000
commit842fb357b445690b16f5ba64067ec286e0f0f9bc (patch)
treee6e0c72f0e068eb2ffa7f8729ff9440ffb07fb46
parent3a119440b87b1bca6519479b76d0c94b92229b42 (diff)
downloadchromium_src-842fb357b445690b16f5ba64067ec286e0f0f9bc.zip
chromium_src-842fb357b445690b16f5ba64067ec286e0f0f9bc.tar.gz
chromium_src-842fb357b445690b16f5ba64067ec286e0f0f9bc.tar.bz2
Host-side implementation of ARC IME.
Changes directly below components/arc/ are for adding mojo IPC definitions and for boilerplate set-up code for the IPC handling. components/arc/ime/arc_ime_ipc_host* concentrates in converting the Chrome's IME framework interface to the defined IPC, so that the upper layer code becomes independent from Mojo details. components/arc/ime/arc_ime_bridge* implements ui::TextInputClient for ARC windows. All it does is to manage focused ARC window and to forward all IME related things to the arc_ime_ipc_host. BUG=574311 Review URL: https://codereview.chromium.org/1557943003 Cr-Commit-Position: refs/heads/master@{#369695}
-rw-r--r--components/arc.gypi6
-rw-r--r--components/arc/BUILD.gn6
-rw-r--r--components/arc/arc_bridge_service.cc25
-rw-r--r--components/arc/arc_bridge_service.h11
-rw-r--r--components/arc/arc_service_manager.cc2
-rw-r--r--components/arc/arc_service_manager.h2
-rw-r--r--components/arc/common/arc_bridge.mojom4
-rw-r--r--components/arc/common/ime.mojom59
-rw-r--r--components/arc/ime/DEPS6
-rw-r--r--components/arc/ime/arc_ime_bridge.cc218
-rw-r--r--components/arc/ime/arc_ime_bridge.h105
-rw-r--r--components/arc/ime/arc_ime_ipc_host.cc132
-rw-r--r--components/arc/ime/arc_ime_ipc_host.h63
13 files changed, 639 insertions, 0 deletions
diff --git a/components/arc.gypi b/components/arc.gypi
index cb38d29..a5e0835 100644
--- a/components/arc.gypi
+++ b/components/arc.gypi
@@ -19,6 +19,7 @@
'../ipc/ipc.gyp:ipc',
'../ui/arc/arc.gyp:arc',
'../ui/aura/aura.gyp:aura',
+ '../ui/base/ime/ui_base_ime.gyp:ui_base_ime',
'../ui/base/ui_base.gyp:ui_base',
'../ui/events/events.gyp:events_base',
],
@@ -34,6 +35,10 @@
'arc/arc_service_manager.h',
'arc/clipboard/arc_clipboard_bridge.cc',
'arc/clipboard/arc_clipboard_bridge.h',
+ 'arc/ime/arc_ime_bridge.cc',
+ 'arc/ime/arc_ime_bridge.h',
+ 'arc/ime/arc_ime_ipc_host.cc',
+ 'arc/ime/arc_ime_ipc_host.h',
'arc/input/arc_input_bridge.h',
'arc/input/arc_input_bridge_impl.cc',
'arc/input/arc_input_bridge_impl.h',
@@ -79,6 +84,7 @@
'arc/common/arc_bridge.mojom',
'arc/common/auth.mojom',
'arc/common/clipboard.mojom',
+ 'arc/common/ime.mojom',
'arc/common/input.mojom',
'arc/common/notifications.mojom',
'arc/common/power.mojom',
diff --git a/components/arc/BUILD.gn b/components/arc/BUILD.gn
index 6ea143d..e753041 100644
--- a/components/arc/BUILD.gn
+++ b/components/arc/BUILD.gn
@@ -17,6 +17,10 @@ static_library("arc") {
"auth/arc_auth_service.h",
"clipboard/arc_clipboard_bridge.cc",
"clipboard/arc_clipboard_bridge.h",
+ "ime/arc_ime_bridge.cc",
+ "ime/arc_ime_bridge.h",
+ "ime/arc_ime_ipc_host.cc",
+ "ime/arc_ime_ipc_host.h",
"input/arc_input_bridge.h",
"input/arc_input_bridge_impl.cc",
"input/arc_input_bridge_impl.h",
@@ -41,6 +45,7 @@ static_library("arc") {
"//ui/arc",
"//ui/aura",
"//ui/base:base",
+ "//ui/base/ime",
"//ui/events",
"//ui/events:dom_keycode_converter",
]
@@ -56,6 +61,7 @@ mojom("arc_bindings") {
"common/arc_bridge.mojom",
"common/auth.mojom",
"common/clipboard.mojom",
+ "common/ime.mojom",
"common/input.mojom",
"common/notifications.mojom",
"common/power.mojom",
diff --git a/components/arc/arc_bridge_service.cc b/components/arc/arc_bridge_service.cc
index 9183477..cbb8bde 100644
--- a/components/arc/arc_bridge_service.cc
+++ b/components/arc/arc_bridge_service.cc
@@ -129,6 +129,30 @@ void ArcBridgeService::CloseClipboardChannel() {
FOR_EACH_OBSERVER(Observer, observer_list(), OnClipboardInstanceClosed());
}
+void ArcBridgeService::OnImeInstanceReady(ImeInstancePtr ime_ptr) {
+ DCHECK(CalledOnValidThread());
+ temporary_ime_ptr_ = std::move(ime_ptr);
+ temporary_ime_ptr_.QueryVersion(base::Bind(
+ &ArcBridgeService::OnImeVersionReady, weak_factory_.GetWeakPtr()));
+}
+
+void ArcBridgeService::OnImeVersionReady(int32_t version) {
+ DCHECK(CalledOnValidThread());
+ ime_ptr_ = std::move(temporary_ime_ptr_);
+ ime_ptr_.set_connection_error_handler(base::Bind(
+ &ArcBridgeService::CloseImeChannel, weak_factory_.GetWeakPtr()));
+ FOR_EACH_OBSERVER(Observer, observer_list(), OnImeInstanceReady());
+}
+
+void ArcBridgeService::CloseImeChannel() {
+ DCHECK(CalledOnValidThread());
+ if (!ime_ptr_)
+ return;
+
+ ime_ptr_.reset();
+ FOR_EACH_OBSERVER(Observer, observer_list(), OnImeInstanceClosed());
+}
+
void ArcBridgeService::OnInputInstanceReady(InputInstancePtr input_ptr) {
DCHECK(CalledOnValidThread());
temporary_input_ptr_ = std::move(input_ptr);
@@ -302,6 +326,7 @@ void ArcBridgeService::CloseAllChannels() {
CloseAppChannel();
CloseAuthChannel();
CloseClipboardChannel();
+ CloseImeChannel();
CloseInputChannel();
CloseNotificationsChannel();
ClosePowerChannel();
diff --git a/components/arc/arc_bridge_service.h b/components/arc/arc_bridge_service.h
index 509c79c..94871c7 100644
--- a/components/arc/arc_bridge_service.h
+++ b/components/arc/arc_bridge_service.h
@@ -85,6 +85,10 @@ class ArcBridgeService : public ArcBridgeHost {
virtual void OnClipboardInstanceReady() {}
virtual void OnClipboardInstanceClosed() {}
+ // Called whenever the ARC IME interface state changes.
+ virtual void OnImeInstanceReady() {}
+ virtual void OnImeInstanceClosed() {}
+
// Called whenever the ARC input interface state changes.
virtual void OnInputInstanceReady() {}
virtual void OnInputInstanceClosed() {}
@@ -149,6 +153,7 @@ class ArcBridgeService : public ArcBridgeHost {
AppInstance* app_instance() { return app_ptr_.get(); }
AuthInstance* auth_instance() { return auth_ptr_.get(); }
ClipboardInstance* clipboard_instance() { return clipboard_ptr_.get(); }
+ ImeInstance* ime_instance() { return ime_ptr_.get(); }
InputInstance* input_instance() { return input_ptr_.get(); }
NotificationsInstance* notifications_instance() {
return notifications_ptr_.get();
@@ -161,6 +166,7 @@ class ArcBridgeService : public ArcBridgeHost {
int32_t app_version() const { return app_ptr_.version(); }
int32_t auth_version() const { return auth_ptr_.version(); }
int32_t clipboard_version() const { return clipboard_ptr_.version(); }
+ int32_t ime_version() const { return ime_ptr_.version(); }
int32_t input_version() const { return input_ptr_.version(); }
int32_t notifications_version() const { return notifications_ptr_.version(); }
int32_t power_version() const { return power_ptr_.version(); }
@@ -172,6 +178,7 @@ class ArcBridgeService : public ArcBridgeHost {
void OnAppInstanceReady(AppInstancePtr app_ptr) override;
void OnAuthInstanceReady(AuthInstancePtr auth_ptr) override;
void OnClipboardInstanceReady(ClipboardInstancePtr clipboard_ptr) override;
+ void OnImeInstanceReady(ImeInstancePtr ime_ptr) override;
void OnInputInstanceReady(InputInstancePtr input_ptr) override;
void OnNotificationsInstanceReady(
NotificationsInstancePtr notifications_ptr) override;
@@ -213,6 +220,7 @@ class ArcBridgeService : public ArcBridgeHost {
void CloseAppChannel();
void CloseAuthChannel();
void CloseClipboardChannel();
+ void CloseImeChannel();
void CloseInputChannel();
void CloseNotificationsChannel();
void ClosePowerChannel();
@@ -224,6 +232,7 @@ class ArcBridgeService : public ArcBridgeHost {
void OnAppVersionReady(int32_t version);
void OnAuthVersionReady(int32_t version);
void OnClipboardVersionReady(int32_t version);
+ void OnImeVersionReady(int32_t version);
void OnInputVersionReady(int32_t version);
void OnNotificationsVersionReady(int32_t version);
void OnPowerVersionReady(int32_t version);
@@ -235,6 +244,7 @@ class ArcBridgeService : public ArcBridgeHost {
AppInstancePtr app_ptr_;
AuthInstancePtr auth_ptr_;
ClipboardInstancePtr clipboard_ptr_;
+ ImeInstancePtr ime_ptr_;
InputInstancePtr input_ptr_;
NotificationsInstancePtr notifications_ptr_;
PowerInstancePtr power_ptr_;
@@ -251,6 +261,7 @@ class ArcBridgeService : public ArcBridgeHost {
AppInstancePtr temporary_app_ptr_;
AuthInstancePtr temporary_auth_ptr_;
ClipboardInstancePtr temporary_clipboard_ptr_;
+ ImeInstancePtr temporary_ime_ptr_;
InputInstancePtr temporary_input_ptr_;
NotificationsInstancePtr temporary_notifications_ptr_;
PowerInstancePtr temporary_power_ptr_;
diff --git a/components/arc/arc_service_manager.cc b/components/arc/arc_service_manager.cc
index 4146738..3769c03 100644
--- a/components/arc/arc_service_manager.cc
+++ b/components/arc/arc_service_manager.cc
@@ -12,6 +12,7 @@
#include "components/arc/arc_bridge_service_impl.h"
#include "components/arc/auth/arc_auth_service.h"
#include "components/arc/clipboard/arc_clipboard_bridge.h"
+#include "components/arc/ime/arc_ime_bridge.h"
#include "components/arc/input/arc_input_bridge.h"
#include "components/arc/power/arc_power_bridge.h"
#include "components/arc/settings/arc_settings_bridge.h"
@@ -35,6 +36,7 @@ ArcServiceManager::ArcServiceManager(
new ArcBridgeServiceImpl(ArcBridgeBootstrap::Create())),
arc_auth_service_(std::move(auth_service)),
arc_clipboard_bridge_(new ArcClipboardBridge(arc_bridge_service_.get())),
+ arc_ime_bridge_(new ArcImeBridge(arc_bridge_service_.get())),
arc_input_bridge_(ArcInputBridge::Create(arc_bridge_service_.get())),
arc_settings_bridge_(std::move(settings_bridge)),
arc_power_bridge_(new ArcPowerBridge(arc_bridge_service_.get())),
diff --git a/components/arc/arc_service_manager.h b/components/arc/arc_service_manager.h
index 0eaa746..8b0bc8c 100644
--- a/components/arc/arc_service_manager.h
+++ b/components/arc/arc_service_manager.h
@@ -15,6 +15,7 @@ namespace arc {
class ArcAuthService;
class ArcBridgeService;
class ArcClipboardBridge;
+class ArcImeBridge;
class ArcInputBridge;
class ArcNotificationManager;
class ArcPowerBridge;
@@ -48,6 +49,7 @@ class ArcServiceManager {
// Individual services
scoped_ptr<ArcAuthService> arc_auth_service_;
scoped_ptr<ArcClipboardBridge> arc_clipboard_bridge_;
+ scoped_ptr<ArcImeBridge> arc_ime_bridge_;
scoped_ptr<ArcInputBridge> arc_input_bridge_;
scoped_ptr<ArcNotificationManager> arc_notification_manager_;
scoped_ptr<ArcSettingsBridge> arc_settings_bridge_;
diff --git a/components/arc/common/arc_bridge.mojom b/components/arc/common/arc_bridge.mojom
index 3844592..4e38413 100644
--- a/components/arc/common/arc_bridge.mojom
+++ b/components/arc/common/arc_bridge.mojom
@@ -7,6 +7,7 @@ module arc;
import "app.mojom";
import "auth.mojom";
import "clipboard.mojom";
+import "ime.mojom";
import "input.mojom";
import "notifications.mojom";
import "power.mojom";
@@ -28,6 +29,9 @@ interface ArcBridgeHost {
// Notifies Chrome that the ClipboardInstance interface is ready.
[MinVersion=2] OnClipboardInstanceReady@109(ClipboardInstance instance_ptr);
+ // Notifies Chrome that the ImeInstance interface is ready.
+ [MinVersion=3] OnImeInstanceReady@110(ImeInstance instance_ptr);
+
// Notifies Chrome that the InputInstnace interface is ready.
OnInputInstanceReady@101(InputInstance instance_ptr);
diff --git a/components/arc/common/ime.mojom b/components/arc/common/ime.mojom
new file mode 100644
index 0000000..9722ddf
--- /dev/null
+++ b/components/arc/common/ime.mojom
@@ -0,0 +1,59 @@
+// Copyright 2016 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 arc;
+
+// Represents the type of text input field currently focused.
+enum TextInputType {
+ NONE,
+ TEXT,
+ PASSWORD,
+ SEARCH,
+ EMAIL,
+ NUMBER,
+ TELEPHONE,
+ URL,
+ DATE,
+ TIME,
+ DATETIME,
+};
+
+// Represents the text insertion points in the container screen coordinates.
+struct CursorRect {
+ int32 left;
+ int32 top;
+ int32 right;
+ int32 bottom;
+};
+
+// Represents a single segment of text currently composed by IME.
+struct CompositionSegment {
+ // Start offset of the segment in UTF-16 index.
+ uint32 start_offset;
+ // End offset of the segment in UTF-16 index.
+ uint32 end_offset;
+ // Indicates that this segment should be emphasized.
+ bool emphasized;
+};
+
+interface ImeHost {
+ // Notifies Chrome that the text input focus is changed.
+ OnTextInputTypeChanged(TextInputType type);
+
+ // Notifies Chrome that the cursor poisition has changed.
+ OnCursorRectChanged(CursorRect rect);
+};
+
+interface ImeInstance {
+ Init(ImeHost host_ptr);
+
+ // Sets composition text and attributes requested by the host IME.
+ SetCompositionText(string text, array<CompositionSegment> segments);
+
+ // Commits the last set composition text and clears the composition.
+ ConfirmCompositionText();
+
+ // Commits the specified text and clears the composition.
+ InsertText(string text);
+};
diff --git a/components/arc/ime/DEPS b/components/arc/ime/DEPS
new file mode 100644
index 0000000..6d03f02
--- /dev/null
+++ b/components/arc/ime/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+ui/aura",
+ "+ui/base/ime",
+ "+ui/events",
+ "+ui/gfx/geometry",
+]
diff --git a/components/arc/ime/arc_ime_bridge.cc b/components/arc/ime/arc_ime_bridge.cc
new file mode 100644
index 0000000..1606f3f
--- /dev/null
+++ b/components/arc/ime/arc_ime_bridge.cc
@@ -0,0 +1,218 @@
+// Copyright 2016 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 "components/arc/ime/arc_ime_bridge.h"
+
+#include "base/logging.h"
+#include "ui/aura/client/focus_client.h"
+#include "ui/aura/env.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_tree_host.h"
+#include "ui/base/ime/input_method.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/events/event.h"
+
+namespace arc {
+
+namespace {
+
+// TODO(kinaba): handling ARC windows by window names is the same temporary
+// workaroud also used in arc/input/arc_input_bridge_impl.cc. We need to move
+// it to more solid implementation after focus handling in components/exo is
+// stabilized.
+bool IsArcWindow(const aura::Window* window) {
+ return window->name() == "ExoSurface";
+}
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// ArcImeBridge main implementation:
+
+ArcImeBridge::ArcImeBridge(ArcBridgeService* arc_bridge_service)
+ : ipc_host_(this, arc_bridge_service),
+ ime_type_(ui::TEXT_INPUT_TYPE_NONE) {
+ aura::Env* env = aura::Env::GetInstanceDontCreate();
+ if (env)
+ env->AddObserver(this);
+}
+
+ArcImeBridge::~ArcImeBridge() {
+ ui::InputMethod* const input_method = GetInputMethod();
+ if (input_method)
+ input_method->DetachTextInputClient(this);
+
+ for (aura::Window* window : arc_windows_.windows())
+ window->RemoveObserver(this);
+ for (aura::Window* root : observing_root_windows_.windows())
+ aura::client::GetFocusClient(root)->RemoveObserver(this);
+ aura::Env* env = aura::Env::GetInstanceDontCreate();
+ if (env)
+ env->RemoveObserver(this);
+}
+
+ui::InputMethod* ArcImeBridge::GetInputMethod() {
+ if (!focused_arc_window_.has_windows())
+ return nullptr;
+ return focused_arc_window_.windows().front()->GetHost()->GetInputMethod();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Overridden from aura::EnvObserver:
+
+void ArcImeBridge::OnWindowInitialized(aura::Window* new_window) {
+ if (IsArcWindow(new_window)) {
+ arc_windows_.Add(new_window);
+ new_window->AddObserver(this);
+ }
+}
+
+void ArcImeBridge::OnWindowAddedToRootWindow(aura::Window* window) {
+ aura::Window* root = window->GetRootWindow();
+ aura::client::FocusClient* focus_client = aura::client::GetFocusClient(root);
+ if (focus_client && !observing_root_windows_.Contains(root)) {
+ focus_client->AddObserver(this);
+ observing_root_windows_.Add(root);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Overridden from aura::client::FocusChangeObserver:
+
+void ArcImeBridge::OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) {
+ if (focused_arc_window_.Contains(lost_focus)) {
+ ui::InputMethod* const input_method = GetInputMethod();
+ if (input_method)
+ input_method->DetachTextInputClient(this);
+ focused_arc_window_.Remove(lost_focus);
+ }
+
+ if (IsArcWindow(gained_focus)) {
+ focused_arc_window_.Add(gained_focus);
+ ui::InputMethod* const input_method = GetInputMethod();
+ if (input_method)
+ input_method->SetFocusedTextInputClient(this);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Overridden from arc::ArcImeIpcHost::Delegate
+
+void ArcImeBridge::OnTextInputTypeChanged(ui::TextInputType type) {
+ if (ime_type_ == type)
+ return;
+ ime_type_ = type;
+
+ ui::InputMethod* const input_method = GetInputMethod();
+ if (input_method)
+ input_method->OnTextInputTypeChanged(this);
+}
+
+void ArcImeBridge::OnCursorRectChanged(const gfx::Rect& rect) {
+ if (cursor_rect_ == rect)
+ return;
+ cursor_rect_ = rect;
+
+ ui::InputMethod* const input_method = GetInputMethod();
+ if (input_method)
+ input_method->OnCaretBoundsChanged(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Oberridden from ui::TextInputClient:
+
+void ArcImeBridge::SetCompositionText(
+ const ui::CompositionText& composition) {
+ ipc_host_.SendSetCompositionText(composition);
+}
+
+void ArcImeBridge::ConfirmCompositionText() {
+ ipc_host_.SendConfirmCompositionText();
+}
+
+void ArcImeBridge::ClearCompositionText() {
+ ipc_host_.SendInsertText(base::string16());
+}
+
+void ArcImeBridge::InsertText(const base::string16& text) {
+ ipc_host_.SendInsertText(text);
+}
+
+void ArcImeBridge::InsertChar(const ui::KeyEvent& event) {
+ // Drop 0x00-0x1f (C0 controls), 0x7f (DEL), and 0x80-0x9f (C1 controls).
+ // See: https://en.wikipedia.org/wiki/Unicode_control_characters
+ // They are control characters and not treated as a text insertion.
+ const base::char16 ch = event.GetCharacter();
+ const bool is_control_char = (0x00 <= ch && ch <= 0x1f) ||
+ (0x7f <= ch && ch <= 0x9f);
+
+ if (!is_control_char && !ui::IsSystemKeyModifier(event.flags()))
+ ipc_host_.SendInsertText(base::string16(1, event.GetText()));
+}
+
+ui::TextInputType ArcImeBridge::GetTextInputType() const {
+ return ime_type_;
+}
+
+gfx::Rect ArcImeBridge::GetCaretBounds() const {
+ return cursor_rect_;
+}
+
+ui::TextInputMode ArcImeBridge::GetTextInputMode() const {
+ return ui::TEXT_INPUT_MODE_DEFAULT;
+}
+
+int ArcImeBridge::GetTextInputFlags() const {
+ return ui::TEXT_INPUT_FLAG_NONE;
+}
+
+bool ArcImeBridge::CanComposeInline() const {
+ return true;
+}
+
+bool ArcImeBridge::GetCompositionCharacterBounds(
+ uint32_t index, gfx::Rect* rect) const {
+ return false;
+}
+
+bool ArcImeBridge::HasCompositionText() const {
+ return true;
+}
+
+bool ArcImeBridge::GetTextRange(gfx::Range* range) const {
+ return false;
+}
+
+bool ArcImeBridge::GetCompositionTextRange(gfx::Range* range) const {
+ return false;
+}
+
+bool ArcImeBridge::GetSelectionRange(gfx::Range* range) const {
+ return false;
+}
+
+bool ArcImeBridge::SetSelectionRange(const gfx::Range& range) {
+ return false;
+}
+
+bool ArcImeBridge::DeleteRange(const gfx::Range& range) {
+ return false;
+}
+
+bool ArcImeBridge::GetTextFromRange(
+ const gfx::Range& range, base::string16* text) const {
+ return false;
+}
+
+bool ArcImeBridge::ChangeTextDirectionAndLayoutAlignment(
+ base::i18n::TextDirection direction) {
+ return false;
+}
+
+bool ArcImeBridge::IsEditCommandEnabled(int command_id) {
+ return false;
+}
+
+} // namespace arc
diff --git a/components/arc/ime/arc_ime_bridge.h b/components/arc/ime/arc_ime_bridge.h
new file mode 100644
index 0000000..65b67f8
--- /dev/null
+++ b/components/arc/ime/arc_ime_bridge.h
@@ -0,0 +1,105 @@
+// Copyright 2016 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.
+
+#ifndef COMPONENTS_ARC_IME_ARC_IME_BRIDGE_H_
+#define COMPONENTS_ARC_IME_ARC_IME_BRIDGE_H_
+
+#include "base/macros.h"
+#include "components/arc/ime/arc_ime_ipc_host.h"
+#include "ui/aura/client/focus_change_observer.h"
+#include "ui/aura/env_observer.h"
+#include "ui/aura/window_observer.h"
+#include "ui/aura/window_tracker.h"
+#include "ui/base/ime/text_input_client.h"
+#include "ui/base/ime/text_input_flags.h"
+#include "ui/base/ime/text_input_type.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ui {
+class InputMethod;
+}
+
+namespace arc {
+
+class ArcBridgeService;
+
+// This class implements ui::TextInputClient and makes ARC windows behave
+// as a text input target in Chrome OS environment.
+class ArcImeBridge : public ArcImeIpcHost::Delegate,
+ public aura::EnvObserver,
+ public aura::WindowObserver,
+ public aura::client::FocusChangeObserver,
+ public ui::TextInputClient {
+ public:
+ explicit ArcImeBridge(ArcBridgeService* arc_bridge_service);
+ ~ArcImeBridge() override;
+
+ // Overridden from aura::EnvObserver:
+ void OnWindowInitialized(aura::Window* new_window) override;
+
+ // Overridden from aura::WindowObserver:
+ void OnWindowAddedToRootWindow(aura::Window* window) override;
+
+ // Overridden from aura::client::FocusChangeObserver:
+ void OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) override;
+
+ // Overridden from ArcImeIpcHost::Delegate:
+ void OnTextInputTypeChanged(ui::TextInputType type) override;
+ void OnCursorRectChanged(const gfx::Rect& rect) override;
+
+ // Overridden from ui::TextInputClient:
+ void SetCompositionText(const ui::CompositionText& composition) override;
+ void ConfirmCompositionText() override;
+ void ClearCompositionText() override;
+ void InsertText(const base::string16& text) override;
+ void InsertChar(const ui::KeyEvent& event) override;
+ ui::TextInputType GetTextInputType() const override;
+ gfx::Rect GetCaretBounds() const override;
+
+ // Overridden from ui::TextInputClient (with default implementation):
+ // TODO(kinaba): Support each of these methods to the extent possible in
+ // Android input method API.
+ ui::TextInputMode GetTextInputMode() const override;
+ int GetTextInputFlags() const override;
+ bool CanComposeInline() const override;
+ bool GetCompositionCharacterBounds(uint32_t index,
+ gfx::Rect* rect) const override;
+ bool HasCompositionText() const override;
+ bool GetTextRange(gfx::Range* range) const override;
+ bool GetCompositionTextRange(gfx::Range* range) const override;
+ bool GetSelectionRange(gfx::Range* range) const override;
+ bool SetSelectionRange(const gfx::Range& range) override;
+ bool DeleteRange(const gfx::Range& range) override;
+ bool GetTextFromRange(const gfx::Range& range,
+ base::string16* text) const override;
+ void OnInputMethodChanged() override {}
+ bool ChangeTextDirectionAndLayoutAlignment(
+ base::i18n::TextDirection direction) override;
+ void ExtendSelectionAndDelete(size_t before, size_t after) override {}
+ void EnsureCaretInRect(const gfx::Rect& rect) override {}
+ bool IsEditCommandEnabled(int command_id) override;
+ void SetEditCommandForNextKeyEvent(int command_id) override {}
+
+ private:
+ ui::InputMethod* GetInputMethod();
+
+ ArcImeIpcHost ipc_host_;
+ ui::TextInputType ime_type_;
+ gfx::Rect cursor_rect_;
+
+ aura::WindowTracker observing_root_windows_;
+ aura::WindowTracker arc_windows_;
+ aura::WindowTracker focused_arc_window_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArcImeBridge);
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_IME_ARC_IME_BRIDGE_H_
diff --git a/components/arc/ime/arc_ime_ipc_host.cc b/components/arc/ime/arc_ime_ipc_host.cc
new file mode 100644
index 0000000..93fcef4
--- /dev/null
+++ b/components/arc/ime/arc_ime_ipc_host.cc
@@ -0,0 +1,132 @@
+// Copyright 2016 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 "components/arc/ime/arc_ime_ipc_host.h"
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/arc/arc_bridge_service.h"
+#include "ui/base/ime/composition_text.h"
+#include "ui/base/ime/text_input_type.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace arc {
+namespace {
+
+ui::TextInputType ConvertTextInputType(arc::TextInputType ipc_type) {
+ // The two enum types are similar, but intentionally made not identical.
+ // We cannot force them to be in sync. If we do, updates in ui::TextInputType
+ // must always be propagated to the arc::TextInputType mojo definition in
+ // ARC container side, which is in a different repository than Chromium.
+ // We don't want such dependency.
+ //
+ // That's why we need a lengthy switch statement instead of static_cast
+ // guarded by a static assert on the two enums to be in sync.
+ switch (ipc_type) {
+ case arc::TEXT_INPUT_TYPE_NONE:
+ return ui::TEXT_INPUT_TYPE_NONE;
+ case arc::TEXT_INPUT_TYPE_TEXT:
+ return ui::TEXT_INPUT_TYPE_TEXT;
+ case arc::TEXT_INPUT_TYPE_PASSWORD:
+ return ui::TEXT_INPUT_TYPE_PASSWORD;
+ case arc::TEXT_INPUT_TYPE_SEARCH:
+ return ui::TEXT_INPUT_TYPE_SEARCH;
+ case arc::TEXT_INPUT_TYPE_EMAIL:
+ return ui::TEXT_INPUT_TYPE_EMAIL;
+ case arc::TEXT_INPUT_TYPE_NUMBER:
+ return ui::TEXT_INPUT_TYPE_NUMBER;
+ case arc::TEXT_INPUT_TYPE_TELEPHONE:
+ return ui::TEXT_INPUT_TYPE_TELEPHONE;
+ case arc::TEXT_INPUT_TYPE_URL:
+ return ui::TEXT_INPUT_TYPE_URL;
+ case arc::TEXT_INPUT_TYPE_DATE:
+ return ui::TEXT_INPUT_TYPE_DATE;
+ case arc::TEXT_INPUT_TYPE_TIME:
+ return ui::TEXT_INPUT_TYPE_TIME;
+ case arc::TEXT_INPUT_TYPE_DATETIME:
+ return ui::TEXT_INPUT_TYPE_DATE_TIME_LOCAL;
+ default:
+ return ui::TEXT_INPUT_TYPE_TEXT;
+ }
+}
+
+mojo::Array<arc::CompositionSegmentPtr> ConvertSegments(
+ const ui::CompositionText& composition) {
+ mojo::Array<arc::CompositionSegmentPtr> segments =
+ mojo::Array<arc::CompositionSegmentPtr>::New(0);
+ for (const ui::CompositionUnderline& underline : composition.underlines) {
+ arc::CompositionSegmentPtr segment = arc::CompositionSegment::New();
+ segment->start_offset = underline.start_offset;
+ segment->end_offset = underline.end_offset;
+ segment->emphasized = (underline.thick ||
+ (composition.selection.start() == underline.start_offset &&
+ composition.selection.end() == underline.end_offset));
+ segments.push_back(std::move(segment));
+ }
+ return segments;
+}
+
+} // namespace
+
+ArcImeIpcHost::ArcImeIpcHost(Delegate* delegate,
+ ArcBridgeService* bridge_service)
+ : binding_(this), delegate_(delegate), bridge_service_(bridge_service) {
+ bridge_service_->AddObserver(this);
+}
+
+ArcImeIpcHost::~ArcImeIpcHost() {
+ bridge_service_->RemoveObserver(this);
+}
+
+void ArcImeIpcHost::OnImeInstanceReady() {
+ arc::ImeHostPtr host;
+ binding_.Bind(mojo::GetProxy(&host));
+ bridge_service_->ime_instance()->Init(std::move(host));
+}
+
+void ArcImeIpcHost::SendSetCompositionText(
+ const ui::CompositionText& composition) {
+ ImeInstance* ime_instance = bridge_service_->ime_instance();
+ if (!ime_instance) {
+ LOG(ERROR) << "ArcImeInstance method called before being ready.";
+ return;
+ }
+
+ ime_instance->SetCompositionText(base::UTF16ToUTF8(composition.text),
+ ConvertSegments(composition));
+}
+
+void ArcImeIpcHost::SendConfirmCompositionText() {
+ ImeInstance* ime_instance = bridge_service_->ime_instance();
+ if (!ime_instance) {
+ LOG(ERROR) << "ArcImeInstance method called before being ready.";
+ return;
+ }
+
+ ime_instance->ConfirmCompositionText();
+}
+
+void ArcImeIpcHost::SendInsertText(const base::string16& text) {
+ ImeInstance* ime_instance = bridge_service_->ime_instance();
+ if (!ime_instance) {
+ LOG(ERROR) << "ArcImeInstance method called before being ready.";
+ return;
+ }
+
+ ime_instance->InsertText(base::UTF16ToUTF8(text));
+}
+
+void ArcImeIpcHost::OnTextInputTypeChanged(arc::TextInputType type) {
+ delegate_->OnTextInputTypeChanged(ConvertTextInputType(type));
+}
+
+void ArcImeIpcHost::OnCursorRectChanged(arc::CursorRectPtr rect) {
+ delegate_->OnCursorRectChanged(gfx::Rect(
+ rect->left,
+ rect->top,
+ rect->right - rect->left,
+ rect->bottom - rect->top));
+}
+
+} // namespace arc
diff --git a/components/arc/ime/arc_ime_ipc_host.h b/components/arc/ime/arc_ime_ipc_host.h
new file mode 100644
index 0000000..e050e84
--- /dev/null
+++ b/components/arc/ime/arc_ime_ipc_host.h
@@ -0,0 +1,63 @@
+// Copyright 2016 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.
+
+#ifndef COMPONENTS_ARC_IME_ARC_IME_IPC_HOST_H_
+#define COMPONENTS_ARC_IME_ARC_IME_IPC_HOST_H_
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/arc/arc_bridge_service.h"
+#include "components/arc/common/ime.mojom.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "ui/base/ime/text_input_type.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace gfx {
+class Rect;
+} // namespace gfx
+
+namespace ui {
+struct CompositionText;
+} // namespace ui
+
+namespace arc {
+
+// This class encapsulates the detail of IME related IPC between
+// Chromium and the ARC container.
+class ArcImeIpcHost : public ImeHost,
+ public ArcBridgeService::Observer {
+ public:
+ // Received IPCs are deserialized and passed to this delegate.
+ class Delegate {
+ public:
+ virtual void OnTextInputTypeChanged(ui::TextInputType type) = 0;
+ virtual void OnCursorRectChanged(const gfx::Rect& rect) = 0;
+ };
+
+ ArcImeIpcHost(Delegate* delegate, ArcBridgeService* bridge_service);
+ ~ArcImeIpcHost() override;
+
+ // arc::ArcBridgeService::Observer:
+ void OnImeInstanceReady() override;
+
+ // Serializes and sends IME related requests through IPCs.
+ void SendSetCompositionText(const ui::CompositionText& composition);
+ void SendConfirmCompositionText();
+ void SendInsertText(const base::string16& text);
+
+ // arc::ImeHost:
+ void OnTextInputTypeChanged(arc::TextInputType type) override;
+ void OnCursorRectChanged(arc::CursorRectPtr rect) override;
+
+ private:
+ mojo::Binding<ImeHost> binding_;
+ Delegate* const delegate_;
+ ArcBridgeService* const bridge_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArcImeIpcHost);
+};
+
+} // namespace arc
+
+#endif // COMPONENTS_ARC_IME_ARC_IME_IPC_HOST_H_