Home | History | Annotate | Download | only in input_method
      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 "chrome/browser/chromeos/input_method/input_method_engine_ibus.h"
      6 
      7 #define XK_MISCELLANY
      8 #include <X11/keysymdef.h>
      9 #include <map>
     10 
     11 #include "base/logging.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/strings/string_number_conversions.h"
     14 #include "base/strings/string_util.h"
     15 #include "base/strings/utf_string_conversions.h"
     16 #include "chromeos/dbus/dbus_thread_manager.h"
     17 #include "chromeos/dbus/ibus/ibus_client.h"
     18 #include "chromeos/dbus/ibus/ibus_component.h"
     19 #include "chromeos/dbus/ibus/ibus_engine_factory_service.h"
     20 #include "chromeos/dbus/ibus/ibus_engine_service.h"
     21 #include "chromeos/dbus/ibus/ibus_lookup_table.h"
     22 #include "chromeos/dbus/ibus/ibus_property.h"
     23 #include "chromeos/dbus/ibus/ibus_text.h"
     24 #include "chromeos/ime/component_extension_ime_manager.h"
     25 #include "chromeos/ime/extension_ime_util.h"
     26 #include "chromeos/ime/ibus_keymap.h"
     27 #include "chromeos/ime/input_method_manager.h"
     28 #include "dbus/object_path.h"
     29 
     30 namespace chromeos {
     31 const char* kErrorNotActive = "IME is not active";
     32 const char* kErrorWrongContext = "Context is not active";
     33 const char* kCandidateNotFound = "Candidate not found";
     34 const char* kEngineBusPrefix = "org.freedesktop.IBus.";
     35 
     36 namespace {
     37 const uint32 kIBusAltKeyMask = 1 << 3;
     38 const uint32 kIBusCtrlKeyMask = 1 << 2;
     39 const uint32 kIBusShiftKeyMask = 1 << 0;
     40 const uint32 kIBusCapsLockMask = 1 << 1;
     41 const uint32 kIBusKeyReleaseMask = 1 << 30;
     42 }
     43 
     44 InputMethodEngineIBus::InputMethodEngineIBus()
     45     : focused_(false),
     46       active_(false),
     47       context_id_(0),
     48       next_context_id_(1),
     49       aux_text_(new IBusText()),
     50       aux_text_visible_(false),
     51       observer_(NULL),
     52       preedit_text_(new IBusText()),
     53       preedit_cursor_(0),
     54       component_(new IBusComponent()),
     55       table_(new IBusLookupTable()),
     56       window_visible_(false),
     57       weak_ptr_factory_(this) {
     58 }
     59 
     60 InputMethodEngineIBus::~InputMethodEngineIBus() {
     61   input_method::InputMethodManager::Get()->RemoveInputMethodExtension(ibus_id_);
     62 
     63   // Do not unset engine before removing input method extension, above function
     64   // may call reset function of engine object.
     65   // TODO(nona): Call Reset manually here and remove relevant code from
     66   //             InputMethodManager once ibus-daemon is gone. (crbug.com/158273)
     67   if (!object_path_.value().empty()) {
     68     GetCurrentService()->UnsetEngine(this);
     69     DBusThreadManager::Get()->RemoveIBusEngineService(object_path_);
     70   }
     71 }
     72 
     73 void InputMethodEngineIBus::Initialize(
     74     InputMethodEngine::Observer* observer,
     75     const char* engine_name,
     76     const char* extension_id,
     77     const char* engine_id,
     78     const char* description,
     79     const std::vector<std::string>& languages,
     80     const std::vector<std::string>& layouts,
     81     const GURL& options_page,
     82     std::string* error) {
     83   DCHECK(observer) << "Observer must not be null.";
     84 
     85   observer_ = observer;
     86   engine_id_ = engine_id;
     87 
     88   input_method::InputMethodManager* manager =
     89       input_method::InputMethodManager::Get();
     90   ComponentExtensionIMEManager* comp_ext_ime_manager
     91       = manager->GetComponentExtensionIMEManager();
     92 
     93   if (comp_ext_ime_manager->IsInitialized() &&
     94       comp_ext_ime_manager->IsWhitelistedExtension(extension_id)) {
     95     ibus_id_ = comp_ext_ime_manager->GetId(extension_id, engine_id);
     96   } else {
     97     ibus_id_ = extension_ime_util::GetInputMethodID(extension_id, engine_id);
     98   }
     99 
    100   component_.reset(new IBusComponent());
    101   component_->set_name(std::string(kEngineBusPrefix) + std::string(engine_id));
    102   component_->set_description(description);
    103   component_->set_author(engine_name);
    104 
    105   // TODO(nona): Remove IBusComponent once ibus is gone.
    106   chromeos::IBusComponent::EngineDescription engine_desc;
    107   engine_desc.engine_id = ibus_id_;
    108   engine_desc.display_name = description;
    109   engine_desc.description = description;
    110   engine_desc.language_code = (languages.empty()) ? "" : languages[0];
    111   engine_desc.author = ibus_id_;
    112 
    113   component_->mutable_engine_description()->push_back(engine_desc);
    114   manager->AddInputMethodExtension(ibus_id_, engine_name, layouts, languages,
    115                                    options_page, this);
    116   // If connection is avaiable, register component. If there are no connection
    117   // to ibus-daemon, OnConnected callback will register component instead.
    118   if (IsConnected())
    119     RegisterComponent();
    120 }
    121 
    122 void InputMethodEngineIBus::StartIme() {
    123   input_method::InputMethodManager* manager =
    124       input_method::InputMethodManager::Get();
    125   if (manager && ibus_id_ == manager->GetCurrentInputMethod().id())
    126     Enable();
    127 }
    128 
    129 bool InputMethodEngineIBus::SetComposition(
    130     int context_id,
    131     const char* text,
    132     int selection_start,
    133     int selection_end,
    134     int cursor,
    135     const std::vector<SegmentInfo>& segments,
    136     std::string* error) {
    137   if (!active_) {
    138     *error = kErrorNotActive;
    139     return false;
    140   }
    141   if (context_id != context_id_ || context_id_ == -1) {
    142     *error = kErrorWrongContext;
    143     return false;
    144   }
    145 
    146   preedit_cursor_ = cursor;
    147   preedit_text_.reset(new IBusText());
    148   preedit_text_->set_text(text);
    149 
    150   preedit_text_->mutable_selection_attributes()->clear();
    151   IBusText::SelectionAttribute selection;
    152   selection.start_index = selection_start;
    153   selection.end_index = selection_end;
    154   preedit_text_->mutable_selection_attributes()->push_back(selection);
    155 
    156   // TODO: Add support for displaying selected text in the composition string.
    157   for (std::vector<SegmentInfo>::const_iterator segment = segments.begin();
    158        segment != segments.end(); ++segment) {
    159     IBusText::UnderlineAttribute underline;
    160 
    161     switch (segment->style) {
    162       case SEGMENT_STYLE_UNDERLINE:
    163         underline.type = IBusText::IBUS_TEXT_UNDERLINE_SINGLE;
    164         break;
    165       case SEGMENT_STYLE_DOUBLE_UNDERLINE:
    166         underline.type = IBusText::IBUS_TEXT_UNDERLINE_DOUBLE;
    167         break;
    168       default:
    169         continue;
    170     }
    171 
    172     underline.start_index = segment->start;
    173     underline.end_index = segment->end;
    174     preedit_text_->mutable_underline_attributes()->push_back(underline);
    175   }
    176 
    177   // TODO(nona): Makes focus out mode configuable, if necessary.
    178   GetCurrentService()->UpdatePreedit(
    179       *preedit_text_.get(),
    180       preedit_cursor_,
    181       true,
    182       chromeos::IBusEngineService::IBUS_ENGINE_PREEEDIT_FOCUS_OUT_MODE_COMMIT);
    183   return true;
    184 }
    185 
    186 bool InputMethodEngineIBus::ClearComposition(int context_id,
    187                                              std::string* error)  {
    188   if (!active_) {
    189     *error = kErrorNotActive;
    190     return false;
    191   }
    192   if (context_id != context_id_ || context_id_ == -1) {
    193     *error = kErrorWrongContext;
    194     return false;
    195   }
    196 
    197   preedit_cursor_ = 0;
    198   preedit_text_.reset(new IBusText());
    199   GetCurrentService()->UpdatePreedit(
    200       *preedit_text_.get(),
    201       0,
    202       false,
    203       chromeos::IBusEngineService::IBUS_ENGINE_PREEEDIT_FOCUS_OUT_MODE_COMMIT);
    204   return true;
    205 }
    206 
    207 bool InputMethodEngineIBus::CommitText(int context_id, const char* text,
    208                                        std::string* error) {
    209   if (!active_) {
    210     // TODO: Commit the text anyways.
    211     *error = kErrorNotActive;
    212     return false;
    213   }
    214   if (context_id != context_id_ || context_id_ == -1) {
    215     *error = kErrorWrongContext;
    216     return false;
    217   }
    218 
    219   GetCurrentService()->CommitText(text);
    220   return true;
    221 }
    222 
    223 bool InputMethodEngineIBus::SetCandidateWindowVisible(bool visible,
    224                                                       std::string* error) {
    225   if (!active_) {
    226     *error = kErrorNotActive;
    227     return false;
    228   }
    229 
    230   window_visible_ = visible;
    231   GetCurrentService()->UpdateLookupTable(*table_.get(), window_visible_);
    232   return true;
    233 }
    234 
    235 void InputMethodEngineIBus::SetCandidateWindowCursorVisible(bool visible) {
    236   table_->set_is_cursor_visible(visible);
    237   // IBus shows candidates on a page where the cursor is placed, so we need to
    238   // set the cursor position appropriately so IBus shows the right page.
    239   // In the case that the cursor is not visible, we always show the first page.
    240   // This trick works because only extension IMEs use this method and extension
    241   // IMEs do not depend on the pagination feature of IBus.
    242   if (!visible)
    243     table_->set_cursor_position(0);
    244   if (active_)
    245     GetCurrentService()->UpdateLookupTable(*table_.get(), window_visible_);
    246 }
    247 
    248 void InputMethodEngineIBus::SetCandidateWindowVertical(bool vertical) {
    249   table_->set_orientation(vertical ? IBusLookupTable::VERTICAL :
    250                           IBusLookupTable::HORIZONTAL);
    251   if (active_)
    252     GetCurrentService()->UpdateLookupTable(*table_.get(), window_visible_);
    253 }
    254 
    255 void InputMethodEngineIBus::SetCandidateWindowPageSize(int size) {
    256   table_->set_page_size(size);
    257   if (active_)
    258     GetCurrentService()->UpdateLookupTable(*table_.get(), window_visible_);
    259 }
    260 
    261 void InputMethodEngineIBus::SetCandidateWindowAuxText(const char* text) {
    262   aux_text_->set_text(text);
    263   if (active_) {
    264     // Should not show auxiliary text if the whole window visibility is false.
    265     GetCurrentService()->UpdateAuxiliaryText(
    266         *aux_text_.get(),
    267         window_visible_ && aux_text_visible_);
    268   }
    269 }
    270 
    271 void InputMethodEngineIBus::SetCandidateWindowAuxTextVisible(bool visible) {
    272   aux_text_visible_ = visible;
    273   if (active_) {
    274     // Should not show auxiliary text if the whole window visibility is false.
    275     GetCurrentService()->UpdateAuxiliaryText(
    276         *aux_text_.get(),
    277         window_visible_ && aux_text_visible_);
    278   }
    279 }
    280 
    281 void InputMethodEngineIBus::SetCandidateWindowPosition(
    282     CandidateWindowPosition position) {
    283   table_->set_show_window_at_composition(position == WINDOW_POS_COMPOSITTION);
    284   if (active_)
    285     GetCurrentService()->UpdateLookupTable(*table_.get(), window_visible_);
    286 }
    287 
    288 bool InputMethodEngineIBus::SetCandidates(
    289     int context_id,
    290     const std::vector<Candidate>& candidates,
    291     std::string* error) {
    292   if (!active_) {
    293     *error = kErrorNotActive;
    294     return false;
    295   }
    296   if (context_id != context_id_ || context_id_ == -1) {
    297     *error = kErrorWrongContext;
    298     return false;
    299   }
    300 
    301   // TODO: Nested candidates
    302   candidate_ids_.clear();
    303   candidate_indexes_.clear();
    304   table_->mutable_candidates()->clear();
    305   for (std::vector<Candidate>::const_iterator ix = candidates.begin();
    306        ix != candidates.end(); ++ix) {
    307     IBusLookupTable::Entry entry;
    308     entry.value = ix->value;
    309     entry.label = ix->label;
    310     entry.annotation = ix->annotation;
    311     entry.description_title = ix->usage.title;
    312     entry.description_body = ix->usage.body;
    313 
    314     // Store a mapping from the user defined ID to the candidate index.
    315     candidate_indexes_[ix->id] = candidate_ids_.size();
    316     candidate_ids_.push_back(ix->id);
    317 
    318     table_->mutable_candidates()->push_back(entry);
    319   }
    320   GetCurrentService()->UpdateLookupTable(*table_.get(), window_visible_);
    321   return true;
    322 }
    323 
    324 bool InputMethodEngineIBus::SetCursorPosition(int context_id, int candidate_id,
    325                                               std::string* error) {
    326   if (!active_) {
    327     *error = kErrorNotActive;
    328     return false;
    329   }
    330   if (context_id != context_id_ || context_id_ == -1) {
    331     *error = kErrorWrongContext;
    332     return false;
    333   }
    334 
    335   std::map<int, int>::const_iterator position =
    336       candidate_indexes_.find(candidate_id);
    337   if (position == candidate_indexes_.end()) {
    338     *error = kCandidateNotFound;
    339     return false;
    340   }
    341 
    342   table_->set_cursor_position(position->second);
    343   GetCurrentService()->UpdateLookupTable(*table_.get(), window_visible_);
    344   return true;
    345 }
    346 
    347 bool InputMethodEngineIBus::SetMenuItems(const std::vector<MenuItem>& items) {
    348   if (!active_)
    349     return false;
    350 
    351   IBusPropertyList properties;
    352   for (std::vector<MenuItem>::const_iterator item = items.begin();
    353        item != items.end(); ++item) {
    354     IBusProperty* property = new IBusProperty();
    355     if (!MenuItemToProperty(*item, property)) {
    356       delete property;
    357       DVLOG(1) << "Bad menu item";
    358       return false;
    359     }
    360     properties.push_back(property);
    361   }
    362   GetCurrentService()->RegisterProperties(properties);
    363   return true;
    364 }
    365 
    366 bool InputMethodEngineIBus::UpdateMenuItems(
    367     const std::vector<MenuItem>& items) {
    368   if (!active_)
    369     return false;
    370 
    371   IBusPropertyList properties;
    372   for (std::vector<MenuItem>::const_iterator item = items.begin();
    373        item != items.end(); ++item) {
    374     IBusProperty* property = new IBusProperty();
    375     if (!MenuItemToProperty(*item, property)) {
    376       delete property;
    377       DVLOG(1) << "Bad menu item";
    378       return false;
    379     }
    380     properties.push_back(property);
    381   }
    382   GetCurrentService()->RegisterProperties(properties);
    383   return true;
    384 }
    385 
    386 bool InputMethodEngineIBus::IsActive() const {
    387   return active_;
    388 }
    389 
    390 void InputMethodEngineIBus::KeyEventDone(input_method::KeyEventHandle* key_data,
    391                                          bool handled) {
    392   KeyEventDoneCallback* callback =
    393       reinterpret_cast<KeyEventDoneCallback*>(key_data);
    394   callback->Run(handled);
    395   delete callback;
    396 }
    397 
    398 bool InputMethodEngineIBus::DeleteSurroundingText(int context_id,
    399                                                   int offset,
    400                                                   size_t number_of_chars,
    401                                                   std::string* error) {
    402   if (!active_) {
    403     *error = kErrorNotActive;
    404     return false;
    405   }
    406   if (context_id != context_id_ || context_id_ == -1) {
    407     *error = kErrorWrongContext;
    408     return false;
    409   }
    410 
    411   if (offset < 0 && static_cast<size_t>(-1 * offset) != size_t(number_of_chars))
    412     return false;  // Currently we can only support preceding text.
    413 
    414   // TODO(nona): Return false if there is ongoing composition.
    415   GetCurrentService()->DeleteSurroundingText(offset, number_of_chars);
    416   return true;
    417 }
    418 
    419 void InputMethodEngineIBus::FocusIn() {
    420   focused_ = true;
    421   if (!active_)
    422     return;
    423   context_id_ = next_context_id_;
    424   ++next_context_id_;
    425 
    426   InputContext context;
    427   context.id = context_id_;
    428   // TODO: Other types
    429   context.type = "text";
    430 
    431   observer_->OnFocus(context);
    432 }
    433 
    434 void InputMethodEngineIBus::FocusOut() {
    435   focused_ = false;
    436   if (!active_)
    437     return;
    438   int context_id = context_id_;
    439   context_id_ = -1;
    440   observer_->OnBlur(context_id);
    441 }
    442 
    443 void InputMethodEngineIBus::Enable() {
    444   active_ = true;
    445   observer_->OnActivate(engine_id_);
    446   FocusIn();
    447 
    448   // Calls RequireSurroundingText once here to notify ibus-daemon to send
    449   // surrounding text to this engine.
    450   GetCurrentService()->RequireSurroundingText();
    451 }
    452 
    453 void InputMethodEngineIBus::Disable() {
    454   active_ = false;
    455   observer_->OnDeactivated(engine_id_);
    456 }
    457 
    458 void InputMethodEngineIBus::PropertyActivate(
    459     const std::string& property_name,
    460     ibus::IBusPropertyState property_state) {
    461   observer_->OnMenuItemActivated(engine_id_, property_name);
    462 }
    463 
    464 void InputMethodEngineIBus::PropertyShow(
    465     const std::string& property_name) {
    466 }
    467 
    468 void InputMethodEngineIBus::PropertyHide(
    469     const std::string& property_name) {
    470 }
    471 
    472 void InputMethodEngineIBus::SetCapability(
    473     IBusCapability capability) {
    474 }
    475 
    476 void InputMethodEngineIBus::Reset() {
    477   observer_->OnReset(engine_id_);
    478 }
    479 
    480 void InputMethodEngineIBus::ProcessKeyEvent(
    481     uint32 keysym,
    482     uint32 keycode,
    483     uint32 state,
    484     const KeyEventDoneCallback& callback) {
    485 
    486   KeyEventDoneCallback *handler = new KeyEventDoneCallback();
    487   *handler = callback;
    488 
    489   KeyboardEvent event;
    490   event.type = !(state & kIBusKeyReleaseMask) ? "keydown" : "keyup";
    491   event.key = input_method::GetIBusKey(keysym);
    492   event.code = input_method::GetIBusKeyCode(keycode);
    493   event.alt_key = state & kIBusAltKeyMask;
    494   event.ctrl_key = state & kIBusCtrlKeyMask;
    495   event.shift_key = state & kIBusShiftKeyMask;
    496   event.caps_lock = state & kIBusCapsLockMask;
    497   observer_->OnKeyEvent(
    498       engine_id_,
    499       event,
    500       reinterpret_cast<input_method::KeyEventHandle*>(handler));
    501 }
    502 
    503 void InputMethodEngineIBus::CandidateClicked(uint32 index,
    504                                              ibus::IBusMouseButton button,
    505                                              uint32 state) {
    506   if (index > candidate_ids_.size()) {
    507     return;
    508   }
    509 
    510   MouseButtonEvent pressed_button;
    511   switch (button) {
    512     case ibus::IBUS_MOUSE_BUTTON_LEFT:
    513       pressed_button = MOUSE_BUTTON_LEFT;
    514       break;
    515     case ibus::IBUS_MOUSE_BUTTON_MIDDLE:
    516       pressed_button = MOUSE_BUTTON_MIDDLE;
    517       break;
    518     case ibus::IBUS_MOUSE_BUTTON_RIGHT:
    519       pressed_button = MOUSE_BUTTON_RIGHT;
    520       break;
    521     default:
    522       DVLOG(1) << "Unknown button: " << button;
    523       pressed_button = MOUSE_BUTTON_LEFT;
    524       break;
    525   }
    526 
    527   observer_->OnCandidateClicked(
    528       engine_id_, candidate_ids_.at(index), pressed_button);
    529 }
    530 
    531 void InputMethodEngineIBus::SetSurroundingText(const std::string& text,
    532                                                uint32 cursor_pos,
    533                                                uint32 anchor_pos) {
    534   observer_->OnSurroundingTextChanged(engine_id_,
    535                                       text,
    536                                       static_cast<int>(cursor_pos),
    537                                       static_cast<int>(anchor_pos));
    538 }
    539 
    540 IBusEngineService* InputMethodEngineIBus::GetCurrentService() {
    541   return DBusThreadManager::Get()->GetIBusEngineService(object_path_);
    542 }
    543 
    544 bool InputMethodEngineIBus::MenuItemToProperty(
    545     const MenuItem& item,
    546     IBusProperty* property) {
    547   property->set_key(item.id);
    548 
    549   if (item.modified & MENU_ITEM_MODIFIED_LABEL) {
    550     property->set_label(item.label);
    551   }
    552   if (item.modified & MENU_ITEM_MODIFIED_VISIBLE) {
    553     property->set_visible(item.visible);
    554   }
    555   if (item.modified & MENU_ITEM_MODIFIED_CHECKED) {
    556     property->set_checked(item.checked);
    557   }
    558   if (item.modified & MENU_ITEM_MODIFIED_ENABLED) {
    559     // TODO(nona): implement sensitive entry(crbug.com/140192).
    560   }
    561   if (item.modified & MENU_ITEM_MODIFIED_STYLE) {
    562     IBusProperty::IBusPropertyType type =
    563         IBusProperty::IBUS_PROPERTY_TYPE_NORMAL;
    564     if (!item.children.empty()) {
    565       type = IBusProperty::IBUS_PROPERTY_TYPE_MENU;
    566     } else {
    567       switch (item.style) {
    568         case MENU_ITEM_STYLE_NONE:
    569           type = IBusProperty::IBUS_PROPERTY_TYPE_NORMAL;
    570           break;
    571         case MENU_ITEM_STYLE_CHECK:
    572           type = IBusProperty::IBUS_PROPERTY_TYPE_TOGGLE;
    573           break;
    574         case MENU_ITEM_STYLE_RADIO:
    575           type = IBusProperty::IBUS_PROPERTY_TYPE_RADIO;
    576           break;
    577         case MENU_ITEM_STYLE_SEPARATOR:
    578           type = IBusProperty::IBUS_PROPERTY_TYPE_SEPARATOR;
    579           break;
    580       }
    581     }
    582     property->set_type(type);
    583   }
    584 
    585   for (std::vector<MenuItem>::const_iterator child = item.children.begin();
    586        child != item.children.end(); ++child) {
    587     IBusProperty* new_property = new IBusProperty();
    588     if (!MenuItemToProperty(*child, new_property)) {
    589       delete new_property;
    590       DVLOG(1) << "Bad menu item child";
    591       return false;
    592     }
    593     property->mutable_sub_properties()->push_back(new_property);
    594   }
    595 
    596   return true;
    597 }
    598 
    599 void InputMethodEngineIBus::OnConnected() {
    600   RegisterComponent();
    601 }
    602 
    603 void InputMethodEngineIBus::OnDisconnected() {
    604 }
    605 
    606 bool InputMethodEngineIBus::IsConnected() {
    607   return DBusThreadManager::Get()->GetIBusClient() != NULL;
    608 }
    609 
    610 void InputMethodEngineIBus::RegisterComponent() {
    611   chromeos::IBusClient* client =
    612       chromeos::DBusThreadManager::Get()->GetIBusClient();
    613   client->RegisterComponent(
    614       *component_.get(),
    615       base::Bind(&InputMethodEngineIBus::OnComponentRegistered,
    616                  weak_ptr_factory_.GetWeakPtr()),
    617       base::Bind(&InputMethodEngineIBus::OnComponentRegistrationFailed,
    618                  weak_ptr_factory_.GetWeakPtr()));
    619 }
    620 
    621 void InputMethodEngineIBus::OnComponentRegistered() {
    622   DBusThreadManager::Get()->GetIBusEngineFactoryService()->
    623       SetCreateEngineHandler(ibus_id_,
    624                              base::Bind(
    625                                  &InputMethodEngineIBus::CreateEngineHandler,
    626                                  weak_ptr_factory_.GetWeakPtr()));
    627 }
    628 
    629 void InputMethodEngineIBus::OnComponentRegistrationFailed() {
    630   DVLOG(1) << "Failed to register input method components.";
    631   // TODO(nona): Implement error handling.
    632 }
    633 
    634 void InputMethodEngineIBus::CreateEngineHandler(
    635     const IBusEngineFactoryService::CreateEngineResponseSender& sender) {
    636   GetCurrentService()->UnsetEngine(this);
    637   DBusThreadManager::Get()->RemoveIBusEngineService(object_path_);
    638 
    639   object_path_ = DBusThreadManager::Get()->GetIBusEngineFactoryService()->
    640       GenerateUniqueObjectPath();
    641 
    642   GetCurrentService()->SetEngine(this);
    643   sender.Run(object_path_);
    644 }
    645 
    646 }  // namespace chromeos
    647