Home | History | Annotate | Download | only in ime
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/base/ime/input_method_ibus.h"
      6 
      7 #include <X11/X.h>
      8 #include <X11/Xlib.h>
      9 #include <X11/Xutil.h>
     10 #undef FocusIn
     11 #undef FocusOut
     12 
     13 #include <algorithm>
     14 #include <cstring>
     15 #include <set>
     16 #include <vector>
     17 
     18 #include "base/basictypes.h"
     19 #include "base/bind.h"
     20 #include "base/chromeos/chromeos_version.h"
     21 #include "base/i18n/char_iterator.h"
     22 #include "base/logging.h"
     23 #include "base/strings/string_util.h"
     24 #include "base/strings/utf_string_conversions.h"
     25 #include "base/third_party/icu/icu_utf.h"
     26 #include "chromeos/dbus/dbus_thread_manager.h"
     27 #include "chromeos/dbus/ibus/ibus_client.h"
     28 #include "chromeos/dbus/ibus/ibus_input_context_client.h"
     29 #include "chromeos/dbus/ibus/ibus_text.h"
     30 #include "ui/base/events/event.h"
     31 #include "ui/base/events/event_constants.h"
     32 #include "ui/base/events/event_utils.h"
     33 #include "ui/base/ime/text_input_client.h"
     34 #include "ui/base/keycodes/keyboard_code_conversion.h"
     35 #include "ui/base/keycodes/keyboard_code_conversion_x.h"
     36 #include "ui/base/keycodes/keyboard_codes.h"
     37 #include "ui/gfx/rect.h"
     38 
     39 namespace {
     40 
     41 const int kIBusReleaseMask = 1 << 30;
     42 const char kClientName[] = "chrome";
     43 const int kMaxRetryCount = 10;
     44 
     45 // Following capability mask is introduced from
     46 // http://ibus.googlecode.com/svn/docs/ibus-1.4/ibus-ibustypes.html#IBusCapabilite
     47 const uint32 kIBusCapabilityPreeditText = 1U;
     48 const uint32 kIBusCapabilityFocus = 8U;
     49 const uint32 kIBusCapabilitySurroundingText = 32U;
     50 
     51 XKeyEvent* GetKeyEvent(XEvent* event) {
     52   DCHECK(event && (event->type == KeyPress || event->type == KeyRelease));
     53   return &event->xkey;
     54 }
     55 
     56 // Converts X (and ibus) state to event flags.
     57 int EventFlagsFromXState(unsigned int state) {
     58   return (state & LockMask ? ui::EF_CAPS_LOCK_DOWN : 0) |
     59       (state & ControlMask ? ui::EF_CONTROL_DOWN : 0) |
     60       (state & ShiftMask ? ui::EF_SHIFT_DOWN : 0) |
     61       (state & Mod1Mask ? ui::EF_ALT_DOWN : 0) |
     62       (state & Button1Mask ? ui::EF_LEFT_MOUSE_BUTTON : 0) |
     63       (state & Button2Mask ? ui::EF_MIDDLE_MOUSE_BUTTON : 0) |
     64       (state & Button3Mask ? ui::EF_RIGHT_MOUSE_BUTTON : 0);
     65 }
     66 
     67 // Converts X state to ibus key and button state.
     68 uint32 IBusStateFromXState(unsigned int state) {
     69   return (state & (LockMask | ControlMask | ShiftMask | Mod1Mask |
     70                    Button1Mask | Button2Mask | Button3Mask));
     71 }
     72 
     73 chromeos::IBusInputContextClient* GetInputContextClient() {
     74   return chromeos::DBusThreadManager::Get()->GetIBusInputContextClient();
     75 }
     76 
     77 // Converts gfx::Rect to ibus::Rect.
     78 chromeos::ibus::Rect GfxRectToIBusRect(const gfx::Rect& rect) {
     79   return chromeos::ibus::Rect(rect.x(), rect.y(), rect.width(), rect.height());
     80 }
     81 
     82 }  // namespace
     83 
     84 namespace ui {
     85 
     86 // InputMethodIBus implementation -----------------------------------------
     87 InputMethodIBus::InputMethodIBus(
     88     internal::InputMethodDelegate* delegate)
     89     : input_context_state_(INPUT_CONTEXT_STOP),
     90       create_input_context_fail_count_(0),
     91       context_focused_(false),
     92       composing_text_(false),
     93       composition_changed_(false),
     94       suppress_next_result_(false),
     95       current_keyevent_id_(0),
     96       weak_ptr_factory_(this) {
     97   SetDelegate(delegate);
     98 
     99   // chromeos::IBusDaemonController is not available in case of some testing,
    100   // e.g. content_browser test can't initialize IBusDaemonController.
    101   DCHECK(!base::chromeos::IsRunningOnChromeOS() ||
    102          chromeos::IBusDaemonController::GetInstance());
    103 
    104   if (chromeos::IBusDaemonController::GetInstance())
    105     chromeos::IBusDaemonController::GetInstance()->AddObserver(this);
    106 }
    107 
    108 InputMethodIBus::~InputMethodIBus() {
    109   AbandonAllPendingKeyEvents();
    110   if (IsContextReady())
    111     DestroyContext();
    112   if (GetInputContextClient())
    113     GetInputContextClient()->SetInputContextHandler(NULL);
    114   if (chromeos::IBusDaemonController::GetInstance())
    115     chromeos::IBusDaemonController::GetInstance()->RemoveObserver(this);
    116 }
    117 
    118 void InputMethodIBus::OnFocus() {
    119   InputMethodBase::OnFocus();
    120   UpdateContextFocusState();
    121 }
    122 
    123 void InputMethodIBus::OnBlur() {
    124   ConfirmCompositionText();
    125   InputMethodBase::OnBlur();
    126   UpdateContextFocusState();
    127 }
    128 
    129 bool InputMethodIBus::OnUntranslatedIMEMessage(const base::NativeEvent& event,
    130                                                NativeEventResult* result) {
    131   return false;
    132 }
    133 
    134 void InputMethodIBus::Init(bool focused) {
    135   // Initializes the connection to ibus daemon. It may happen asynchronously,
    136   // and as soon as the connection is established, the |context_| will be
    137   // created automatically.
    138 
    139   // Create the input context if the connection is already established.
    140   if (IsConnected())
    141     CreateContext();
    142 
    143   InputMethodBase::Init(focused);
    144 }
    145 
    146 void InputMethodIBus::ProcessKeyEventDone(uint32 id,
    147                                           XEvent* event,
    148                                           uint32 ibus_keyval,
    149                                           uint32 ibus_keycode,
    150                                           uint32 ibus_state,
    151                                           bool is_handled) {
    152   DCHECK(event);
    153   std::set<uint32>::iterator it = pending_key_events_.find(id);
    154 
    155   if (it == pending_key_events_.end())
    156     return;  // Abandoned key event.
    157 
    158   if (event->type == KeyPress) {
    159     if (is_handled) {
    160       // IME event has a priority to be handled, so that character composer
    161       // should be reset.
    162       character_composer_.Reset();
    163     } else {
    164       // If IME does not handle key event, passes keyevent to character composer
    165       // to be able to compose complex characters.
    166       is_handled = ExecuteCharacterComposer(ibus_keyval, ibus_keycode,
    167                                             ibus_state);
    168     }
    169   }
    170 
    171   if (event->type == KeyPress || event->type == KeyRelease)
    172     ProcessKeyEventPostIME(event, ibus_state, is_handled);
    173 
    174   // Do not use |it| for erasing, ProcessKeyEventPostIME may change the
    175   // |pending_key_events_|.
    176   pending_key_events_.erase(id);
    177 }
    178 
    179 bool InputMethodIBus::DispatchKeyEvent(const base::NativeEvent& native_event) {
    180   DCHECK(native_event && (native_event->type == KeyPress ||
    181                           native_event->type == KeyRelease));
    182   DCHECK(system_toplevel_window_focused());
    183 
    184   uint32 ibus_keyval = 0;
    185   uint32 ibus_keycode = 0;
    186   uint32 ibus_state = 0;
    187   IBusKeyEventFromNativeKeyEvent(
    188       native_event,
    189       &ibus_keyval, &ibus_keycode, &ibus_state);
    190 
    191   // If |context_| is not usable, then we can only dispatch the key event as is.
    192   // We also dispatch the key event directly if the current text input type is
    193   // TEXT_INPUT_TYPE_PASSWORD, to bypass the input method.
    194   // Note: We need to send the key event to ibus even if the |context_| is not
    195   // enabled, so that ibus can have a chance to enable the |context_|.
    196   if (!context_focused_ ||
    197       GetTextInputType() == TEXT_INPUT_TYPE_PASSWORD ||
    198       !GetInputContextClient() ||
    199       GetInputContextClient()->IsXKBLayout()) {
    200     if (native_event->type == KeyPress) {
    201       if (ExecuteCharacterComposer(ibus_keyval, ibus_keycode, ibus_state)) {
    202         // Treating as PostIME event if character composer handles key event and
    203         // generates some IME event,
    204         ProcessKeyEventPostIME(native_event, ibus_state, true);
    205         return true;
    206       }
    207       ProcessUnfilteredKeyPressEvent(native_event, ibus_state);
    208     } else {
    209       DispatchKeyEventPostIME(native_event);
    210     }
    211     return true;
    212   }
    213 
    214   pending_key_events_.insert(current_keyevent_id_);
    215 
    216   // Since |native_event| might be treated as XEvent whose size is bigger than
    217   // XKeyEvent e.g. in CopyNativeEvent() in ui/base/events/event.cc, allocating
    218   // |event| as XKeyEvent and casting it to XEvent is unsafe. crbug.com/151884
    219   XEvent* event = new XEvent;
    220   *event = *native_event;
    221   const chromeos::IBusInputContextClient::ProcessKeyEventCallback callback =
    222       base::Bind(&InputMethodIBus::ProcessKeyEventDone,
    223                  weak_ptr_factory_.GetWeakPtr(),
    224                  current_keyevent_id_,
    225                  base::Owned(event),  // Pass the ownership of |event|.
    226                  ibus_keyval,
    227                  ibus_keycode,
    228                  ibus_state);
    229 
    230   GetInputContextClient()->ProcessKeyEvent(ibus_keyval,
    231                                            ibus_keycode,
    232                                            ibus_state,
    233                                            callback,
    234                                            base::Bind(callback, false));
    235   ++current_keyevent_id_;
    236 
    237   // We don't want to suppress the result generated by this key event, but it
    238   // may cause problem. See comment in ResetContext() method.
    239   suppress_next_result_ = false;
    240   return true;
    241 }
    242 
    243 bool InputMethodIBus::DispatchFabricatedKeyEvent(const ui::KeyEvent& event) {
    244   // TODO(bryeung): The fabricated events should also pass through IME.
    245   if (event.type() == ET_KEY_PRESSED) {
    246     ProcessUnfilteredFabricatedKeyPressEvent(
    247         ET_KEY_PRESSED, event.key_code(), event.flags());
    248   } else {
    249     DispatchFabricatedKeyEventPostIME(
    250         ET_KEY_RELEASED,
    251         event.key_code(),
    252         event.flags());
    253   }
    254   return true;
    255 }
    256 
    257 void InputMethodIBus::OnTextInputTypeChanged(const TextInputClient* client) {
    258   if (IsContextReady() && IsTextInputClientFocused(client)) {
    259     ResetContext();
    260     UpdateContextFocusState();
    261   }
    262   InputMethodBase::OnTextInputTypeChanged(client);
    263 }
    264 
    265 void InputMethodIBus::OnCaretBoundsChanged(const TextInputClient* client) {
    266   if (!context_focused_ || !IsTextInputClientFocused(client))
    267     return;
    268 
    269   // The current text input type should not be NONE if |context_| is focused.
    270   DCHECK(!IsTextInputTypeNone());
    271   const gfx::Rect rect = GetTextInputClient()->GetCaretBounds();
    272 
    273   gfx::Rect composition_head;
    274   if (!GetTextInputClient()->GetCompositionCharacterBounds(0,
    275                                                            &composition_head)) {
    276     composition_head = rect;
    277   }
    278 
    279   GetInputContextClient()->SetCursorLocation(
    280       GfxRectToIBusRect(rect),
    281       GfxRectToIBusRect(composition_head));
    282 
    283   ui::Range text_range;
    284   ui::Range selection_range;
    285   string16 surrounding_text;
    286   if (!GetTextInputClient()->GetTextRange(&text_range) ||
    287       !GetTextInputClient()->GetTextFromRange(text_range, &surrounding_text) ||
    288       !GetTextInputClient()->GetSelectionRange(&selection_range)) {
    289     previous_surrounding_text_.clear();
    290     previous_selection_range_ = ui::Range::InvalidRange();
    291     return;
    292   }
    293 
    294   if (previous_selection_range_ == selection_range &&
    295       previous_surrounding_text_ == surrounding_text)
    296     return;
    297 
    298   previous_selection_range_ = selection_range;
    299   previous_surrounding_text_ = surrounding_text;
    300 
    301   if (!selection_range.IsValid()) {
    302     // TODO(nona): Ideally selection_range should not be invalid.
    303     // TODO(nona): If javascript changes the focus on page loading, even (0,0)
    304     //             can not be obtained. Need investigation.
    305     return;
    306   }
    307 
    308   // Here SetSurroundingText accepts relative position of |surrounding_text|, so
    309   // we have to convert |selection_range| from node coordinates to
    310   // |surrounding_text| coordinates.
    311   GetInputContextClient()->SetSurroundingText(
    312       UTF16ToUTF8(surrounding_text),
    313       selection_range.start() - text_range.start(),
    314       selection_range.end() - text_range.start());
    315 }
    316 
    317 void InputMethodIBus::CancelComposition(const TextInputClient* client) {
    318   if (context_focused_ && IsTextInputClientFocused(client))
    319     ResetContext();
    320 }
    321 
    322 void InputMethodIBus::OnInputLocaleChanged() {
    323   // Not supported.
    324 }
    325 
    326 std::string InputMethodIBus::GetInputLocale() {
    327   // Not supported.
    328   return "";
    329 }
    330 
    331 base::i18n::TextDirection InputMethodIBus::GetInputTextDirection() {
    332   // Not supported.
    333   return base::i18n::UNKNOWN_DIRECTION;
    334 }
    335 
    336 bool InputMethodIBus::IsActive() {
    337   return true;
    338 }
    339 
    340 bool InputMethodIBus::IsCandidatePopupOpen() const {
    341   // TODO(yukishiino): Implement this method.
    342   return false;
    343 }
    344 
    345 void InputMethodIBus::OnWillChangeFocusedClient(TextInputClient* focused_before,
    346                                                 TextInputClient* focused) {
    347   ConfirmCompositionText();
    348 }
    349 
    350 void InputMethodIBus::OnDidChangeFocusedClient(TextInputClient* focused_before,
    351                                                TextInputClient* focused) {
    352   // Force to update the input type since client's TextInputStateChanged()
    353   // function might not be called if text input types before the client loses
    354   // focus and after it acquires focus again are the same.
    355   OnTextInputTypeChanged(focused);
    356 
    357   UpdateContextFocusState();
    358   // Force to update caret bounds, in case the client thinks that the caret
    359   // bounds has not changed.
    360   OnCaretBoundsChanged(focused);
    361 }
    362 
    363 void InputMethodIBus::CreateContext() {
    364   DCHECK(IsConnected());
    365 
    366   if (input_context_state_ != INPUT_CONTEXT_STOP) {
    367     DVLOG(1) << "Input context is already created or waiting ibus-daemon"
    368                 " response.";
    369     return;
    370   }
    371 
    372   input_context_state_ = INPUT_CONTEXT_WAIT_CREATE_INPUT_CONTEXT_RESPONSE;
    373 
    374   // Creates the input context asynchronously.
    375   DCHECK(!IsContextReady());
    376   chromeos::DBusThreadManager::Get()->GetIBusClient()->CreateInputContext(
    377       kClientName,
    378       base::Bind(&InputMethodIBus::CreateInputContextDone,
    379                  weak_ptr_factory_.GetWeakPtr()),
    380       base::Bind(&InputMethodIBus::CreateInputContextFail,
    381                  weak_ptr_factory_.GetWeakPtr()));
    382 }
    383 
    384 void InputMethodIBus::SetUpSignalHandlers() {
    385   DCHECK(IsContextReady());
    386 
    387   // We should reset the handler to NULL before |this| is deleted so handler
    388   // functions are not called after |this| is deleted.
    389   GetInputContextClient()->SetInputContextHandler(this);
    390 
    391   GetInputContextClient()->SetCapabilities(
    392       kIBusCapabilityPreeditText | kIBusCapabilityFocus |
    393       kIBusCapabilitySurroundingText);
    394 
    395   UpdateContextFocusState();
    396   // Since ibus-daemon is launched in an on-demand basis on Chrome OS, RWHVA (or
    397   // equivalents) might call OnCaretBoundsChanged() before the daemon starts. To
    398   // save the case, call OnCaretBoundsChanged() here.
    399   OnCaretBoundsChanged(GetTextInputClient());
    400   OnInputMethodChanged();
    401 }
    402 
    403 void InputMethodIBus::DestroyContext() {
    404   if (input_context_state_ == INPUT_CONTEXT_STOP)
    405     return;
    406   input_context_state_ = INPUT_CONTEXT_STOP;
    407   chromeos::IBusInputContextClient* input_context = GetInputContextClient();
    408   if (!input_context)
    409     return;
    410   if (input_context->IsObjectProxyReady()) {
    411     // We can't use IsContextReady here because we want to destroy object proxy
    412     // regardless of connection. The IsContextReady contains connection check.
    413     ResetInputContext();
    414     DCHECK(!IsContextReady());
    415   }
    416 }
    417 
    418 void InputMethodIBus::ConfirmCompositionText() {
    419   TextInputClient* client = GetTextInputClient();
    420   if (client && client->HasCompositionText())
    421     client->ConfirmCompositionText();
    422 
    423   ResetContext();
    424 }
    425 
    426 void InputMethodIBus::ResetContext() {
    427   if (!context_focused_ || !GetTextInputClient())
    428     return;
    429 
    430   DCHECK(system_toplevel_window_focused());
    431 
    432   // Because ibus runs in asynchronous mode, the input method may still send us
    433   // results after sending out the reset request, so we use a flag to discard
    434   // all results generated by previous key events. But because ibus does not
    435   // have a mechanism to identify each key event and corresponding results, this
    436   // approach will not work for some corner cases. For example if the user types
    437   // very fast, then the next key event may come in before the |context_| is
    438   // really reset. Then we actually cannot know whether or not the next
    439   // result should be discard.
    440   suppress_next_result_ = true;
    441 
    442   composition_.Clear();
    443   result_text_.clear();
    444   composing_text_ = false;
    445   composition_changed_ = false;
    446 
    447   // We need to abandon all pending key events, but as above comment says, there
    448   // is no reliable way to abandon all results generated by these abandoned key
    449   // events.
    450   AbandonAllPendingKeyEvents();
    451 
    452   // This function runs asynchronously.
    453   // Note: some input method engines may not support reset method, such as
    454   // ibus-anthy. But as we control all input method engines by ourselves, we can
    455   // make sure that all of the engines we are using support it correctly.
    456   GetInputContextClient()->Reset();
    457 
    458   character_composer_.Reset();
    459 }
    460 
    461 void InputMethodIBus::UpdateContextFocusState() {
    462   if (!IsContextReady()) {
    463     context_focused_ = false;
    464     return;
    465   }
    466 
    467   const bool old_context_focused = context_focused_;
    468   // Use switch here in case we are going to add more text input types.
    469   switch (GetTextInputType()) {
    470     case TEXT_INPUT_TYPE_NONE:
    471     case TEXT_INPUT_TYPE_PASSWORD:
    472       context_focused_ = false;
    473       break;
    474     default:
    475       context_focused_ = true;
    476       break;
    477   }
    478 
    479   // We only focus in |context_| when the focus is in a normal textfield.
    480   // ibus_input_context_focus_{in|out}() run asynchronously.
    481   if (old_context_focused && !context_focused_)
    482     GetInputContextClient()->FocusOut();
    483   else if (!old_context_focused && context_focused_)
    484     GetInputContextClient()->FocusIn();
    485 
    486   if (context_focused_) {
    487     uint32 capability = kIBusCapabilityFocus | kIBusCapabilitySurroundingText;
    488     if (CanComposeInline())
    489       capability |= kIBusCapabilityPreeditText;
    490     GetInputContextClient()->SetCapabilities(capability);
    491   }
    492 }
    493 
    494 void InputMethodIBus::ProcessKeyEventPostIME(
    495     const base::NativeEvent& native_event,
    496     uint32 ibus_state,
    497     bool handled) {
    498   TextInputClient* client = GetTextInputClient();
    499 
    500   if (!client) {
    501     // As ibus works asynchronously, there is a chance that the focused client
    502     // loses focus before this method gets called.
    503     DispatchKeyEventPostIME(native_event);
    504     return;
    505   }
    506 
    507   if (native_event->type == KeyPress && handled)
    508     ProcessFilteredKeyPressEvent(native_event);
    509 
    510   // In case the focus was changed by the key event. The |context_| should have
    511   // been reset when the focused window changed.
    512   if (client != GetTextInputClient())
    513     return;
    514 
    515   if (HasInputMethodResult())
    516     ProcessInputMethodResult(native_event, handled);
    517 
    518   // In case the focus was changed when sending input method results to the
    519   // focused window.
    520   if (client != GetTextInputClient())
    521     return;
    522 
    523   if (native_event->type == KeyPress && !handled)
    524     ProcessUnfilteredKeyPressEvent(native_event, ibus_state);
    525   else if (native_event->type == KeyRelease)
    526     DispatchKeyEventPostIME(native_event);
    527 }
    528 
    529 void InputMethodIBus::IBusKeyEventFromNativeKeyEvent(
    530     const base::NativeEvent& native_event,
    531     uint32* ibus_keyval,
    532     uint32* ibus_keycode,
    533     uint32* ibus_state) {
    534   DCHECK(native_event);  // A fabricated event is not supported here.
    535   XKeyEvent* x_key = GetKeyEvent(native_event);
    536 
    537   // Yes, ibus uses X11 keysym. We cannot use XLookupKeysym(), which doesn't
    538   // translate Shift and CapsLock states.
    539   KeySym keysym = NoSymbol;
    540   ::XLookupString(x_key, NULL, 0, &keysym, NULL);
    541   *ibus_keyval = keysym;
    542   *ibus_keycode = x_key->keycode;
    543   *ibus_state = IBusStateFromXState(x_key->state);
    544   if (native_event->type == KeyRelease)
    545     *ibus_state |= kIBusReleaseMask;
    546 }
    547 
    548 void InputMethodIBus::ProcessFilteredKeyPressEvent(
    549     const base::NativeEvent& native_event) {
    550   if (NeedInsertChar())
    551     DispatchKeyEventPostIME(native_event);
    552   else
    553     DispatchFabricatedKeyEventPostIME(
    554         ET_KEY_PRESSED,
    555         VKEY_PROCESSKEY,
    556         EventFlagsFromXState(GetKeyEvent(native_event)->state));
    557 }
    558 
    559 void InputMethodIBus::ProcessUnfilteredKeyPressEvent(
    560     const base::NativeEvent& native_event,
    561     uint32 ibus_state) {
    562   // For a fabricated event, ProcessUnfilteredFabricatedKeyPressEvent should be
    563   // called instead.
    564   DCHECK(native_event);
    565 
    566   TextInputClient* client = GetTextInputClient();
    567   DispatchKeyEventPostIME(native_event);
    568 
    569   // We shouldn't dispatch the character anymore if the key event dispatch
    570   // caused focus change. For example, in the following scenario,
    571   // 1. visit a web page which has a <textarea>.
    572   // 2. click Omnibox.
    573   // 3. enable Korean IME, press A, then press Tab to move the focus to the web
    574   //    page.
    575   // We should return here not to send the Tab key event to RWHV.
    576   if (client != GetTextInputClient())
    577     return;
    578 
    579   const uint32 event_flags = EventFlagsFromXState(ibus_state);
    580 
    581   // If a key event was not filtered by |context_| and |character_composer_|,
    582   // then it means the key event didn't generate any result text. So we need
    583   // to send corresponding character to the focused text input client.
    584   client = GetTextInputClient();
    585 
    586   uint16 ch = 0;
    587   if (!(event_flags & ui::EF_CONTROL_DOWN))
    588     ch = ui::GetCharacterFromXEvent(native_event);
    589   if (!ch) {
    590     ch = ui::GetCharacterFromKeyCode(
    591         ui::KeyboardCodeFromNative(native_event), event_flags);
    592   }
    593 
    594   if (client && ch)
    595     client->InsertChar(ch, event_flags);
    596 }
    597 
    598 void InputMethodIBus::ProcessUnfilteredFabricatedKeyPressEvent(
    599     EventType type,
    600     KeyboardCode key_code,
    601     int event_flags) {
    602   TextInputClient* client = GetTextInputClient();
    603   DispatchFabricatedKeyEventPostIME(type, key_code, event_flags);
    604 
    605   if (client != GetTextInputClient())
    606     return;
    607 
    608   client = GetTextInputClient();
    609   const uint16 ch = ui::GetCharacterFromKeyCode(key_code, event_flags);
    610   if (client && ch)
    611     client->InsertChar(ch, event_flags);
    612 }
    613 
    614 void InputMethodIBus::ProcessInputMethodResult(
    615     const base::NativeEvent& native_event,
    616     bool handled) {
    617   TextInputClient* client = GetTextInputClient();
    618   DCHECK(client);
    619 
    620   if (result_text_.length()) {
    621     if (handled && NeedInsertChar()) {
    622       const uint32 state =
    623           EventFlagsFromXState(GetKeyEvent(native_event)->state);
    624       for (string16::const_iterator i = result_text_.begin();
    625            i != result_text_.end(); ++i) {
    626         client->InsertChar(*i, state);
    627       }
    628     } else {
    629       client->InsertText(result_text_);
    630       composing_text_ = false;
    631     }
    632   }
    633 
    634   if (composition_changed_ && !IsTextInputTypeNone()) {
    635     if (composition_.text.length()) {
    636       composing_text_ = true;
    637       client->SetCompositionText(composition_);
    638     } else if (result_text_.empty()) {
    639       client->ClearCompositionText();
    640     }
    641   }
    642 
    643   // We should not clear composition text here, as it may belong to the next
    644   // composition session.
    645   result_text_.clear();
    646   composition_changed_ = false;
    647 }
    648 
    649 bool InputMethodIBus::NeedInsertChar() const {
    650   return GetTextInputClient() &&
    651       (IsTextInputTypeNone() ||
    652        (!composing_text_ && result_text_.length() == 1));
    653 }
    654 
    655 bool InputMethodIBus::HasInputMethodResult() const {
    656   return result_text_.length() || composition_changed_;
    657 }
    658 
    659 void InputMethodIBus::AbandonAllPendingKeyEvents() {
    660   pending_key_events_.clear();
    661 }
    662 
    663 void InputMethodIBus::CommitText(const chromeos::IBusText& text) {
    664   if (suppress_next_result_ || text.text().empty())
    665     return;
    666 
    667   // We need to receive input method result even if the text input type is
    668   // TEXT_INPUT_TYPE_NONE, to make sure we can always send correct
    669   // character for each key event to the focused text input client.
    670   if (!GetTextInputClient())
    671     return;
    672 
    673   const string16 utf16_text = UTF8ToUTF16(text.text());
    674   if (utf16_text.empty())
    675     return;
    676 
    677   // Append the text to the buffer, because commit signal might be fired
    678   // multiple times when processing a key event.
    679   result_text_.append(utf16_text);
    680 
    681   // If we are not handling key event, do not bother sending text result if the
    682   // focused text input client does not support text input.
    683   if (pending_key_events_.empty() && !IsTextInputTypeNone()) {
    684     GetTextInputClient()->InsertText(utf16_text);
    685     result_text_.clear();
    686   }
    687 }
    688 
    689 void InputMethodIBus::ForwardKeyEvent(uint32 keyval,
    690                                       uint32 keycode,
    691                                       uint32 state) {
    692   KeyboardCode ui_key_code = KeyboardCodeFromXKeysym(keyval);
    693   if (!ui_key_code)
    694     return;
    695 
    696   const EventType event_type =
    697       (state & kIBusReleaseMask) ? ET_KEY_RELEASED : ET_KEY_PRESSED;
    698   const int event_flags = EventFlagsFromXState(state);
    699 
    700   // It is not clear when the input method will forward us a fake key event.
    701   // If there is a pending key event, then we may already received some input
    702   // method results, so we dispatch this fake key event directly rather than
    703   // calling ProcessKeyEventPostIME(), which will clear pending input method
    704   // results.
    705   if (event_type == ET_KEY_PRESSED) {
    706     ProcessUnfilteredFabricatedKeyPressEvent(event_type, ui_key_code,
    707                                              event_flags);
    708   } else {
    709     DispatchFabricatedKeyEventPostIME(event_type, ui_key_code, event_flags);
    710   }
    711 }
    712 
    713 void InputMethodIBus::ShowPreeditText() {
    714   if (suppress_next_result_ || IsTextInputTypeNone())
    715     return;
    716 
    717   composing_text_ = true;
    718 }
    719 
    720 void InputMethodIBus::UpdatePreeditText(const chromeos::IBusText& text,
    721                                         uint32 cursor_pos,
    722                                         bool visible) {
    723   if (suppress_next_result_ || IsTextInputTypeNone())
    724     return;
    725 
    726   // |visible| argument is very confusing. For example, what's the correct
    727   // behavior when:
    728   // 1. OnUpdatePreeditText() is called with a text and visible == false, then
    729   // 2. OnShowPreeditText() is called afterwards.
    730   //
    731   // If it's only for clearing the current preedit text, then why not just use
    732   // OnHidePreeditText()?
    733   if (!visible) {
    734     HidePreeditText();
    735     return;
    736   }
    737 
    738   ExtractCompositionText(text, cursor_pos, &composition_);
    739 
    740   composition_changed_ = true;
    741 
    742   // In case OnShowPreeditText() is not called.
    743   if (composition_.text.length())
    744     composing_text_ = true;
    745 
    746   // If we receive a composition text without pending key event, then we need to
    747   // send it to the focused text input client directly.
    748   if (pending_key_events_.empty()) {
    749     GetTextInputClient()->SetCompositionText(composition_);
    750     composition_changed_ = false;
    751     composition_.Clear();
    752   }
    753 }
    754 
    755 void InputMethodIBus::HidePreeditText() {
    756   if (composition_.text.empty() || IsTextInputTypeNone())
    757     return;
    758 
    759   // Intentionally leaves |composing_text_| unchanged.
    760   composition_changed_ = true;
    761   composition_.Clear();
    762 
    763   if (pending_key_events_.empty()) {
    764     TextInputClient* client = GetTextInputClient();
    765     if (client && client->HasCompositionText())
    766       client->ClearCompositionText();
    767     composition_changed_ = false;
    768   }
    769 }
    770 
    771 void InputMethodIBus::DeleteSurroundingText(int32 offset, uint32 length) {
    772   if (!composition_.text.empty())
    773     return;  // do nothing if there is ongoing composition.
    774   if (offset < 0 && static_cast<uint32>(-1 * offset) != length)
    775     return;  // only preceding text can be deletable.
    776   if (GetTextInputClient())
    777     GetTextInputClient()->ExtendSelectionAndDelete(length, 0U);
    778 }
    779 
    780 void InputMethodIBus::ResetInputContext() {
    781   context_focused_ = false;
    782 
    783   ConfirmCompositionText();
    784 
    785   // We are dead, so we need to ask the client to stop relying on us.
    786   OnInputMethodChanged();
    787   GetInputContextClient()->ResetObjectProxy();
    788 }
    789 
    790 void InputMethodIBus::CreateInputContextDone(
    791     const dbus::ObjectPath& object_path) {
    792   DCHECK_NE(INPUT_CONTEXT_RUNNING, input_context_state_);
    793 
    794   if (input_context_state_ == INPUT_CONTEXT_STOP) {
    795     // DestroyContext has already been called.
    796     return;
    797   }
    798 
    799   chromeos::DBusThreadManager::Get()->GetIBusInputContextClient()
    800       ->Initialize(chromeos::DBusThreadManager::Get()->GetIBusBus(),
    801                    object_path);
    802 
    803   input_context_state_ = INPUT_CONTEXT_RUNNING;
    804   DCHECK(IsContextReady());
    805   SetUpSignalHandlers();
    806 }
    807 
    808 void InputMethodIBus::CreateInputContextFail() {
    809   DCHECK_NE(INPUT_CONTEXT_RUNNING, input_context_state_);
    810   if (input_context_state_ == INPUT_CONTEXT_STOP) {
    811     // CreateInputContext failed but the input context is no longer
    812     // necessary, thus do nothing.
    813     return;
    814   }
    815 
    816   if (++create_input_context_fail_count_ >= kMaxRetryCount) {
    817     DVLOG(1) << "CreateInputContext failed even tried "
    818              << kMaxRetryCount << " times, give up.";
    819     create_input_context_fail_count_ = 0;
    820     input_context_state_ = INPUT_CONTEXT_STOP;
    821     return;
    822   }
    823 
    824   // Try CreateInputContext again.
    825   chromeos::DBusThreadManager::Get()->GetIBusClient()->CreateInputContext(
    826       kClientName,
    827       base::Bind(&InputMethodIBus::CreateInputContextDone,
    828                  weak_ptr_factory_.GetWeakPtr()),
    829       base::Bind(&InputMethodIBus::CreateInputContextFail,
    830                  weak_ptr_factory_.GetWeakPtr()));
    831 }
    832 
    833 bool InputMethodIBus::IsConnected() {
    834   return chromeos::DBusThreadManager::Get()->GetIBusBus() != NULL;
    835 }
    836 
    837 bool InputMethodIBus::IsContextReady() {
    838   if (!IsConnected())
    839     return false;
    840   if (!GetInputContextClient())
    841     return false;
    842   return GetInputContextClient()->IsObjectProxyReady();
    843 }
    844 
    845 bool InputMethodIBus::ExecuteCharacterComposer(uint32 ibus_keyval,
    846                                                uint32 ibus_keycode,
    847                                                uint32 ibus_state) {
    848   bool consumed = character_composer_.FilterKeyPress(
    849       ibus_keyval,
    850       ibus_keycode,
    851       EventFlagsFromXState(ibus_state));
    852 
    853   suppress_next_result_ = false;
    854   chromeos::IBusText preedit;
    855   preedit.set_text(
    856       UTF16ToUTF8(character_composer_.preedit_string()));
    857   UpdatePreeditText(preedit, preedit.text().size(),
    858                     !preedit.text().empty());
    859    std::string commit_text =
    860       UTF16ToUTF8(character_composer_.composed_character());
    861   if (!commit_text.empty()) {
    862     chromeos::IBusText ibus_text;
    863     ibus_text.set_text(commit_text);
    864     CommitText(ibus_text);
    865   }
    866   return consumed;
    867 }
    868 
    869 void InputMethodIBus::OnConnected() {
    870   DCHECK(IsConnected());
    871   // If already input context is initialized, do nothing.
    872   if (IsContextReady())
    873     return;
    874 
    875   DestroyContext();
    876   CreateContext();
    877 }
    878 
    879 void InputMethodIBus::OnDisconnected() {
    880   DestroyContext();
    881 }
    882 
    883 void InputMethodIBus::ExtractCompositionText(
    884     const chromeos::IBusText& text,
    885     uint32 cursor_position,
    886     CompositionText* out_composition) const {
    887   out_composition->Clear();
    888   out_composition->text = UTF8ToUTF16(text.text());
    889 
    890   if (out_composition->text.empty())
    891     return;
    892 
    893   // ibus uses character index for cursor position and attribute range, but we
    894   // use char16 offset for them. So we need to do conversion here.
    895   std::vector<size_t> char16_offsets;
    896   size_t length = out_composition->text.length();
    897   base::i18n::UTF16CharIterator char_iterator(&out_composition->text);
    898   do {
    899     char16_offsets.push_back(char_iterator.array_pos());
    900   } while (char_iterator.Advance());
    901 
    902   // The text length in Unicode characters.
    903   uint32 char_length = static_cast<uint32>(char16_offsets.size());
    904   // Make sure we can convert the value of |char_length| as well.
    905   char16_offsets.push_back(length);
    906 
    907   size_t cursor_offset =
    908       char16_offsets[std::min(char_length, cursor_position)];
    909 
    910   out_composition->selection = Range(cursor_offset);
    911 
    912   const std::vector<chromeos::IBusText::UnderlineAttribute>&
    913       underline_attributes = text.underline_attributes();
    914   const std::vector<chromeos::IBusText::SelectionAttribute>&
    915       selection_attributes = text.selection_attributes();
    916 
    917   if (!underline_attributes.empty()) {
    918     for (size_t i = 0; i < underline_attributes.size(); ++i) {
    919       const uint32 start = underline_attributes[i].start_index;
    920       const uint32 end = underline_attributes[i].end_index;
    921       if (start >= end)
    922         continue;
    923       CompositionUnderline underline(
    924           char16_offsets[start], char16_offsets[end],
    925           SK_ColorBLACK, false /* thick */);
    926       if (underline_attributes[i].type ==
    927           chromeos::IBusText::IBUS_TEXT_UNDERLINE_DOUBLE)
    928         underline.thick = true;
    929       else if (underline_attributes[i].type ==
    930                chromeos::IBusText::IBUS_TEXT_UNDERLINE_ERROR)
    931         underline.color = SK_ColorRED;
    932       out_composition->underlines.push_back(underline);
    933     }
    934   }
    935 
    936   if (!selection_attributes.empty()) {
    937     LOG_IF(ERROR, selection_attributes.size() != 1)
    938         << "Chrome does not support multiple selection";
    939     for (uint32 i = 0; i < selection_attributes.size(); ++i) {
    940       const uint32 start = selection_attributes[i].start_index;
    941       const uint32 end = selection_attributes[i].end_index;
    942       if (start >= end)
    943         continue;
    944       CompositionUnderline underline(
    945           char16_offsets[start], char16_offsets[end],
    946           SK_ColorBLACK, true /* thick */);
    947       out_composition->underlines.push_back(underline);
    948       // If the cursor is at start or end of this underline, then we treat
    949       // it as the selection range as well, but make sure to set the cursor
    950       // position to the selection end.
    951       if (underline.start_offset == cursor_offset) {
    952         out_composition->selection.set_start(underline.end_offset);
    953         out_composition->selection.set_end(cursor_offset);
    954       } else if (underline.end_offset == cursor_offset) {
    955         out_composition->selection.set_start(underline.start_offset);
    956         out_composition->selection.set_end(cursor_offset);
    957       }
    958     }
    959   }
    960 
    961   // Use a black thin underline by default.
    962   if (out_composition->underlines.empty()) {
    963     out_composition->underlines.push_back(CompositionUnderline(
    964         0, length, SK_ColorBLACK, false /* thick */));
    965   }
    966 }
    967 
    968 }  // namespace ui
    969