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