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
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"
47 namespace input_method
{
51 bool Contains(const std::vector
<std::string
>& container
,
52 const std::string
& value
) {
53 return std::find(container
.begin(), container
.end(), value
) !=
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
;
74 if (base::StartsWith(component_id
, "xkb:", base::CompareCase::SENSITIVE
)) {
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-");
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
)) {
91 category
= INPUT_METHOD_CATEGORY_KO
;
92 } else if (base::StartsWith(component_id
, "vkd_",
93 base::CompareCase::SENSITIVE
)) {
95 category
= INPUT_METHOD_CATEGORY_M17N
;
96 } else if (component_id
.find("-t-i0-") > 0) {
98 category
= INPUT_METHOD_CATEGORY_T13N
;
108 // ------------------------ InputMethodManagerImpl::StateImpl
110 InputMethodManagerImpl::StateImpl::StateImpl(InputMethodManagerImpl
* manager
,
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()
147 for (size_t i
= 0; i
< active_input_method_ids
.size(); ++i
) {
148 os
<< " '" << active_input_method_ids
[i
] << "',";
151 os
<< "enabled_extension_imes (size=" << enabled_extension_imes
.size()
153 for (size_t i
= 0; i
< enabled_extension_imes
.size(); ++i
) {
154 os
<< " '" << enabled_extension_imes
[i
] << "'\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();
162 os
<< " '" << it
->first
<< "' => '" << it
->second
.id() << "',\n";
164 os
<< "pending_input_method_id: '" << pending_input_method_id
<< "'\n";
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
);
186 result
->push_back(*descriptor
);
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
);
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
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
);
220 std::map
<std::string
, InputMethodDescriptor
>::const_iterator ix
=
221 extra_input_methods
.find(input_method_id
);
222 if (ix
!= extra_input_methods
.end())
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
)
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
]);
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
);
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()
287 : extension_ime_util::GetInputMethodIDByEngineID(
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
304 if (!manager_
->IsLoginKeyboard(input_method_id
) ||
305 added_ids
.count(input_method_id
)) {
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
]))
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
;
338 if (!Contains(*new_active_input_method_ids
, input_method_id
))
339 new_active_input_method_ids
->push_back(input_method_id
);
344 bool InputMethodManagerImpl::StateImpl::EnableInputMethod(
345 const std::string
& input_method_id
) {
346 if (!EnableInputMethodImpl(input_method_id
, &active_input_method_ids
))
349 manager_
->ReconfigureIMFramework(this);
353 bool InputMethodManagerImpl::StateImpl::ReplaceEnabledInputMethods(
354 const std::vector
<std::string
>& new_active_input_method_ids
) {
355 if (manager_
->ui_session_
== STATE_TERMINATING
)
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";
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());
393 void InputMethodManagerImpl::StateImpl::ChangeInputMethod(
394 const std::string
& input_method_id
,
396 if (manager_
->ui_session_
== STATE_TERMINATING
)
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);
408 descriptor
= manager_
->LookupInputMethod(
409 manager_
->util_
.MigrateInputMethod(input_method_id
), this);
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
;
427 // Always change input method even if it is the same.
428 // TODO(komatsu): Revisit if this is neccessary.
430 manager_
->ChangeInputMethodInternal(*descriptor
, profile
, show_message
,
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
)
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
);
465 DVLOG(1) << "AddInputMethodExtension: already added: " << id
<< ", "
466 << descriptor
.name();
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.
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();
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
);
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();
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();
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(),
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();
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
) {
588 if (!oem_layout
.empty()) {
589 // If the OEM layout information is provided, use it.
592 // Otherwise, determine the hardware keyboard from the locale.
593 std::vector
<std::string
> input_method_ids
;
594 if (manager_
->util_
.GetInputMethodIdsFromLanguageCode(
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];
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();
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() {
653 if (active_input_method_ids
.empty()) {
654 DVLOG(1) << "active input method is empty";
658 if (current_input_method
.id().empty()) {
659 DVLOG(1) << "current_input_method is unknown";
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())
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())
684 if (previous_input_method
.id().empty() ||
685 previous_input_method
.id() == current_input_method
.id()) {
686 SwitchToNextInputMethod();
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();
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())
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();
742 if (active_input_method_ids
.empty()) {
743 DVLOG(1) << "active input method is empty";
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"));
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"));
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"));
768 if (input_method_ids_to_switch
.empty()) {
769 DVLOG(1) << "Unexpected VKEY: " << accelerator
.key_code();
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()
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
) {
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
),
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());
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() {
906 void InputMethodManagerImpl::SetUISessionState(UISessionState new_ui_session
) {
907 ui_session_
= new_ui_session
;
908 switch (ui_session_
) {
909 case STATE_LOGIN_SCREEN
:
911 case STATE_BROWSER_SCREEN
:
913 case STATE_LOCK_SCREEN
:
915 case STATE_TERMINATING
: {
916 if (candidate_window_controller_
.get())
917 candidate_window_controller_
.reset();
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
) {
933 std::string input_method_id_to_switch
= input_method_id
;
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
]);
955 util_
.GetInputMethodDescriptorFromId(input_method_id_to_switch
);
957 LOG(ERROR
) << "Unknown input method id: " << input_method_id_to_switch
;
963 void InputMethodManagerImpl::ChangeInputMethodInternal(
964 const InputMethodDescriptor
& descriptor
,
968 // No need to switch input method when terminating.
969 if (ui_session_
== STATE_TERMINATING
)
972 if (candidate_window_controller_
.get())
973 candidate_window_controller_
->Hide();
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();
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
);
1005 engine
->Enable(component_id
);
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.
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();
1063 engine
->PropertyActivate(key
);
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() {
1086 ComponentExtensionIMEManager
*
1087 InputMethodManagerImpl::GetComponentExtensionIMEManager() {
1088 return component_extension_ime_manager_
.get();
1091 scoped_refptr
<InputMethodManager::State
> InputMethodManagerImpl::CreateNewState(
1093 StateImpl
* new_state
= new StateImpl(this, profile
);
1095 // Active IM should be set to owner/user's default.
1096 PrefService
* prefs
= g_browser_process
->local_state();
1097 PrefService
* user_prefs
= profile
? profile
->GetPrefs() : nullptr;
1098 std::string initial_input_method_id
;
1100 initial_input_method_id
=
1101 user_prefs
->GetString(prefs::kLanguageCurrentInputMethod
);
1103 if (initial_input_method_id
.empty()) {
1104 initial_input_method_id
=
1105 prefs
->GetString(chromeos::language_prefs::kPreferredKeyboardLayout
);
1108 const InputMethodDescriptor
* descriptor
=
1109 GetInputMethodUtil()->GetInputMethodDescriptorFromId(
1110 initial_input_method_id
.empty()
1111 ? GetInputMethodUtil()->GetFallbackInputMethodDescriptor().id()
1112 : initial_input_method_id
);
1114 new_state
->active_input_method_ids
.push_back(descriptor
->id());
1115 new_state
->current_input_method
= *descriptor
;
1117 return scoped_refptr
<InputMethodManager::State
>(new_state
);
1120 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
1121 CandidateWindowController
* candidate_window_controller
) {
1122 candidate_window_controller_
.reset(candidate_window_controller
);
1123 candidate_window_controller_
->AddObserver(this);
1126 void InputMethodManagerImpl::SetImeKeyboardForTesting(ImeKeyboard
* keyboard
) {
1127 keyboard_
.reset(keyboard
);
1130 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
1131 scoped_ptr
<ComponentExtensionIMEManagerDelegate
> delegate
) {
1132 component_extension_ime_manager_
->Initialize(delegate
.Pass());
1133 util_
.ResetInputMethods(
1134 component_extension_ime_manager_
->GetAllIMEAsInputMethodDescriptor());
1137 void InputMethodManagerImpl::CandidateClicked(int index
) {
1138 IMEEngineHandlerInterface
* engine
=
1139 IMEBridge::Get()->GetCurrentEngineHandler();
1141 engine
->CandidateClicked(index
);
1144 void InputMethodManagerImpl::CandidateWindowOpened() {
1145 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver
,
1146 candidate_window_observers_
,
1147 CandidateWindowOpened(this));
1150 void InputMethodManagerImpl::CandidateWindowClosed() {
1151 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver
,
1152 candidate_window_observers_
,
1153 CandidateWindowClosed(this));
1156 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
1157 if (candidate_window_controller_
.get())
1160 candidate_window_controller_
.reset(
1161 CandidateWindowController::CreateCandidateWindowController());
1162 candidate_window_controller_
->AddObserver(this);
1165 } // namespace input_method
1166 } // namespace chromeos