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