- add sources.
[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 "chrome/browser/browser_process.h"
17 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
18 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
19 #include "chrome/browser/chromeos/input_method/input_method_engine_ibus.h"
20 #include "chrome/browser/chromeos/language_preferences.h"
21 #include "chromeos/dbus/dbus_thread_manager.h"
22 #include "chromeos/dbus/ibus/ibus_client.h"
23 #include "chromeos/ime/component_extension_ime_manager.h"
24 #include "chromeos/ime/extension_ime_util.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   const InputMethodDescriptor* ime =
49       util_.GetInputMethodDescriptorFromId(layout);
50   return ime ? ime->is_login_keyboard() : false;
51 }
52
53 InputMethodManagerImpl::InputMethodManagerImpl(
54     scoped_ptr<InputMethodDelegate> delegate)
55     : delegate_(delegate.Pass()),
56       state_(STATE_LOGIN_SCREEN),
57       util_(delegate_.get(), GetSupportedInputMethods()),
58       component_extension_ime_manager_(new ComponentExtensionIMEManager()),
59       weak_ptr_factory_(this) {
60   IBusDaemonController::GetInstance()->AddObserver(this);
61 }
62
63 InputMethodManagerImpl::~InputMethodManagerImpl() {
64   if (ibus_controller_.get())
65     ibus_controller_->RemoveObserver(this);
66   IBusDaemonController::GetInstance()->RemoveObserver(this);
67   if (candidate_window_controller_.get()) {
68     candidate_window_controller_->RemoveObserver(this);
69     candidate_window_controller_->Shutdown();
70   }
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_->Shutdown();
109         candidate_window_controller_.reset();
110       }
111       break;
112     }
113   }
114 }
115
116 scoped_ptr<InputMethodDescriptors>
117 InputMethodManagerImpl::GetSupportedInputMethods() const {
118   return whitelist_.GetSupportedInputMethods();
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 void InputMethodManagerImpl::EnableLayouts(const std::string& language_code,
160                                            const std::string& initial_layout) {
161   if (state_ == STATE_TERMINATING)
162     return;
163
164   std::vector<std::string> candidates;
165   // Add input methods associated with the language.
166   util_.GetInputMethodIdsFromLanguageCode(language_code,
167                                           kKeyboardLayoutsOnly,
168                                           &candidates);
169   // Add the hardware keyboard as well. We should always add this so users
170   // can use the hardware keyboard on the login screen and the screen locker.
171   candidates.push_back(util_.GetHardwareInputMethodId());
172
173   std::vector<std::string> layouts;
174   // First, add the initial input method ID, if it's requested, to
175   // layouts, so it appears first on the list of active input
176   // methods at the input language status menu.
177   if (util_.IsValidInputMethodId(initial_layout) &&
178       IsLoginKeyboard(initial_layout)) {
179     layouts.push_back(initial_layout);
180   } else if (!initial_layout.empty()) {
181     DVLOG(1) << "EnableLayouts: ignoring non-keyboard or invalid ID: "
182              << initial_layout;
183   }
184
185   // Add candidates to layouts, while skipping duplicates.
186   for (size_t i = 0; i < candidates.size(); ++i) {
187     const std::string& candidate = candidates[i];
188     // Not efficient, but should be fine, as the two vectors are very
189     // short (2-5 items).
190     if (!Contains(layouts, candidate) && IsLoginKeyboard(candidate))
191       layouts.push_back(candidate);
192   }
193
194   active_input_method_ids_.swap(layouts);
195   ChangeInputMethod(initial_layout);  // you can pass empty |initial_layout|.
196 }
197
198 // Adds new input method to given list.
199 bool InputMethodManagerImpl::EnableInputMethodImpl(
200     const std::string& input_method_id,
201     std::vector<std::string>& new_active_input_method_ids) const {
202   if (!util_.IsValidInputMethodId(input_method_id)) {
203     DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id;
204     return false;
205   }
206
207   if (!Contains(new_active_input_method_ids, input_method_id))
208     new_active_input_method_ids.push_back(input_method_id);
209
210   return true;
211 }
212
213 // Starts or stops the system input method framework as needed.
214 void InputMethodManagerImpl::ReconfigureIMFramework() {
215   if (component_extension_ime_manager_->IsInitialized())
216     LoadNecessaryComponentExtensions();
217
218   if (ContainsOnlyKeyboardLayout(active_input_method_ids_)) {
219     // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
220     // at crbug.com/27051.
221     // TODO(yusukes): We can safely call Stop(); here once crbug.com/26443
222     // is implemented.
223   } else {
224     MaybeInitializeCandidateWindowController();
225     IBusDaemonController::GetInstance()->Start();
226   }
227 }
228
229 bool InputMethodManagerImpl::EnableInputMethod(
230     const std::string& input_method_id) {
231   if (!EnableInputMethodImpl(input_method_id, active_input_method_ids_))
232     return false;
233
234   ReconfigureIMFramework();
235   return true;
236 }
237
238 bool InputMethodManagerImpl::EnableInputMethods(
239     const std::vector<std::string>& new_active_input_method_ids) {
240   if (state_ == STATE_TERMINATING)
241     return false;
242
243   // Filter unknown or obsolete IDs.
244   std::vector<std::string> new_active_input_method_ids_filtered;
245
246   for (size_t i = 0; i < new_active_input_method_ids.size(); ++i)
247     EnableInputMethodImpl(new_active_input_method_ids[i],
248                           new_active_input_method_ids_filtered);
249
250   if (new_active_input_method_ids_filtered.empty()) {
251     DVLOG(1) << "EnableInputMethods: No valid input method ID";
252     return false;
253   }
254
255   // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
256   // keep relative order of the extension input method IDs.
257   for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
258     const std::string& input_method_id = active_input_method_ids_[i];
259     if (extension_ime_util::IsExtensionIME(input_method_id))
260       new_active_input_method_ids_filtered.push_back(input_method_id);
261   }
262   active_input_method_ids_.swap(new_active_input_method_ids_filtered);
263
264   ReconfigureIMFramework();
265
266   // If |current_input_method| is no longer in |active_input_method_ids_|,
267   // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
268   ChangeInputMethod(current_input_method_.id());
269   return true;
270 }
271
272 void InputMethodManagerImpl::ChangeInputMethod(
273     const std::string& input_method_id) {
274   ChangeInputMethodInternal(input_method_id, false);
275 }
276
277 bool InputMethodManagerImpl::ChangeInputMethodInternal(
278     const std::string& input_method_id,
279     bool show_message) {
280   if (state_ == STATE_TERMINATING)
281     return false;
282
283   std::string input_method_id_to_switch = input_method_id;
284
285   // Sanity check.
286   if (!InputMethodIsActivated(input_method_id)) {
287     scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
288     DCHECK(!input_methods->empty());
289     input_method_id_to_switch = input_methods->at(0).id();
290     if (!input_method_id.empty()) {
291       DVLOG(1) << "Can't change the current input method to "
292                << input_method_id << " since the engine is not enabled. "
293                << "Switch to " << input_method_id_to_switch << " instead.";
294     }
295   }
296
297   if (!component_extension_ime_manager_->IsInitialized() ||
298       (!InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch) &&
299        !IsIBusConnectionAlive())) {
300     // We can't change input method before the initialization of component
301     // extension ime manager or before connection to ibus-daemon is not
302     // established. ChangeInputMethod will be called with
303     // |pending_input_method_| when the both initialization is done.
304     pending_input_method_ = input_method_id_to_switch;
305     return false;
306   }
307
308   pending_input_method_.clear();
309   IBusEngineHandlerInterface* engine = IBusBridge::Get()->GetEngineHandler();
310   const std::string current_input_method_id = current_input_method_.id();
311   IBusClient* client = DBusThreadManager::Get()->GetIBusClient();
312   if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
313     FOR_EACH_OBSERVER(InputMethodManager::Observer,
314                       observers_,
315                       InputMethodPropertyChanged(this));
316     if (engine) {
317       engine->Disable();
318       IBusBridge::Get()->SetEngineHandler(NULL);
319     }
320   } else {
321     DCHECK(client);
322     client->SetGlobalEngine(input_method_id_to_switch,
323                             base::Bind(&base::DoNothing));
324   }
325
326   if (current_input_method_id != input_method_id_to_switch) {
327     // Clear input method properties unconditionally if
328     // |input_method_id_to_switch| is not equal to |current_input_method_id|.
329     //
330     // When switching to another input method and no text area is focused,
331     // RegisterProperties signal for the new input method will NOT be sent
332     // until a text area is focused. Therefore, we have to clear the old input
333     // method properties here to keep the input method switcher status
334     // consistent.
335     //
336     // When |input_method_id_to_switch| and |current_input_method_id| are the
337     // same, the properties shouldn't be cleared. If we do that, something
338     // wrong happens in step #4 below:
339     // 1. Enable "xkb:us::eng" and "mozc". Switch to "mozc".
340     // 2. Focus Omnibox. IME properties for mozc are sent to Chrome.
341     // 3. Switch to "xkb:us::eng". No function in this file is called.
342     // 4. Switch back to "mozc". ChangeInputMethod("mozc") is called, but it's
343     //    basically NOP since ibus-daemon's current IME is already "mozc".
344     //    IME properties are not sent to Chrome for the same reason.
345     // TODO(nona): Revisit above comment once ibus-daemon is gone.
346     ibus_controller_->ClearProperties();
347
348     const InputMethodDescriptor* descriptor = NULL;
349     if (!extension_ime_util::IsExtensionIME(input_method_id_to_switch)) {
350       descriptor =
351           util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
352     } else {
353       std::map<std::string, InputMethodDescriptor>::const_iterator i =
354           extra_input_methods_.find(input_method_id_to_switch);
355       DCHECK(i != extra_input_methods_.end());
356       descriptor = &(i->second);
357     }
358     DCHECK(descriptor);
359
360     previous_input_method_ = current_input_method_;
361     current_input_method_ = *descriptor;
362   }
363
364   // Change the keyboard layout to a preferred layout for the input method.
365   if (!xkeyboard_->SetCurrentKeyboardLayoutByName(
366           current_input_method_.GetPreferredKeyboardLayout())) {
367     LOG(ERROR) << "Failed to change keyboard layout to "
368                << current_input_method_.GetPreferredKeyboardLayout();
369   }
370
371   // Update input method indicators (e.g. "US", "DV") in Chrome windows.
372   FOR_EACH_OBSERVER(InputMethodManager::Observer,
373                     observers_,
374                     InputMethodChanged(this, show_message));
375   return true;
376 }
377
378 void InputMethodManagerImpl::OnComponentExtensionInitialized(
379     scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
380   DCHECK(thread_checker_.CalledOnValidThread());
381   component_extension_ime_manager_->Initialize(delegate.Pass());
382   util_.SetComponentExtensions(
383       component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor());
384
385   LoadNecessaryComponentExtensions();
386
387   if (!pending_input_method_.empty())
388     ChangeInputMethodInternal(pending_input_method_, false);
389
390 }
391
392 void InputMethodManagerImpl::LoadNecessaryComponentExtensions() {
393   if (!component_extension_ime_manager_->IsInitialized())
394     return;
395   // Load component extensions but also update |active_input_method_ids_| as
396   // some component extension IMEs may have been removed from the Chrome OS
397   // image. If specified component extension IME no longer exists, falling back
398   // to an existing IME.
399   std::vector<std::string> unfiltered_input_method_ids =
400       active_input_method_ids_;
401   active_input_method_ids_.clear();
402   for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) {
403     if (!component_extension_ime_manager_->IsComponentExtensionIMEId(
404         unfiltered_input_method_ids[i])) {
405       // Legacy IMEs or xkb layouts are alwayes active.
406       active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
407     } else if (component_extension_ime_manager_->IsWhitelisted(
408         unfiltered_input_method_ids[i])) {
409       component_extension_ime_manager_->LoadComponentExtensionIME(
410           unfiltered_input_method_ids[i]);
411       active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
412     }
413   }
414 }
415
416 void InputMethodManagerImpl::ActivateInputMethodProperty(
417     const std::string& key) {
418   DCHECK(!key.empty());
419   ibus_controller_->ActivateInputMethodProperty(key);
420 }
421
422 void InputMethodManagerImpl::AddInputMethodExtension(
423     const std::string& id,
424     const std::string& name,
425     const std::vector<std::string>& layouts,
426     const std::vector<std::string>& languages,
427     const GURL& options_url,
428     InputMethodEngine* engine) {
429   if (state_ == STATE_TERMINATING)
430     return;
431
432   if (!extension_ime_util::IsExtensionIME(id) &&
433       !ComponentExtensionIMEManager::IsComponentExtensionIMEId(id)) {
434     DVLOG(1) << id << " is not a valid extension input method ID.";
435     return;
436   }
437
438   extra_input_methods_[id] =
439       InputMethodDescriptor(id, name, layouts, languages, false, options_url);
440   if (Contains(enabled_extension_imes_, id) &&
441       !ComponentExtensionIMEManager::IsComponentExtensionIMEId(id)) {
442     if (!Contains(active_input_method_ids_, id)) {
443       active_input_method_ids_.push_back(id);
444     } else {
445       DVLOG(1) << "AddInputMethodExtension: alread added: "
446                << id << ", " << name;
447       // Call Start() anyway, just in case.
448     }
449
450     // Ensure that the input method daemon is running.
451     MaybeInitializeCandidateWindowController();
452     IBusDaemonController::GetInstance()->Start();
453   }
454
455   extra_input_method_instances_[id] =
456       static_cast<InputMethodEngineIBus*>(engine);
457 }
458
459 void InputMethodManagerImpl::RemoveInputMethodExtension(const std::string& id) {
460   if (!extension_ime_util::IsExtensionIME(id))
461     DVLOG(1) << id << " is not a valid extension input method ID.";
462
463   std::vector<std::string>::iterator i = std::find(
464       active_input_method_ids_.begin(), active_input_method_ids_.end(), id);
465   if (i != active_input_method_ids_.end())
466     active_input_method_ids_.erase(i);
467   extra_input_methods_.erase(id);
468
469   if (ContainsOnlyKeyboardLayout(active_input_method_ids_)) {
470     // Do NOT call ibus_controller_->Stop(); here to work around a crash issue
471     // at crosbug.com/27051.
472     // TODO(yusukes): We can safely call Stop(); here once crosbug.com/26443
473     // is implemented.
474   }
475
476   // If |current_input_method| is no longer in |active_input_method_ids_|,
477   // switch to the first one in |active_input_method_ids_|.
478   ChangeInputMethod(current_input_method_.id());
479
480   std::map<std::string, InputMethodEngineIBus*>::iterator ite =
481       extra_input_method_instances_.find(id);
482   if (ite == extra_input_method_instances_.end()) {
483     DVLOG(1) << "The engine instance of " << id << " has already gone.";
484   } else {
485     // Do NOT release the actual instance here. This class does not take an
486     // onwership of engine instance.
487     extra_input_method_instances_.erase(ite);
488   }
489 }
490
491 void InputMethodManagerImpl::GetInputMethodExtensions(
492     InputMethodDescriptors* result) {
493   // Build the extension input method descriptors from the extra input
494   // methods cache |extra_input_methods_|.
495   std::map<std::string, InputMethodDescriptor>::iterator iter;
496   for (iter = extra_input_methods_.begin(); iter != extra_input_methods_.end();
497        ++iter) {
498     if (extension_ime_util::IsExtensionIME(iter->first))
499       result->push_back(iter->second);
500   }
501 }
502
503 void InputMethodManagerImpl::SetEnabledExtensionImes(
504     std::vector<std::string>* ids) {
505   enabled_extension_imes_.clear();
506   enabled_extension_imes_.insert(enabled_extension_imes_.end(),
507                                  ids->begin(),
508                                  ids->end());
509
510   bool active_imes_changed = false;
511
512   for (std::map<std::string, InputMethodDescriptor>::iterator extra_iter =
513        extra_input_methods_.begin(); extra_iter != extra_input_methods_.end();
514        ++extra_iter) {
515     if (ComponentExtensionIMEManager::IsComponentExtensionIMEId(
516         extra_iter->first))
517       continue;  // Do not filter component extension.
518     std::vector<std::string>::iterator active_iter = std::find(
519         active_input_method_ids_.begin(), active_input_method_ids_.end(),
520         extra_iter->first);
521
522     bool active = active_iter != active_input_method_ids_.end();
523     bool enabled = Contains(enabled_extension_imes_, extra_iter->first);
524
525     if (active && !enabled)
526       active_input_method_ids_.erase(active_iter);
527
528     if (!active && enabled)
529       active_input_method_ids_.push_back(extra_iter->first);
530
531     if (active == !enabled)
532       active_imes_changed = true;
533   }
534
535   if (active_imes_changed) {
536     MaybeInitializeCandidateWindowController();
537     IBusDaemonController::GetInstance()->Start();
538
539     // If |current_input_method| is no longer in |active_input_method_ids_|,
540     // switch to the first one in |active_input_method_ids_|.
541     ChangeInputMethod(current_input_method_.id());
542   }
543 }
544
545 void InputMethodManagerImpl::SetInputMethodDefault() {
546   // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
547   // and US dvorak keyboard layouts.
548   if (g_browser_process && g_browser_process->local_state()) {
549     const std::string locale = g_browser_process->GetApplicationLocale();
550     // If the preferred keyboard for the login screen has been saved, use it.
551     PrefService* prefs = g_browser_process->local_state();
552     std::string initial_input_method_id =
553         prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
554     if (initial_input_method_id.empty()) {
555       // If kPreferredKeyboardLayout is not specified, use the hardware layout.
556       initial_input_method_id =
557           GetInputMethodUtil()->GetHardwareInputMethodId();
558     }
559     EnableLayouts(locale, initial_input_method_id);
560   }
561 }
562
563 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
564   // Sanity checks.
565   if (active_input_method_ids_.empty()) {
566     DVLOG(1) << "active input method is empty";
567     return false;
568   }
569   if (current_input_method_.id().empty()) {
570     DVLOG(1) << "current_input_method_ is unknown";
571     return false;
572   }
573
574   // Do not consume key event if there is only one input method is enabled.
575   // Ctrl+Space or Alt+Shift may be used by other application.
576   if (active_input_method_ids_.size() == 1)
577     return false;
578
579   // Find the next input method and switch to it.
580   SwitchToNextInputMethodInternal(active_input_method_ids_,
581                                   current_input_method_.id());
582   return true;
583 }
584
585 bool InputMethodManagerImpl::SwitchToPreviousInputMethod(
586     const ui::Accelerator& accelerator) {
587   // Sanity check.
588   if (active_input_method_ids_.empty()) {
589     DVLOG(1) << "active input method is empty";
590     return false;
591   }
592
593   // Do not consume key event if there is only one input method is enabled.
594   // Ctrl+Space or Alt+Shift may be used by other application.
595   if (active_input_method_ids_.size() == 1)
596     return false;
597
598   if (accelerator.type() == ui::ET_KEY_RELEASED)
599     return true;
600
601   if (previous_input_method_.id().empty() ||
602       previous_input_method_.id() == current_input_method_.id()) {
603     return SwitchToNextInputMethod();
604   }
605
606   std::vector<std::string>::const_iterator iter =
607       std::find(active_input_method_ids_.begin(),
608                 active_input_method_ids_.end(),
609                 previous_input_method_.id());
610   if (iter == active_input_method_ids_.end()) {
611     // previous_input_method_ is not supported.
612     return SwitchToNextInputMethod();
613   }
614   ChangeInputMethodInternal(*iter, true);
615   return true;
616 }
617
618 bool InputMethodManagerImpl::SwitchInputMethod(
619     const ui::Accelerator& accelerator) {
620   // Sanity check.
621   if (active_input_method_ids_.empty()) {
622     DVLOG(1) << "active input method is empty";
623     return false;
624   }
625
626   // Get the list of input method ids for the |accelerator|. For example, get
627   // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
628   std::vector<std::string> input_method_ids_to_switch;
629   switch (accelerator.key_code()) {
630     case ui::VKEY_CONVERT:  // Henkan key on JP106 keyboard
631       input_method_ids_to_switch.push_back(nacl_mozc_jp_id);
632       break;
633     case ui::VKEY_NONCONVERT:  // Muhenkan key on JP106 keyboard
634       input_method_ids_to_switch.push_back("xkb:jp::jpn");
635       break;
636     case ui::VKEY_DBE_SBCSCHAR:  // ZenkakuHankaku key on JP106 keyboard
637     case ui::VKEY_DBE_DBCSCHAR:
638       input_method_ids_to_switch.push_back(nacl_mozc_jp_id);
639       input_method_ids_to_switch.push_back("xkb:jp::jpn");
640       break;
641     default:
642       NOTREACHED();
643       break;
644   }
645   if (input_method_ids_to_switch.empty()) {
646     DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
647     return false;
648   }
649
650   // Obtain the intersection of input_method_ids_to_switch and
651   // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
652   // preserved.
653   std::vector<std::string> ids;
654   for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
655     const std::string& id = input_method_ids_to_switch[i];
656     if (Contains(active_input_method_ids_, id))
657       ids.push_back(id);
658   }
659   if (ids.empty()) {
660     // No input method for the accelerator is active. For example, we should
661     // just ignore VKEY_HANGUL when mozc-hangul is not active.
662     return false;
663   }
664
665   SwitchToNextInputMethodInternal(ids, current_input_method_.id());
666   return true;  // consume the accelerator.
667 }
668
669 void InputMethodManagerImpl::SwitchToNextInputMethodInternal(
670     const std::vector<std::string>& input_method_ids,
671     const std::string& current_input_method_id) {
672   std::vector<std::string>::const_iterator iter =
673       std::find(input_method_ids.begin(),
674                 input_method_ids.end(),
675                 current_input_method_id);
676   if (iter != input_method_ids.end())
677     ++iter;
678   if (iter == input_method_ids.end())
679     iter = input_method_ids.begin();
680   ChangeInputMethodInternal(*iter, true);
681 }
682
683 InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const {
684   if (current_input_method_.id().empty())
685     return InputMethodUtil::GetFallbackInputMethodDescriptor();
686   return current_input_method_;
687 }
688
689 InputMethodPropertyList
690 InputMethodManagerImpl::GetCurrentInputMethodProperties() const {
691   // This check is necessary since an IME property (e.g. for Pinyin) might be
692   // sent from ibus-daemon AFTER the current input method is switched to XKB.
693   if (InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id()))
694     return InputMethodPropertyList();
695   return ibus_controller_->GetCurrentProperties();
696 }
697
698 XKeyboard* InputMethodManagerImpl::GetXKeyboard() {
699   return xkeyboard_.get();
700 }
701
702 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
703   return &util_;
704 }
705
706 ComponentExtensionIMEManager*
707     InputMethodManagerImpl::GetComponentExtensionIMEManager() {
708   DCHECK(thread_checker_.CalledOnValidThread());
709   return component_extension_ime_manager_.get();
710 }
711
712 void InputMethodManagerImpl::OnConnected() {
713   for (std::map<std::string, InputMethodEngineIBus*>::iterator ite =
714           extra_input_method_instances_.begin();
715        ite != extra_input_method_instances_.end();
716        ite++) {
717     if (Contains(enabled_extension_imes_, ite->first) ||
718         (component_extension_ime_manager_->IsInitialized() &&
719          component_extension_ime_manager_->IsWhitelisted(ite->first))) {
720       ite->second->OnConnected();
721     }
722   }
723
724   if (!pending_input_method_.empty())
725     ChangeInputMethodInternal(pending_input_method_, false);
726 }
727
728 void InputMethodManagerImpl::OnDisconnected() {
729   for (std::map<std::string, InputMethodEngineIBus*>::iterator ite =
730           extra_input_method_instances_.begin();
731        ite != extra_input_method_instances_.end();
732        ite++) {
733     if (Contains(enabled_extension_imes_, ite->first))
734       ite->second->OnDisconnected();
735   }
736 }
737
738 void InputMethodManagerImpl::InitializeComponentExtension() {
739   ComponentExtensionIMEManagerImpl* impl =
740       new ComponentExtensionIMEManagerImpl();
741   scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate(impl);
742   impl->InitializeAsync(base::Bind(
743                        &InputMethodManagerImpl::OnComponentExtensionInitialized,
744                        weak_ptr_factory_.GetWeakPtr(),
745                        base::Passed(&delegate)));
746 }
747
748 void InputMethodManagerImpl::Init(base::SequencedTaskRunner* ui_task_runner) {
749   DCHECK(!ibus_controller_.get());
750   DCHECK(thread_checker_.CalledOnValidThread());
751
752   ibus_controller_.reset(IBusController::Create());
753   xkeyboard_.reset(XKeyboard::Create());
754   ibus_controller_->AddObserver(this);
755
756   // We can't call impl->Initialize here, because file thread is not available
757   // at this moment.
758   ui_task_runner->PostTask(
759       FROM_HERE,
760       base::Bind(&InputMethodManagerImpl::InitializeComponentExtension,
761                  weak_ptr_factory_.GetWeakPtr()));
762 }
763
764 void InputMethodManagerImpl::SetIBusControllerForTesting(
765     IBusController* ibus_controller) {
766   ibus_controller_.reset(ibus_controller);
767   ibus_controller_->AddObserver(this);
768 }
769
770 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
771     CandidateWindowController* candidate_window_controller) {
772   candidate_window_controller_.reset(candidate_window_controller);
773   candidate_window_controller_->Init();
774   candidate_window_controller_->AddObserver(this);
775 }
776
777 void InputMethodManagerImpl::SetXKeyboardForTesting(XKeyboard* xkeyboard) {
778   xkeyboard_.reset(xkeyboard);
779 }
780
781 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
782     scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
783   OnComponentExtensionInitialized(delegate.Pass());
784 }
785
786 void InputMethodManagerImpl::PropertyChanged() {
787   FOR_EACH_OBSERVER(InputMethodManager::Observer,
788                     observers_,
789                     InputMethodPropertyChanged(this));
790 }
791
792 void InputMethodManagerImpl::CandidateWindowOpened() {
793   FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
794                     candidate_window_observers_,
795                     CandidateWindowOpened(this));
796 }
797
798 void InputMethodManagerImpl::CandidateWindowClosed() {
799   FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
800                     candidate_window_observers_,
801                     CandidateWindowClosed(this));
802 }
803
804 void InputMethodManagerImpl::OnScreenLocked() {
805   saved_previous_input_method_ = previous_input_method_;
806   saved_current_input_method_ = current_input_method_;
807   saved_active_input_method_ids_ = active_input_method_ids_;
808
809   const std::string hardware_keyboard_id = util_.GetHardwareInputMethodId();
810   // We'll add the hardware keyboard if it's not included in
811   // |active_input_method_list| so that the user can always use the hardware
812   // keyboard on the screen locker.
813   bool should_add_hardware_keyboard = true;
814
815   active_input_method_ids_.clear();
816   for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) {
817     const std::string& input_method_id = saved_active_input_method_ids_[i];
818     // Skip if it's not a keyboard layout. Drop input methods including
819     // extension ones.
820     if (!IsLoginKeyboard(input_method_id))
821       continue;
822     active_input_method_ids_.push_back(input_method_id);
823     if (input_method_id == hardware_keyboard_id)
824       should_add_hardware_keyboard = false;
825   }
826   if (should_add_hardware_keyboard)
827     active_input_method_ids_.push_back(hardware_keyboard_id);
828
829   ChangeInputMethod(current_input_method_.id());
830 }
831
832 void InputMethodManagerImpl::OnScreenUnlocked() {
833   previous_input_method_ = saved_previous_input_method_;
834   current_input_method_ = saved_current_input_method_;
835   active_input_method_ids_ = saved_active_input_method_ids_;
836
837   ChangeInputMethod(current_input_method_.id());
838 }
839
840 bool InputMethodManagerImpl::InputMethodIsActivated(
841     const std::string& input_method_id) {
842   return Contains(active_input_method_ids_, input_method_id);
843 }
844
845 bool InputMethodManagerImpl::ContainsOnlyKeyboardLayout(
846     const std::vector<std::string>& value) {
847   for (size_t i = 0; i < value.size(); ++i) {
848     if (!InputMethodUtil::IsKeyboardLayout(value[i]))
849       return false;
850   }
851   return true;
852 }
853
854 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
855   if (candidate_window_controller_.get())
856     return;
857
858   candidate_window_controller_.reset(
859       CandidateWindowController::CreateCandidateWindowController());
860   if (candidate_window_controller_->Init())
861     candidate_window_controller_->AddObserver(this);
862   else
863     DVLOG(1) << "Failed to initialize the candidate window controller";
864 }
865
866 bool InputMethodManagerImpl::IsIBusConnectionAlive() {
867   return DBusThreadManager::Get() && DBusThreadManager::Get()->GetIBusClient();
868 }
869
870 }  // namespace input_method
871 }  // namespace chromeos