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 <sstream>
10
11#include "ash/ime/input_method_menu_item.h"
12#include "ash/ime/input_method_menu_manager.h"
13#include "base/basictypes.h"
14#include "base/bind.h"
15#include "base/location.h"
16#include "base/memory/scoped_ptr.h"
17#include "base/metrics/histogram.h"
18#include "base/metrics/sparse_histogram.h"
19#include "base/prefs/pref_service.h"
20#include "base/strings/string_split.h"
21#include "base/strings/string_util.h"
22#include "base/strings/stringprintf.h"
23#include "base/sys_info.h"
24#include "chrome/browser/browser_process.h"
25#include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
26#include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
27#include "chrome/browser/chromeos/input_method/input_method_engine.h"
28#include "chrome/browser/chromeos/language_preferences.h"
29#include "chrome/browser/chromeos/login/session/user_session_manager.h"
30#include "chrome/browser/chromeos/profiles/profile_helper.h"
31#include "chrome/browser/profiles/profile_manager.h"
32#include "chrome/common/pref_names.h"
33#include "chromeos/ime/component_extension_ime_manager.h"
34#include "chromeos/ime/extension_ime_util.h"
35#include "chromeos/ime/fake_ime_keyboard.h"
36#include "chromeos/ime/ime_keyboard.h"
37#include "chromeos/ime/input_method_delegate.h"
38#include "components/user_manager/user_manager.h"
39#include "third_party/icu/source/common/unicode/uloc.h"
40#include "ui/base/accelerators/accelerator.h"
41#include "ui/keyboard/keyboard_controller.h"
42#include "ui/keyboard/keyboard_util.h"
43
44namespace chromeos {
45namespace input_method {
46
47namespace {
48
49bool Contains(const std::vector<std::string>& container,
50              const std::string& value) {
51  return std::find(container.begin(), container.end(), value) !=
52      container.end();
53}
54
55enum InputMethodCategory {
56  INPUT_METHOD_CATEGORY_UNKNOWN = 0,
57  INPUT_METHOD_CATEGORY_XKB,  // XKB input methods
58  INPUT_METHOD_CATEGORY_ZH,   // Chinese input methods
59  INPUT_METHOD_CATEGORY_JA,   // Japanese input methods
60  INPUT_METHOD_CATEGORY_KO,   // Korean input methods
61  INPUT_METHOD_CATEGORY_M17N, // Multilingualization input methods
62  INPUT_METHOD_CATEGORY_T13N, // Transliteration input methods
63  INPUT_METHOD_CATEGORY_MAX
64};
65
66InputMethodCategory GetInputMethodCategory(const std::string& input_method_id,
67                                           char* first_char = NULL) {
68  const std::string component_id =
69      extension_ime_util::GetComponentIDByInputMethodID(input_method_id);
70  InputMethodCategory category = INPUT_METHOD_CATEGORY_UNKNOWN;
71  char ch = 0;
72  if (StartsWithASCII(component_id, "xkb:", true)) {
73    ch = component_id[4];
74    category = INPUT_METHOD_CATEGORY_XKB;
75  } else if (StartsWithASCII(component_id, "zh-", true)) {
76    size_t pos = component_id.find("-t-i0-");
77    if (pos > 0)
78      pos += 6;
79    ch = component_id[pos];
80    category = INPUT_METHOD_CATEGORY_ZH;
81  } else if (StartsWithASCII(component_id, "nacl_mozc_", true)) {
82    ch = component_id[10];
83    category = INPUT_METHOD_CATEGORY_JA;
84  } else if (StartsWithASCII(component_id, "hangul_", true)) {
85    ch = component_id[7];
86    category = INPUT_METHOD_CATEGORY_KO;
87  } else if (StartsWithASCII(component_id, "vkd_", true)) {
88    ch = component_id[4];
89    category = INPUT_METHOD_CATEGORY_M17N;
90  } else if (component_id.find("-t-i0-") > 0) {
91    ch = component_id[0];
92    category = INPUT_METHOD_CATEGORY_T13N;
93  }
94
95  if (first_char)
96    *first_char = ch;
97  return category;
98}
99
100}  // namespace
101
102// ------------------------ InputMethodManagerImpl::StateImpl
103
104InputMethodManagerImpl::StateImpl::StateImpl(InputMethodManagerImpl* manager,
105                                             Profile* profile)
106    : profile(profile), manager_(manager) {
107}
108
109InputMethodManagerImpl::StateImpl::~StateImpl() {
110}
111
112void InputMethodManagerImpl::StateImpl::InitFrom(const StateImpl& other) {
113  previous_input_method = other.previous_input_method;
114  current_input_method = other.current_input_method;
115
116  active_input_method_ids = other.active_input_method_ids;
117
118  pending_input_method_id = other.pending_input_method_id;
119
120  enabled_extension_imes = other.enabled_extension_imes;
121  extra_input_methods = other.extra_input_methods;
122}
123
124bool InputMethodManagerImpl::StateImpl::IsActive() const {
125  return manager_->state_.get() == this;
126}
127
128std::string InputMethodManagerImpl::StateImpl::Dump() const {
129  std::ostringstream os;
130
131  os << "################# "
132     << (profile ? profile->GetProfileName() : std::string("NULL"))
133     << " #################\n";
134
135  os << "previous_input_method: '"
136     << previous_input_method.GetPreferredKeyboardLayout() << "'\n";
137  os << "current_input_method: '"
138     << current_input_method.GetPreferredKeyboardLayout() << "'\n";
139  os << "active_input_method_ids (size=" << active_input_method_ids.size()
140     << "):";
141  for (size_t i = 0; i < active_input_method_ids.size(); ++i) {
142    os << " '" << active_input_method_ids[i] << "',";
143  }
144  os << "\n";
145  os << "enabled_extension_imes (size=" << enabled_extension_imes.size()
146     << "):";
147  for (size_t i = 0; i < enabled_extension_imes.size(); ++i) {
148    os << " '" << enabled_extension_imes[i] << "'\n";
149  }
150  os << "\n";
151  os << "extra_input_methods (size=" << extra_input_methods.size() << "):";
152  for (std::map<std::string, InputMethodDescriptor>::const_iterator it =
153           extra_input_methods.begin();
154       it != extra_input_methods.end();
155       ++it) {
156    os << " '" << it->first << "' => '" << it->second.id() << "',\n";
157  }
158  os << "pending_input_method_id: '" << pending_input_method_id << "'\n";
159
160  return os.str();
161}
162
163scoped_refptr<InputMethodManager::State>
164InputMethodManagerImpl::StateImpl::Clone() const {
165  scoped_refptr<StateImpl> new_state(new StateImpl(this->manager_, profile));
166  new_state->InitFrom(*this);
167  return scoped_refptr<InputMethodManager::State>(new_state.get());
168}
169
170scoped_ptr<InputMethodDescriptors>
171InputMethodManagerImpl::StateImpl::GetActiveInputMethods() const {
172  scoped_ptr<InputMethodDescriptors> result(new InputMethodDescriptors);
173  // Build the active input method descriptors from the active input
174  // methods cache |active_input_method_ids|.
175  for (size_t i = 0; i < active_input_method_ids.size(); ++i) {
176    const std::string& input_method_id = active_input_method_ids[i];
177    const InputMethodDescriptor* descriptor =
178        manager_->util_.GetInputMethodDescriptorFromId(input_method_id);
179    if (descriptor) {
180      result->push_back(*descriptor);
181    } else {
182      std::map<std::string, InputMethodDescriptor>::const_iterator ix =
183          extra_input_methods.find(input_method_id);
184      if (ix != extra_input_methods.end())
185        result->push_back(ix->second);
186      else
187        DVLOG(1) << "Descriptor is not found for: " << input_method_id;
188    }
189  }
190  if (result->empty()) {
191    // Initially |active_input_method_ids| is empty. browser_tests might take
192    // this path.
193    result->push_back(
194        InputMethodUtil::GetFallbackInputMethodDescriptor());
195  }
196  return result.Pass();
197}
198
199const std::vector<std::string>&
200InputMethodManagerImpl::StateImpl::GetActiveInputMethodIds() const {
201  return active_input_method_ids;
202}
203
204size_t InputMethodManagerImpl::StateImpl::GetNumActiveInputMethods() const {
205  return active_input_method_ids.size();
206}
207
208const InputMethodDescriptor*
209InputMethodManagerImpl::StateImpl::GetInputMethodFromId(
210    const std::string& input_method_id) const {
211  const InputMethodDescriptor* ime =
212      manager_->util_.GetInputMethodDescriptorFromId(input_method_id);
213  if (!ime) {
214    std::map<std::string, InputMethodDescriptor>::const_iterator ix =
215        extra_input_methods.find(input_method_id);
216    if (ix != extra_input_methods.end())
217      ime = &ix->second;
218  }
219  return ime;
220}
221
222void InputMethodManagerImpl::StateImpl::EnableLoginLayouts(
223    const std::string& language_code,
224    const std::vector<std::string>& initial_layouts) {
225  if (manager_->ui_session_ == STATE_TERMINATING)
226    return;
227
228  // First, hardware keyboard layout should be shown.
229  std::vector<std::string> candidates =
230      manager_->util_.GetHardwareLoginInputMethodIds();
231
232  // Second, locale based input method should be shown.
233  // Add input methods associated with the language.
234  std::vector<std::string> layouts_from_locale;
235  manager_->util_.GetInputMethodIdsFromLanguageCode(
236      language_code, kKeyboardLayoutsOnly, &layouts_from_locale);
237  candidates.insert(candidates.end(), layouts_from_locale.begin(),
238                    layouts_from_locale.end());
239
240  std::vector<std::string> layouts;
241  // First, add the initial input method ID, if it's requested, to
242  // layouts, so it appears first on the list of active input
243  // methods at the input language status menu.
244  for (size_t i = 0; i < initial_layouts.size(); ++i) {
245    if (manager_->util_.IsValidInputMethodId(initial_layouts[i])) {
246      if (manager_->IsLoginKeyboard(initial_layouts[i])) {
247        layouts.push_back(initial_layouts[i]);
248      } else {
249        DVLOG(1)
250            << "EnableLoginLayouts: ignoring non-login initial keyboard layout:"
251            << initial_layouts[i];
252      }
253    } else if (!initial_layouts[i].empty()) {
254      DVLOG(1) << "EnableLoginLayouts: ignoring non-keyboard or invalid ID: "
255               << initial_layouts[i];
256    }
257  }
258
259  // Add candidates to layouts, while skipping duplicates.
260  for (size_t i = 0; i < candidates.size(); ++i) {
261    const std::string& candidate = candidates[i];
262    // Not efficient, but should be fine, as the two vectors are very
263    // short (2-5 items).
264    if (!Contains(layouts, candidate) && manager_->IsLoginKeyboard(candidate))
265      layouts.push_back(candidate);
266  }
267
268  manager_->MigrateInputMethods(&layouts);
269  active_input_method_ids.swap(layouts);
270
271  if (IsActive()) {
272    // Initialize candidate window controller and widgets such as
273    // candidate window, infolist and mode indicator.  Note, mode
274    // indicator is used by only keyboard layout input methods.
275    if (active_input_method_ids.size() > 1)
276      manager_->MaybeInitializeCandidateWindowController();
277
278    // you can pass empty |initial_layout|.
279    ChangeInputMethod(initial_layouts.empty()
280                          ? std::string()
281                          : extension_ime_util::GetInputMethodIDByEngineID(
282                                initial_layouts[0]),
283                      false);
284  }
285}
286
287void InputMethodManagerImpl::StateImpl::EnableLockScreenLayouts() {
288  std::set<std::string> added_ids;
289
290  const std::vector<std::string>& hardware_keyboard_ids =
291      manager_->util_.GetHardwareLoginInputMethodIds();
292
293  std::vector<std::string> new_active_input_method_ids;
294  for (size_t i = 0; i < active_input_method_ids.size(); ++i) {
295    const std::string& input_method_id = active_input_method_ids[i];
296    // Skip if it's not a keyboard layout. Drop input methods including
297    // extension ones.
298    if (!manager_->IsLoginKeyboard(input_method_id) ||
299        added_ids.count(input_method_id)) {
300      continue;
301    }
302    new_active_input_method_ids.push_back(input_method_id);
303    added_ids.insert(input_method_id);
304  }
305
306  // We'll add the hardware keyboard if it's not included in
307  // |active_input_method_ids| so that the user can always use the hardware
308  // keyboard on the screen locker.
309  for (size_t i = 0; i < hardware_keyboard_ids.size(); ++i) {
310    if (added_ids.count(hardware_keyboard_ids[i]))
311      continue;
312    new_active_input_method_ids.push_back(hardware_keyboard_ids[i]);
313    added_ids.insert(hardware_keyboard_ids[i]);
314  }
315
316  active_input_method_ids.swap(new_active_input_method_ids);
317
318  // Re-check current_input_method.
319  ChangeInputMethod(current_input_method.id(), false);
320}
321
322// Adds new input method to given list.
323bool InputMethodManagerImpl::StateImpl::EnableInputMethodImpl(
324    const std::string& input_method_id,
325    std::vector<std::string>* new_active_input_method_ids) const {
326  DCHECK(new_active_input_method_ids);
327  if (!manager_->util_.IsValidInputMethodId(input_method_id)) {
328    DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id;
329    return false;
330  }
331
332  if (!Contains(*new_active_input_method_ids, input_method_id))
333    new_active_input_method_ids->push_back(input_method_id);
334
335  return true;
336}
337
338bool InputMethodManagerImpl::StateImpl::EnableInputMethod(
339    const std::string& input_method_id) {
340  if (!EnableInputMethodImpl(input_method_id, &active_input_method_ids))
341    return false;
342
343  manager_->ReconfigureIMFramework(this);
344  return true;
345}
346
347bool InputMethodManagerImpl::StateImpl::ReplaceEnabledInputMethods(
348    const std::vector<std::string>& new_active_input_method_ids) {
349  if (manager_->ui_session_ == STATE_TERMINATING)
350    return false;
351
352  // Filter unknown or obsolete IDs.
353  std::vector<std::string> new_active_input_method_ids_filtered;
354
355  for (size_t i = 0; i < new_active_input_method_ids.size(); ++i)
356    EnableInputMethodImpl(new_active_input_method_ids[i],
357                          &new_active_input_method_ids_filtered);
358
359  if (new_active_input_method_ids_filtered.empty()) {
360    DVLOG(1) << "ReplaceEnabledInputMethods: No valid input method ID";
361    return false;
362  }
363
364  // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
365  // keep relative order of the extension input method IDs.
366  for (size_t i = 0; i < active_input_method_ids.size(); ++i) {
367    const std::string& input_method_id = active_input_method_ids[i];
368    if (extension_ime_util::IsExtensionIME(input_method_id))
369      new_active_input_method_ids_filtered.push_back(input_method_id);
370  }
371  active_input_method_ids.swap(new_active_input_method_ids_filtered);
372  manager_->MigrateInputMethods(&active_input_method_ids);
373
374  manager_->ReconfigureIMFramework(this);
375
376  // If |current_input_method| is no longer in |active_input_method_ids|,
377  // ChangeInputMethod() picks the first one in |active_input_method_ids|.
378  ChangeInputMethod(current_input_method.id(), false);
379
380  // Record histogram for active input method count.
381  UMA_HISTOGRAM_COUNTS("InputMethod.ActiveCount",
382                       active_input_method_ids.size());
383
384  return true;
385}
386
387void InputMethodManagerImpl::StateImpl::ChangeInputMethod(
388    const std::string& input_method_id,
389    bool show_message) {
390  if (manager_->ui_session_ == STATE_TERMINATING)
391    return;
392
393  bool notify_menu = false;
394  // For 3rd party IME, when the user just logged in, SetEnabledExtensionImes
395  // happens after activating the 3rd party IME.
396  // So here to record the 3rd party IME to be activated, and activate it
397  // when SetEnabledExtensionImes happens later.
398  if (MethodAwaitsExtensionLoad(input_method_id))
399    pending_input_method_id = input_method_id;
400
401  // Always lookup input method, even if it is the same as
402  // |current_input_method| because If it is no longer in
403  // |active_input_method_ids|, pick the first one in
404  // |active_input_method_ids|.
405  const InputMethodDescriptor* descriptor =
406      manager_->LookupInputMethod(input_method_id, this);
407  if (descriptor->id() != current_input_method.id()) {
408    previous_input_method = current_input_method;
409    current_input_method = *descriptor;
410    notify_menu = true;
411  }
412
413  // Always change input method even if it is the same.
414  // TODO(komatsu): Revisit if this is neccessary.
415  if (IsActive())
416    manager_->ChangeInputMethodInternal(*descriptor, show_message, notify_menu);
417  manager_->RecordInputMethodUsage(current_input_method.id());
418}
419
420bool InputMethodManagerImpl::StateImpl::MethodAwaitsExtensionLoad(
421    const std::string& input_method_id) const {
422  // For 3rd party IME, when the user just logged in, SetEnabledExtensionImes
423  // happens after activating the 3rd party IME.
424  // So here to record the 3rd party IME to be activated, and activate it
425  // when SetEnabledExtensionImes happens later.
426  return !InputMethodIsActivated(input_method_id) &&
427         extension_ime_util::IsExtensionIME(input_method_id);
428}
429
430void InputMethodManagerImpl::StateImpl::AddInputMethodExtension(
431    const std::string& extension_id,
432    const InputMethodDescriptors& descriptors,
433    InputMethodEngineInterface* engine) {
434  if (manager_->ui_session_ == STATE_TERMINATING)
435    return;
436
437  DCHECK(engine);
438
439  manager_->engine_map_[extension_id] = engine;
440
441  bool contain = false;
442  for (size_t i = 0; i < descriptors.size(); i++) {
443    const InputMethodDescriptor& descriptor = descriptors[i];
444    const std::string& id = descriptor.id();
445    extra_input_methods[id] = descriptor;
446    if (Contains(enabled_extension_imes, id)) {
447      if (!Contains(active_input_method_ids, id)) {
448        active_input_method_ids.push_back(id);
449      } else {
450        DVLOG(1) << "AddInputMethodExtension: already added: " << id << ", "
451                 << descriptor.name();
452      }
453      contain = true;
454    }
455  }
456
457  if (IsActive()) {
458    if (extension_id == extension_ime_util::GetExtensionIDFromInputMethodID(
459                            current_input_method.id())) {
460      IMEBridge::Get()->SetCurrentEngineHandler(engine);
461      engine->Enable(extension_ime_util::GetComponentIDByInputMethodID(
462          current_input_method.id()));
463    }
464
465    // Ensure that the input method daemon is running.
466    if (contain)
467      manager_->MaybeInitializeCandidateWindowController();
468  }
469}
470
471void InputMethodManagerImpl::StateImpl::RemoveInputMethodExtension(
472    const std::string& extension_id) {
473  // Remove the active input methods with |extension_id|.
474  std::vector<std::string> new_active_input_method_ids;
475  for (size_t i = 0; i < active_input_method_ids.size(); ++i) {
476    if (extension_id != extension_ime_util::GetExtensionIDFromInputMethodID(
477                            active_input_method_ids[i]))
478      new_active_input_method_ids.push_back(active_input_method_ids[i]);
479  }
480  active_input_method_ids.swap(new_active_input_method_ids);
481
482  // Remove the extra input methods with |extension_id|.
483  std::map<std::string, InputMethodDescriptor> new_extra_input_methods;
484  for (std::map<std::string, InputMethodDescriptor>::iterator i =
485           extra_input_methods.begin();
486       i != extra_input_methods.end();
487       ++i) {
488    if (extension_id !=
489        extension_ime_util::GetExtensionIDFromInputMethodID(i->first))
490      new_extra_input_methods[i->first] = i->second;
491  }
492  extra_input_methods.swap(new_extra_input_methods);
493
494  if (IsActive()) {
495    if (IMEBridge::Get()->GetCurrentEngineHandler() ==
496        manager_->engine_map_[extension_id]) {
497      IMEBridge::Get()->SetCurrentEngineHandler(NULL);
498    }
499    manager_->engine_map_.erase(extension_id);
500  }
501
502  // If |current_input_method| is no longer in |active_input_method_ids|,
503  // switch to the first one in |active_input_method_ids|.
504  ChangeInputMethod(current_input_method.id(), false);
505}
506
507void InputMethodManagerImpl::StateImpl::GetInputMethodExtensions(
508    InputMethodDescriptors* result) {
509  // Build the extension input method descriptors from the extra input
510  // methods cache |extra_input_methods|.
511  std::map<std::string, InputMethodDescriptor>::iterator iter;
512  for (iter = extra_input_methods.begin(); iter != extra_input_methods.end();
513       ++iter) {
514    if (extension_ime_util::IsExtensionIME(iter->first))
515      result->push_back(iter->second);
516  }
517}
518
519void InputMethodManagerImpl::StateImpl::SetEnabledExtensionImes(
520    std::vector<std::string>* ids) {
521  enabled_extension_imes.clear();
522  enabled_extension_imes.insert(
523      enabled_extension_imes.end(), ids->begin(), ids->end());
524  bool active_imes_changed = false;
525  bool switch_to_pending = false;
526
527  for (std::map<std::string, InputMethodDescriptor>::iterator extra_iter =
528           extra_input_methods.begin();
529       extra_iter != extra_input_methods.end();
530       ++extra_iter) {
531    if (extension_ime_util::IsComponentExtensionIME(extra_iter->first))
532      continue;  // Do not filter component extension.
533
534    if (pending_input_method_id == extra_iter->first)
535      switch_to_pending = true;
536
537    std::vector<std::string>::iterator active_iter =
538        std::find(active_input_method_ids.begin(),
539                  active_input_method_ids.end(),
540                  extra_iter->first);
541
542    bool active = active_iter != active_input_method_ids.end();
543    bool enabled = Contains(enabled_extension_imes, extra_iter->first);
544
545    if (active && !enabled)
546      active_input_method_ids.erase(active_iter);
547
548    if (!active && enabled)
549      active_input_method_ids.push_back(extra_iter->first);
550
551    if (active == !enabled)
552      active_imes_changed = true;
553  }
554
555  if (IsActive() && active_imes_changed) {
556    manager_->MaybeInitializeCandidateWindowController();
557
558    if (switch_to_pending) {
559      ChangeInputMethod(pending_input_method_id, false);
560      pending_input_method_id.clear();
561    } else {
562      // If |current_input_method| is no longer in |active_input_method_ids_|,
563      // switch to the first one in |active_input_method_ids_|.
564      ChangeInputMethod(current_input_method.id(), false);
565    }
566  }
567}
568
569void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefaultFromVPD(
570    const std::string& locale,
571    const std::string& oem_layout) {
572  std::string layout;
573  if (!oem_layout.empty()) {
574    // If the OEM layout information is provided, use it.
575    layout = oem_layout;
576  } else {
577    // Otherwise, determine the hardware keyboard from the locale.
578    std::vector<std::string> input_method_ids;
579    if (manager_->util_.GetInputMethodIdsFromLanguageCode(
580            locale,
581            chromeos::input_method::kKeyboardLayoutsOnly,
582            &input_method_ids)) {
583      // The output list |input_method_ids| is sorted by popularity, hence
584      // input_method_ids[0] now contains the most popular keyboard layout
585      // for the given locale.
586      DCHECK_GE(input_method_ids.size(), 1U);
587      layout = input_method_ids[0];
588    }
589  }
590
591  if (layout.empty())
592    return;
593
594  std::vector<std::string> layouts;
595  base::SplitString(layout, ',', &layouts);
596  manager_->MigrateInputMethods(&layouts);
597
598  PrefService* prefs = g_browser_process->local_state();
599  prefs->SetString(prefs::kHardwareKeyboardLayout, JoinString(layouts, ","));
600
601  // This asks the file thread to save the prefs (i.e. doesn't block).
602  // The latest values of Local State reside in memory so we can safely
603  // get the value of kHardwareKeyboardLayout even if the data is not
604  // yet saved to disk.
605  prefs->CommitPendingWrite();
606
607  manager_->util_.UpdateHardwareLayoutCache();
608
609  EnableLoginLayouts(locale, layouts);
610  manager_->LoadNecessaryComponentExtensions(this);
611}
612
613void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefault() {
614  // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
615  // and US dvorak keyboard layouts.
616  if (g_browser_process && g_browser_process->local_state()) {
617    const std::string locale = g_browser_process->GetApplicationLocale();
618    // If the preferred keyboard for the login screen has been saved, use it.
619    PrefService* prefs = g_browser_process->local_state();
620    std::string initial_input_method_id =
621        prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
622    std::vector<std::string> input_methods_to_be_enabled;
623    if (initial_input_method_id.empty()) {
624      // If kPreferredKeyboardLayout is not specified, use the hardware layout.
625      input_methods_to_be_enabled =
626          manager_->util_.GetHardwareLoginInputMethodIds();
627    } else {
628      input_methods_to_be_enabled.push_back(initial_input_method_id);
629    }
630    EnableLoginLayouts(locale, input_methods_to_be_enabled);
631    manager_->LoadNecessaryComponentExtensions(this);
632  }
633}
634
635bool InputMethodManagerImpl::StateImpl::SwitchToNextInputMethod() {
636  // Sanity checks.
637  if (active_input_method_ids.empty()) {
638    DVLOG(1) << "active input method is empty";
639    return false;
640  }
641
642  if (current_input_method.id().empty()) {
643    DVLOG(1) << "current_input_method is unknown";
644    return false;
645  }
646
647  // Do not consume key event if there is only one input method is enabled.
648  // Ctrl+Space or Alt+Shift may be used by other application.
649  if (active_input_method_ids.size() == 1)
650    return false;
651
652  // Find the next input method and switch to it.
653  SwitchToNextInputMethodInternal(active_input_method_ids,
654                                  current_input_method.id());
655  return true;
656}
657
658bool InputMethodManagerImpl::StateImpl::SwitchToPreviousInputMethod(
659    const ui::Accelerator& accelerator) {
660  // Sanity check.
661  if (active_input_method_ids.empty()) {
662    DVLOG(1) << "active input method is empty";
663    return false;
664  }
665
666  // Do not consume key event if there is only one input method is enabled.
667  // Ctrl+Space or Alt+Shift may be used by other application.
668  if (active_input_method_ids.size() == 1)
669    return false;
670
671  if (accelerator.type() == ui::ET_KEY_RELEASED)
672    return true;
673
674  if (previous_input_method.id().empty() ||
675      previous_input_method.id() == current_input_method.id()) {
676    return SwitchToNextInputMethod();
677  }
678
679  std::vector<std::string>::const_iterator iter =
680      std::find(active_input_method_ids.begin(),
681                active_input_method_ids.end(),
682                previous_input_method.id());
683  if (iter == active_input_method_ids.end()) {
684    // previous_input_method is not supported.
685    return SwitchToNextInputMethod();
686  }
687  ChangeInputMethod(*iter, true);
688
689  return true;
690}
691
692bool InputMethodManagerImpl::StateImpl::SwitchInputMethod(
693    const ui::Accelerator& accelerator) {
694  // Sanity check.
695  if (active_input_method_ids.empty()) {
696    DVLOG(1) << "active input method is empty";
697    return false;
698  }
699
700  // Get the list of input method ids for the |accelerator|. For example, get
701  // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
702  std::vector<std::string> input_method_ids_to_switch;
703  switch (accelerator.key_code()) {
704    case ui::VKEY_CONVERT:  // Henkan key on JP106 keyboard
705      input_method_ids_to_switch.push_back(
706          extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
707      break;
708    case ui::VKEY_NONCONVERT:  // Muhenkan key on JP106 keyboard
709      input_method_ids_to_switch.push_back(
710          extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
711      break;
712    case ui::VKEY_DBE_SBCSCHAR:  // ZenkakuHankaku key on JP106 keyboard
713    case ui::VKEY_DBE_DBCSCHAR:
714      input_method_ids_to_switch.push_back(
715          extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
716      input_method_ids_to_switch.push_back(
717          extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
718      break;
719    default:
720      NOTREACHED();
721      break;
722  }
723  if (input_method_ids_to_switch.empty()) {
724    DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
725    return false;
726  }
727
728  // Obtain the intersection of input_method_ids_to_switch and
729  // active_input_method_ids. The order of IDs in active_input_method_ids is
730  // preserved.
731  std::vector<std::string> ids;
732  for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
733    const std::string& id = input_method_ids_to_switch[i];
734    if (Contains(active_input_method_ids, id))
735      ids.push_back(id);
736  }
737  if (ids.empty()) {
738    // No input method for the accelerator is active. For example, we should
739    // just ignore VKEY_HANGUL when mozc-hangul is not active.
740    return false;
741  }
742
743  SwitchToNextInputMethodInternal(ids, current_input_method.id());
744  return true;  // consume the accelerator.
745}
746
747void InputMethodManagerImpl::StateImpl::SwitchToNextInputMethodInternal(
748    const std::vector<std::string>& input_method_ids,
749    const std::string& current_input_methodid) {
750  std::vector<std::string>::const_iterator iter = std::find(
751      input_method_ids.begin(), input_method_ids.end(), current_input_methodid);
752  if (iter != input_method_ids.end())
753    ++iter;
754  if (iter == input_method_ids.end())
755    iter = input_method_ids.begin();
756  ChangeInputMethod(*iter, true);
757}
758
759InputMethodDescriptor InputMethodManagerImpl::StateImpl::GetCurrentInputMethod()
760    const {
761  if (current_input_method.id().empty())
762    return InputMethodUtil::GetFallbackInputMethodDescriptor();
763
764  return current_input_method;
765}
766
767bool InputMethodManagerImpl::StateImpl::InputMethodIsActivated(
768    const std::string& input_method_id) const {
769  return Contains(active_input_method_ids, input_method_id);
770}
771
772// ------------------------ InputMethodManagerImpl
773bool InputMethodManagerImpl::IsLoginKeyboard(
774    const std::string& layout) const {
775  return util_.IsLoginKeyboard(layout);
776}
777
778bool InputMethodManagerImpl::MigrateInputMethods(
779    std::vector<std::string>* input_method_ids) {
780  return util_.MigrateInputMethods(input_method_ids);
781}
782
783// Starts or stops the system input method framework as needed.
784void InputMethodManagerImpl::ReconfigureIMFramework(
785    InputMethodManagerImpl::StateImpl* state) {
786  LoadNecessaryComponentExtensions(state);
787
788  // Initialize candidate window controller and widgets such as
789  // candidate window, infolist and mode indicator.  Note, mode
790  // indicator is used by only keyboard layout input methods.
791  if (state_.get() == state)
792    MaybeInitializeCandidateWindowController();
793}
794
795void InputMethodManagerImpl::SetState(
796    scoped_refptr<InputMethodManager::State> state) {
797  DCHECK(state.get());
798  InputMethodManagerImpl::StateImpl* new_impl_state =
799      static_cast<InputMethodManagerImpl::StateImpl*>(state.get());
800  const bool need_update_current_input_method =
801      (state_.get()
802           ? state_->current_input_method.id() !=
803                 new_impl_state->current_input_method.id()
804           : true);
805  state_ = new_impl_state;
806
807  if (state_.get() && state_->active_input_method_ids.size()) {
808    // Initialize candidate window controller and widgets such as
809    // candidate window, infolist and mode indicator.  Note, mode
810    // indicator is used by only keyboard layout input methods.
811    MaybeInitializeCandidateWindowController();
812
813    if (need_update_current_input_method) {
814      ChangeInputMethodInternal(state_->current_input_method,
815                                false /* show_message */,
816                                true /* notify_menu */);
817    } else {
818      // Update input method indicators (e.g. "US", "DV") in Chrome windows.
819      FOR_EACH_OBSERVER(InputMethodManager::Observer,
820                        observers_,
821                        InputMethodChanged(this, false /* show_message */));
822    }
823  }
824}
825
826scoped_refptr<InputMethodManager::State>
827InputMethodManagerImpl::GetActiveIMEState() {
828  return scoped_refptr<InputMethodManager::State>(state_.get());
829}
830
831InputMethodManagerImpl::InputMethodManagerImpl(
832    scoped_ptr<InputMethodDelegate> delegate,
833    bool enable_extension_loading)
834    : delegate_(delegate.Pass()),
835      ui_session_(STATE_LOGIN_SCREEN),
836      state_(NULL),
837      util_(delegate_.get()),
838      component_extension_ime_manager_(new ComponentExtensionIMEManager()),
839      enable_extension_loading_(enable_extension_loading) {
840  if (base::SysInfo::IsRunningOnChromeOS())
841    keyboard_.reset(ImeKeyboard::Create());
842  else
843    keyboard_.reset(new FakeImeKeyboard());
844
845  // Initializes the system IME list.
846  scoped_ptr<ComponentExtensionIMEManagerDelegate> comp_delegate(
847      new ComponentExtensionIMEManagerImpl());
848  component_extension_ime_manager_->Initialize(comp_delegate.Pass());
849  const InputMethodDescriptors& descriptors =
850      component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor();
851  util_.ResetInputMethods(descriptors);
852
853  // Initializes the stat id map.
854  std::map<int, std::vector<std::string> > buckets;
855  for (InputMethodDescriptors::const_iterator it = descriptors.begin();
856       it != descriptors.end(); ++it) {
857    char first_char;
858    int cat_id = static_cast<int>(
859        GetInputMethodCategory(it->id(), &first_char));
860    int key = cat_id * 1000 + first_char;
861    buckets[key].push_back(it->id());
862  }
863  for (std::map<int, std::vector<std::string>>::iterator i =
864       buckets.begin(); i != buckets.end(); ++i) {
865    std::sort(i->second.begin(), i->second.end());
866    for (size_t j = 0; j < i->second.size() && j < 100; ++j) {
867      int key = i->first * 100 + j;
868      stat_id_map_[i->second[j]] = key;
869    }
870  }
871}
872
873InputMethodManagerImpl::~InputMethodManagerImpl() {
874  if (candidate_window_controller_.get())
875    candidate_window_controller_->RemoveObserver(this);
876}
877
878void InputMethodManagerImpl::RecordInputMethodUsage(
879    std::string input_method_id) {
880  UMA_HISTOGRAM_ENUMERATION("InputMethod.Category",
881                            GetInputMethodCategory(input_method_id),
882                            INPUT_METHOD_CATEGORY_MAX);
883  UMA_HISTOGRAM_SPARSE_SLOWLY("InputMethod.ID",
884                              stat_id_map_[input_method_id]);
885}
886
887void InputMethodManagerImpl::AddObserver(
888    InputMethodManager::Observer* observer) {
889  observers_.AddObserver(observer);
890}
891
892void InputMethodManagerImpl::AddCandidateWindowObserver(
893    InputMethodManager::CandidateWindowObserver* observer) {
894  candidate_window_observers_.AddObserver(observer);
895}
896
897void InputMethodManagerImpl::RemoveObserver(
898    InputMethodManager::Observer* observer) {
899  observers_.RemoveObserver(observer);
900}
901
902void InputMethodManagerImpl::RemoveCandidateWindowObserver(
903    InputMethodManager::CandidateWindowObserver* observer) {
904  candidate_window_observers_.RemoveObserver(observer);
905}
906
907InputMethodManager::UISessionState InputMethodManagerImpl::GetUISessionState() {
908  return ui_session_;
909}
910
911void InputMethodManagerImpl::SetUISessionState(UISessionState new_ui_session) {
912  ui_session_ = new_ui_session;
913  switch (ui_session_) {
914    case STATE_LOGIN_SCREEN:
915      break;
916    case STATE_BROWSER_SCREEN:
917      break;
918    case STATE_LOCK_SCREEN:
919      break;
920    case STATE_TERMINATING: {
921      if (candidate_window_controller_.get())
922        candidate_window_controller_.reset();
923      break;
924    }
925  }
926}
927
928scoped_ptr<InputMethodDescriptors>
929InputMethodManagerImpl::GetSupportedInputMethods() const {
930  return scoped_ptr<InputMethodDescriptors>(new InputMethodDescriptors).Pass();
931}
932
933const InputMethodDescriptor* InputMethodManagerImpl::LookupInputMethod(
934    const std::string& input_method_id,
935    InputMethodManagerImpl::StateImpl* state) {
936  DCHECK(state);
937
938  std::string input_method_id_to_switch = input_method_id;
939
940  // Sanity check
941  if (!state->InputMethodIsActivated(input_method_id)) {
942    scoped_ptr<InputMethodDescriptors> input_methods(
943        state->GetActiveInputMethods());
944    DCHECK(!input_methods->empty());
945    input_method_id_to_switch = input_methods->at(0).id();
946    if (!input_method_id.empty()) {
947      DVLOG(1) << "Can't change the current input method to "
948               << input_method_id << " since the engine is not enabled. "
949               << "Switch to " << input_method_id_to_switch << " instead.";
950    }
951  }
952
953  const InputMethodDescriptor* descriptor = NULL;
954  if (extension_ime_util::IsExtensionIME(input_method_id_to_switch)) {
955    DCHECK(state->extra_input_methods.find(input_method_id_to_switch) !=
956           state->extra_input_methods.end());
957    descriptor = &(state->extra_input_methods[input_method_id_to_switch]);
958  } else {
959    descriptor =
960        util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
961    if (!descriptor)
962      LOG(ERROR) << "Unknown input method id: " << input_method_id_to_switch;
963  }
964  DCHECK(descriptor);
965  return descriptor;
966}
967
968void InputMethodManagerImpl::ChangeInputMethodInternal(
969    const InputMethodDescriptor& descriptor,
970    bool show_message,
971    bool notify_menu) {
972  // No need to switch input method when terminating.
973  if (ui_session_ == STATE_TERMINATING)
974    return;
975
976  if (candidate_window_controller_.get())
977    candidate_window_controller_->Hide();
978
979  if (notify_menu) {
980    // Clear property list.  Property list would be updated by
981    // extension IMEs via InputMethodEngine::(Set|Update)MenuItems.
982    // If the current input method is a keyboard layout, empty
983    // properties are sufficient.
984    const ash::ime::InputMethodMenuItemList empty_menu_item_list;
985    ash::ime::InputMethodMenuManager* input_method_menu_manager =
986        ash::ime::InputMethodMenuManager::GetInstance();
987    input_method_menu_manager->SetCurrentInputMethodMenuItemList(
988            empty_menu_item_list);
989  }
990
991  // Disable the current engine handler.
992  IMEEngineHandlerInterface* engine =
993      IMEBridge::Get()->GetCurrentEngineHandler();
994  if (engine)
995    engine->Disable();
996
997  // Configure the next engine handler.
998  // This must be after |current_input_method| has been set to new input
999  // method, because engine's Enable() method needs to access it.
1000  const std::string& extension_id =
1001      extension_ime_util::GetExtensionIDFromInputMethodID(descriptor.id());
1002  const std::string& component_id =
1003      extension_ime_util::GetComponentIDByInputMethodID(descriptor.id());
1004  engine = engine_map_[extension_id];
1005
1006  IMEBridge::Get()->SetCurrentEngineHandler(engine);
1007
1008  if (engine) {
1009    engine->Enable(component_id);
1010  } else {
1011    // If no engine to enable, cancel the virtual keyboard url override so that
1012    // it can use the fallback system virtual keyboard UI.
1013    keyboard::SetOverrideContentUrl(GURL());
1014    keyboard::KeyboardController* keyboard_controller =
1015        keyboard::KeyboardController::GetInstance();
1016    if (keyboard_controller)
1017      keyboard_controller->Reload();
1018  }
1019
1020  // Change the keyboard layout to a preferred layout for the input method.
1021  if (!keyboard_->SetCurrentKeyboardLayoutByName(
1022          descriptor.GetPreferredKeyboardLayout())) {
1023    LOG(ERROR) << "Failed to change keyboard layout to "
1024               << descriptor.GetPreferredKeyboardLayout();
1025  }
1026
1027  // Update input method indicators (e.g. "US", "DV") in Chrome windows.
1028  FOR_EACH_OBSERVER(InputMethodManager::Observer,
1029                    observers_,
1030                    InputMethodChanged(this, show_message));
1031}
1032
1033void InputMethodManagerImpl::LoadNecessaryComponentExtensions(
1034    InputMethodManagerImpl::StateImpl* state) {
1035  // Load component extensions but also update |active_input_method_ids| as
1036  // some component extension IMEs may have been removed from the Chrome OS
1037  // image. If specified component extension IME no longer exists, falling back
1038  // to an existing IME.
1039  DCHECK(state);
1040  std::vector<std::string> unfiltered_input_method_ids;
1041  unfiltered_input_method_ids.swap(state->active_input_method_ids);
1042  for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) {
1043    if (!extension_ime_util::IsComponentExtensionIME(
1044        unfiltered_input_method_ids[i])) {
1045      // Legacy IMEs or xkb layouts are alwayes active.
1046      state->active_input_method_ids.push_back(unfiltered_input_method_ids[i]);
1047    } else if (component_extension_ime_manager_->IsWhitelisted(
1048        unfiltered_input_method_ids[i])) {
1049      if (enable_extension_loading_) {
1050        component_extension_ime_manager_->LoadComponentExtensionIME(
1051            state->profile, unfiltered_input_method_ids[i]);
1052      }
1053
1054      state->active_input_method_ids.push_back(unfiltered_input_method_ids[i]);
1055    }
1056  }
1057}
1058
1059void InputMethodManagerImpl::ActivateInputMethodMenuItem(
1060    const std::string& key) {
1061  DCHECK(!key.empty());
1062
1063  if (ash::ime::InputMethodMenuManager::GetInstance()->
1064      HasInputMethodMenuItemForKey(key)) {
1065    IMEEngineHandlerInterface* engine =
1066        IMEBridge::Get()->GetCurrentEngineHandler();
1067    if (engine)
1068      engine->PropertyActivate(key);
1069    return;
1070  }
1071
1072  DVLOG(1) << "ActivateInputMethodMenuItem: unknown key: " << key;
1073}
1074
1075bool InputMethodManagerImpl::IsISOLevel5ShiftUsedByCurrentInputMethod() const {
1076  return keyboard_->IsISOLevel5ShiftAvailable();
1077}
1078
1079bool InputMethodManagerImpl::IsAltGrUsedByCurrentInputMethod() const {
1080  return keyboard_->IsAltGrAvailable();
1081}
1082
1083ImeKeyboard* InputMethodManagerImpl::GetImeKeyboard() {
1084  return keyboard_.get();
1085}
1086
1087InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
1088  return &util_;
1089}
1090
1091ComponentExtensionIMEManager*
1092    InputMethodManagerImpl::GetComponentExtensionIMEManager() {
1093  return component_extension_ime_manager_.get();
1094}
1095
1096scoped_refptr<InputMethodManager::State> InputMethodManagerImpl::CreateNewState(
1097    Profile* profile) {
1098  return scoped_refptr<InputMethodManager::State>(new StateImpl(this, profile));
1099}
1100
1101void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
1102    CandidateWindowController* candidate_window_controller) {
1103  candidate_window_controller_.reset(candidate_window_controller);
1104  candidate_window_controller_->AddObserver(this);
1105}
1106
1107void InputMethodManagerImpl::SetImeKeyboardForTesting(ImeKeyboard* keyboard) {
1108  keyboard_.reset(keyboard);
1109}
1110
1111void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
1112    scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
1113  component_extension_ime_manager_->Initialize(delegate.Pass());
1114  util_.ResetInputMethods(
1115      component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor());
1116}
1117
1118void InputMethodManagerImpl::CandidateClicked(int index) {
1119  IMEEngineHandlerInterface* engine =
1120      IMEBridge::Get()->GetCurrentEngineHandler();
1121  if (engine)
1122    engine->CandidateClicked(index);
1123}
1124
1125void InputMethodManagerImpl::CandidateWindowOpened() {
1126  FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
1127                    candidate_window_observers_,
1128                    CandidateWindowOpened(this));
1129}
1130
1131void InputMethodManagerImpl::CandidateWindowClosed() {
1132  FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
1133                    candidate_window_observers_,
1134                    CandidateWindowClosed(this));
1135}
1136
1137void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
1138  if (candidate_window_controller_.get())
1139    return;
1140
1141  candidate_window_controller_.reset(
1142      CandidateWindowController::CreateCandidateWindowController());
1143  candidate_window_controller_->AddObserver(this);
1144}
1145
1146}  // namespace input_method
1147}  // namespace chromeos
1148