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 "ash/ime/input_method_menu_item.h"
     10 #include "ash/ime/input_method_menu_manager.h"
     11 #include "base/basictypes.h"
     12 #include "base/bind.h"
     13 #include "base/location.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/prefs/pref_service.h"
     16 #include "base/strings/string_split.h"
     17 #include "base/strings/string_util.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/sys_info.h"
     20 #include "chrome/browser/browser_process.h"
     21 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
     22 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
     23 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
     24 #include "chrome/browser/chromeos/language_preferences.h"
     25 #include "chrome/browser/chromeos/login/users/user_manager.h"
     26 #include "chrome/browser/profiles/profile_manager.h"
     27 #include "chrome/common/pref_names.h"
     28 #include "chromeos/ime/component_extension_ime_manager.h"
     29 #include "chromeos/ime/extension_ime_util.h"
     30 #include "chromeos/ime/fake_ime_keyboard.h"
     31 #include "chromeos/ime/ime_keyboard.h"
     32 #include "chromeos/ime/input_method_delegate.h"
     33 #include "third_party/icu/source/common/unicode/uloc.h"
     34 #include "ui/base/accelerators/accelerator.h"
     35 
     36 namespace chromeos {
     37 namespace input_method {
     38 
     39 namespace {
     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 }  // namespace
     48 
     49 bool InputMethodManagerImpl::IsLoginKeyboard(
     50     const std::string& layout) const {
     51   return util_.IsLoginKeyboard(layout);
     52 }
     53 
     54 bool InputMethodManagerImpl::MigrateInputMethods(
     55     std::vector<std::string>* input_method_ids) {
     56   return util_.MigrateInputMethods(input_method_ids);
     57 }
     58 
     59 InputMethodManagerImpl::InputMethodManagerImpl(
     60     scoped_ptr<InputMethodDelegate> delegate)
     61     : delegate_(delegate.Pass()),
     62       state_(STATE_LOGIN_SCREEN),
     63       util_(delegate_.get(), whitelist_.GetSupportedInputMethods()),
     64       component_extension_ime_manager_(new ComponentExtensionIMEManager()),
     65       weak_ptr_factory_(this) {
     66 }
     67 
     68 InputMethodManagerImpl::~InputMethodManagerImpl() {
     69   if (candidate_window_controller_.get())
     70     candidate_window_controller_->RemoveObserver(this);
     71 }
     72 
     73 void InputMethodManagerImpl::AddObserver(
     74     InputMethodManager::Observer* observer) {
     75   observers_.AddObserver(observer);
     76 }
     77 
     78 void InputMethodManagerImpl::AddCandidateWindowObserver(
     79     InputMethodManager::CandidateWindowObserver* observer) {
     80   candidate_window_observers_.AddObserver(observer);
     81 }
     82 
     83 void InputMethodManagerImpl::RemoveObserver(
     84     InputMethodManager::Observer* observer) {
     85   observers_.RemoveObserver(observer);
     86 }
     87 
     88 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
     89     InputMethodManager::CandidateWindowObserver* observer) {
     90   candidate_window_observers_.RemoveObserver(observer);
     91 }
     92 
     93 void InputMethodManagerImpl::SetState(State new_state) {
     94   const State old_state = state_;
     95   state_ = new_state;
     96   switch (state_) {
     97     case STATE_LOGIN_SCREEN:
     98       break;
     99     case STATE_BROWSER_SCREEN:
    100       if (old_state == STATE_LOCK_SCREEN)
    101         OnScreenUnlocked();
    102       break;
    103     case STATE_LOCK_SCREEN:
    104       OnScreenLocked();
    105       break;
    106     case STATE_TERMINATING: {
    107       if (candidate_window_controller_.get())
    108         candidate_window_controller_.reset();
    109       break;
    110     }
    111   }
    112 }
    113 
    114 scoped_ptr<InputMethodDescriptors>
    115 InputMethodManagerImpl::GetSupportedInputMethods() const {
    116   if (!IsXkbComponentExtensionAvailable())
    117     return whitelist_.GetSupportedInputMethods().Pass();
    118   return scoped_ptr<InputMethodDescriptors>(new InputMethodDescriptors).Pass();
    119 }
    120 
    121 scoped_ptr<InputMethodDescriptors>
    122 InputMethodManagerImpl::GetActiveInputMethods() const {
    123   scoped_ptr<InputMethodDescriptors> result(new InputMethodDescriptors);
    124   // Build the active input method descriptors from the active input
    125   // methods cache |active_input_method_ids_|.
    126   for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
    127     const std::string& input_method_id = active_input_method_ids_[i];
    128     const InputMethodDescriptor* descriptor =
    129         util_.GetInputMethodDescriptorFromId(input_method_id);
    130     if (descriptor) {
    131       result->push_back(*descriptor);
    132     } else {
    133       std::map<std::string, InputMethodDescriptor>::const_iterator ix =
    134           extra_input_methods_.find(input_method_id);
    135       if (ix != extra_input_methods_.end())
    136         result->push_back(ix->second);
    137       else
    138         DVLOG(1) << "Descriptor is not found for: " << input_method_id;
    139     }
    140   }
    141   if (result->empty()) {
    142     // Initially |active_input_method_ids_| is empty. browser_tests might take
    143     // this path.
    144     result->push_back(
    145         InputMethodUtil::GetFallbackInputMethodDescriptor());
    146   }
    147   return result.Pass();
    148 }
    149 
    150 const std::vector<std::string>&
    151 InputMethodManagerImpl::GetActiveInputMethodIds() const {
    152   return active_input_method_ids_;
    153 }
    154 
    155 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
    156   return active_input_method_ids_.size();
    157 }
    158 
    159 const InputMethodDescriptor* InputMethodManagerImpl::GetInputMethodFromId(
    160     const std::string& input_method_id) const {
    161   const InputMethodDescriptor* ime = util_.GetInputMethodDescriptorFromId(
    162       input_method_id);
    163   if (!ime) {
    164     std::map<std::string, InputMethodDescriptor>::const_iterator ix =
    165         extra_input_methods_.find(input_method_id);
    166     if (ix != extra_input_methods_.end())
    167       ime = &ix->second;
    168   }
    169   return ime;
    170 }
    171 
    172 void InputMethodManagerImpl::EnableLoginLayouts(
    173     const std::string& language_code,
    174     const std::vector<std::string>& initial_layouts) {
    175   if (state_ == STATE_TERMINATING)
    176     return;
    177 
    178   // First, hardware keyboard layout should be shown.
    179   std::vector<std::string> candidates =
    180       util_.GetHardwareLoginInputMethodIds();
    181 
    182   // Seocnd, locale based input method should be shown.
    183   // Add input methods associated with the language.
    184   std::vector<std::string> layouts_from_locale;
    185   util_.GetInputMethodIdsFromLanguageCode(language_code,
    186                                           kKeyboardLayoutsOnly,
    187                                           &layouts_from_locale);
    188   candidates.insert(candidates.end(), layouts_from_locale.begin(),
    189                     layouts_from_locale.end());
    190 
    191   std::vector<std::string> layouts;
    192   // First, add the initial input method ID, if it's requested, to
    193   // layouts, so it appears first on the list of active input
    194   // methods at the input language status menu.
    195   for (size_t i = 0; i < initial_layouts.size(); ++i) {
    196     if (util_.IsValidInputMethodId(initial_layouts[i])) {
    197       if (IsLoginKeyboard(initial_layouts[i])) {
    198         layouts.push_back(initial_layouts[i]);
    199       } else {
    200         DVLOG(1)
    201             << "EnableLoginLayouts: ignoring non-login initial keyboard layout:"
    202             << initial_layouts[i];
    203       }
    204     } else if (!initial_layouts[i].empty()) {
    205       DVLOG(1) << "EnableLoginLayouts: ignoring non-keyboard or invalid ID: "
    206                << initial_layouts[i];
    207     }
    208   }
    209 
    210   // Add candidates to layouts, while skipping duplicates.
    211   for (size_t i = 0; i < candidates.size(); ++i) {
    212     const std::string& candidate = candidates[i];
    213     // Not efficient, but should be fine, as the two vectors are very
    214     // short (2-5 items).
    215     if (!Contains(layouts, candidate) && IsLoginKeyboard(candidate))
    216       layouts.push_back(candidate);
    217   }
    218 
    219   MigrateInputMethods(&layouts);
    220   active_input_method_ids_.swap(layouts);
    221 
    222   // Initialize candidate window controller and widgets such as
    223   // candidate window, infolist and mode indicator.  Note, mode
    224   // indicator is used by only keyboard layout input methods.
    225   if (active_input_method_ids_.size() > 1)
    226     MaybeInitializeCandidateWindowController();
    227 
    228   // you can pass empty |initial_layout|.
    229   ChangeInputMethod(initial_layouts.empty() ? "" :
    230       extension_ime_util::GetInputMethodIDByEngineID(initial_layouts[0]));
    231 }
    232 
    233 // Adds new input method to given list.
    234 bool InputMethodManagerImpl::EnableInputMethodImpl(
    235     const std::string& input_method_id,
    236     std::vector<std::string>* new_active_input_method_ids) const {
    237   DCHECK(new_active_input_method_ids);
    238   if (!util_.IsValidInputMethodId(input_method_id)) {
    239     DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id;
    240     return false;
    241   }
    242 
    243   if (!Contains(*new_active_input_method_ids, input_method_id))
    244     new_active_input_method_ids->push_back(input_method_id);
    245 
    246   return true;
    247 }
    248 
    249 // Starts or stops the system input method framework as needed.
    250 void InputMethodManagerImpl::ReconfigureIMFramework() {
    251   LoadNecessaryComponentExtensions();
    252 
    253   // Initialize candidate window controller and widgets such as
    254   // candidate window, infolist and mode indicator.  Note, mode
    255   // indicator is used by only keyboard layout input methods.
    256   MaybeInitializeCandidateWindowController();
    257 }
    258 
    259 bool InputMethodManagerImpl::EnableInputMethod(
    260     const std::string& input_method_id) {
    261   if (!EnableInputMethodImpl(input_method_id, &active_input_method_ids_))
    262     return false;
    263 
    264   ReconfigureIMFramework();
    265   return true;
    266 }
    267 
    268 bool InputMethodManagerImpl::ReplaceEnabledInputMethods(
    269     const std::vector<std::string>& new_active_input_method_ids) {
    270   if (state_ == STATE_TERMINATING)
    271     return false;
    272 
    273   // Filter unknown or obsolete IDs.
    274   std::vector<std::string> new_active_input_method_ids_filtered;
    275 
    276   for (size_t i = 0; i < new_active_input_method_ids.size(); ++i)
    277     EnableInputMethodImpl(new_active_input_method_ids[i],
    278                           &new_active_input_method_ids_filtered);
    279 
    280   if (new_active_input_method_ids_filtered.empty()) {
    281     DVLOG(1) << "ReplaceEnabledInputMethods: No valid input method ID";
    282     return false;
    283   }
    284 
    285   // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
    286   // keep relative order of the extension input method IDs.
    287   for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
    288     const std::string& input_method_id = active_input_method_ids_[i];
    289     if (extension_ime_util::IsExtensionIME(input_method_id))
    290       new_active_input_method_ids_filtered.push_back(input_method_id);
    291   }
    292   active_input_method_ids_.swap(new_active_input_method_ids_filtered);
    293   MigrateInputMethods(&active_input_method_ids_);
    294 
    295   ReconfigureIMFramework();
    296 
    297   // If |current_input_method| is no longer in |active_input_method_ids_|,
    298   // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
    299   ChangeInputMethod(current_input_method_.id());
    300   return true;
    301 }
    302 
    303 void InputMethodManagerImpl::ChangeInputMethod(
    304     const std::string& input_method_id) {
    305   ChangeInputMethodInternal(input_method_id, false);
    306 }
    307 
    308 bool InputMethodManagerImpl::ChangeInputMethodInternal(
    309     const std::string& input_method_id,
    310     bool show_message) {
    311   if (state_ == STATE_TERMINATING)
    312     return false;
    313 
    314   std::string input_method_id_to_switch = input_method_id;
    315 
    316   // Sanity check.
    317   if (!InputMethodIsActivated(input_method_id)) {
    318     scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
    319     DCHECK(!input_methods->empty());
    320     input_method_id_to_switch = input_methods->at(0).id();
    321     if (!input_method_id.empty()) {
    322       DVLOG(1) << "Can't change the current input method to "
    323                << input_method_id << " since the engine is not enabled. "
    324                << "Switch to " << input_method_id_to_switch << " instead.";
    325     }
    326   }
    327 
    328   if (!component_extension_ime_manager_->IsInitialized() &&
    329       !InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
    330     // We can't change input method before the initialization of
    331     // component extension ime manager.  ChangeInputMethod will be
    332     // called with |pending_input_method_| when the initialization is
    333     // done.
    334     pending_input_method_ = input_method_id_to_switch;
    335     return false;
    336   }
    337   pending_input_method_.clear();
    338 
    339   // Hide candidate window and info list.
    340   if (candidate_window_controller_.get())
    341     candidate_window_controller_->Hide();
    342 
    343   // Disable the current engine handler.
    344   IMEEngineHandlerInterface* engine =
    345       IMEBridge::Get()->GetCurrentEngineHandler();
    346   if (engine)
    347     engine->Disable();
    348 
    349   // Configure the next engine handler.
    350   if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch) &&
    351       !extension_ime_util::IsKeyboardLayoutExtension(
    352           input_method_id_to_switch)) {
    353     IMEBridge::Get()->SetCurrentEngineHandler(NULL);
    354   } else {
    355     IMEEngineHandlerInterface* next_engine =
    356         profile_engine_map_[GetProfile()][input_method_id_to_switch];
    357     if (next_engine) {
    358       IMEBridge::Get()->SetCurrentEngineHandler(next_engine);
    359       next_engine->Enable();
    360     }
    361   }
    362 
    363   // TODO(komatsu): Check if it is necessary to perform the above routine
    364   // when the current input method is equal to |input_method_id_to_swich|.
    365   if (current_input_method_.id() != input_method_id_to_switch) {
    366     // Clear property list.  Property list would be updated by
    367     // extension IMEs via InputMethodEngine::(Set|Update)MenuItems.
    368     // If the current input method is a keyboard layout, empty
    369     // properties are sufficient.
    370     const ash::ime::InputMethodMenuItemList empty_menu_item_list;
    371     ash::ime::InputMethodMenuManager* input_method_menu_manager =
    372         ash::ime::InputMethodMenuManager::GetInstance();
    373     input_method_menu_manager->SetCurrentInputMethodMenuItemList(
    374             empty_menu_item_list);
    375 
    376     const InputMethodDescriptor* descriptor = NULL;
    377     if (extension_ime_util::IsExtensionIME(input_method_id_to_switch)) {
    378       DCHECK(extra_input_methods_.find(input_method_id_to_switch) !=
    379              extra_input_methods_.end());
    380       descriptor = &(extra_input_methods_[input_method_id_to_switch]);
    381     } else {
    382       descriptor =
    383           util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
    384       if (!descriptor)
    385         LOG(ERROR) << "Unknown input method id: " << input_method_id_to_switch;
    386     }
    387     DCHECK(descriptor);
    388 
    389     previous_input_method_ = current_input_method_;
    390     current_input_method_ = *descriptor;
    391   }
    392 
    393   // Change the keyboard layout to a preferred layout for the input method.
    394   if (!keyboard_->SetCurrentKeyboardLayoutByName(
    395           current_input_method_.GetPreferredKeyboardLayout())) {
    396     LOG(ERROR) << "Failed to change keyboard layout to "
    397                << current_input_method_.GetPreferredKeyboardLayout();
    398   }
    399 
    400   // Update input method indicators (e.g. "US", "DV") in Chrome windows.
    401   FOR_EACH_OBSERVER(InputMethodManager::Observer,
    402                     observers_,
    403                     InputMethodChanged(this, show_message));
    404   return true;
    405 }
    406 
    407 bool InputMethodManagerImpl::IsXkbComponentExtensionAvailable() const {
    408   if (!component_extension_ime_manager_->IsInitialized())
    409     return false;
    410   InputMethodDescriptors imes =
    411       component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor();
    412   for (size_t i = 0; i < imes.size(); ++i) {
    413     if (StartsWithASCII(extension_ime_util::MaybeGetLegacyXkbId(
    414         imes[i].id()), "xkb:", true))
    415       return true;
    416   }
    417   return false;
    418 }
    419 
    420 void InputMethodManagerImpl::OnComponentExtensionInitialized(
    421     scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
    422   DCHECK(thread_checker_.CalledOnValidThread());
    423   component_extension_ime_manager_->Initialize(delegate.Pass());
    424   InputMethodDescriptors imes =
    425       component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor();
    426   // In case of XKB extension is not available (e.g. linux_chromeos), don't
    427   // reset the input methods in InputMethodUtil, Instead append input methods.
    428   if (IsXkbComponentExtensionAvailable())
    429     util_.ResetInputMethods(imes);
    430   else
    431     util_.AppendInputMethods(imes);
    432 
    433   LoadNecessaryComponentExtensions();
    434 
    435   if (!pending_input_method_.empty())
    436     ChangeInputMethodInternal(pending_input_method_, false);
    437 }
    438 
    439 void InputMethodManagerImpl::LoadNecessaryComponentExtensions() {
    440   if (!component_extension_ime_manager_->IsInitialized())
    441     return;
    442   // Load component extensions but also update |active_input_method_ids_| as
    443   // some component extension IMEs may have been removed from the Chrome OS
    444   // image. If specified component extension IME no longer exists, falling back
    445   // to an existing IME.
    446   std::vector<std::string> unfiltered_input_method_ids;
    447   unfiltered_input_method_ids.swap(active_input_method_ids_);
    448   for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) {
    449     if (!extension_ime_util::IsComponentExtensionIME(
    450         unfiltered_input_method_ids[i])) {
    451       // Legacy IMEs or xkb layouts are alwayes active.
    452       active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
    453     } else if (component_extension_ime_manager_->IsWhitelisted(
    454         unfiltered_input_method_ids[i])) {
    455       component_extension_ime_manager_->LoadComponentExtensionIME(
    456           unfiltered_input_method_ids[i]);
    457       active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
    458     }
    459   }
    460   // TODO(shuchen): move this call in ComponentExtensionIMEManager.
    461   component_extension_ime_manager_->NotifyInitialized();
    462 }
    463 
    464 void InputMethodManagerImpl::ActivateInputMethodMenuItem(
    465     const std::string& key) {
    466   DCHECK(!key.empty());
    467 
    468   if (ash::ime::InputMethodMenuManager::GetInstance()->
    469       HasInputMethodMenuItemForKey(key)) {
    470     IMEEngineHandlerInterface* engine =
    471         IMEBridge::Get()->GetCurrentEngineHandler();
    472     if (engine)
    473       engine->PropertyActivate(key);
    474     return;
    475   }
    476 
    477   DVLOG(1) << "ActivateInputMethodMenuItem: unknown key: " << key;
    478 }
    479 
    480 void InputMethodManagerImpl::AddInputMethodExtension(
    481     Profile* profile,
    482     const std::string& id,
    483     InputMethodEngineInterface* engine) {
    484   if (state_ == STATE_TERMINATING)
    485     return;
    486 
    487   DCHECK(engine);
    488 
    489   profile_engine_map_[profile][id] = engine;
    490 
    491   if (id == current_input_method_.id()) {
    492     IMEBridge::Get()->SetCurrentEngineHandler(engine);
    493     engine->Enable();
    494   }
    495 
    496   if (extension_ime_util::IsComponentExtensionIME(id))
    497     return;
    498 
    499   CHECK(extension_ime_util::IsExtensionIME(id))
    500       << id << "is not a valid extension input method ID";
    501 
    502   const InputMethodDescriptor& descriptor = engine->GetDescriptor();
    503   extra_input_methods_[id] = descriptor;
    504 
    505   if (Contains(enabled_extension_imes_, id)) {
    506     if (!Contains(active_input_method_ids_, id)) {
    507       active_input_method_ids_.push_back(id);
    508     } else {
    509       DVLOG(1) << "AddInputMethodExtension: alread added: "
    510                << id << ", " << descriptor.name();
    511     }
    512 
    513     // Ensure that the input method daemon is running.
    514     MaybeInitializeCandidateWindowController();
    515   }
    516 }
    517 
    518 void InputMethodManagerImpl::RemoveInputMethodExtension(Profile* profile,
    519                                                         const std::string& id) {
    520   if (!extension_ime_util::IsExtensionIME(id))
    521     DVLOG(1) << id << " is not a valid extension input method ID.";
    522 
    523   std::vector<std::string>::iterator i = std::find(
    524       active_input_method_ids_.begin(), active_input_method_ids_.end(), id);
    525   if (i != active_input_method_ids_.end())
    526     active_input_method_ids_.erase(i);
    527   extra_input_methods_.erase(id);
    528 
    529   EngineMap& engine_map = profile_engine_map_[profile];
    530   if (IMEBridge::Get()->GetCurrentEngineHandler() == engine_map[id])
    531     IMEBridge::Get()->SetCurrentEngineHandler(NULL);
    532   engine_map.erase(id);
    533 
    534   // No need to switch input method when terminating.
    535   if (state_ != STATE_TERMINATING) {
    536     // If |current_input_method| is no longer in |active_input_method_ids_|,
    537     // switch to the first one in |active_input_method_ids_|.
    538     ChangeInputMethod(current_input_method_.id());
    539   }
    540 }
    541 
    542 void InputMethodManagerImpl::GetInputMethodExtensions(
    543     InputMethodDescriptors* result) {
    544   // Build the extension input method descriptors from the extra input
    545   // methods cache |extra_input_methods_|.
    546   std::map<std::string, InputMethodDescriptor>::iterator iter;
    547   for (iter = extra_input_methods_.begin(); iter != extra_input_methods_.end();
    548        ++iter) {
    549     if (extension_ime_util::IsExtensionIME(iter->first))
    550       result->push_back(iter->second);
    551   }
    552 }
    553 
    554 void InputMethodManagerImpl::SetEnabledExtensionImes(
    555     std::vector<std::string>* ids) {
    556   enabled_extension_imes_.clear();
    557   enabled_extension_imes_.insert(enabled_extension_imes_.end(),
    558                                  ids->begin(),
    559                                  ids->end());
    560 
    561   bool active_imes_changed = false;
    562 
    563   for (std::map<std::string, InputMethodDescriptor>::iterator extra_iter =
    564        extra_input_methods_.begin(); extra_iter != extra_input_methods_.end();
    565        ++extra_iter) {
    566     if (extension_ime_util::IsComponentExtensionIME(
    567         extra_iter->first))
    568       continue;  // Do not filter component extension.
    569     std::vector<std::string>::iterator active_iter = std::find(
    570         active_input_method_ids_.begin(), active_input_method_ids_.end(),
    571         extra_iter->first);
    572 
    573     bool active = active_iter != active_input_method_ids_.end();
    574     bool enabled = Contains(enabled_extension_imes_, extra_iter->first);
    575 
    576     if (active && !enabled)
    577       active_input_method_ids_.erase(active_iter);
    578 
    579     if (!active && enabled)
    580       active_input_method_ids_.push_back(extra_iter->first);
    581 
    582     if (active == !enabled)
    583       active_imes_changed = true;
    584   }
    585 
    586   if (active_imes_changed) {
    587     MaybeInitializeCandidateWindowController();
    588 
    589     // If |current_input_method| is no longer in |active_input_method_ids_|,
    590     // switch to the first one in |active_input_method_ids_|.
    591     ChangeInputMethod(current_input_method_.id());
    592   }
    593 }
    594 
    595 void InputMethodManagerImpl::SetInputMethodLoginDefaultFromVPD(
    596     const std::string& locale, const std::string& oem_layout) {
    597   std::string layout;
    598   if (!oem_layout.empty()) {
    599     // If the OEM layout information is provided, use it.
    600     layout = oem_layout;
    601   } else {
    602     // Otherwise, determine the hardware keyboard from the locale.
    603     std::vector<std::string> input_method_ids;
    604     if (util_.GetInputMethodIdsFromLanguageCode(
    605         locale,
    606         chromeos::input_method::kKeyboardLayoutsOnly,
    607         &input_method_ids)) {
    608       // The output list |input_method_ids| is sorted by popularity, hence
    609       // input_method_ids[0] now contains the most popular keyboard layout
    610       // for the given locale.
    611       DCHECK_GE(input_method_ids.size(), 1U);
    612       layout = input_method_ids[0];
    613     }
    614   }
    615 
    616   if (layout.empty())
    617     return;
    618 
    619   std::vector<std::string> layouts;
    620   base::SplitString(layout, ',', &layouts);
    621   MigrateInputMethods(&layouts);
    622 
    623   PrefService* prefs = g_browser_process->local_state();
    624   prefs->SetString(prefs::kHardwareKeyboardLayout, JoinString(layouts, ","));
    625 
    626   // This asks the file thread to save the prefs (i.e. doesn't block).
    627   // The latest values of Local State reside in memory so we can safely
    628   // get the value of kHardwareKeyboardLayout even if the data is not
    629   // yet saved to disk.
    630   prefs->CommitPendingWrite();
    631 
    632   util_.UpdateHardwareLayoutCache();
    633 
    634   EnableLoginLayouts(locale, layouts);
    635 }
    636 
    637 void InputMethodManagerImpl::SetInputMethodLoginDefault() {
    638   // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
    639   // and US dvorak keyboard layouts.
    640   if (g_browser_process && g_browser_process->local_state()) {
    641     const std::string locale = g_browser_process->GetApplicationLocale();
    642     // If the preferred keyboard for the login screen has been saved, use it.
    643     PrefService* prefs = g_browser_process->local_state();
    644     std::string initial_input_method_id =
    645         prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
    646     std::vector<std::string> input_methods_to_be_enabled;
    647     if (initial_input_method_id.empty()) {
    648       // If kPreferredKeyboardLayout is not specified, use the hardware layout.
    649       input_methods_to_be_enabled = util_.GetHardwareLoginInputMethodIds();
    650     } else {
    651       input_methods_to_be_enabled.push_back(initial_input_method_id);
    652     }
    653     EnableLoginLayouts(locale, input_methods_to_be_enabled);
    654   }
    655 }
    656 
    657 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
    658   // Sanity checks.
    659   if (active_input_method_ids_.empty()) {
    660     DVLOG(1) << "active input method is empty";
    661     return false;
    662   }
    663 
    664   if (current_input_method_.id().empty()) {
    665     DVLOG(1) << "current_input_method_ is unknown";
    666     return false;
    667   }
    668 
    669   // Do not consume key event if there is only one input method is enabled.
    670   // Ctrl+Space or Alt+Shift may be used by other application.
    671   if (active_input_method_ids_.size() == 1)
    672     return false;
    673 
    674   // Find the next input method and switch to it.
    675   SwitchToNextInputMethodInternal(active_input_method_ids_,
    676                                   current_input_method_.id());
    677   return true;
    678 }
    679 
    680 bool InputMethodManagerImpl::SwitchToPreviousInputMethod(
    681     const ui::Accelerator& accelerator) {
    682   // Sanity check.
    683   if (active_input_method_ids_.empty()) {
    684     DVLOG(1) << "active input method is empty";
    685     return false;
    686   }
    687 
    688   // Do not consume key event if there is only one input method is enabled.
    689   // Ctrl+Space or Alt+Shift may be used by other application.
    690   if (active_input_method_ids_.size() == 1)
    691     return false;
    692 
    693   if (accelerator.type() == ui::ET_KEY_RELEASED)
    694     return true;
    695 
    696   if (previous_input_method_.id().empty() ||
    697       previous_input_method_.id() == current_input_method_.id()) {
    698     return SwitchToNextInputMethod();
    699   }
    700 
    701   std::vector<std::string>::const_iterator iter =
    702       std::find(active_input_method_ids_.begin(),
    703                 active_input_method_ids_.end(),
    704                 previous_input_method_.id());
    705   if (iter == active_input_method_ids_.end()) {
    706     // previous_input_method_ is not supported.
    707     return SwitchToNextInputMethod();
    708   }
    709   ChangeInputMethodInternal(*iter, true);
    710   return true;
    711 }
    712 
    713 bool InputMethodManagerImpl::SwitchInputMethod(
    714     const ui::Accelerator& accelerator) {
    715   // Sanity check.
    716   if (active_input_method_ids_.empty()) {
    717     DVLOG(1) << "active input method is empty";
    718     return false;
    719   }
    720 
    721   // Get the list of input method ids for the |accelerator|. For example, get
    722   // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
    723   std::vector<std::string> input_method_ids_to_switch;
    724   switch (accelerator.key_code()) {
    725     case ui::VKEY_CONVERT:  // Henkan key on JP106 keyboard
    726       input_method_ids_to_switch.push_back(
    727           extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
    728       break;
    729     case ui::VKEY_NONCONVERT:  // Muhenkan key on JP106 keyboard
    730       input_method_ids_to_switch.push_back(
    731           extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
    732       break;
    733     case ui::VKEY_DBE_SBCSCHAR:  // ZenkakuHankaku key on JP106 keyboard
    734     case ui::VKEY_DBE_DBCSCHAR:
    735       input_method_ids_to_switch.push_back(
    736           extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
    737       input_method_ids_to_switch.push_back(
    738           extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
    739       break;
    740     default:
    741       NOTREACHED();
    742       break;
    743   }
    744   if (input_method_ids_to_switch.empty()) {
    745     DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
    746     return false;
    747   }
    748 
    749   // Obtain the intersection of input_method_ids_to_switch and
    750   // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
    751   // preserved.
    752   std::vector<std::string> ids;
    753   for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
    754     const std::string& id = input_method_ids_to_switch[i];
    755     if (Contains(active_input_method_ids_, id))
    756       ids.push_back(id);
    757   }
    758   if (ids.empty()) {
    759     // No input method for the accelerator is active. For example, we should
    760     // just ignore VKEY_HANGUL when mozc-hangul is not active.
    761     return false;
    762   }
    763 
    764   SwitchToNextInputMethodInternal(ids, current_input_method_.id());
    765   return true;  // consume the accelerator.
    766 }
    767 
    768 void InputMethodManagerImpl::SwitchToNextInputMethodInternal(
    769     const std::vector<std::string>& input_method_ids,
    770     const std::string& current_input_method_id) {
    771   std::vector<std::string>::const_iterator iter =
    772       std::find(input_method_ids.begin(),
    773                 input_method_ids.end(),
    774                 current_input_method_id);
    775   if (iter != input_method_ids.end())
    776     ++iter;
    777   if (iter == input_method_ids.end())
    778     iter = input_method_ids.begin();
    779   ChangeInputMethodInternal(*iter, true);
    780 }
    781 
    782 InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const {
    783   if (current_input_method_.id().empty())
    784     return InputMethodUtil::GetFallbackInputMethodDescriptor();
    785 
    786   return current_input_method_;
    787 }
    788 
    789 bool InputMethodManagerImpl::IsISOLevel5ShiftUsedByCurrentInputMethod() const {
    790   return keyboard_->IsISOLevel5ShiftAvailable();
    791 }
    792 
    793 bool InputMethodManagerImpl::IsAltGrUsedByCurrentInputMethod() const {
    794   return keyboard_->IsAltGrAvailable();
    795 }
    796 
    797 ImeKeyboard* InputMethodManagerImpl::GetImeKeyboard() {
    798   return keyboard_.get();
    799 }
    800 
    801 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
    802   return &util_;
    803 }
    804 
    805 ComponentExtensionIMEManager*
    806     InputMethodManagerImpl::GetComponentExtensionIMEManager() {
    807   DCHECK(thread_checker_.CalledOnValidThread());
    808   return component_extension_ime_manager_.get();
    809 }
    810 
    811 void InputMethodManagerImpl::InitializeComponentExtension() {
    812   ComponentExtensionIMEManagerImpl* impl =
    813       new ComponentExtensionIMEManagerImpl();
    814   scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate(impl);
    815   impl->InitializeAsync(base::Bind(
    816                        &InputMethodManagerImpl::OnComponentExtensionInitialized,
    817                        weak_ptr_factory_.GetWeakPtr(),
    818                        base::Passed(&delegate)));
    819 }
    820 
    821 void InputMethodManagerImpl::Init(base::SequencedTaskRunner* ui_task_runner) {
    822   DCHECK(thread_checker_.CalledOnValidThread());
    823 
    824   if (base::SysInfo::IsRunningOnChromeOS())
    825     keyboard_.reset(ImeKeyboard::Create());
    826   else
    827     keyboard_.reset(new FakeImeKeyboard());
    828 
    829   // We can't call impl->Initialize here, because file thread is not available
    830   // at this moment.
    831   ui_task_runner->PostTask(
    832       FROM_HERE,
    833       base::Bind(&InputMethodManagerImpl::InitializeComponentExtension,
    834                  weak_ptr_factory_.GetWeakPtr()));
    835 }
    836 
    837 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
    838     CandidateWindowController* candidate_window_controller) {
    839   candidate_window_controller_.reset(candidate_window_controller);
    840   candidate_window_controller_->AddObserver(this);
    841 }
    842 
    843 void InputMethodManagerImpl::SetImeKeyboardForTesting(ImeKeyboard* keyboard) {
    844   keyboard_.reset(keyboard);
    845 }
    846 
    847 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
    848     scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
    849   OnComponentExtensionInitialized(delegate.Pass());
    850 }
    851 
    852 void InputMethodManagerImpl::CandidateClicked(int index) {
    853   IMEEngineHandlerInterface* engine =
    854       IMEBridge::Get()->GetCurrentEngineHandler();
    855   if (engine)
    856     engine->CandidateClicked(index);
    857 }
    858 
    859 void InputMethodManagerImpl::CandidateWindowOpened() {
    860   FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
    861                     candidate_window_observers_,
    862                     CandidateWindowOpened(this));
    863 }
    864 
    865 void InputMethodManagerImpl::CandidateWindowClosed() {
    866   FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
    867                     candidate_window_observers_,
    868                     CandidateWindowClosed(this));
    869 }
    870 
    871 void InputMethodManagerImpl::OnScreenLocked() {
    872   saved_previous_input_method_ = previous_input_method_;
    873   saved_current_input_method_ = current_input_method_;
    874   saved_active_input_method_ids_ = active_input_method_ids_;
    875 
    876   std::set<std::string> added_ids_;
    877 
    878   const std::vector<std::string>& hardware_keyboard_ids =
    879       util_.GetHardwareLoginInputMethodIds();
    880 
    881   active_input_method_ids_.clear();
    882   for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) {
    883     const std::string& input_method_id = saved_active_input_method_ids_[i];
    884     // Skip if it's not a keyboard layout. Drop input methods including
    885     // extension ones.
    886     if (!IsLoginKeyboard(input_method_id) ||
    887         added_ids_.find(input_method_id) != added_ids_.end())
    888       continue;
    889     active_input_method_ids_.push_back(input_method_id);
    890     added_ids_.insert(input_method_id);
    891   }
    892 
    893   // We'll add the hardware keyboard if it's not included in
    894   // |active_input_method_ids_| so that the user can always use the hardware
    895   // keyboard on the screen locker.
    896   for (size_t i = 0; i < hardware_keyboard_ids.size(); ++i) {
    897     if (added_ids_.find(hardware_keyboard_ids[i]) == added_ids_.end()) {
    898       active_input_method_ids_.push_back(hardware_keyboard_ids[i]);
    899       added_ids_.insert(hardware_keyboard_ids[i]);
    900     }
    901   }
    902 
    903   ChangeInputMethod(current_input_method_.id());
    904 }
    905 
    906 void InputMethodManagerImpl::OnScreenUnlocked() {
    907   previous_input_method_ = saved_previous_input_method_;
    908   current_input_method_ = saved_current_input_method_;
    909   active_input_method_ids_ = saved_active_input_method_ids_;
    910 
    911   ChangeInputMethod(current_input_method_.id());
    912 }
    913 
    914 bool InputMethodManagerImpl::InputMethodIsActivated(
    915     const std::string& input_method_id) {
    916   return Contains(active_input_method_ids_, input_method_id);
    917 }
    918 
    919 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
    920   if (candidate_window_controller_.get())
    921     return;
    922 
    923   candidate_window_controller_.reset(
    924       CandidateWindowController::CreateCandidateWindowController());
    925   candidate_window_controller_->AddObserver(this);
    926 }
    927 
    928 Profile* InputMethodManagerImpl::GetProfile() const {
    929   return ProfileManager::GetActiveUserProfile();
    930 }
    931 
    932 }  // namespace input_method
    933 }  // namespace chromeos
    934