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