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_manager_impl.h"
      6 
      7 #include <algorithm>  // std::find
      8 
      9 #include "base/basictypes.h"
     10 #include "base/bind.h"
     11 #include "base/location.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/prefs/pref_service.h"
     14 #include "base/strings/string_util.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "chrome/browser/browser_process.h"
     17 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
     18 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
     19 #include "chrome/browser/chromeos/input_method/input_method_engine_ibus.h"
     20 #include "chrome/browser/chromeos/language_preferences.h"
     21 #include "chromeos/dbus/dbus_thread_manager.h"
     22 #include "chromeos/dbus/ibus/ibus_client.h"
     23 #include "chromeos/dbus/ibus/ibus_input_context_client.h"
     24 #include "chromeos/ime/component_extension_ime_manager.h"
     25 #include "chromeos/ime/extension_ime_util.h"
     26 #include "chromeos/ime/input_method_delegate.h"
     27 #include "chromeos/ime/xkeyboard.h"
     28 #include "third_party/icu/source/common/unicode/uloc.h"
     29 #include "ui/base/accelerators/accelerator.h"
     30 
     31 namespace chromeos {
     32 namespace input_method {
     33 
     34 namespace {
     35 
     36 const char nacl_mozc_us_id[] =
     37     "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us";
     38 const char nacl_mozc_jp_id[] =
     39     "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp";
     40 
     41 bool Contains(const std::vector<std::string>& container,
     42               const std::string& value) {
     43   return std::find(container.begin(), container.end(), value) !=
     44       container.end();
     45 }
     46 
     47 const struct MigrationInputMethodList {
     48   const char* old_input_method;
     49   const char* new_input_method;
     50 } kMigrationInputMethodList[] = {
     51   { "mozc", "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us" },
     52   { "mozc-jp", "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_jp" },
     53   { "mozc-dv", "_comp_ime_fpfbhcjppmaeaijcidgiibchfbnhbeljnacl_mozc_us" },
     54   { "pinyin", "_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabzh-t-i0-pinyin" },
     55   { "pinyin-dv", "_comp_ime_nmblnjkfdkabgdofidlkienfnnbjhnabzh-t-i0-pinyin" },
     56   { "mozc-chewing",
     57     "_comp_ime_ekbifjdfhkmdeeajnolmgdlmkllopefizh-hant-t-i0-und "},
     58   { "m17n:zh:cangjie",
     59     "_comp_ime_gjhclobljhjhgoebiipblnmdodbmpdgdzh-hant-t-i0-cangjie-1987" },
     60   { "_comp_ime_jcffnbbngddhenhcnebafkbdomehdhpdzh-t-i0-wubi-1986",
     61     "_comp_ime_gjhclobljhjhgoebiipblnmdodbmpdgdzh-t-i0-wubi-1986" },
     62   // TODO(nona): Remove following migration map in M31.
     63   { "m17n:ta:itrans",
     64     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_ta_itrans" },
     65   { "m17n:ta:tamil99",
     66     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_ta_tamil99" },
     67   { "m17n:ta:typewriter",
     68     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_ta_typewriter" },
     69   { "m17n:ta:inscript",
     70     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_ta_phone" },
     71   { "m17n:ta:phonetic",
     72     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_ta_inscript" },
     73   { "m17n:th:pattachote",
     74     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_th_pattajoti" },
     75   { "m17n:th:tis820", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_th_tis" },
     76   { "m17n:th:kesmanee",
     77     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_th" },
     78   { "m17n:vi:tcvn", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_tcvn" },
     79   { "m17n:vi:viqr", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_viqr" },
     80   { "m17n:vi:telex",
     81     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_telex" },
     82   { "m17n:vi:vni",
     83     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_vi_vni" },
     84   { "m17n:am:sera",
     85     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_ethi" },
     86   { "m17n:bn:itrans",
     87     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_bn_phone" },
     88   { "m17n:gu:itrans",
     89     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_gu_phone" },
     90   { "m17n:hi:itrans",
     91     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_deva_phone" },
     92   { "m17n:kn:itrans",
     93     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_kn_phone" },
     94   { "m17n:ml:itrans",
     95     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_ml_phone" },
     96   { "m17n:mr:itrans",
     97     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_deva_phone" },
     98   { "m17n:te:itrans",
     99     "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_te_phone" },
    100   { "m17n:fa:isiri", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_fa" },
    101   { "m17n:ar:kbd", "_comp_ime_jhffeifommiaekmbkkjlpmilogcfdohpvkd_ar" },
    102   // TODO(nona): Remove following migration map in M32
    103   { "m17n:zh:quick",
    104     "_comp_ime_ekbifjdfhkmdeeajnolmgdlmkllopefizh-hant-t-i0-und" },
    105 };
    106 
    107 const struct MigrationHangulKeyboardToInputMethodID {
    108   const char* keyboard_id;
    109   const char* ime_id;
    110 } kMigrationHangulKeyboardToInputMethodID[] = {
    111   { "2", "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_2set" },
    112   { "3f", "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setfinal" },
    113   { "39", "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3set390" },
    114   { "3s", "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_3setnoshift" },
    115   { "ro", "_comp_ime_bdgdidmhaijohebebipajioienkglgfohangul_romaja" },
    116 };
    117 
    118 }  // namespace
    119 
    120 bool InputMethodManagerImpl::IsFullLatinKeyboard(
    121     const std::string& layout) const {
    122   const std::string& lang = util_.GetLanguageCodeFromInputMethodId(layout);
    123   return full_latin_keyboard_checker.IsFullLatinKeyboard(layout, lang);
    124 }
    125 
    126 InputMethodManagerImpl::InputMethodManagerImpl(
    127     scoped_ptr<InputMethodDelegate> delegate)
    128     : delegate_(delegate.Pass()),
    129       state_(STATE_LOGIN_SCREEN),
    130       util_(delegate_.get(), GetSupportedInputMethods()),
    131       component_extension_ime_manager_(new ComponentExtensionIMEManager()),
    132       weak_ptr_factory_(this) {
    133   IBusDaemonController::GetInstance()->AddObserver(this);
    134 }
    135 
    136 InputMethodManagerImpl::~InputMethodManagerImpl() {
    137   if (ibus_controller_.get())
    138     ibus_controller_->RemoveObserver(this);
    139   IBusDaemonController::GetInstance()->RemoveObserver(this);
    140   if (candidate_window_controller_.get()) {
    141     candidate_window_controller_->RemoveObserver(this);
    142     candidate_window_controller_->Shutdown();
    143   }
    144 }
    145 
    146 void InputMethodManagerImpl::AddObserver(
    147     InputMethodManager::Observer* observer) {
    148   observers_.AddObserver(observer);
    149 }
    150 
    151 void InputMethodManagerImpl::AddCandidateWindowObserver(
    152     InputMethodManager::CandidateWindowObserver* observer) {
    153   candidate_window_observers_.AddObserver(observer);
    154 }
    155 
    156 void InputMethodManagerImpl::RemoveObserver(
    157     InputMethodManager::Observer* observer) {
    158   observers_.RemoveObserver(observer);
    159 }
    160 
    161 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
    162     InputMethodManager::CandidateWindowObserver* observer) {
    163   candidate_window_observers_.RemoveObserver(observer);
    164 }
    165 
    166 void InputMethodManagerImpl::SetState(State new_state) {
    167   const State old_state = state_;
    168   state_ = new_state;
    169   switch (state_) {
    170     case STATE_LOGIN_SCREEN:
    171       break;
    172     case STATE_BROWSER_SCREEN:
    173       if (old_state == STATE_LOCK_SCREEN)
    174         OnScreenUnlocked();
    175       break;
    176     case STATE_LOCK_SCREEN:
    177       OnScreenLocked();
    178       break;
    179     case STATE_TERMINATING: {
    180       if (candidate_window_controller_.get()) {
    181         candidate_window_controller_->Shutdown();
    182         candidate_window_controller_.reset();
    183       }
    184       break;
    185     }
    186   }
    187 }
    188 
    189 scoped_ptr<InputMethodDescriptors>
    190 InputMethodManagerImpl::GetSupportedInputMethods() const {
    191   return whitelist_.GetSupportedInputMethods();
    192 }
    193 
    194 scoped_ptr<InputMethodDescriptors>
    195 InputMethodManagerImpl::GetActiveInputMethods() const {
    196   scoped_ptr<InputMethodDescriptors> result(new InputMethodDescriptors);
    197   // Build the active input method descriptors from the active input
    198   // methods cache |active_input_method_ids_|.
    199   for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
    200     const std::string& input_method_id = active_input_method_ids_[i];
    201     const InputMethodDescriptor* descriptor =
    202         util_.GetInputMethodDescriptorFromId(input_method_id);
    203     if (descriptor) {
    204       result->push_back(*descriptor);
    205     } else {
    206       std::map<std::string, InputMethodDescriptor>::const_iterator ix =
    207           extra_input_methods_.find(input_method_id);
    208       if (ix != extra_input_methods_.end())
    209         result->push_back(ix->second);
    210       else
    211         DVLOG(1) << "Descriptor is not found for: " << input_method_id;
    212     }
    213   }
    214   if (result->empty()) {
    215     // Initially |active_input_method_ids_| is empty. browser_tests might take
    216     // this path.
    217     result->push_back(
    218         InputMethodUtil::GetFallbackInputMethodDescriptor());
    219   }
    220   return result.Pass();
    221 }
    222 
    223 const std::vector<std::string>&
    224 InputMethodManagerImpl::GetActiveInputMethodIds() const {
    225   return active_input_method_ids_;
    226 }
    227 
    228 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
    229   return active_input_method_ids_.size();
    230 }
    231 
    232 void InputMethodManagerImpl::EnableLayouts(const std::string& language_code,
    233                                            const std::string& initial_layout) {
    234   if (state_ == STATE_TERMINATING)
    235     return;
    236 
    237   std::vector<std::string> candidates;
    238   // Add input methods associated with the language.
    239   util_.GetInputMethodIdsFromLanguageCode(language_code,
    240                                           kKeyboardLayoutsOnly,
    241                                           &candidates);
    242   // Add the hardware keyboard as well. We should always add this so users
    243   // can use the hardware keyboard on the login screen and the screen locker.
    244   candidates.push_back(util_.GetHardwareInputMethodId());
    245 
    246   std::vector<std::string> layouts;
    247   // First, add the initial input method ID, if it's requested, to
    248   // layouts, so it appears first on the list of active input
    249   // methods at the input language status menu.
    250   if (util_.IsValidInputMethodId(initial_layout) &&
    251       InputMethodUtil::IsKeyboardLayout(initial_layout)) {
    252     layouts.push_back(initial_layout);
    253   } else if (!initial_layout.empty()) {
    254     DVLOG(1) << "EnableLayouts: ignoring non-keyboard or invalid ID: "
    255              << initial_layout;
    256   }
    257 
    258   // Add candidates to layouts, while skipping duplicates.
    259   for (size_t i = 0; i < candidates.size(); ++i) {
    260     const std::string& candidate = candidates[i];
    261     // Not efficient, but should be fine, as the two vectors are very
    262     // short (2-5 items).
    263     if (!Contains(layouts, candidate))
    264       layouts.push_back(candidate);
    265   }
    266 
    267   active_input_method_ids_.swap(layouts);
    268   ChangeInputMethod(initial_layout);  // you can pass empty |initial_layout|.
    269 }
    270 
    271 // Adds new input method to given list.
    272 bool InputMethodManagerImpl::EnableInputMethodImpl(
    273     const std::string& input_method_id,
    274     std::vector<std::string>& new_active_input_method_ids) const {
    275   if (!util_.IsValidInputMethodId(input_method_id)) {
    276     DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id;
    277     return false;
    278   }
    279 
    280   if (!Contains(new_active_input_method_ids, input_method_id))
    281     new_active_input_method_ids.push_back(input_method_id);
    282 
    283   return true;
    284 }
    285 
    286 // Starts or stops the system input method framework as needed.
    287 void InputMethodManagerImpl::ReconfigureIMFramework() {
    288   if (component_extension_ime_manager_->IsInitialized())
    289     LoadNecessaryComponentExtensions();
    290 
    291   if (ContainsOnlyKeyboardLayout(active_input_method_ids_)) {
    292     // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
    293     // at crbug.com/27051.
    294     // TODO(yusukes): We can safely call Stop(); here once crbug.com/26443
    295     // is implemented.
    296   } else {
    297     MaybeInitializeCandidateWindowController();
    298     IBusDaemonController::GetInstance()->Start();
    299   }
    300 }
    301 
    302 bool InputMethodManagerImpl::EnableInputMethod(
    303     const std::string& input_method_id) {
    304   if (!EnableInputMethodImpl(input_method_id, active_input_method_ids_))
    305     return false;
    306 
    307   ReconfigureIMFramework();
    308   return true;
    309 }
    310 
    311 bool InputMethodManagerImpl::EnableInputMethods(
    312     const std::vector<std::string>& new_active_input_method_ids) {
    313   if (state_ == STATE_TERMINATING)
    314     return false;
    315 
    316   // Filter unknown or obsolete IDs.
    317   std::vector<std::string> new_active_input_method_ids_filtered;
    318 
    319   for (size_t i = 0; i < new_active_input_method_ids.size(); ++i)
    320     EnableInputMethodImpl(new_active_input_method_ids[i],
    321                           new_active_input_method_ids_filtered);
    322 
    323   if (new_active_input_method_ids_filtered.empty()) {
    324     DVLOG(1) << "EnableInputMethods: No valid input method ID";
    325     return false;
    326   }
    327 
    328   // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
    329   // keep relative order of the extension input method IDs.
    330   for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
    331     const std::string& input_method_id = active_input_method_ids_[i];
    332     if (extension_ime_util::IsExtensionIME(input_method_id))
    333       new_active_input_method_ids_filtered.push_back(input_method_id);
    334   }
    335   active_input_method_ids_.swap(new_active_input_method_ids_filtered);
    336 
    337   ReconfigureIMFramework();
    338 
    339   // If |current_input_method| is no longer in |active_input_method_ids_|,
    340   // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
    341   ChangeInputMethod(current_input_method_.id());
    342   return true;
    343 }
    344 
    345 bool InputMethodManagerImpl::MigrateOldInputMethods(
    346     std::vector<std::string>* input_method_ids) {
    347   bool rewritten = false;
    348   for (size_t i = 0; i < input_method_ids->size(); ++i) {
    349     for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kMigrationInputMethodList); ++j) {
    350       if (input_method_ids->at(i) ==
    351           kMigrationInputMethodList[j].old_input_method) {
    352         input_method_ids->at(i).assign(
    353             kMigrationInputMethodList[j].new_input_method);
    354         rewritten = true;
    355       }
    356     }
    357   }
    358   std::vector<std::string>::iterator it =
    359       std::unique(input_method_ids->begin(), input_method_ids->end());
    360   input_method_ids->resize(std::distance(input_method_ids->begin(), it));
    361   return rewritten;
    362 }
    363 
    364 bool InputMethodManagerImpl::MigrateKoreanKeyboard(
    365     const std::string& keyboard_id,
    366     std::vector<std::string>* input_method_ids) {
    367   std::vector<std::string>::iterator it =
    368       std::find(active_input_method_ids_.begin(),
    369                 active_input_method_ids_.end(),
    370                 "mozc-hangul");
    371   if (it == active_input_method_ids_.end())
    372     return false;
    373 
    374   for (size_t i = 0;
    375        i < ARRAYSIZE_UNSAFE(kMigrationHangulKeyboardToInputMethodID); ++i) {
    376     if (kMigrationHangulKeyboardToInputMethodID[i].keyboard_id == keyboard_id) {
    377       *it = kMigrationHangulKeyboardToInputMethodID[i].ime_id;
    378       input_method_ids->assign(active_input_method_ids_.begin(),
    379                                active_input_method_ids_.end());
    380       return true;
    381     }
    382   }
    383   return false;
    384 }
    385 
    386 bool InputMethodManagerImpl::SetInputMethodConfig(
    387     const std::string& section,
    388     const std::string& config_name,
    389     const InputMethodConfigValue& value) {
    390   DCHECK(section != language_prefs::kGeneralSectionName ||
    391          config_name != language_prefs::kPreloadEnginesConfigName);
    392 
    393   if (state_ == STATE_TERMINATING)
    394     return false;
    395 
    396   return ibus_controller_->SetInputMethodConfig(section, config_name, value);
    397 }
    398 
    399 void InputMethodManagerImpl::ChangeInputMethod(
    400     const std::string& input_method_id) {
    401   ChangeInputMethodInternal(input_method_id, false);
    402 }
    403 
    404 bool InputMethodManagerImpl::ChangeInputMethodInternal(
    405     const std::string& input_method_id,
    406     bool show_message) {
    407   if (state_ == STATE_TERMINATING)
    408     return false;
    409 
    410   std::string input_method_id_to_switch = input_method_id;
    411 
    412   // Sanity check.
    413   if (!InputMethodIsActivated(input_method_id)) {
    414     scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
    415     DCHECK(!input_methods->empty());
    416     input_method_id_to_switch = input_methods->at(0).id();
    417     if (!input_method_id.empty()) {
    418       DVLOG(1) << "Can't change the current input method to "
    419                << input_method_id << " since the engine is not enabled. "
    420                << "Switch to " << input_method_id_to_switch << " instead.";
    421     }
    422   }
    423 
    424   if (!component_extension_ime_manager_->IsInitialized() ||
    425       (!InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch) &&
    426        !IsIBusConnectionAlive())) {
    427     // We can't change input method before the initialization of component
    428     // extension ime manager or before connection to ibus-daemon is not
    429     // established. ChangeInputMethod will be called with
    430     // |pending_input_method_| when the both initialization is done.
    431     pending_input_method_ = input_method_id_to_switch;
    432     return false;
    433   }
    434 
    435   pending_input_method_.clear();
    436   IBusInputContextClient* input_context =
    437       chromeos::DBusThreadManager::Get()->GetIBusInputContextClient();
    438   const std::string current_input_method_id = current_input_method_.id();
    439   IBusClient* client = DBusThreadManager::Get()->GetIBusClient();
    440   if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
    441     FOR_EACH_OBSERVER(InputMethodManager::Observer,
    442                       observers_,
    443                       InputMethodPropertyChanged(this));
    444     // Hack for fixing http://crosbug.com/p/12798
    445     // We should notify IME switching to ibus-daemon, otherwise
    446     // IBusPreeditFocusMode does not work. To achieve it, change engine to
    447     // itself if the next engine is XKB layout.
    448     if (current_input_method_id.empty() ||
    449         InputMethodUtil::IsKeyboardLayout(current_input_method_id)) {
    450       if (input_context)
    451         input_context->Reset();
    452     } else {
    453       if (client)
    454         client->SetGlobalEngine(current_input_method_id,
    455                                 base::Bind(&base::DoNothing));
    456     }
    457     if (input_context)
    458       input_context->SetIsXKBLayout(true);
    459   } else {
    460     DCHECK(client);
    461     client->SetGlobalEngine(input_method_id_to_switch,
    462                             base::Bind(&base::DoNothing));
    463     if (input_context)
    464       input_context->SetIsXKBLayout(false);
    465   }
    466 
    467   if (current_input_method_id != input_method_id_to_switch) {
    468     // Clear input method properties unconditionally if
    469     // |input_method_id_to_switch| is not equal to |current_input_method_id|.
    470     //
    471     // When switching to another input method and no text area is focused,
    472     // RegisterProperties signal for the new input method will NOT be sent
    473     // until a text area is focused. Therefore, we have to clear the old input
    474     // method properties here to keep the input method switcher status
    475     // consistent.
    476     //
    477     // When |input_method_id_to_switch| and |current_input_method_id| are the
    478     // same, the properties shouldn't be cleared. If we do that, something
    479     // wrong happens in step #4 below:
    480     // 1. Enable "xkb:us::eng" and "mozc". Switch to "mozc".
    481     // 2. Focus Omnibox. IME properties for mozc are sent to Chrome.
    482     // 3. Switch to "xkb:us::eng". No function in this file is called.
    483     // 4. Switch back to "mozc". ChangeInputMethod("mozc") is called, but it's
    484     //    basically NOP since ibus-daemon's current IME is already "mozc".
    485     //    IME properties are not sent to Chrome for the same reason.
    486     // TODO(nona): Revisit above comment once ibus-daemon is gone.
    487     ibus_controller_->ClearProperties();
    488 
    489     const InputMethodDescriptor* descriptor = NULL;
    490     if (!extension_ime_util::IsExtensionIME(input_method_id_to_switch)) {
    491       descriptor =
    492           util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
    493     } else {
    494       std::map<std::string, InputMethodDescriptor>::const_iterator i =
    495           extra_input_methods_.find(input_method_id_to_switch);
    496       DCHECK(i != extra_input_methods_.end());
    497       descriptor = &(i->second);
    498     }
    499     DCHECK(descriptor);
    500 
    501     previous_input_method_ = current_input_method_;
    502     current_input_method_ = *descriptor;
    503   }
    504 
    505   // Change the keyboard layout to a preferred layout for the input method.
    506   if (!xkeyboard_->SetCurrentKeyboardLayoutByName(
    507           current_input_method_.GetPreferredKeyboardLayout())) {
    508     LOG(ERROR) << "Failed to change keyboard layout to "
    509                << current_input_method_.GetPreferredKeyboardLayout();
    510   }
    511 
    512   // Update input method indicators (e.g. "US", "DV") in Chrome windows.
    513   FOR_EACH_OBSERVER(InputMethodManager::Observer,
    514                     observers_,
    515                     InputMethodChanged(this, show_message));
    516   return true;
    517 }
    518 
    519 void InputMethodManagerImpl::OnComponentExtensionInitialized(
    520     scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
    521   DCHECK(thread_checker_.CalledOnValidThread());
    522   component_extension_ime_manager_->Initialize(delegate.Pass());
    523   util_.SetComponentExtensions(
    524       component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor());
    525 
    526   LoadNecessaryComponentExtensions();
    527 
    528   if (!pending_input_method_.empty())
    529     ChangeInputMethodInternal(pending_input_method_, false);
    530 
    531 }
    532 
    533 void InputMethodManagerImpl::LoadNecessaryComponentExtensions() {
    534   if (!component_extension_ime_manager_->IsInitialized())
    535     return;
    536   // Load component extensions but also update |active_input_method_ids_| as
    537   // some component extension IMEs may have been removed from the Chrome OS
    538   // image. If specified component extension IME no longer exists, falling back
    539   // to an existing IME.
    540   std::vector<std::string> unfiltered_input_method_ids =
    541       active_input_method_ids_;
    542   active_input_method_ids_.clear();
    543   for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) {
    544     if (!component_extension_ime_manager_->IsComponentExtensionIMEId(
    545         unfiltered_input_method_ids[i])) {
    546       // Legacy IMEs or xkb layouts are alwayes active.
    547       active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
    548     } else if (component_extension_ime_manager_->IsWhitelisted(
    549         unfiltered_input_method_ids[i])) {
    550       component_extension_ime_manager_->LoadComponentExtensionIME(
    551           unfiltered_input_method_ids[i]);
    552       active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
    553     }
    554   }
    555 }
    556 
    557 void InputMethodManagerImpl::ActivateInputMethodProperty(
    558     const std::string& key) {
    559   DCHECK(!key.empty());
    560   ibus_controller_->ActivateInputMethodProperty(key);
    561 }
    562 
    563 void InputMethodManagerImpl::AddInputMethodExtension(
    564     const std::string& id,
    565     const std::string& name,
    566     const std::vector<std::string>& layouts,
    567     const std::vector<std::string>& languages,
    568     const GURL& options_url,
    569     InputMethodEngine* engine) {
    570   if (state_ == STATE_TERMINATING)
    571     return;
    572 
    573   if (!extension_ime_util::IsExtensionIME(id) &&
    574       !ComponentExtensionIMEManager::IsComponentExtensionIMEId(id)) {
    575     DVLOG(1) << id << " is not a valid extension input method ID.";
    576     return;
    577   }
    578 
    579   extra_input_methods_[id] =
    580       InputMethodDescriptor(id, name, layouts, languages, options_url);
    581   if (Contains(enabled_extension_imes_, id) &&
    582       !ComponentExtensionIMEManager::IsComponentExtensionIMEId(id)) {
    583     if (!Contains(active_input_method_ids_, id)) {
    584       active_input_method_ids_.push_back(id);
    585     } else {
    586       DVLOG(1) << "AddInputMethodExtension: alread added: "
    587                << id << ", " << name;
    588       // Call Start() anyway, just in case.
    589     }
    590 
    591     // Ensure that the input method daemon is running.
    592     MaybeInitializeCandidateWindowController();
    593     IBusDaemonController::GetInstance()->Start();
    594   }
    595 
    596   extra_input_method_instances_[id] =
    597       static_cast<InputMethodEngineIBus*>(engine);
    598 }
    599 
    600 void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string& id) {
    601   if (!extension_ime_util::IsExtensionIME(id))
    602     DVLOG(1) << id << " is not a valid extension input method ID.";
    603 
    604   std::vector<std::string>::iterator i = std::find(
    605       active_input_method_ids_.begin(), active_input_method_ids_.end(), id);
    606   if (i != active_input_method_ids_.end())
    607     active_input_method_ids_.erase(i);
    608   extra_input_methods_.erase(id);
    609 
    610   if (ContainsOnlyKeyboardLayout(active_input_method_ids_)) {
    611     // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
    612     // at crosbug.com/27051.
    613     // TODO(yusukes): We can safely call Stop(); here once crosbug.com/26443
    614     // is implemented.
    615   }
    616 
    617   // If |current_input_method| is no longer in |active_input_method_ids_|,
    618   // switch to the first one in |active_input_method_ids_|.
    619   ChangeInputMethod(current_input_method_.id());
    620 
    621   std::map<std::string, InputMethodEngineIBus*>::iterator ite =
    622       extra_input_method_instances_.find(id);
    623   if (ite == extra_input_method_instances_.end()) {
    624     DVLOG(1) << "The engine instance of " << id << " has already gone.";
    625   } else {
    626     // Do NOT release the actual instance here. This class does not take an
    627     // onwership of engine instance.
    628     extra_input_method_instances_.erase(ite);
    629   }
    630 }
    631 
    632 void InputMethodManagerImpl::GetInputMethodExtensions(
    633     InputMethodDescriptors* result) {
    634   // Build the extension input method descriptors from the extra input
    635   // methods cache |extra_input_methods_|.
    636   std::map<std::string, InputMethodDescriptor>::iterator iter;
    637   for (iter = extra_input_methods_.begin(); iter != extra_input_methods_.end();
    638        ++iter) {
    639     if (extension_ime_util::IsExtensionIME(iter->first))
    640       result->push_back(iter->second);
    641   }
    642 }
    643 
    644 void InputMethodManagerImpl::SetEnabledExtensionImes(
    645     std::vector<std::string>* ids) {
    646   enabled_extension_imes_.clear();
    647   enabled_extension_imes_.insert(enabled_extension_imes_.end(),
    648                                  ids->begin(),
    649                                  ids->end());
    650 
    651   bool active_imes_changed = false;
    652 
    653   for (std::map<std::string, InputMethodDescriptor>::iterator extra_iter =
    654        extra_input_methods_.begin(); extra_iter != extra_input_methods_.end();
    655        ++extra_iter) {
    656     if (ComponentExtensionIMEManager::IsComponentExtensionIMEId(
    657         extra_iter->first))
    658       continue;  // Do not filter component extension.
    659     std::vector<std::string>::iterator active_iter = std::find(
    660         active_input_method_ids_.begin(), active_input_method_ids_.end(),
    661         extra_iter->first);
    662 
    663     bool active = active_iter != active_input_method_ids_.end();
    664     bool enabled = Contains(enabled_extension_imes_, extra_iter->first);
    665 
    666     if (active && !enabled)
    667       active_input_method_ids_.erase(active_iter);
    668 
    669     if (!active && enabled)
    670       active_input_method_ids_.push_back(extra_iter->first);
    671 
    672     if (active == !enabled)
    673       active_imes_changed = true;
    674   }
    675 
    676   if (active_imes_changed) {
    677     MaybeInitializeCandidateWindowController();
    678     IBusDaemonController::GetInstance()->Start();
    679 
    680     // If |current_input_method| is no longer in |active_input_method_ids_|,
    681     // switch to the first one in |active_input_method_ids_|.
    682     ChangeInputMethod(current_input_method_.id());
    683   }
    684 }
    685 
    686 void InputMethodManagerImpl::SetInputMethodDefault() {
    687   // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
    688   // and US dvorak keyboard layouts.
    689   if (g_browser_process && g_browser_process->local_state()) {
    690     const std::string locale = g_browser_process->GetApplicationLocale();
    691     // If the preferred keyboard for the login screen has been saved, use it.
    692     PrefService* prefs = g_browser_process->local_state();
    693     std::string initial_input_method_id =
    694         prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
    695     if (initial_input_method_id.empty()) {
    696       // If kPreferredKeyboardLayout is not specified, use the hardware layout.
    697       initial_input_method_id =
    698           GetInputMethodUtil()->GetHardwareInputMethodId();
    699     }
    700     EnableLayouts(locale, initial_input_method_id);
    701   }
    702 }
    703 
    704 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
    705   // Sanity checks.
    706   if (active_input_method_ids_.empty()) {
    707     DVLOG(1) << "active input method is empty";
    708     return false;
    709   }
    710   if (current_input_method_.id().empty()) {
    711     DVLOG(1) << "current_input_method_ is unknown";
    712     return false;
    713   }
    714 
    715   // Do not consume key event if there is only one input method is enabled.
    716   // Ctrl+Space or Alt+Shift may be used by other application.
    717   if (active_input_method_ids_.size() == 1)
    718     return false;
    719 
    720   // Find the next input method and switch to it.
    721   SwitchToNextInputMethodInternal(active_input_method_ids_,
    722                                   current_input_method_.id());
    723   return true;
    724 }
    725 
    726 bool InputMethodManagerImpl::SwitchToPreviousInputMethod(
    727     const ui::Accelerator& accelerator) {
    728   // Sanity check.
    729   if (active_input_method_ids_.empty()) {
    730     DVLOG(1) << "active input method is empty";
    731     return false;
    732   }
    733 
    734   // Do not consume key event if there is only one input method is enabled.
    735   // Ctrl+Space or Alt+Shift may be used by other application.
    736   if (active_input_method_ids_.size() == 1)
    737     return false;
    738 
    739   if (accelerator.type() == ui::ET_KEY_RELEASED)
    740     return true;
    741 
    742   if (previous_input_method_.id().empty() ||
    743       previous_input_method_.id() == current_input_method_.id()) {
    744     return SwitchToNextInputMethod();
    745   }
    746 
    747   std::vector<std::string>::const_iterator iter =
    748       std::find(active_input_method_ids_.begin(),
    749                 active_input_method_ids_.end(),
    750                 previous_input_method_.id());
    751   if (iter == active_input_method_ids_.end()) {
    752     // previous_input_method_ is not supported.
    753     return SwitchToNextInputMethod();
    754   }
    755   ChangeInputMethodInternal(*iter, true);
    756   return true;
    757 }
    758 
    759 bool InputMethodManagerImpl::SwitchInputMethod(
    760     const ui::Accelerator& accelerator) {
    761   // Sanity check.
    762   if (active_input_method_ids_.empty()) {
    763     DVLOG(1) << "active input method is empty";
    764     return false;
    765   }
    766 
    767   // Get the list of input method ids for the |accelerator|. For example, get
    768   // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
    769   std::vector<std::string> input_method_ids_to_switch;
    770   switch (accelerator.key_code()) {
    771     case ui::VKEY_CONVERT:  // Henkan key on JP106 keyboard
    772       input_method_ids_to_switch.push_back(nacl_mozc_jp_id);
    773       break;
    774     case ui::VKEY_NONCONVERT:  // Muhenkan key on JP106 keyboard
    775       input_method_ids_to_switch.push_back("xkb:jp::jpn");
    776       break;
    777     case ui::VKEY_DBE_SBCSCHAR:  // ZenkakuHankaku key on JP106 keyboard
    778     case ui::VKEY_DBE_DBCSCHAR:
    779       input_method_ids_to_switch.push_back(nacl_mozc_jp_id);
    780       input_method_ids_to_switch.push_back("xkb:jp::jpn");
    781       break;
    782     default:
    783       NOTREACHED();
    784       break;
    785   }
    786   if (input_method_ids_to_switch.empty()) {
    787     DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
    788     return false;
    789   }
    790 
    791   // Obtain the intersection of input_method_ids_to_switch and
    792   // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
    793   // preserved.
    794   std::vector<std::string> ids;
    795   for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
    796     const std::string& id = input_method_ids_to_switch[i];
    797     if (Contains(active_input_method_ids_, id))
    798       ids.push_back(id);
    799   }
    800   if (ids.empty()) {
    801     // No input method for the accelerator is active. For example, we should
    802     // just ignore VKEY_HANGUL when mozc-hangul is not active.
    803     return false;
    804   }
    805 
    806   SwitchToNextInputMethodInternal(ids, current_input_method_.id());
    807   return true;  // consume the accelerator.
    808 }
    809 
    810 void InputMethodManagerImpl::SwitchToNextInputMethodInternal(
    811     const std::vector<std::string>& input_method_ids,
    812     const std::string& current_input_method_id) {
    813   std::vector<std::string>::const_iterator iter =
    814       std::find(input_method_ids.begin(),
    815                 input_method_ids.end(),
    816                 current_input_method_id);
    817   if (iter != input_method_ids.end())
    818     ++iter;
    819   if (iter == input_method_ids.end())
    820     iter = input_method_ids.begin();
    821   ChangeInputMethodInternal(*iter, true);
    822 }
    823 
    824 InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const {
    825   if (current_input_method_.id().empty())
    826     return InputMethodUtil::GetFallbackInputMethodDescriptor();
    827   return current_input_method_;
    828 }
    829 
    830 InputMethodPropertyList
    831 InputMethodManagerImpl::GetCurrentInputMethodProperties() const {
    832   // This check is necessary since an IME property (e.g. for Pinyin) might be
    833   // sent from ibus-daemon AFTER the current input method is switched to XKB.
    834   if (InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id()))
    835     return InputMethodPropertyList();
    836   return ibus_controller_->GetCurrentProperties();
    837 }
    838 
    839 XKeyboard* InputMethodManagerImpl::GetXKeyboard() {
    840   return xkeyboard_.get();
    841 }
    842 
    843 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
    844   return &util_;
    845 }
    846 
    847 ComponentExtensionIMEManager*
    848     InputMethodManagerImpl::GetComponentExtensionIMEManager() {
    849   DCHECK(thread_checker_.CalledOnValidThread());
    850   return component_extension_ime_manager_.get();
    851 }
    852 
    853 void InputMethodManagerImpl::OnConnected() {
    854   for (std::map<std::string, InputMethodEngineIBus*>::iterator ite =
    855           extra_input_method_instances_.begin();
    856        ite != extra_input_method_instances_.end();
    857        ite++) {
    858     if (Contains(enabled_extension_imes_, ite->first) ||
    859         (component_extension_ime_manager_->IsInitialized() &&
    860          component_extension_ime_manager_->IsWhitelisted(ite->first))) {
    861       ite->second->OnConnected();
    862     }
    863   }
    864 
    865   if (!pending_input_method_.empty())
    866     ChangeInputMethodInternal(pending_input_method_, false);
    867 }
    868 
    869 void InputMethodManagerImpl::OnDisconnected() {
    870   for (std::map<std::string, InputMethodEngineIBus*>::iterator ite =
    871           extra_input_method_instances_.begin();
    872        ite != extra_input_method_instances_.end();
    873        ite++) {
    874     if (Contains(enabled_extension_imes_, ite->first))
    875       ite->second->OnDisconnected();
    876   }
    877 }
    878 
    879 void InputMethodManagerImpl::InitializeComponentExtension() {
    880   ComponentExtensionIMEManagerImpl* impl =
    881       new ComponentExtensionIMEManagerImpl();
    882   scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate(impl);
    883   impl->InitializeAsync(base::Bind(
    884                        &InputMethodManagerImpl::OnComponentExtensionInitialized,
    885                        weak_ptr_factory_.GetWeakPtr(),
    886                        base::Passed(&delegate)));
    887 }
    888 
    889 void InputMethodManagerImpl::Init(base::SequencedTaskRunner* ui_task_runner) {
    890   DCHECK(!ibus_controller_.get());
    891   DCHECK(thread_checker_.CalledOnValidThread());
    892 
    893   ibus_controller_.reset(IBusController::Create());
    894   xkeyboard_.reset(XKeyboard::Create());
    895   ibus_controller_->AddObserver(this);
    896 
    897   // We can't call impl->Initialize here, because file thread is not available
    898   // at this moment.
    899   ui_task_runner->PostTask(
    900       FROM_HERE,
    901       base::Bind(&InputMethodManagerImpl::InitializeComponentExtension,
    902                  weak_ptr_factory_.GetWeakPtr()));
    903 }
    904 
    905 void InputMethodManagerImpl::SetIBusControllerForTesting(
    906     IBusController* ibus_controller) {
    907   ibus_controller_.reset(ibus_controller);
    908   ibus_controller_->AddObserver(this);
    909 }
    910 
    911 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
    912     CandidateWindowController* candidate_window_controller) {
    913   candidate_window_controller_.reset(candidate_window_controller);
    914   candidate_window_controller_->Init();
    915   candidate_window_controller_->AddObserver(this);
    916 }
    917 
    918 void InputMethodManagerImpl::SetXKeyboardForTesting(XKeyboard* xkeyboard) {
    919   xkeyboard_.reset(xkeyboard);
    920 }
    921 
    922 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
    923     scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
    924   OnComponentExtensionInitialized(delegate.Pass());
    925 }
    926 
    927 void InputMethodManagerImpl::PropertyChanged() {
    928   FOR_EACH_OBSERVER(InputMethodManager::Observer,
    929                     observers_,
    930                     InputMethodPropertyChanged(this));
    931 }
    932 
    933 void InputMethodManagerImpl::CandidateWindowOpened() {
    934   FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
    935                     candidate_window_observers_,
    936                     CandidateWindowOpened(this));
    937 }
    938 
    939 void InputMethodManagerImpl::CandidateWindowClosed() {
    940   FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
    941                     candidate_window_observers_,
    942                     CandidateWindowClosed(this));
    943 }
    944 
    945 void InputMethodManagerImpl::OnScreenLocked() {
    946   saved_previous_input_method_ = previous_input_method_;
    947   saved_current_input_method_ = current_input_method_;
    948   saved_active_input_method_ids_ = active_input_method_ids_;
    949 
    950   const std::string hardware_keyboard_id = util_.GetHardwareInputMethodId();
    951   // We'll add the hardware keyboard if it's not included in
    952   // |active_input_method_list| so that the user can always use the hardware
    953   // keyboard on the screen locker.
    954   bool should_add_hardware_keyboard = true;
    955 
    956   active_input_method_ids_.clear();
    957   for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) {
    958     const std::string& input_method_id = saved_active_input_method_ids_[i];
    959     // Skip if it's not a keyboard layout. Drop input methods including
    960     // extension ones.
    961     if (!InputMethodUtil::IsKeyboardLayout(input_method_id))
    962       continue;
    963     active_input_method_ids_.push_back(input_method_id);
    964     if (input_method_id == hardware_keyboard_id)
    965       should_add_hardware_keyboard = false;
    966   }
    967   if (should_add_hardware_keyboard)
    968     active_input_method_ids_.push_back(hardware_keyboard_id);
    969 
    970   ChangeInputMethod(current_input_method_.id());
    971 }
    972 
    973 void InputMethodManagerImpl::OnScreenUnlocked() {
    974   previous_input_method_ = saved_previous_input_method_;
    975   current_input_method_ = saved_current_input_method_;
    976   active_input_method_ids_ = saved_active_input_method_ids_;
    977 
    978   ChangeInputMethod(current_input_method_.id());
    979 }
    980 
    981 bool InputMethodManagerImpl::InputMethodIsActivated(
    982     const std::string& input_method_id) {
    983   return Contains(active_input_method_ids_, input_method_id);
    984 }
    985 
    986 bool InputMethodManagerImpl::ContainsOnlyKeyboardLayout(
    987     const std::vector<std::string>& value) {
    988   for (size_t i = 0; i < value.size(); ++i) {
    989     if (!InputMethodUtil::IsKeyboardLayout(value[i]))
    990       return false;
    991   }
    992   return true;
    993 }
    994 
    995 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
    996   if (candidate_window_controller_.get())
    997     return;
    998 
    999   candidate_window_controller_.reset(
   1000       CandidateWindowController::CreateCandidateWindowController());
   1001   if (candidate_window_controller_->Init())
   1002     candidate_window_controller_->AddObserver(this);
   1003   else
   1004     DVLOG(1) << "Failed to initialize the candidate window controller";
   1005 }
   1006 
   1007 bool InputMethodManagerImpl::IsIBusConnectionAlive() {
   1008   return DBusThreadManager::Get() && DBusThreadManager::Get()->GetIBusClient();
   1009 }
   1010 
   1011 }  // namespace input_method
   1012 }  // namespace chromeos
   1013