summaryrefslogtreecommitdiffstats
path: root/ui/base/ime
diff options
context:
space:
mode:
authornona@chromium.org <nona@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-21 04:50:36 +0000
committernona@chromium.org <nona@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-21 04:50:36 +0000
commit17dc1ffebada70c3144c88f693ec828f3e7b2817 (patch)
treebebfc759e1be1e89cb30e4d7bd02e379b2c50aef /ui/base/ime
parent9db7d8a03a6d20ae3ba21fbe9153151138a032cc (diff)
downloadchromium_src-17dc1ffebada70c3144c88f693ec828f3e7b2817.zip
chromium_src-17dc1ffebada70c3144c88f693ec828f3e7b2817.tar.gz
chromium_src-17dc1ffebada70c3144c88f693ec828f3e7b2817.tar.bz2
Replace libibus api to chrome dbus module.
This CL depends on codereview.chromium.org/10447022 for testing. This CL contains following changes: 1. Manages connection between ibus-daemon and chrome manually instead of libibus. Instead of "connect" and "disconnect" signals, we manually call OnConnected or OnDisconnected in chrome/browser/chromeos/input_method/ibus_controller_impl.cc which launches or kills the ibus-daemon. 2. Manages the life span of input context. The instance of InputContextClient is managed by DBusThreadManager as singleton object. The InputContextClient handles only one input context but it is sufficient. 3. Handles input context related method call or signal without libibus. There are no regression about handling method call and signal handling. BUG=126947 TEST=unit_tests,chromeos_unittests,ui_unittests,dbus_unittests and manually checked on alex Review URL: https://chromiumcodereview.appspot.com/10388220 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@143346 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/base/ime')
-rw-r--r--ui/base/ime/ibus_client.h82
-rw-r--r--ui/base/ime/ibus_client_impl.cc260
-rw-r--r--ui/base/ime/ibus_client_impl.h42
-rw-r--r--ui/base/ime/ime.gypi22
-rw-r--r--ui/base/ime/ime_unittests.gypi16
-rw-r--r--ui/base/ime/input_method_factory.cc6
-rw-r--r--ui/base/ime/input_method_ibus.cc324
-rw-r--r--ui/base/ime/input_method_ibus.h90
-rw-r--r--ui/base/ime/input_method_ibus_unittest.cc250
-rw-r--r--ui/base/ime/mock_ibus_client.cc170
-rw-r--r--ui/base/ime/mock_ibus_client.h45
-rw-r--r--ui/base/ime/mock_input_method.cc2
-rw-r--r--ui/base/ime/mock_input_method.h2
-rw-r--r--ui/base/ime/text_input_test_support.cc2
14 files changed, 652 insertions, 661 deletions
diff --git a/ui/base/ime/ibus_client.h b/ui/base/ime/ibus_client.h
index ca13ec0..fb679cf 100644
--- a/ui/base/ime/ibus_client.h
+++ b/ui/base/ime/ibus_client.h
@@ -6,17 +6,15 @@
#define UI_BASE_IME_IBUS_CLIENT_H_
#pragma once
-#include <glib/gtypes.h>
-
#include "base/basictypes.h"
#include "base/event_types.h"
#include "base/string16.h"
+#include "chromeos/dbus/ibus/ibus_client.h"
+#include "chromeos/dbus/ibus/ibus_input_context_client.h"
+#include "chromeos/dbus/ibus/ibus_text.h"
#include "ui/base/events.h"
#include "ui/base/ui_export.h"
-
-typedef struct _IBusBus IBusBus;
-typedef struct _IBusInputContext IBusInputContext;
-typedef struct _IBusText IBusText;
+#include "ui/gfx/rect.h"
namespace gfx {
class Rect;
@@ -29,6 +27,8 @@ namespace internal {
// An interface implemented by the object that sends and receives an event to
// and from ibus-daemon.
+// TODO(nona): Remove all methods except GetInputMethodType and
+// SetCursorLocation.
class UI_EXPORT IBusClient {
public:
// A class to hold all data related to a key event being processed by the
@@ -45,10 +45,13 @@ class UI_EXPORT IBusClient {
// context.
class PendingCreateICRequest {
public:
- virtual ~PendingCreateICRequest() {};
- // Stores the result input context to |input_method_|, or abandon it if
- // |input_method_| is NULL.
- virtual void StoreOrAbandonInputContext(IBusInputContext* ic) = 0;
+ virtual ~PendingCreateICRequest() {}
+ // Set up signal handlers, or destroy object proxy if the input context is
+ // already abandoned.
+ virtual void InitOrAbandonInputContext() = 0;
+
+ // Called if the create input context method call is failed.
+ virtual void OnCreateInputContextFailed() = 0;
};
enum InlineCompositionCapability {
@@ -66,59 +69,44 @@ class UI_EXPORT IBusClient {
virtual ~IBusClient() {}
- // Gets a D-Bus connection to ibus-daemon. An implementation should establish
- // a connection to the daemon when the method is called first. After that, the
- // implementation should return the same object as before.
- virtual IBusBus* GetConnection() = 0;
+ // Returns true if the connection to ibus-daemon is established.
+ virtual bool IsConnected() = 0;
- // Returns true if |bus| is connected.
- virtual bool IsConnected(IBusBus* bus) = 0;
+ // Returns true if the input context is ready to use.
+ virtual bool IsContextReady() = 0;
// Creates a new input context asynchronously. An implementation has to call
// PendingCreateICRequest::StoreOrAbandonInputContext() with the newly created
// context when the asynchronous request succeeds.
- virtual void CreateContext(IBusBus* bus,
- PendingCreateICRequest* request) = 0;
+ // TODO(nona): We can omit the first argument(need unittests fix).
+ virtual void CreateContext(PendingCreateICRequest* request) = 0;
- // Destroys the proxy object for the |context|. An implementation must send
- // "destroy" signal to the context object.
- virtual void DestroyProxy(IBusInputContext* context) = 0;
+ // Destroys the proxy object in input context client.
+ virtual void DestroyProxy() = 0;
- // Updates the set of capabilities of the |context|.
- virtual void SetCapabilities(IBusInputContext* context,
- InlineCompositionCapability inline_type) = 0;
+ // Updates the set of capabilities.
+ virtual void SetCapabilities(InlineCompositionCapability inline_type) = 0;
- // Focuses the |context| asynchronously.
- virtual void FocusIn(IBusInputContext* context) = 0;
- // Blurs the |context| asynchronously.
- virtual void FocusOut(IBusInputContext* context) = 0;
- // Resets the |context| asynchronously.
- virtual void Reset(IBusInputContext* context) = 0;
+ // Focuses the context asynchronously.
+ virtual void FocusIn() = 0;
+ // Blurs the context asynchronously.
+ virtual void FocusOut() = 0;
+ // Resets the context asynchronously.
+ virtual void Reset() = 0;
// Returns the current input method type.
virtual InputMethodType GetInputMethodType() = 0;
// Resets the cursor location asynchronously.
- virtual void SetCursorLocation(IBusInputContext* context,
- const gfx::Rect& cursor_location,
+ virtual void SetCursorLocation(const gfx::Rect& cursor_location,
const gfx::Rect& composition_head) = 0;
// Sends the key to ibus-daemon asynchronously.
- virtual void SendKeyEvent(IBusInputContext* context,
- uint32 keyval,
- uint32 keycode,
- uint32 state,
- PendingKeyEvent* pending_key) = 0;
-
- // Called by InputMethodIBus::OnUpdatePreeditText to convert |text| into a
- // CompositionText.
- virtual void ExtractCompositionText(IBusText* text,
- guint cursor_position,
- CompositionText* out_composition) = 0;
-
- // Called by InputMethodIBus::OnCommitText to convert |text| into a Unicode
- // string.
- virtual string16 ExtractCommitText(IBusText* text) = 0;
+ virtual void SendKeyEvent(
+ uint32 keyval,
+ uint32 keycode,
+ uint32 state,
+ const chromeos::IBusInputContextClient::ProcessKeyEventCallback& cb) = 0;
};
} // namespace internal
diff --git a/ui/base/ime/ibus_client_impl.cc b/ui/base/ime/ibus_client_impl.cc
index 3707b43..19f7595a 100644
--- a/ui/base/ime/ibus_client_impl.cc
+++ b/ui/base/ime/ibus_client_impl.cc
@@ -4,30 +4,16 @@
#include "ui/base/ime/ibus_client_impl.h"
-#include <ibus.h>
-#include <X11/X.h>
-#include <X11/Xlib.h>
-#undef FocusIn
-#undef FocusOut
-
#include "base/basictypes.h"
#include "base/i18n/char_iterator.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/ibus/ibus_client.h"
+#include "chromeos/dbus/ibus/ibus_input_context_client.h"
#include "ui/base/ime/composition_text.h"
#include "ui/gfx/rect.h"
-// input_method_ibus.cc assumes X and IBus use the same mask for Lock, Control,
-// Shift, Alt, and buttons. Check if the assumption is really correct.
-COMPILE_ASSERT(IBUS_LOCK_MASK == LockMask, test_mask);
-COMPILE_ASSERT(IBUS_CONTROL_MASK == ControlMask, test_mask);
-COMPILE_ASSERT(IBUS_SHIFT_MASK == ShiftMask, test_mask);
-COMPILE_ASSERT(IBUS_MOD1_MASK == Mod1Mask, test_mask);
-COMPILE_ASSERT(IBUS_BUTTON1_MASK == Button1Mask, test_mask);
-COMPILE_ASSERT(IBUS_BUTTON2_MASK == Button2Mask, test_mask);
-COMPILE_ASSERT(IBUS_BUTTON3_MASK == Button3Mask, test_mask);
-COMPILE_ASSERT(IBUS_RELEASE_MASK == ui::kIBusReleaseMask, test_mask);
-
namespace ui {
namespace internal {
@@ -35,34 +21,34 @@ namespace {
const char kClientName[] = "chrome";
-XKeyEvent* GetKeyEvent(XEvent* event) {
- DCHECK(event && (event->type == KeyPress || event->type == KeyRelease));
- return &event->xkey;
+// Following capability mask is introduced from
+// http://ibus.googlecode.com/svn/docs/ibus-1.4/ibus-ibustypes.html#IBusCapabilite
+const uint32 kIBusCapabilityPreeditText = 1U;
+const uint32 kIBusCapabilityFocus = 8U;
+
+chromeos::IBusInputContextClient* GetInputContextClient() {
+ chromeos::IBusInputContextClient* client =
+ chromeos::DBusThreadManager::Get()->GetIBusInputContextClient();
+ DCHECK(client->IsConnected());
+ return client;
}
-void ProcessKeyEventDone(IBusInputContext* context,
- GAsyncResult* res,
- IBusClient::PendingKeyEvent* data) {
- DCHECK(context);
- DCHECK(res);
- DCHECK(data);
- const gboolean handled = ibus_input_context_process_key_event_async_finish(
- context, res, NULL);
- data->ProcessPostIME(handled);
- delete data;
+// TODO(nona): Move this function to InputMethodIBus
+void CreateInputContextDone(IBusClientImpl::PendingCreateICRequest* ic_request,
+ const dbus::ObjectPath& object_path) {
+ chromeos::DBusThreadManager::Get()->GetIBusInputContextClient()
+ ->Initialize(chromeos::DBusThreadManager::Get()->GetIBusBus(),
+ object_path);
+
+ ic_request->InitOrAbandonInputContext();
+ delete ic_request;
}
-void CreateInputContextDone(IBusBus* bus,
- GAsyncResult* res,
- IBusClient::PendingCreateICRequest* data) {
- DCHECK(bus);
- DCHECK(res);
- DCHECK(data);
- IBusInputContext* context =
- ibus_bus_create_input_context_async_finish(bus, res, NULL);
- if (context)
- data->StoreOrAbandonInputContext(context);
- delete data;
+// TODO(nona): Move this function to InputMethodIBus
+void CreateInputContextFail(
+ IBusClientImpl::PendingCreateICRequest* ic_request) {
+ ic_request->OnCreateInputContextFailed();
+ delete ic_request;
}
} // namespace
@@ -73,51 +59,49 @@ IBusClientImpl::IBusClientImpl() {
IBusClientImpl::~IBusClientImpl() {
}
-IBusBus* IBusClientImpl::GetConnection() {
- ibus_init();
- return ibus_bus_new();
+bool IBusClientImpl::IsConnected() {
+ return chromeos::DBusThreadManager::Get()->GetIBusBus() != NULL;
}
-bool IBusClientImpl::IsConnected(IBusBus* bus) {
- return ibus_bus_is_connected(bus) == TRUE;
+bool IBusClientImpl::IsContextReady() {
+ return IsConnected() &&
+ chromeos::DBusThreadManager::Get()->GetIBusInputContextClient()
+ ->IsConnected();
}
-void IBusClientImpl::CreateContext(IBusBus* bus,
- PendingCreateICRequest* request) {
- ibus_bus_create_input_context_async(
- bus,
+void IBusClientImpl::CreateContext(PendingCreateICRequest* request) {
+ DCHECK(IsConnected());
+ chromeos::DBusThreadManager::Get()->GetIBusClient()->CreateInputContext(
kClientName,
- -1, // no timeout
- NULL, // no cancellation object
- reinterpret_cast<GAsyncReadyCallback>(CreateInputContextDone),
- request);
+ base::Bind(&CreateInputContextDone,
+ base::Unretained(request)),
+ base::Bind(&CreateInputContextFail,
+ base::Unretained(request)));
}
-void IBusClientImpl::DestroyProxy(IBusInputContext* context) {
- // ibus_proxy_destroy() will not really release the object, caller still need
- // to call g_object_unref() explicitly.
- ibus_proxy_destroy(IBUS_PROXY(context));
+void IBusClientImpl::DestroyProxy() {
+ GetInputContextClient()->ResetObjectProxy();
}
-void IBusClientImpl::SetCapabilities(IBusInputContext* context,
- InlineCompositionCapability inline_type) {
- // TODO(penghuang): support surrounding text.
- guint32 capabilities =
- inline_type == INLINE_COMPOSITION ? IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS
- : IBUS_CAP_FOCUS;
- ibus_input_context_set_capabilities(context, capabilities);
+void IBusClientImpl::SetCapabilities(InlineCompositionCapability inline_type) {
+ // TODO(nona): support surrounding text.
+ uint32 capabilities =
+ inline_type == INLINE_COMPOSITION ?
+ kIBusCapabilityPreeditText | kIBusCapabilityFocus
+ : kIBusCapabilityFocus;
+ GetInputContextClient()->SetCapabilities(capabilities);
}
-void IBusClientImpl::FocusIn(IBusInputContext* context) {
- ibus_input_context_focus_in(context);
+void IBusClientImpl::FocusIn() {
+ GetInputContextClient()->FocusIn();
}
-void IBusClientImpl::FocusOut(IBusInputContext* context) {
- ibus_input_context_focus_out(context);
+void IBusClientImpl::FocusOut() {
+ GetInputContextClient()->FocusOut();
}
-void IBusClientImpl::Reset(IBusInputContext* context) {
- ibus_input_context_reset(context);
+void IBusClientImpl::Reset() {
+ GetInputContextClient()->Reset();
}
IBusClient::InputMethodType IBusClientImpl::GetInputMethodType() {
@@ -125,128 +109,20 @@ IBusClient::InputMethodType IBusClientImpl::GetInputMethodType() {
return INPUT_METHOD_NORMAL;
}
-void IBusClientImpl::SetCursorLocation(IBusInputContext* context,
- const gfx::Rect& cursor_location,
+void IBusClientImpl::SetCursorLocation(const gfx::Rect& cursor_location,
const gfx::Rect& composition_head) {
- ibus_input_context_set_cursor_location(context,
- cursor_location.x(),
- cursor_location.y(),
- cursor_location.width(),
- cursor_location.height());
-}
-
-void IBusClientImpl::SendKeyEvent(IBusInputContext* context,
- uint32 keyval,
- uint32 keycode,
- uint32 state,
- PendingKeyEvent* pending_key) {
- // Note:
- // 1. We currently set timeout to -1, because ibus doesn't have a mechanism to
- // associate input method results to corresponding key event, thus there is
- // actually no way to abandon results generated by a specific key event. So we
- // actually cannot abandon a specific key event and its result but accept
- // following key events and their results. So a timeout to abandon a key event
- // will not work.
- // 2. We set GCancellable to NULL, because the operation of cancelling a async
- // request also happens asynchronously, thus it's actually useless to us.
- //
- // The fundemental problem of ibus' async API is: it uses Glib's GIO API to
- // realize async communication, but in fact, GIO API is specially designed for
- // concurrent tasks, though it supports async communication as well, the API
- // is much more complicated than an ordinary message based async communication
- // API (such as Chrome's IPC).
- // Thus it's very complicated, if not impossible, to implement a client that
- // fully utilize asynchronous communication without potential problem.
- ibus_input_context_process_key_event_async(
- context,
- keyval,
- keycode,
- state,
- -1, // no timeout
- NULL, // no cancellation object
- reinterpret_cast<GAsyncReadyCallback>(ProcessKeyEventDone),
- pending_key);
-}
-
-// TODO(yusukes): Write a unit test for this function once build bots start
-// supporting ibus-1.4.
-void IBusClientImpl::ExtractCompositionText(IBusText* text,
- guint cursor_position,
- CompositionText* out_composition) {
- out_composition->Clear();
- out_composition->text = UTF8ToUTF16(text->text);
-
- if (out_composition->text.empty())
- return;
-
- // ibus uses character index for cursor position and attribute range, but we
- // use char16 offset for them. So we need to do conversion here.
- std::vector<size_t> char16_offsets;
- size_t length = out_composition->text.length();
- base::i18n::UTF16CharIterator char_iterator(&out_composition->text);
- do {
- char16_offsets.push_back(char_iterator.array_pos());
- } while (char_iterator.Advance());
-
- // The text length in Unicode characters.
- guint char_length = static_cast<guint>(char16_offsets.size());
- // Make sure we can convert the value of |char_length| as well.
- char16_offsets.push_back(length);
-
- size_t cursor_offset =
- char16_offsets[std::min(char_length, cursor_position)];
-
- out_composition->selection = Range(cursor_offset);
- if (text->attrs) {
- guint i = 0;
- while (true) {
- IBusAttribute* attr = ibus_attr_list_get(text->attrs, i++);
- if (!attr)
- break;
- if (attr->type != IBUS_ATTR_TYPE_UNDERLINE &&
- attr->type != IBUS_ATTR_TYPE_BACKGROUND) {
- continue;
- }
- guint start = std::min(char_length, attr->start_index);
- guint end = std::min(char_length, attr->end_index);
- if (start >= end)
- continue;
- CompositionUnderline underline(
- char16_offsets[start], char16_offsets[end],
- SK_ColorBLACK, false /* thick */);
- if (attr->type == IBUS_ATTR_TYPE_BACKGROUND) {
- underline.thick = true;
- // If the cursor is at start or end of this underline, then we treat
- // it as the selection range as well, but make sure to set the cursor
- // position to the selection end.
- if (underline.start_offset == cursor_offset) {
- out_composition->selection.set_start(underline.end_offset);
- out_composition->selection.set_end(cursor_offset);
- } else if (underline.end_offset == cursor_offset) {
- out_composition->selection.set_start(underline.start_offset);
- out_composition->selection.set_end(cursor_offset);
- }
- } else if (attr->type == IBUS_ATTR_TYPE_UNDERLINE) {
- if (attr->value == IBUS_ATTR_UNDERLINE_DOUBLE)
- underline.thick = true;
- else if (attr->value == IBUS_ATTR_UNDERLINE_ERROR)
- underline.color = SK_ColorRED;
- }
- out_composition->underlines.push_back(underline);
- }
- }
-
- // Use a black thin underline by default.
- if (out_composition->underlines.empty()) {
- out_composition->underlines.push_back(CompositionUnderline(
- 0, length, SK_ColorBLACK, false /* thick */));
- }
-}
-
-string16 IBusClientImpl::ExtractCommitText(IBusText* text) {
- if (!text || !text->text)
- return WideToUTF16(L"");
- return UTF8ToUTF16(text->text);
+ GetInputContextClient()->SetCursorLocation(cursor_location.x(),
+ cursor_location.y(),
+ cursor_location.width(),
+ cursor_location.height());
+}
+
+void IBusClientImpl::SendKeyEvent(
+ uint32 keyval,
+ uint32 keycode,
+ uint32 state,
+ const chromeos::IBusInputContextClient::ProcessKeyEventCallback& callback) {
+ GetInputContextClient()->ProcessKeyEvent(keyval, keycode, state, callback);
}
} // namespace internal
diff --git a/ui/base/ime/ibus_client_impl.h b/ui/base/ime/ibus_client_impl.h
index db20fc8..ae543ce 100644
--- a/ui/base/ime/ibus_client_impl.h
+++ b/ui/base/ime/ibus_client_impl.h
@@ -11,45 +11,35 @@
namespace ui {
-static const int kIBusReleaseMask = 1 << 30;
-
namespace internal {
// An interface implemented by the object that sends and receives an event to
// and from ibus-daemon.
class UI_EXPORT IBusClientImpl : public IBusClient {
-public:
+ public:
IBusClientImpl();
virtual ~IBusClientImpl();
// ui::internal::IBusClient overrides:
- virtual IBusBus* GetConnection() OVERRIDE;
- virtual bool IsConnected(IBusBus* bus) OVERRIDE;
- virtual void CreateContext(IBusBus* bus,
- PendingCreateICRequest* request) OVERRIDE;
- virtual void DestroyProxy(IBusInputContext* context) OVERRIDE;
+ virtual bool IsConnected() OVERRIDE;
+ virtual bool IsContextReady() OVERRIDE;
+ virtual void CreateContext(PendingCreateICRequest* request) OVERRIDE;
+ virtual void DestroyProxy() OVERRIDE;
virtual void SetCapabilities(
- IBusInputContext* context,
InlineCompositionCapability inline_type) OVERRIDE;
- virtual void FocusIn(IBusInputContext* context) OVERRIDE;
- virtual void FocusOut(IBusInputContext* context) OVERRIDE;
- virtual void Reset(IBusInputContext* context) OVERRIDE;
+ virtual void FocusIn() OVERRIDE;
+ virtual void FocusOut() OVERRIDE;
+ virtual void Reset() OVERRIDE;
virtual InputMethodType GetInputMethodType() OVERRIDE;
- virtual void SetCursorLocation(IBusInputContext* context,
- const gfx::Rect& cursor_location,
+ virtual void SetCursorLocation(const gfx::Rect& cursor_location,
const gfx::Rect& composition_head) OVERRIDE;
- virtual void SendKeyEvent(IBusInputContext* context,
- uint32 keyval,
- uint32 keycode,
- uint32 state,
- PendingKeyEvent* pending_key) OVERRIDE;
- virtual void ExtractCompositionText(
- IBusText* text,
- guint cursor_position,
- CompositionText* out_composition) OVERRIDE;
- virtual string16 ExtractCommitText(IBusText* text) OVERRIDE;
-
-private:
+ virtual void SendKeyEvent(
+ uint32 keyval,
+ uint32 keycode,
+ uint32 state,
+ const chromeos::IBusInputContextClient::ProcessKeyEventCallback&
+ cb) OVERRIDE;
+ private:
DISALLOW_COPY_AND_ASSIGN(IBusClientImpl);
};
diff --git a/ui/base/ime/ime.gypi b/ui/base/ime/ime.gypi
index 55b830f..64c4c7d 100644
--- a/ui/base/ime/ime.gypi
+++ b/ui/base/ime/ime.gypi
@@ -21,8 +21,6 @@
'input_method_factory.h',
'input_method_ibus.cc',
'input_method_ibus.h',
- 'mock_ibus_client.cc',
- 'mock_ibus_client.h',
'mock_input_method.cc',
'mock_input_method.h',
'text_input_client.cc',
@@ -45,30 +43,12 @@
['include', 'text_input_client\\.(cc|h)$'],
],
}],
- ['use_ibus==1', {
+ ['chromeos==0', {
'sources!': [
- 'mock_input_method.cc',
- 'mock_input_method.h',
- ],
- }, {
- # Exlude files that depend on ibus. Note that input_method_ibus.* do NOT
- # depend on it dispite the file names.
- 'sources!': [
- 'ibus_client_impl.cc',
- 'ibus_client_impl.h',
- ],
- }],
- ['use_x11==0', {
- # Exclude files that depend on glib.
- 'sources!': [
- 'character_composer.cc',
- 'character_composer.h',
'ibus_client_impl.cc',
'ibus_client_impl.h',
'input_method_ibus.cc',
'input_method_ibus.h',
- 'mock_ibus_client.cc',
- 'mock_ibus_client.h',
],
}],
],
diff --git a/ui/base/ime/ime_unittests.gypi b/ui/base/ime/ime_unittests.gypi
index 28d4cec..3a375eb 100644
--- a/ui/base/ime/ime_unittests.gypi
+++ b/ui/base/ime/ime_unittests.gypi
@@ -3,15 +3,21 @@
# found in the LICENSE file.
{
+ 'variables': {
+ 'ime_test_files': [
+ 'character_composer_unittest.cc',
+ 'input_method_ibus_unittest.cc',
+ 'mock_ibus_client.h',
+ 'mock_ibus_client.cc',
+ ],
+ },
'sources': [
- 'character_composer_unittest.cc',
- 'input_method_ibus_unittest.cc',
+ '<@(ime_test_files)',
],
'conditions': [
- ['use_aura==0 or use_x11==0', {
+ ['use_aura==0 or use_x11==0 or chromeos==0', {
'sources!': [
- 'character_composer_unittest.cc',
- 'input_method_ibus_unittest.cc',
+ '<@(ime_test_files)',
],
}],
],
diff --git a/ui/base/ime/input_method_factory.cc b/ui/base/ime/input_method_factory.cc
index 377a122..33f7698 100644
--- a/ui/base/ime/input_method_factory.cc
+++ b/ui/base/ime/input_method_factory.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright (c) 2012 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.
@@ -6,7 +6,7 @@
#include "ui/base/ime/input_method_delegate.h"
-#if defined(HAVE_IBUS)
+#if defined(OS_CHROMEOS)
#include "ui/base/ime/input_method_ibus.h"
#else
#include "ui/base/ime/mock_input_method.h"
@@ -15,7 +15,7 @@
namespace ui {
InputMethod* CreateInputMethod(internal::InputMethodDelegate* delegate) {
-#if defined(HAVE_IBUS)
+#if defined(OS_CHROMEOS)
return new InputMethodIBus(delegate);
#else
return new MockInputMethod(delegate);
diff --git a/ui/base/ime/input_method_ibus.cc b/ui/base/ime/input_method_ibus.cc
index f3a6f87..8d9ce4c 100644
--- a/ui/base/ime/input_method_ibus.cc
+++ b/ui/base/ime/input_method_ibus.cc
@@ -4,8 +4,6 @@
#include "ui/base/ime/input_method_ibus.h"
-#include <glib.h>
-#include <glib-object.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -18,13 +16,17 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/i18n/char_iterator.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/third_party/icu/icu_utf.h"
#include "base/utf_string_conversions.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/ibus/ibus_input_context_client.h"
+#include "chromeos/dbus/ibus/ibus_text.h"
#include "ui/base/events.h"
#include "ui/base/ime/ibus_client_impl.h"
-#include "ui/base/ime/mock_ibus_client.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/keycodes/keyboard_code_conversion.h"
#include "ui/base/keycodes/keyboard_code_conversion_x.h"
@@ -33,6 +35,8 @@
namespace {
+const int kIBusReleaseMask = 1 << 30;
+
XKeyEvent* GetKeyEvent(XEvent* event) {
DCHECK(event && (event->type == KeyPress || event->type == KeyRelease));
return &event->xkey;
@@ -50,15 +54,15 @@ int EventFlagsFromXFlags(unsigned int flags) {
}
// Converts X flags to ibus key state flags.
-guint32 IBusStateFromXFlags(unsigned int flags) {
+uint32 IBusStateFromXFlags(unsigned int flags) {
return (flags & (LockMask | ControlMask | ShiftMask | Mod1Mask |
Button1Mask | Button2Mask | Button3Mask));
}
void IBusKeyEventFromNativeKeyEvent(const base::NativeEvent& native_event,
- guint32* ibus_keyval,
- guint32* ibus_keycode,
- guint32* ibus_state) {
+ uint32* ibus_keyval,
+ uint32* ibus_keycode,
+ uint32* ibus_state) {
DCHECK(native_event); // A fabricated event is not supported here.
XKeyEvent* x_key = GetKeyEvent(native_event);
@@ -70,7 +74,7 @@ void IBusKeyEventFromNativeKeyEvent(const base::NativeEvent& native_event,
*ibus_keycode = x_key->keycode;
*ibus_state = IBusStateFromXFlags(x_key->state);
if (native_event->type == KeyRelease)
- *ibus_state |= ui::kIBusReleaseMask;
+ *ibus_state |= kIBusReleaseMask;
}
} // namespace
@@ -83,7 +87,7 @@ class InputMethodIBus::PendingKeyEventImpl
public:
PendingKeyEventImpl(InputMethodIBus* input_method,
const base::NativeEvent& native_event,
- guint32 ibus_keyval);
+ uint32 ibus_keyval);
virtual ~PendingKeyEventImpl();
// internal::IBusClient::PendingKeyEvent overrides:
@@ -106,7 +110,7 @@ class InputMethodIBus::PendingKeyEventImpl
// bitwise copy.
XKeyEvent x_event_;
- const guint32 ibus_keyval_;
+ const uint32 ibus_keyval_;
DISALLOW_COPY_AND_ASSIGN(PendingKeyEventImpl);
};
@@ -114,7 +118,7 @@ class InputMethodIBus::PendingKeyEventImpl
InputMethodIBus::PendingKeyEventImpl::PendingKeyEventImpl(
InputMethodIBus* input_method,
const base::NativeEvent& native_event,
- guint32 ibus_keyval)
+ uint32 ibus_keyval)
: input_method_(input_method),
ibus_keyval_(ibus_keyval) {
DCHECK(input_method_);
@@ -155,7 +159,8 @@ class InputMethodIBus::PendingCreateICRequestImpl
virtual ~PendingCreateICRequestImpl();
// internal::IBusClient::PendingCreateICRequest overrides:
- virtual void StoreOrAbandonInputContext(IBusInputContext* ic) OVERRIDE;
+ virtual void InitOrAbandonInputContext() OVERRIDE;
+ virtual void OnCreateInputContextFailed() OVERRIDE;
// Abandon this pending key event. Its result will just be discarded.
void Abandon() {
@@ -188,22 +193,19 @@ InputMethodIBus::PendingCreateICRequestImpl::~PendingCreateICRequestImpl() {
}
}
-void InputMethodIBus::PendingCreateICRequestImpl::StoreOrAbandonInputContext(
- IBusInputContext* ic) {
- // TODO(yusukes): If the connection between Chrome and ibus-daemon terminates
- // for some reason, the create ic request will fail. In that case, NULL ic
- // will be passed to this function. We might want to call
+void InputMethodIBus::PendingCreateICRequestImpl::OnCreateInputContextFailed() {
+ // TODO(nona): If the connection between Chrome and ibus-daemon terminates
+ // for some reason, the create ic request will fail. We might want to call
// ibus_client_->CreateContext() again after some delay.
- if (!ic)
- return;
+}
+void InputMethodIBus::PendingCreateICRequestImpl::InitOrAbandonInputContext() {
if (input_method_) {
- input_method_->SetContext(ic);
+ DCHECK(ibus_client_->IsContextReady());
+ input_method_->SetUpSignalHandlers();
} else {
- // Since DestroyProxy() will not really release the object, we still need
- // to call g_object_unref() explicitly.
- ibus_client_->DestroyProxy(ic);
- g_object_unref(ic);
+ ibus_client_->DestroyProxy();
+ DCHECK(!ibus_client_->IsContextReady());
}
}
@@ -211,29 +213,19 @@ void InputMethodIBus::PendingCreateICRequestImpl::StoreOrAbandonInputContext(
InputMethodIBus::InputMethodIBus(
internal::InputMethodDelegate* delegate)
:
-#if defined(HAVE_IBUS)
ibus_client_(new internal::IBusClientImpl),
-#else
- ibus_client_(new internal::MockIBusClient),
-#endif
- context_(NULL),
pending_create_ic_request_(NULL),
context_focused_(false),
composing_text_(false),
composition_changed_(false),
- suppress_next_result_(false) {
+ suppress_next_result_(false),
+ weak_ptr_factory_(this) {
SetDelegate(delegate);
}
InputMethodIBus::~InputMethodIBus() {
AbandonAllPendingKeyEvents();
DestroyContext();
-
- // Disconnect bus signals
- g_signal_handlers_disconnect_by_func(
- GetBus(), reinterpret_cast<gpointer>(OnIBusConnectedThunk), this);
- g_signal_handlers_disconnect_by_func(
- GetBus(), reinterpret_cast<gpointer>(OnIBusDisconnectedThunk), this);
}
void InputMethodIBus::set_ibus_client(
@@ -260,30 +252,30 @@ void InputMethodIBus::Init(bool focused) {
// Initializes the connection to ibus daemon. It may happen asynchronously,
// and as soon as the connection is established, the |context_| will be
// created automatically.
- IBusBus* bus = GetBus();
- // connect bus signals
- g_signal_connect(bus, "connected",
- G_CALLBACK(OnIBusConnectedThunk), this);
- g_signal_connect(bus, "disconnected",
- G_CALLBACK(OnIBusDisconnectedThunk), this);
-
- // Creates the |context_| if the connection is already established. In such
- // case, we will not get "connected" signal.
- if (ibus_client_->IsConnected(bus))
+ // Create the input context if the connection is already established.
+ if (ibus_client_->IsConnected())
CreateContext();
InputMethodBase::Init(focused);
}
+// static
+void InputMethodIBus::ProcessKeyEventDone(
+ PendingKeyEventImpl* pending_key_event, bool is_handled) {
+ DCHECK(pending_key_event);
+ pending_key_event->ProcessPostIME(is_handled);
+ delete pending_key_event;
+}
+
void InputMethodIBus::DispatchKeyEvent(const base::NativeEvent& native_event) {
DCHECK(native_event && (native_event->type == KeyPress ||
native_event->type == KeyRelease));
DCHECK(system_toplevel_window_focused());
- guint32 ibus_keyval = 0;
- guint32 ibus_keycode = 0;
- guint32 ibus_state = 0;
+ uint32 ibus_keyval = 0;
+ uint32 ibus_keycode = 0;
+ uint32 ibus_state = 0;
IBusKeyEventFromNativeKeyEvent(
native_event, &ibus_keyval, &ibus_keycode, &ibus_state);
@@ -307,11 +299,11 @@ void InputMethodIBus::DispatchKeyEvent(const base::NativeEvent& native_event) {
new PendingKeyEventImpl(this, native_event, ibus_keyval);
pending_key_events_.insert(pending_key);
- ibus_client_->SendKeyEvent(context_,
- ibus_keyval,
+ ibus_client_->SendKeyEvent(ibus_keyval,
ibus_keycode,
ibus_state,
- pending_key);
+ base::Bind(&InputMethodIBus::ProcessKeyEventDone,
+ base::Unretained(pending_key)));
// We don't want to suppress the result generated by this key event, but it
// may cause problem. See comment in ResetContext() method.
@@ -319,7 +311,7 @@ void InputMethodIBus::DispatchKeyEvent(const base::NativeEvent& native_event) {
}
void InputMethodIBus::OnTextInputTypeChanged(const TextInputClient* client) {
- if (context_ && IsTextInputClientFocused(client)) {
+ if (ibus_client_->IsContextReady() && IsTextInputClientFocused(client)) {
ResetContext();
UpdateContextFocusState();
}
@@ -341,7 +333,7 @@ void InputMethodIBus::OnCaretBoundsChanged(const TextInputClient* client) {
}
// This function runs asynchronously.
- ibus_client_->SetCursorLocation(context_, rect, composition_head);
+ ibus_client_->SetCursorLocation(rect, composition_head);
}
void InputMethodIBus::CancelComposition(const TextInputClient* client) {
@@ -382,37 +374,42 @@ void InputMethodIBus::OnDidChangeFocusedClient(TextInputClient* focused_before,
}
void InputMethodIBus::CreateContext() {
- DCHECK(!context_);
- DCHECK(GetBus());
- DCHECK(ibus_client_->IsConnected(GetBus()));
+ DCHECK(ibus_client_->IsConnected());
DCHECK(!pending_create_ic_request_);
// Creates the input context asynchronously.
pending_create_ic_request_ = new PendingCreateICRequestImpl(
this, ibus_client_.get(), &pending_create_ic_request_);
- ibus_client_->CreateContext(GetBus(), pending_create_ic_request_);
+ ibus_client_->CreateContext(pending_create_ic_request_);
}
-void InputMethodIBus::SetContext(IBusInputContext* ic) {
- DCHECK(ic);
- DCHECK(!context_);
- context_ = ic;
+void InputMethodIBus::SetUpSignalHandlers() {
+ DCHECK(ibus_client_->IsContextReady());
// connect input context signals
- g_signal_connect(ic, "commit-text",
- G_CALLBACK(OnCommitTextThunk), this);
- g_signal_connect(ic, "forward-key-event",
- G_CALLBACK(OnForwardKeyEventThunk), this);
- g_signal_connect(ic, "update-preedit-text",
- G_CALLBACK(OnUpdatePreeditTextThunk), this);
- g_signal_connect(ic, "show-preedit-text",
- G_CALLBACK(OnShowPreeditTextThunk), this);
- g_signal_connect(ic, "hide-preedit-text",
- G_CALLBACK(OnHidePreeditTextThunk), this);
- g_signal_connect(ic, "destroy",
- G_CALLBACK(OnDestroyThunk), this);
-
- ibus_client_->SetCapabilities(ic, internal::IBusClient::INLINE_COMPOSITION);
+ chromeos::IBusInputContextClient* input_context_client =
+ chromeos::DBusThreadManager::Get()->GetIBusInputContextClient();
+ input_context_client->SetCommitTextHandler(
+ base::Bind(&InputMethodIBus::OnCommitText,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ input_context_client->SetForwardKeyEventHandler(
+ base::Bind(&InputMethodIBus::OnForwardKeyEvent,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ input_context_client->SetUpdatePreeditTextHandler(
+ base::Bind(&InputMethodIBus::OnUpdatePreeditText,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ input_context_client->SetShowPreeditTextHandler(
+ base::Bind(&InputMethodIBus::OnShowPreeditText,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ input_context_client->SetHidePreeditTextHandler(
+ base::Bind(&InputMethodIBus::OnHidePreeditText,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ ibus_client_->SetCapabilities(internal::IBusClient::INLINE_COMPOSITION);
UpdateContextFocusState();
// Since ibus-daemon is launched in an on-demand basis on Chrome OS, RWHVA (or
@@ -424,16 +421,13 @@ void InputMethodIBus::SetContext(IBusInputContext* ic) {
void InputMethodIBus::DestroyContext() {
if (pending_create_ic_request_) {
- DCHECK(!context_);
+ DCHECK(!ibus_client_->IsContextReady());
// |pending_create_ic_request_| will be deleted in CreateInputContextDone().
pending_create_ic_request_->Abandon();
pending_create_ic_request_ = NULL;
- } else if (context_) {
- // DestroyProxy() will not really release the resource of |context_|
- // object. We still need to handle "destroy" signal and call
- // g_object_unref() there.
- ibus_client_->DestroyProxy(context_);
- DCHECK(!context_);
+ } else if (ibus_client_->IsContextReady()) {
+ ResetInputContext();
+ DCHECK(!ibus_client_->IsContextReady());
}
}
@@ -475,13 +469,13 @@ void InputMethodIBus::ResetContext() {
// Note: some input method engines may not support reset method, such as
// ibus-anthy. But as we control all input method engines by ourselves, we can
// make sure that all of the engines we are using support it correctly.
- ibus_client_->Reset(context_);
+ ibus_client_->Reset();
character_composer_.Reset();
}
void InputMethodIBus::UpdateContextFocusState() {
- if (!context_) {
+ if (!ibus_client_->IsContextReady()) {
context_focused_ = false;
return;
}
@@ -501,21 +495,21 @@ void InputMethodIBus::UpdateContextFocusState() {
// We only focus in |context_| when the focus is in a normal textfield.
// ibus_input_context_focus_{in|out}() run asynchronously.
if (old_context_focused && !context_focused_)
- ibus_client_->FocusOut(context_);
+ ibus_client_->FocusOut();
else if (!old_context_focused && context_focused_)
- ibus_client_->FocusIn(context_);
+ ibus_client_->FocusIn();
if (context_focused_) {
internal::IBusClient::InlineCompositionCapability capability =
CanComposeInline() ? internal::IBusClient::INLINE_COMPOSITION
: internal::IBusClient::OFF_THE_SPOT_COMPOSITION;
- ibus_client_->SetCapabilities(context_, capability);
+ ibus_client_->SetCapabilities(capability);
}
}
void InputMethodIBus::ProcessKeyEventPostIME(
const base::NativeEvent& native_event,
- guint32 ibus_keyval,
+ uint32 ibus_keyval,
bool handled) {
TextInputClient* client = GetTextInputClient();
@@ -561,7 +555,7 @@ void InputMethodIBus::ProcessFilteredKeyPressEvent(
void InputMethodIBus::ProcessUnfilteredKeyPressEvent(
const base::NativeEvent& native_event,
- guint32 ibus_keyval) {
+ uint32 ibus_keyval) {
// For a fabricated event, ProcessUnfilteredFabricatedKeyPressEvent should be
// called instead.
DCHECK(native_event);
@@ -621,7 +615,7 @@ void InputMethodIBus::ProcessUnfilteredFabricatedKeyPressEvent(
EventType type,
KeyboardCode key_code,
int flags,
- guint32 ibus_keyval) {
+ uint32 ibus_keyval) {
TextInputClient* client = GetTextInputClient();
DispatchFabricatedKeyEventPostIME(type, key_code, flags);
@@ -718,10 +712,8 @@ void InputMethodIBus::AbandonAllPendingKeyEvents() {
pending_key_events_.clear();
}
-void InputMethodIBus::OnCommitText(
- IBusInputContext* context, IBusText* text) {
- DCHECK_EQ(context_, context);
- if (suppress_next_result_ || !text)
+void InputMethodIBus::OnCommitText(const chromeos::ibus::IBusText& text) {
+ if (suppress_next_result_ || text.text().empty())
return;
// We need to receive input method result even if the text input type is
@@ -730,7 +722,7 @@ void InputMethodIBus::OnCommitText(
if (!GetTextInputClient())
return;
- const string16 utf16_text = ibus_client_->ExtractCommitText(text);
+ const string16 utf16_text = UTF8ToUTF16(text.text());
if (utf16_text.empty())
return;
@@ -748,12 +740,9 @@ void InputMethodIBus::OnCommitText(
}
}
-void InputMethodIBus::OnForwardKeyEvent(IBusInputContext* context,
- guint keyval,
- guint keycode,
- guint state) {
- DCHECK_EQ(context_, context);
-
+void InputMethodIBus::OnForwardKeyEvent(uint32 keyval,
+ uint32 keycode,
+ uint32 state) {
KeyboardCode ui_key_code = KeyboardCodeFromXKeysym(keyval);
if (!ui_key_code)
return;
@@ -775,19 +764,16 @@ void InputMethodIBus::OnForwardKeyEvent(IBusInputContext* context,
}
}
-void InputMethodIBus::OnShowPreeditText(IBusInputContext* context) {
- DCHECK_EQ(context_, context);
+void InputMethodIBus::OnShowPreeditText() {
if (suppress_next_result_ || IsTextInputTypeNone())
return;
composing_text_ = true;
}
-void InputMethodIBus::OnUpdatePreeditText(IBusInputContext* context,
- IBusText* text,
- guint cursor_pos,
- gboolean visible) {
- DCHECK_EQ(context_, context);
+void InputMethodIBus::OnUpdatePreeditText(const chromeos::ibus::IBusText& text,
+ uint32 cursor_pos,
+ bool visible) {
if (suppress_next_result_ || IsTextInputTypeNone())
return;
@@ -799,11 +785,11 @@ void InputMethodIBus::OnUpdatePreeditText(IBusInputContext* context,
// If it's only for clearing the current preedit text, then why not just use
// OnHidePreeditText()?
if (!visible) {
- OnHidePreeditText(context);
+ OnHidePreeditText();
return;
}
- ibus_client_->ExtractCompositionText(text, cursor_pos, &composition_);
+ ExtractCompositionText(text, cursor_pos, &composition_);
composition_changed_ = true;
@@ -822,8 +808,7 @@ void InputMethodIBus::OnUpdatePreeditText(IBusInputContext* context,
}
}
-void InputMethodIBus::OnHidePreeditText(IBusInputContext* context) {
- DCHECK_EQ(context_, context);
+void InputMethodIBus::OnHidePreeditText() {
if (composition_.text.empty() || IsTextInputTypeNone())
return;
@@ -839,46 +824,113 @@ void InputMethodIBus::OnHidePreeditText(IBusInputContext* context) {
}
}
-void InputMethodIBus::OnDestroy(IBusInputContext* context) {
- DCHECK_EQ(context_, context);
- g_object_unref(context_);
- context_ = NULL;
+void InputMethodIBus::ResetInputContext() {
context_focused_ = false;
ConfirmCompositionText();
// We are dead, so we need to ask the client to stop relying on us.
- // We cannot do it in DestroyContext(), because OnDestroy() may be called
- // automatically.
OnInputMethodChanged();
+ ibus_client_->DestroyProxy();
}
-void InputMethodIBus::OnIBusConnected(IBusBus* bus) {
- DCHECK_EQ(GetBus(), bus);
- DCHECK(ibus_client_->IsConnected(bus));
+void InputMethodIBus::OnConnected() {
+ DCHECK(ibus_client_->IsConnected());
+ // If already input context is initialized, do nothing.
+ if (ibus_client_->IsContextReady())
+ return;
DestroyContext();
CreateContext();
}
-void InputMethodIBus::OnIBusDisconnected(IBusBus* bus) {
- DCHECK_EQ(GetBus(), bus);
-
- // TODO(suzhe): Make sure if we really do not need to handle this signal.
- // And I'm actually wondering if ibus-daemon will release the resource of the
- // |context_| correctly when the connection is lost.
+void InputMethodIBus::OnDisconnected() {
+ DestroyContext();
}
-IBusBus* InputMethodIBus::GetBus() {
- // Everything happens in UI thread, so we do not need to care about
- // synchronization issue.
- static IBusBus* ibus = NULL;
+void InputMethodIBus::ExtractCompositionText(
+ const chromeos::ibus::IBusText& text,
+ uint32 cursor_position,
+ CompositionText* out_composition) const {
+ out_composition->Clear();
+ out_composition->text = UTF8ToUTF16(text.text());
+
+ if (out_composition->text.empty())
+ return;
+
+ // ibus uses character index for cursor position and attribute range, but we
+ // use char16 offset for them. So we need to do conversion here.
+ std::vector<size_t> char16_offsets;
+ size_t length = out_composition->text.length();
+ base::i18n::UTF16CharIterator char_iterator(&out_composition->text);
+ do {
+ char16_offsets.push_back(char_iterator.array_pos());
+ } while (char_iterator.Advance());
+
+ // The text length in Unicode characters.
+ uint32 char_length = static_cast<uint32>(char16_offsets.size());
+ // Make sure we can convert the value of |char_length| as well.
+ char16_offsets.push_back(length);
+
+ size_t cursor_offset =
+ char16_offsets[std::min(char_length, cursor_position)];
+
+ out_composition->selection = Range(cursor_offset);
+
+ const std::vector<chromeos::ibus::IBusText::UnderlineAttribute>&
+ underline_attributes = text.underline_attributes();
+ const std::vector<chromeos::ibus::IBusText::SelectionAttribute>&
+ selection_attributes = text.selection_attributes();
+
+ if (!underline_attributes.empty()) {
+ for (size_t i = 0; i < underline_attributes.size(); ++i) {
+ const uint32 start = underline_attributes[i].start_index;
+ const uint32 end = underline_attributes[i].end_index;
+ if (start >= end)
+ continue;
+ CompositionUnderline underline(
+ char16_offsets[start], char16_offsets[end],
+ SK_ColorBLACK, false /* thick */);
+ if (underline_attributes[i].type ==
+ chromeos::ibus::IBusText::IBUS_TEXT_UNDERLINE_DOUBLE)
+ underline.thick = true;
+ else if (underline_attributes[i].type ==
+ chromeos::ibus::IBusText::IBUS_TEXT_UNDERLINE_ERROR)
+ underline.color = SK_ColorRED;
+ out_composition->underlines.push_back(underline);
+ }
+ }
+
+ if (!selection_attributes.empty()) {
+ LOG_IF(ERROR, selection_attributes.size() != 1)
+ << "Chrome does not support multiple selection";
+ for (uint32 i = 0; i < selection_attributes.size(); ++i) {
+ const uint32 start = selection_attributes[i].start_index;
+ const uint32 end = selection_attributes[i].end_index;
+ if (start >= end)
+ continue;
+ CompositionUnderline underline(
+ char16_offsets[start], char16_offsets[end],
+ SK_ColorBLACK, true /* thick */);
+ out_composition->underlines.push_back(underline);
+ // If the cursor is at start or end of this underline, then we treat
+ // it as the selection range as well, but make sure to set the cursor
+ // position to the selection end.
+ if (underline.start_offset == cursor_offset) {
+ out_composition->selection.set_start(underline.end_offset);
+ out_composition->selection.set_end(cursor_offset);
+ } else if (underline.end_offset == cursor_offset) {
+ out_composition->selection.set_start(underline.start_offset);
+ out_composition->selection.set_end(cursor_offset);
+ }
+ }
+ }
- if (!ibus) {
- ibus = ibus_client_->GetConnection();
- DCHECK(ibus);
+ // Use a black thin underline by default.
+ if (out_composition->underlines.empty()) {
+ out_composition->underlines.push_back(CompositionUnderline(
+ 0, length, SK_ColorBLACK, false /* thick */));
}
- return ibus;
}
} // namespace ui
diff --git a/ui/base/ime/input_method_ibus.h b/ui/base/ime/input_method_ibus.h
index 23dbd22..d5c1a90 100644
--- a/ui/base/ime/input_method_ibus.h
+++ b/ui/base/ime/input_method_ibus.h
@@ -12,18 +12,17 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
-#include "ui/base/glib/glib_integers.h"
-#include "ui/base/glib/glib_signal.h"
+#include "base/memory/weak_ptr.h"
#include "ui/base/ime/character_composer.h"
#include "ui/base/ime/composition_text.h"
#include "ui/base/ime/ibus_client.h"
#include "ui/base/ime/input_method_base.h"
-// Forward declarations, so that we don't need to include ibus.h in this file.
-typedef struct _GAsyncResult GAsyncResult;
-typedef struct _IBusBus IBusBus;
-typedef struct _IBusInputContext IBusInputContext;
-typedef struct _IBusText IBusText;
+namespace chromeos {
+namespace ibus {
+class IBusText;
+} // namespace ibus
+} // namespace chromeos
namespace ui {
@@ -46,6 +45,12 @@ class UI_EXPORT InputMethodIBus : public InputMethodBase {
virtual base::i18n::TextDirection GetInputTextDirection() OVERRIDE;
virtual bool IsActive() OVERRIDE;
+ // Called when the connection with ibus-daemon is established.
+ virtual void OnConnected();
+
+ // Called when the connection with ibus-daemon is shutdowned.
+ virtual void OnDisconnected();
+
// Sets |new_client| as a new IBusClient. InputMethodIBus owns the object.
// A client has to be set before InputMethodIBus::Init() is called.
void set_ibus_client(scoped_ptr<internal::IBusClient> new_client);
@@ -54,8 +59,10 @@ class UI_EXPORT InputMethodIBus : public InputMethodBase {
internal::IBusClient* ibus_client() const;
protected:
- // Returns the global IBusBus instance. Protected: for testing.
- IBusBus* GetBus();
+ // Converts |text| into CompositionText.
+ void ExtractCompositionText(const chromeos::ibus::IBusText& text,
+ uint32 cursor_position,
+ CompositionText* out_composition) const;
private:
class PendingKeyEventImpl;
@@ -67,28 +74,28 @@ class UI_EXPORT InputMethodIBus : public InputMethodBase {
virtual void OnDidChangeFocusedClient(TextInputClient* focused_before,
TextInputClient* focused) OVERRIDE;
- // Creates |context_| instance asynchronously.
+ // Creates context asynchronously.
void CreateContext();
- // Sets |context_| and hooks necessary signals.
- void SetContext(IBusInputContext* ic);
+ // Sets necessary signal handlers.
+ void SetUpSignalHandlers();
- // Destroys |context_| instance.
+ // Destroys context.
void DestroyContext();
// Asks the client to confirm current composition text.
void ConfirmCompositionText();
- // Resets |context_| and abandon all pending results and key events.
+ // Resets context and abandon all pending results and key events.
void ResetContext();
- // Checks the availability of focused text input client and update focus state
- // of |context_|.
+ // Checks the availability of focused text input client and update focus
+ // state.
void UpdateContextFocusState();
// Process a key returned from the input method.
void ProcessKeyEventPostIME(const base::NativeEvent& native_key_event,
- guint32 ibus_keycode,
+ uint32 ibus_keycode,
bool handled);
// Processes a key event that was already filtered by the input method.
@@ -97,11 +104,11 @@ class UI_EXPORT InputMethodIBus : public InputMethodBase {
// Processes a key event that was not filtered by the input method.
void ProcessUnfilteredKeyPressEvent(const base::NativeEvent& native_key_event,
- guint32 ibus_keycode);
+ uint32 ibus_keycode);
void ProcessUnfilteredFabricatedKeyPressEvent(EventType type,
KeyboardCode key_code,
int flags,
- guint32 ibus_keyval);
+ uint32 ibus_keyval);
// Sends input method result caused by the given key event to the focused text
// input client.
@@ -127,37 +134,33 @@ class UI_EXPORT InputMethodIBus : public InputMethodBase {
// focus, the text input type is changed or we are destroyed.
void AbandonAllPendingKeyEvents();
+ // Releases context focus and confirms the composition text. Then destroy
+ // object proxy.
+ void ResetInputContext();
+
// Event handlers for IBusInputContext:
- CHROMEG_CALLBACK_1(InputMethodIBus, void, OnCommitText,
- IBusInputContext*, IBusText*);
- CHROMEG_CALLBACK_3(InputMethodIBus, void, OnForwardKeyEvent,
- IBusInputContext*, guint, guint, guint);
- CHROMEG_CALLBACK_0(InputMethodIBus, void, OnShowPreeditText,
- IBusInputContext*);
- CHROMEG_CALLBACK_3(InputMethodIBus, void, OnUpdatePreeditText,
- IBusInputContext*, IBusText*, guint, gboolean);
- CHROMEG_CALLBACK_0(InputMethodIBus, void, OnHidePreeditText,
- IBusInputContext*);
- CHROMEG_CALLBACK_0(InputMethodIBus, void, OnDestroy, IBusInputContext*);
-
- // Event handlers for IBusBus:
- CHROMEG_CALLBACK_0(InputMethodIBus, void, OnIBusConnected, IBusBus*);
- CHROMEG_CALLBACK_0(InputMethodIBus, void, OnIBusDisconnected, IBusBus*);
+ void OnCommitText(const chromeos::ibus::IBusText& text);
+ void OnForwardKeyEvent(uint32 keyval, uint32 keycode, uint32 status);
+ void OnShowPreeditText();
+ void OnUpdatePreeditText(const chromeos::ibus::IBusText& text,
+ uint32 cursor_pos,
+ bool visible);
+ void OnHidePreeditText();
+
+ void CreateInputContextDone(PendingCreateICRequestImpl* ic_request,
+ const dbus::ObjectPath& object_path);
+ static void ProcessKeyEventDone(PendingKeyEventImpl* pending_key_event,
+ bool is_handled);
scoped_ptr<internal::IBusClient> ibus_client_;
- // The input context for actual text input. Note that we don't have to support
- // a "fake" IBus input context anymore since the latest Chrome for Chrome OS
- // can handle input method hot keys (e.g. Shift+Alt) by itself.
- IBusInputContext* context_;
-
// All pending key events. Note: we do not own these object, we just save
// pointers to these object so that we can abandon them when necessary.
// They will be deleted in ProcessKeyEventDone().
std::set<PendingKeyEventImpl*> pending_key_events_;
- // The pending request for creating the |context_| instance. We need to keep
- // this pointer so that we can receive or abandon the result.
+ // The pending request for creating the input context. We need to keep this
+ // pointer so that we can receive or abandon the result.
PendingCreateICRequestImpl* pending_create_ic_request_;
// Pending composition text generated by the current pending key event.
@@ -170,7 +173,7 @@ class UI_EXPORT InputMethodIBus : public InputMethodBase {
// processing result of the pending key event.
string16 result_text_;
- // Indicates if |context_| is focused or not.
+ // Indicates if input context is focused or not.
bool context_focused_;
// Indicates if there is an ongoing composition text.
@@ -187,6 +190,9 @@ class UI_EXPORT InputMethodIBus : public InputMethodBase {
// including dead key etc.
CharacterComposer character_composer_;
+ // Used for making callbacks.
+ base::WeakPtrFactory<InputMethodIBus> weak_ptr_factory_;
+
DISALLOW_COPY_AND_ASSIGN(InputMethodIBus);
};
diff --git a/ui/base/ime/input_method_ibus_unittest.cc b/ui/base/ime/input_method_ibus_unittest.cc
index 12f8ed5..fbdecc3 100644
--- a/ui/base/ime/input_method_ibus_unittest.cc
+++ b/ui/base/ime/input_method_ibus_unittest.cc
@@ -2,19 +2,34 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <glib-object.h>
-
#include <cstring>
+#include "base/i18n/char_iterator.h"
#include "base/memory/scoped_ptr.h"
+#include "base/utf_string_conversions.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/ibus/ibus_input_context_client.h"
+#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ime/input_method_delegate.h"
#include "ui/base/ime/input_method_ibus.h"
#include "ui/base/ime/mock_ibus_client.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/gfx/rect.h"
-#include "testing/gtest/include/gtest/gtest.h"
namespace ui {
+namespace {
+
+uint32 GetOffsetInUTF16(const std::string& utf8_string, uint32 utf8_offset) {
+ string16 utf16_string = UTF8ToUTF16(utf8_string);
+ DCHECK_LT(utf8_offset, utf16_string.size());
+ base::i18n::UTF16CharIterator char_iterator(&utf16_string);
+ for (size_t i = 0; i < utf8_offset; ++i)
+ char_iterator.Advance();
+ return char_iterator.array_pos();
+}
+
+} // namespace
+
class TestableInputMethodIBus : public InputMethodIBus {
public:
@@ -25,7 +40,7 @@ class TestableInputMethodIBus : public InputMethodIBus {
}
// Change access rights.
- using InputMethodIBus::GetBus;
+ using InputMethodIBus::ExtractCompositionText;
};
class InputMethodIBusTest : public internal::InputMethodDelegate,
@@ -41,6 +56,13 @@ class InputMethodIBusTest : public internal::InputMethodDelegate,
// testing::Test overrides:
virtual void SetUp() OVERRIDE {
+ // |thread_manager_| will be released by DBusThreadManager::Shutdown
+ // function in TearDown function.
+ // Current MockIBusInputContext is strongly depend on gmock, but gmock is
+ // banned in ui/*. So just use stab implementation for testing.
+ // TODO(nona): Introduce gmock-free MockIBusInputContext.
+ chromeos::DBusThreadManager::Initialize();
+ chromeos::DBusThreadManager::Get()->InitIBusBus("dummy address");
scoped_ptr<internal::IBusClient> client(new internal::MockIBusClient);
ime_.reset(new TestableInputMethodIBus(this, client.Pass()));
ime_->SetFocusedTextInputClient(this);
@@ -50,6 +72,7 @@ class InputMethodIBusTest : public internal::InputMethodDelegate,
if (ime_.get())
ime_->SetFocusedTextInputClient(NULL);
ime_.reset();
+ chromeos::DBusThreadManager::Shutdown();
}
// ui::internal::InputMethodDelegate overrides:
@@ -114,10 +137,6 @@ class InputMethodIBusTest : public internal::InputMethodDelegate,
virtual bool ChangeTextDirectionAndLayoutAlignment(
base::i18n::TextDirection direction) OVERRIDE { return false; }
- IBusBus* GetBus() {
- return ime_->GetBus();
- }
-
internal::MockIBusClient* GetClient() {
return static_cast<internal::MockIBusClient*>(ime_->ibus_client());
}
@@ -284,7 +303,7 @@ TEST_F(InputMethodIBusTest, InitiallyDisconnected) {
EXPECT_EQ(0U, GetClient()->create_context_call_count_);
// Start the daemon.
GetClient()->is_connected_ = true;
- g_signal_emit_by_name(GetBus(), "connected");
+ ime_->OnConnected();
// A context should be created upon the signal delivery.
EXPECT_EQ(1U, GetClient()->create_context_call_count_);
EXPECT_EQ(1U, GetClient()->set_capabilities_call_count_);
@@ -298,7 +317,7 @@ TEST_F(InputMethodIBusTest, Disconnect) {
ime_->Init(true);
EXPECT_EQ(1U, GetClient()->create_context_call_count_);
GetClient()->is_connected_ = false;
- g_signal_emit_by_name(GetBus(), "disconnected");
+ ime_->OnDisconnected();
}
// Confirm that ui::InputMethodIBus re-creates an input context when ibus-daemon
@@ -310,9 +329,9 @@ TEST_F(InputMethodIBusTest, DisconnectThenReconnect) {
EXPECT_EQ(1U, GetClient()->set_capabilities_call_count_);
EXPECT_EQ(0U, GetClient()->destroy_proxy_call_count_);
GetClient()->is_connected_ = false;
- g_signal_emit_by_name(GetBus(), "disconnected");
+ ime_->OnDisconnected();
GetClient()->is_connected_ = true;
- g_signal_emit_by_name(GetBus(), "connected");
+ ime_->OnConnected();
// Check if the old context is deleted.
EXPECT_EQ(1U, GetClient()->destroy_proxy_call_count_);
// Check if a new context is created.
@@ -374,7 +393,7 @@ TEST_F(InputMethodIBusTest, FocusIn_Text) {
ime_->OnTextInputTypeChanged(this);
// Start the daemon.
GetClient()->is_connected_ = true;
- g_signal_emit_by_name(GetBus(), "connected");
+ ime_->OnConnected();
// A context should be created upon the signal delivery.
EXPECT_EQ(1U, GetClient()->create_context_call_count_);
// Since a form has focus, IBusClient::FocusIn() should be called.
@@ -395,7 +414,7 @@ TEST_F(InputMethodIBusTest, FocusIn_Password) {
input_type_ = TEXT_INPUT_TYPE_PASSWORD;
ime_->OnTextInputTypeChanged(this);
GetClient()->is_connected_ = true;
- g_signal_emit_by_name(GetBus(), "connected");
+ ime_->OnConnected();
EXPECT_EQ(1U, GetClient()->create_context_call_count_);
// Since a form has focus, IBusClient::FocusIn() should NOT be called.
EXPECT_EQ(0U, GetClient()->focus_in_call_count_);
@@ -463,6 +482,207 @@ TEST_F(InputMethodIBusTest, OnCaretBoundsChanged) {
EXPECT_EQ(3U, GetClient()->set_cursor_location_call_count_);
}
-// TODO(yusukes): Write more tests, especially for key event functions.
+TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_NoAttribute) {
+ const char kSampleText[] = "Sample Text";
+ const uint32 kCursorPos = 2UL;
+
+ const string16 utf16_string = UTF8ToUTF16(kSampleText);
+ chromeos::ibus::IBusText ibus_text;
+ ibus_text.set_text(kSampleText);
+
+ CompositionText composition_text;
+ ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
+ EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
+ // If there is no selection, |selection| represents cursor position.
+ EXPECT_EQ(kCursorPos, composition_text.selection.start());
+ EXPECT_EQ(kCursorPos, composition_text.selection.end());
+ // If there is no underline, |underlines| contains one underline and it is
+ // whole text underline.
+ ASSERT_EQ(1UL, composition_text.underlines.size());
+ EXPECT_EQ(0UL, composition_text.underlines[0].start_offset);
+ EXPECT_EQ(utf16_string.size(), composition_text.underlines[0].end_offset);
+ EXPECT_FALSE(composition_text.underlines[0].thick);
+}
+
+TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_SingleUnderline) {
+ const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
+ "\xE3\x81\x88\xE3\x81\x8A";
+ const uint32 kCursorPos = 2UL;
+
+ // Set up ibus text with one underline attribute.
+ chromeos::ibus::IBusText ibus_text;
+ ibus_text.set_text(kSampleText);
+ chromeos::ibus::IBusText::UnderlineAttribute underline;
+ underline.type = chromeos::ibus::IBusText::IBUS_TEXT_UNDERLINE_SINGLE;
+ underline.start_index = 1UL;
+ underline.end_index = 4UL;
+ ibus_text.mutable_underline_attributes()->push_back(underline);
+
+ CompositionText composition_text;
+ ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
+ EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
+ // If there is no selection, |selection| represents cursor position.
+ EXPECT_EQ(kCursorPos, composition_text.selection.start());
+ EXPECT_EQ(kCursorPos, composition_text.selection.end());
+ ASSERT_EQ(1UL, composition_text.underlines.size());
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
+ composition_text.underlines[0].start_offset);
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
+ composition_text.underlines[0].end_offset);
+ // Single underline represents as black thin line.
+ EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
+ EXPECT_FALSE(composition_text.underlines[0].thick);
+}
+
+TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_DoubleUnderline) {
+ const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
+ "\xE3\x81\x88\xE3\x81\x8A";
+ const uint32 kCursorPos = 2UL;
+
+ // Set up ibus text with one underline attribute.
+ chromeos::ibus::IBusText ibus_text;
+ ibus_text.set_text(kSampleText);
+ chromeos::ibus::IBusText::UnderlineAttribute underline;
+ underline.type = chromeos::ibus::IBusText::IBUS_TEXT_UNDERLINE_DOUBLE;
+ underline.start_index = 1UL;
+ underline.end_index = 4UL;
+ ibus_text.mutable_underline_attributes()->push_back(underline);
+
+ CompositionText composition_text;
+ ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
+ EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
+ // If there is no selection, |selection| represents cursor position.
+ EXPECT_EQ(kCursorPos, composition_text.selection.start());
+ EXPECT_EQ(kCursorPos, composition_text.selection.end());
+ ASSERT_EQ(1UL, composition_text.underlines.size());
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
+ composition_text.underlines[0].start_offset);
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
+ composition_text.underlines[0].end_offset);
+ // Double underline represents as black thick line.
+ EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
+ EXPECT_TRUE(composition_text.underlines[0].thick);
+}
+
+TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_ErrorUnderline) {
+ const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
+ "\xE3\x81\x88\xE3\x81\x8A";
+ const uint32 kCursorPos = 2UL;
+
+ // Set up ibus text with one underline attribute.
+ chromeos::ibus::IBusText ibus_text;
+ ibus_text.set_text(kSampleText);
+ chromeos::ibus::IBusText::UnderlineAttribute underline;
+ underline.type = chromeos::ibus::IBusText::IBUS_TEXT_UNDERLINE_ERROR;
+ underline.start_index = 1UL;
+ underline.end_index = 4UL;
+ ibus_text.mutable_underline_attributes()->push_back(underline);
+
+ CompositionText composition_text;
+ ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
+ EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
+ EXPECT_EQ(kCursorPos, composition_text.selection.start());
+ EXPECT_EQ(kCursorPos, composition_text.selection.end());
+ ASSERT_EQ(1UL, composition_text.underlines.size());
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
+ composition_text.underlines[0].start_offset);
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
+ composition_text.underlines[0].end_offset);
+ // Error underline represents as red thin line.
+ EXPECT_EQ(SK_ColorRED, composition_text.underlines[0].color);
+ EXPECT_FALSE(composition_text.underlines[0].thick);
+}
+
+TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_Selection) {
+ const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
+ "\xE3\x81\x88\xE3\x81\x8A";
+ const uint32 kCursorPos = 2UL;
+
+ // Set up ibus text with one underline attribute.
+ chromeos::ibus::IBusText ibus_text;
+ ibus_text.set_text(kSampleText);
+ chromeos::ibus::IBusText::SelectionAttribute selection;
+ selection.start_index = 1UL;
+ selection.end_index = 4UL;
+ ibus_text.mutable_selection_attributes()->push_back(selection);
+
+ CompositionText composition_text;
+ ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
+ EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
+ EXPECT_EQ(kCursorPos, composition_text.selection.start());
+ EXPECT_EQ(kCursorPos, composition_text.selection.end());
+ ASSERT_EQ(1UL, composition_text.underlines.size());
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.start_index),
+ composition_text.underlines[0].start_offset);
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.end_index),
+ composition_text.underlines[0].end_offset);
+ EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
+ EXPECT_TRUE(composition_text.underlines[0].thick);
+}
+
+TEST_F(InputMethodIBusTest,
+ ExtractCompositionTextTest_SelectionStartWithCursor) {
+ const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
+ "\xE3\x81\x88\xE3\x81\x8A";
+ const uint32 kCursorPos = 1UL;
+
+ // Set up ibus text with one underline attribute.
+ chromeos::ibus::IBusText ibus_text;
+ ibus_text.set_text(kSampleText);
+ chromeos::ibus::IBusText::SelectionAttribute selection;
+ selection.start_index = kCursorPos;
+ selection.end_index = 4UL;
+ ibus_text.mutable_selection_attributes()->push_back(selection);
+
+ CompositionText composition_text;
+ ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
+ EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
+ // If the cursor position is same as selection bounds, selection start
+ // position become opposit side of selection from cursor.
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.end_index),
+ composition_text.selection.start());
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
+ composition_text.selection.end());
+ ASSERT_EQ(1UL, composition_text.underlines.size());
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.start_index),
+ composition_text.underlines[0].start_offset);
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.end_index),
+ composition_text.underlines[0].end_offset);
+ EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
+ EXPECT_TRUE(composition_text.underlines[0].thick);
+}
+
+TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_SelectionEndWithCursor) {
+ const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
+ "\xE3\x81\x88\xE3\x81\x8A";
+ const uint32 kCursorPos = 4UL;
+
+ // Set up ibus text with one underline attribute.
+ chromeos::ibus::IBusText ibus_text;
+ ibus_text.set_text(kSampleText);
+ chromeos::ibus::IBusText::SelectionAttribute selection;
+ selection.start_index = 1UL;
+ selection.end_index = kCursorPos;
+ ibus_text.mutable_selection_attributes()->push_back(selection);
+
+ CompositionText composition_text;
+ ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
+ EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
+ // If the cursor position is same as selection bounds, selection start
+ // position become opposit side of selection from cursor.
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.start_index),
+ composition_text.selection.start());
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
+ composition_text.selection.end());
+ ASSERT_EQ(1UL, composition_text.underlines.size());
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.start_index),
+ composition_text.underlines[0].start_offset);
+ EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.end_index),
+ composition_text.underlines[0].end_offset);
+ EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
+ EXPECT_TRUE(composition_text.underlines[0].thick);
+}
+
+// TODO(nona): Write more tests, especially for key event functions.
} // namespace ui
diff --git a/ui/base/ime/mock_ibus_client.cc b/ui/base/ime/mock_ibus_client.cc
index de6a341..7f42b55 100644
--- a/ui/base/ime/mock_ibus_client.cc
+++ b/ui/base/ime/mock_ibus_client.cc
@@ -1,123 +1,13 @@
// Copyright (c) 2012 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.
+// TODO(nona): Remove this file.
#include "ui/base/ime/mock_ibus_client.h"
-#include <glib-object.h>
-
#include "base/logging.h"
#include "base/utf_string_conversions.h"
-// Define a dummy IBusBus class. A real GObject is necessary here to make it
-// possible to send/receive GObject signals through the object.
-typedef struct _IBusBus {
- GObject parent;
-} IBusBus;
-typedef struct _IBusBusClass {
- GObjectClass parent;
-} IBusBusClass;
-G_DEFINE_TYPE(IBusBus, ibus_bus, G_TYPE_OBJECT)
-
-// Define a dummy IBusText class.
-typedef struct _IBusText {
- GObject parent;
-} IBusText;
-typedef struct _IBusTextClass {
- GObjectClass parent;
-} IBusTextClass;
-G_DEFINE_TYPE(IBusText, ibus_text, G_TYPE_OBJECT)
-
-// Define a dummy IBusInputContext class.
-typedef struct _IBusInputContext {
- GObject parent;
-} IBusInputContext;
-typedef struct _IBusInputContextClass {
- GObjectClass parent;
-} IBusInputContextClass;
-G_DEFINE_TYPE(IBusInputContext, ibus_input_context, G_TYPE_OBJECT)
-
-namespace {
-
-IBusBus* CreateBusObject() {
- return reinterpret_cast<IBusBus*>(g_object_new(ibus_bus_get_type(), NULL));
-}
-
-IBusInputContext* CreateContextObject() {
- return reinterpret_cast<IBusInputContext*>(
- g_object_new(ibus_input_context_get_type(), NULL));
-}
-
-void AddVoidSignal(gpointer klass, const char* signal_name) {
- g_signal_new(signal_name,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- 0, // class_offset
- NULL, // accumulator
- NULL, // accu_data
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE,
- 0);
-}
-
-} // namespace
-
-static void ibus_bus_init(IBusBus* obj) {}
-static void ibus_bus_class_init(IBusBusClass* klass) {
- AddVoidSignal(klass, "connected");
- AddVoidSignal(klass, "disconnected");
-}
-static void ibus_text_init(IBusText* obj) {}
-static void ibus_text_class_init(IBusTextClass* klass) {}
-static void ibus_input_context_init(IBusInputContext* obj) {}
-static void ibus_input_context_class_init(IBusInputContextClass* klass) {
- AddVoidSignal(klass, "show-preedit-text");
- AddVoidSignal(klass, "hide-preedit-text");
- AddVoidSignal(klass, "destroy");
-
- g_signal_new("commit-text",
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL,
- NULL,
- g_cclosure_marshal_VOID__OBJECT,
- G_TYPE_NONE,
- 1,
- ibus_text_get_type());
-
- // TODO(yusukes): Support forward-key-event and update-preedit-text signals.
- // To do that, we have to generate custom marshallers using glib-genmarshal.
-
- /*
- g_signal_new("forward-key-event",
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL,
- NULL,
- marshal_VOID__UINT_UNIT_UNIT,
- G_TYPE_NONE,
- 3,
- G_TYPE_UINT,
- G_TYPE_UINT,
- G_TYPE_UINT);
-
- g_signal_new("update-preedit-text",
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- 0,
- NULL,
- NULL,
- marshal_VOID__OBJECT_UINT_BOOLEAN,
- G_TYPE_NONE,
- 3,
- IBUS_TYPE_TEXT,
- G_TYPE_UINT,
- G_TYPE_BOOLEAN);
- */
-}
-
namespace ui {
namespace internal {
@@ -132,33 +22,31 @@ MockIBusClient::~MockIBusClient() {
// whether the new context created by CreateContextObject() is immediately
// destroyed by checking the counter in MockIBusClient::DestroyProxy().
const unsigned int initial_call_count = destroy_proxy_call_count_;
- create_ic_request_->StoreOrAbandonInputContext(CreateContextObject());
+ create_ic_request_->InitOrAbandonInputContext();
DCHECK_EQ(initial_call_count + 1, destroy_proxy_call_count_);
}
}
-IBusBus* MockIBusClient::GetConnection() {
- g_type_init();
- return CreateBusObject();
+bool MockIBusClient::IsConnected() {
+ return is_connected_;
}
-bool MockIBusClient::IsConnected(IBusBus* bus) {
- return is_connected_;
+bool MockIBusClient::IsContextReady() {
+ return is_context_ready_;
}
-void MockIBusClient::CreateContext(IBusBus* bus,
- PendingCreateICRequest* request) {
+void MockIBusClient::CreateContext(PendingCreateICRequest* request) {
++create_context_call_count_;
-
switch (create_context_result_) {
case kCreateContextSuccess:
// Create a new context immediately.
- request->StoreOrAbandonInputContext(CreateContextObject());
+ is_context_ready_ = true;
+ request->InitOrAbandonInputContext();
delete request;
break;
case kCreateContextFail:
// Emulate an IPC failure. Pass NULL to the request object.
- request->StoreOrAbandonInputContext(NULL);
+ request->OnCreateInputContextFailed();
delete request;
break;
case kCreateContextNoResponse:
@@ -172,25 +60,24 @@ void MockIBusClient::CreateContext(IBusBus* bus,
}
}
-void MockIBusClient::DestroyProxy(IBusInputContext* context) {
+void MockIBusClient::DestroyProxy() {
++destroy_proxy_call_count_;
- g_signal_emit_by_name(context, "destroy");
+ is_context_ready_ = false;
}
-void MockIBusClient::SetCapabilities(IBusInputContext* context,
- InlineCompositionCapability inline_type) {
+void MockIBusClient::SetCapabilities(InlineCompositionCapability inline_type) {
++set_capabilities_call_count_;
}
-void MockIBusClient::FocusIn(IBusInputContext* context) {
+void MockIBusClient::FocusIn() {
++focus_in_call_count_;
}
-void MockIBusClient::FocusOut(IBusInputContext* context) {
+void MockIBusClient::FocusOut() {
++focus_out_call_count_;
}
-void MockIBusClient::Reset(IBusInputContext* context) {
+void MockIBusClient::Reset() {
++reset_call_count_;
}
@@ -198,37 +85,26 @@ IBusClient::InputMethodType MockIBusClient::GetInputMethodType() {
return input_method_type_;
}
-void MockIBusClient::SetCursorLocation(IBusInputContext* context,
- const gfx::Rect& cursor_location,
+void MockIBusClient::SetCursorLocation(const gfx::Rect& cursor_location,
const gfx::Rect& composition_head) {
++set_cursor_location_call_count_;
}
-void MockIBusClient::SendKeyEvent(IBusInputContext* context,
- uint32 keyval,
- uint32 keycode,
- uint32 state,
- PendingKeyEvent* pending_key) {
+void MockIBusClient::SendKeyEvent(
+ uint32 keyval,
+ uint32 keycode,
+ uint32 state,
+ const chromeos::IBusInputContextClient::ProcessKeyEventCallback& callback) {
// TODO(yusukes): implement this function.
}
-void MockIBusClient::ExtractCompositionText(IBusText* text,
- guint cursor_position,
- CompositionText* out_composition) {
- *out_composition = composition_text_;
-}
-
-string16 MockIBusClient::ExtractCommitText(IBusText* text) {
- return commit_text_;
-}
-
void MockIBusClient::ResetFlags() {
create_context_result_ = kCreateContextSuccess;
create_ic_request_.reset();
is_connected_ = false;
+ is_context_ready_ = false;
input_method_type_ = INPUT_METHOD_NORMAL;
- composition_text_.Clear();
commit_text_.clear();
create_context_call_count_ = 0;
diff --git a/ui/base/ime/mock_ibus_client.h b/ui/base/ime/mock_ibus_client.h
index 144fe1e..514cb1e 100644
--- a/ui/base/ime/mock_ibus_client.h
+++ b/ui/base/ime/mock_ibus_client.h
@@ -1,6 +1,7 @@
// Copyright (c) 2012 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.
+// TODO(nona): Remove this file.
#ifndef UI_BASE_IME_MOCK_IBUS_CLIENT_H_
#define UI_BASE_IME_MOCK_IBUS_CLIENT_H_
@@ -17,37 +18,29 @@ namespace internal {
// A dummy IBusClient implementation for testing which requires neither
// ibus-daemon nor ibus header files.
class UI_EXPORT MockIBusClient : public IBusClient {
-public:
+ public:
MockIBusClient();
virtual ~MockIBusClient();
// ui::internal::IBusClient overrides:
- virtual IBusBus* GetConnection() OVERRIDE;
- virtual bool IsConnected(IBusBus* bus) OVERRIDE;
- virtual void CreateContext(IBusBus* bus,
- PendingCreateICRequest* request) OVERRIDE;
- virtual void DestroyProxy(IBusInputContext* context) OVERRIDE;
+ virtual bool IsConnected() OVERRIDE;
+ virtual bool IsContextReady() OVERRIDE;
+ virtual void CreateContext(PendingCreateICRequest* request) OVERRIDE;
+ virtual void DestroyProxy() OVERRIDE;
virtual void SetCapabilities(
- IBusInputContext* context,
InlineCompositionCapability inline_type) OVERRIDE;
- virtual void FocusIn(IBusInputContext* context) OVERRIDE;
- virtual void FocusOut(IBusInputContext* context) OVERRIDE;
- virtual void Reset(IBusInputContext* context) OVERRIDE;
+ virtual void FocusIn() OVERRIDE;
+ virtual void FocusOut() OVERRIDE;
+ virtual void Reset() OVERRIDE;
virtual InputMethodType GetInputMethodType() OVERRIDE;
- virtual void SetCursorLocation(IBusInputContext* context,
- const gfx::Rect& cursor_location,
+ virtual void SetCursorLocation(const gfx::Rect& cursor_location,
const gfx::Rect& composition_head) OVERRIDE;
- virtual void SendKeyEvent(IBusInputContext* context,
- uint32 keyval,
- uint32 keycode,
- uint32 state,
- PendingKeyEvent* pending_key) OVERRIDE;
- virtual void ExtractCompositionText(
- IBusText* text,
- guint cursor_position,
- CompositionText* out_composition) OVERRIDE;
- virtual string16 ExtractCommitText(IBusText* text) OVERRIDE;
-
+ virtual void SendKeyEvent(
+ uint32 keyval,
+ uint32 keycode,
+ uint32 state,
+ const chromeos::IBusInputContextClient::ProcessKeyEventCallback&
+ cb) OVERRIDE;
// See comments in CreateContext().
enum CreateContextResult {
kCreateContextSuccess,
@@ -65,10 +58,10 @@ public:
// A value which IsConnected() will return.
bool is_connected_;
+ // A value which IsContextReady() will return.
+ bool is_context_ready_;
// A value which GetInputMethodType() will return.
InputMethodType input_method_type_;
- // A text which ExtractCompositionText() will return.
- CompositionText composition_text_;
// A text which ExtractCommitText() will return.
string16 commit_text_;
@@ -80,7 +73,7 @@ public:
unsigned int reset_call_count_;
unsigned int set_cursor_location_call_count_;
-private:
+ private:
DISALLOW_COPY_AND_ASSIGN(MockIBusClient);
};
diff --git a/ui/base/ime/mock_input_method.cc b/ui/base/ime/mock_input_method.cc
index 461f19c..9f6e032 100644
--- a/ui/base/ime/mock_input_method.cc
+++ b/ui/base/ime/mock_input_method.cc
@@ -22,7 +22,7 @@
namespace {
#if defined(USE_X11)
-guint32 EventFlagsFromXFlags(unsigned int flags) {
+uint32 EventFlagsFromXFlags(unsigned int flags) {
return (flags & LockMask ? ui::EF_CAPS_LOCK_DOWN : 0U) |
(flags & ControlMask ? ui::EF_CONTROL_DOWN : 0U) |
(flags & ShiftMask ? ui::EF_SHIFT_DOWN : 0U) |
diff --git a/ui/base/ime/mock_input_method.h b/ui/base/ime/mock_input_method.h
index 73042f3..a2d08922 100644
--- a/ui/base/ime/mock_input_method.h
+++ b/ui/base/ime/mock_input_method.h
@@ -6,6 +6,8 @@
#define UI_BASE_IME_MOCK_INPUT_METHOD_H_
#pragma once
+#include <string>
+
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "ui/base/ime/input_method.h"
diff --git a/ui/base/ime/text_input_test_support.cc b/ui/base/ime/text_input_test_support.cc
index 6e7df1c..0f4d964 100644
--- a/ui/base/ime/text_input_test_support.cc
+++ b/ui/base/ime/text_input_test_support.cc
@@ -4,7 +4,9 @@
#include "ui/base/ime/text_input_test_support.h"
+#if defined(OS_CHROMEOS)
#include "chromeos/dbus/dbus_thread_manager.h"
+#endif // OS_CHROMEOS
namespace ui {