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