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/location.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/sparse_histogram.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/strings/string_split.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/sys_info.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
24 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
25 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
26 #include "chrome/browser/chromeos/input_method/input_method_switch_recorder.h"
27 #include "chrome/browser/chromeos/language_preferences.h"
28 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
29 #include "chrome/browser/chromeos/profiles/profile_helper.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/common/pref_names.h"
32 #include "components/user_manager/user_manager.h"
33 #include "third_party/icu/source/common/unicode/uloc.h"
34 #include "ui/base/accelerators/accelerator.h"
35 #include "ui/base/ime/chromeos/component_extension_ime_manager.h"
36 #include "ui/base/ime/chromeos/extension_ime_util.h"
37 #include "ui/base/ime/chromeos/fake_ime_keyboard.h"
38 #include "ui/base/ime/chromeos/ime_keyboard.h"
39 #include "ui/base/ime/chromeos/input_method_delegate.h"
40 #include "ui/chromeos/ime/input_method_menu_item.h"
41 #include "ui/chromeos/ime/input_method_menu_manager.h"
42 #include "ui/keyboard/keyboard_controller.h"
43 #include "ui/keyboard/keyboard_util.h"
46 namespace input_method
{
50 bool Contains(const std::vector
<std::string
>& container
,
51 const std::string
& value
) {
52 return std::find(container
.begin(), container
.end(), value
) !=
56 enum InputMethodCategory
{
57 INPUT_METHOD_CATEGORY_UNKNOWN
= 0,
58 INPUT_METHOD_CATEGORY_XKB
, // XKB input methods
59 INPUT_METHOD_CATEGORY_ZH
, // Chinese input methods
60 INPUT_METHOD_CATEGORY_JA
, // Japanese input methods
61 INPUT_METHOD_CATEGORY_KO
, // Korean input methods
62 INPUT_METHOD_CATEGORY_M17N
, // Multilingualization input methods
63 INPUT_METHOD_CATEGORY_T13N
, // Transliteration input methods
64 INPUT_METHOD_CATEGORY_MAX
67 InputMethodCategory
GetInputMethodCategory(const std::string
& input_method_id
,
68 char* first_char
= NULL
) {
69 const std::string component_id
=
70 extension_ime_util::GetComponentIDByInputMethodID(input_method_id
);
71 InputMethodCategory category
= INPUT_METHOD_CATEGORY_UNKNOWN
;
73 if (StartsWithASCII(component_id
, "xkb:", true)) {
75 category
= INPUT_METHOD_CATEGORY_XKB
;
76 } else if (StartsWithASCII(component_id
, "zh-", true)) {
77 size_t pos
= component_id
.find("-t-i0-");
80 ch
= component_id
[pos
];
81 category
= INPUT_METHOD_CATEGORY_ZH
;
82 } else if (StartsWithASCII(component_id
, "nacl_mozc_", true)) {
83 ch
= component_id
[10];
84 category
= INPUT_METHOD_CATEGORY_JA
;
85 } else if (StartsWithASCII(component_id
, "hangul_", true)) {
87 category
= INPUT_METHOD_CATEGORY_KO
;
88 } else if (StartsWithASCII(component_id
, "vkd_", true)) {
90 category
= INPUT_METHOD_CATEGORY_M17N
;
91 } else if (component_id
.find("-t-i0-") > 0) {
93 category
= INPUT_METHOD_CATEGORY_T13N
;
103 // ------------------------ InputMethodManagerImpl::StateImpl
105 InputMethodManagerImpl::StateImpl::StateImpl(InputMethodManagerImpl
* manager
,
107 : profile(profile
), manager_(manager
) {
110 InputMethodManagerImpl::StateImpl::~StateImpl() {
113 void InputMethodManagerImpl::StateImpl::InitFrom(const StateImpl
& other
) {
114 previous_input_method
= other
.previous_input_method
;
115 current_input_method
= other
.current_input_method
;
117 active_input_method_ids
= other
.active_input_method_ids
;
119 pending_input_method_id
= other
.pending_input_method_id
;
121 enabled_extension_imes
= other
.enabled_extension_imes
;
122 extra_input_methods
= other
.extra_input_methods
;
125 bool InputMethodManagerImpl::StateImpl::IsActive() const {
126 return manager_
->state_
.get() == this;
129 std::string
InputMethodManagerImpl::StateImpl::Dump() const {
130 std::ostringstream os
;
132 os
<< "################# "
133 << (profile
? profile
->GetProfileUserName() : std::string("NULL"))
134 << " #################\n";
136 os
<< "previous_input_method: '"
137 << previous_input_method
.GetPreferredKeyboardLayout() << "'\n";
138 os
<< "current_input_method: '"
139 << current_input_method
.GetPreferredKeyboardLayout() << "'\n";
140 os
<< "active_input_method_ids (size=" << active_input_method_ids
.size()
142 for (size_t i
= 0; i
< active_input_method_ids
.size(); ++i
) {
143 os
<< " '" << active_input_method_ids
[i
] << "',";
146 os
<< "enabled_extension_imes (size=" << enabled_extension_imes
.size()
148 for (size_t i
= 0; i
< enabled_extension_imes
.size(); ++i
) {
149 os
<< " '" << enabled_extension_imes
[i
] << "'\n";
152 os
<< "extra_input_methods (size=" << extra_input_methods
.size() << "):";
153 for (std::map
<std::string
, InputMethodDescriptor
>::const_iterator it
=
154 extra_input_methods
.begin();
155 it
!= extra_input_methods
.end();
157 os
<< " '" << it
->first
<< "' => '" << it
->second
.id() << "',\n";
159 os
<< "pending_input_method_id: '" << pending_input_method_id
<< "'\n";
164 scoped_refptr
<InputMethodManager::State
>
165 InputMethodManagerImpl::StateImpl::Clone() const {
166 scoped_refptr
<StateImpl
> new_state(new StateImpl(this->manager_
, profile
));
167 new_state
->InitFrom(*this);
168 return scoped_refptr
<InputMethodManager::State
>(new_state
.get());
171 scoped_ptr
<InputMethodDescriptors
>
172 InputMethodManagerImpl::StateImpl::GetActiveInputMethods() const {
173 scoped_ptr
<InputMethodDescriptors
> result(new InputMethodDescriptors
);
174 // Build the active input method descriptors from the active input
175 // methods cache |active_input_method_ids|.
176 for (size_t i
= 0; i
< active_input_method_ids
.size(); ++i
) {
177 const std::string
& input_method_id
= active_input_method_ids
[i
];
178 const InputMethodDescriptor
* descriptor
=
179 manager_
->util_
.GetInputMethodDescriptorFromId(input_method_id
);
181 result
->push_back(*descriptor
);
183 std::map
<std::string
, InputMethodDescriptor
>::const_iterator ix
=
184 extra_input_methods
.find(input_method_id
);
185 if (ix
!= extra_input_methods
.end())
186 result
->push_back(ix
->second
);
188 DVLOG(1) << "Descriptor is not found for: " << input_method_id
;
191 if (result
->empty()) {
192 // Initially |active_input_method_ids| is empty. browser_tests might take
195 InputMethodUtil::GetFallbackInputMethodDescriptor());
197 return result
.Pass();
200 const std::vector
<std::string
>&
201 InputMethodManagerImpl::StateImpl::GetActiveInputMethodIds() const {
202 return active_input_method_ids
;
205 size_t InputMethodManagerImpl::StateImpl::GetNumActiveInputMethods() const {
206 return active_input_method_ids
.size();
209 const InputMethodDescriptor
*
210 InputMethodManagerImpl::StateImpl::GetInputMethodFromId(
211 const std::string
& input_method_id
) const {
212 const InputMethodDescriptor
* ime
=
213 manager_
->util_
.GetInputMethodDescriptorFromId(input_method_id
);
215 std::map
<std::string
, InputMethodDescriptor
>::const_iterator ix
=
216 extra_input_methods
.find(input_method_id
);
217 if (ix
!= extra_input_methods
.end())
223 void InputMethodManagerImpl::StateImpl::EnableLoginLayouts(
224 const std::string
& language_code
,
225 const std::vector
<std::string
>& initial_layouts
) {
226 if (manager_
->ui_session_
== STATE_TERMINATING
)
229 // First, hardware keyboard layout should be shown.
230 std::vector
<std::string
> candidates
=
231 manager_
->util_
.GetHardwareLoginInputMethodIds();
233 // Second, locale based input method should be shown.
234 // Add input methods associated with the language.
235 std::vector
<std::string
> layouts_from_locale
;
236 manager_
->util_
.GetInputMethodIdsFromLanguageCode(
237 language_code
, kKeyboardLayoutsOnly
, &layouts_from_locale
);
238 candidates
.insert(candidates
.end(), layouts_from_locale
.begin(),
239 layouts_from_locale
.end());
241 std::vector
<std::string
> layouts
;
242 // First, add the initial input method ID, if it's requested, to
243 // layouts, so it appears first on the list of active input
244 // methods at the input language status menu.
245 for (size_t i
= 0; i
< initial_layouts
.size(); ++i
) {
246 if (manager_
->util_
.IsValidInputMethodId(initial_layouts
[i
])) {
247 if (manager_
->IsLoginKeyboard(initial_layouts
[i
])) {
248 layouts
.push_back(initial_layouts
[i
]);
251 << "EnableLoginLayouts: ignoring non-login initial keyboard layout:"
252 << initial_layouts
[i
];
254 } else if (!initial_layouts
[i
].empty()) {
255 DVLOG(1) << "EnableLoginLayouts: ignoring non-keyboard or invalid ID: "
256 << initial_layouts
[i
];
260 // Add candidates to layouts, while skipping duplicates.
261 for (size_t i
= 0; i
< candidates
.size(); ++i
) {
262 const std::string
& candidate
= candidates
[i
];
263 // Not efficient, but should be fine, as the two vectors are very
264 // short (2-5 items).
265 if (!Contains(layouts
, candidate
) && manager_
->IsLoginKeyboard(candidate
))
266 layouts
.push_back(candidate
);
269 manager_
->MigrateInputMethods(&layouts
);
270 active_input_method_ids
.swap(layouts
);
273 // Initialize candidate window controller and widgets such as
274 // candidate window, infolist and mode indicator. Note, mode
275 // indicator is used by only keyboard layout input methods.
276 if (active_input_method_ids
.size() > 1)
277 manager_
->MaybeInitializeCandidateWindowController();
279 // you can pass empty |initial_layout|.
280 ChangeInputMethod(initial_layouts
.empty()
282 : extension_ime_util::GetInputMethodIDByEngineID(
288 void InputMethodManagerImpl::StateImpl::EnableLockScreenLayouts() {
289 std::set
<std::string
> added_ids
;
291 const std::vector
<std::string
>& hardware_keyboard_ids
=
292 manager_
->util_
.GetHardwareLoginInputMethodIds();
294 std::vector
<std::string
> new_active_input_method_ids
;
295 for (size_t i
= 0; i
< active_input_method_ids
.size(); ++i
) {
296 const std::string
& input_method_id
= active_input_method_ids
[i
];
297 // Skip if it's not a keyboard layout. Drop input methods including
299 if (!manager_
->IsLoginKeyboard(input_method_id
) ||
300 added_ids
.count(input_method_id
)) {
303 new_active_input_method_ids
.push_back(input_method_id
);
304 added_ids
.insert(input_method_id
);
307 // We'll add the hardware keyboard if it's not included in
308 // |active_input_method_ids| so that the user can always use the hardware
309 // keyboard on the screen locker.
310 for (size_t i
= 0; i
< hardware_keyboard_ids
.size(); ++i
) {
311 if (added_ids
.count(hardware_keyboard_ids
[i
]))
313 new_active_input_method_ids
.push_back(hardware_keyboard_ids
[i
]);
314 added_ids
.insert(hardware_keyboard_ids
[i
]);
317 active_input_method_ids
.swap(new_active_input_method_ids
);
319 // Re-check current_input_method.
320 ChangeInputMethod(current_input_method
.id(), false);
323 // Adds new input method to given list.
324 bool InputMethodManagerImpl::StateImpl::EnableInputMethodImpl(
325 const std::string
& input_method_id
,
326 std::vector
<std::string
>* new_active_input_method_ids
) const {
327 DCHECK(new_active_input_method_ids
);
328 if (!manager_
->util_
.IsValidInputMethodId(input_method_id
)) {
329 DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id
;
333 if (!Contains(*new_active_input_method_ids
, input_method_id
))
334 new_active_input_method_ids
->push_back(input_method_id
);
339 bool InputMethodManagerImpl::StateImpl::EnableInputMethod(
340 const std::string
& input_method_id
) {
341 if (!EnableInputMethodImpl(input_method_id
, &active_input_method_ids
))
344 manager_
->ReconfigureIMFramework(this);
348 bool InputMethodManagerImpl::StateImpl::ReplaceEnabledInputMethods(
349 const std::vector
<std::string
>& new_active_input_method_ids
) {
350 if (manager_
->ui_session_
== STATE_TERMINATING
)
353 // Filter unknown or obsolete IDs.
354 std::vector
<std::string
> new_active_input_method_ids_filtered
;
356 for (size_t i
= 0; i
< new_active_input_method_ids
.size(); ++i
)
357 EnableInputMethodImpl(new_active_input_method_ids
[i
],
358 &new_active_input_method_ids_filtered
);
360 if (new_active_input_method_ids_filtered
.empty()) {
361 DVLOG(1) << "ReplaceEnabledInputMethods: No valid input method ID";
365 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
366 // keep relative order of the extension input method IDs.
367 for (size_t i
= 0; i
< active_input_method_ids
.size(); ++i
) {
368 const std::string
& input_method_id
= active_input_method_ids
[i
];
369 if (extension_ime_util::IsExtensionIME(input_method_id
))
370 new_active_input_method_ids_filtered
.push_back(input_method_id
);
372 active_input_method_ids
.swap(new_active_input_method_ids_filtered
);
373 manager_
->MigrateInputMethods(&active_input_method_ids
);
375 manager_
->ReconfigureIMFramework(this);
377 // If |current_input_method| is no longer in |active_input_method_ids|,
378 // ChangeInputMethod() picks the first one in |active_input_method_ids|.
379 ChangeInputMethod(current_input_method
.id(), false);
381 // Record histogram for active input method count.
382 UMA_HISTOGRAM_COUNTS("InputMethod.ActiveCount",
383 active_input_method_ids
.size());
388 void InputMethodManagerImpl::StateImpl::ChangeInputMethod(
389 const std::string
& input_method_id
,
391 if (manager_
->ui_session_
== STATE_TERMINATING
)
394 bool notify_menu
= false;
396 // Always lookup input method, even if it is the same as
397 // |current_input_method| because If it is no longer in
398 // |active_input_method_ids|, pick the first one in
399 // |active_input_method_ids|.
400 const InputMethodDescriptor
* descriptor
=
401 manager_
->LookupInputMethod(input_method_id
, this);
403 descriptor
= manager_
->LookupInputMethod(
404 manager_
->util_
.MigrateInputMethod(input_method_id
), this);
409 // For 3rd party IME, when the user just logged in, SetEnabledExtensionImes
410 // happens after activating the 3rd party IME.
411 // So here to record the 3rd party IME to be activated, and activate it
412 // when SetEnabledExtensionImes happens later.
413 if (MethodAwaitsExtensionLoad(descriptor
->id()))
414 pending_input_method_id
= descriptor
->id();
416 if (descriptor
->id() != current_input_method
.id()) {
417 previous_input_method
= current_input_method
;
418 current_input_method
= *descriptor
;
422 // Always change input method even if it is the same.
423 // TODO(komatsu): Revisit if this is neccessary.
425 manager_
->ChangeInputMethodInternal(*descriptor
, show_message
, notify_menu
);
426 manager_
->RecordInputMethodUsage(current_input_method
.id());
429 bool InputMethodManagerImpl::StateImpl::MethodAwaitsExtensionLoad(
430 const std::string
& input_method_id
) const {
431 // For 3rd party IME, when the user just logged in, SetEnabledExtensionImes
432 // happens after activating the 3rd party IME.
433 // So here to record the 3rd party IME to be activated, and activate it
434 // when SetEnabledExtensionImes happens later.
435 return !InputMethodIsActivated(input_method_id
) &&
436 extension_ime_util::IsExtensionIME(input_method_id
);
439 void InputMethodManagerImpl::StateImpl::AddInputMethodExtension(
440 const std::string
& extension_id
,
441 const InputMethodDescriptors
& descriptors
,
442 InputMethodEngineInterface
* engine
) {
443 if (manager_
->ui_session_
== STATE_TERMINATING
)
448 manager_
->engine_map_
[extension_id
] = engine
;
450 bool contain
= false;
451 for (size_t i
= 0; i
< descriptors
.size(); i
++) {
452 const InputMethodDescriptor
& descriptor
= descriptors
[i
];
453 const std::string
& id
= descriptor
.id();
454 extra_input_methods
[id
] = descriptor
;
455 if (Contains(enabled_extension_imes
, id
)) {
456 if (!Contains(active_input_method_ids
, id
)) {
457 active_input_method_ids
.push_back(id
);
459 DVLOG(1) << "AddInputMethodExtension: already added: " << id
<< ", "
460 << descriptor
.name();
467 if (extension_id
== extension_ime_util::GetExtensionIDFromInputMethodID(
468 current_input_method
.id())) {
469 IMEBridge::Get()->SetCurrentEngineHandler(engine
);
470 engine
->Enable(extension_ime_util::GetComponentIDByInputMethodID(
471 current_input_method
.id()));
474 // Ensure that the input method daemon is running.
476 manager_
->MaybeInitializeCandidateWindowController();
480 void InputMethodManagerImpl::StateImpl::RemoveInputMethodExtension(
481 const std::string
& extension_id
) {
482 // Remove the active input methods with |extension_id|.
483 std::vector
<std::string
> new_active_input_method_ids
;
484 for (size_t i
= 0; i
< active_input_method_ids
.size(); ++i
) {
485 if (extension_id
!= extension_ime_util::GetExtensionIDFromInputMethodID(
486 active_input_method_ids
[i
]))
487 new_active_input_method_ids
.push_back(active_input_method_ids
[i
]);
489 active_input_method_ids
.swap(new_active_input_method_ids
);
491 // Remove the extra input methods with |extension_id|.
492 std::map
<std::string
, InputMethodDescriptor
> new_extra_input_methods
;
493 for (std::map
<std::string
, InputMethodDescriptor
>::iterator i
=
494 extra_input_methods
.begin();
495 i
!= extra_input_methods
.end();
498 extension_ime_util::GetExtensionIDFromInputMethodID(i
->first
))
499 new_extra_input_methods
[i
->first
] = i
->second
;
501 extra_input_methods
.swap(new_extra_input_methods
);
504 if (IMEBridge::Get()->GetCurrentEngineHandler() ==
505 manager_
->engine_map_
[extension_id
]) {
506 IMEBridge::Get()->SetCurrentEngineHandler(NULL
);
508 manager_
->engine_map_
.erase(extension_id
);
511 // If |current_input_method| is no longer in |active_input_method_ids|,
512 // switch to the first one in |active_input_method_ids|.
513 ChangeInputMethod(current_input_method
.id(), false);
516 void InputMethodManagerImpl::StateImpl::GetInputMethodExtensions(
517 InputMethodDescriptors
* result
) {
518 // Build the extension input method descriptors from the extra input
519 // methods cache |extra_input_methods|.
520 std::map
<std::string
, InputMethodDescriptor
>::iterator iter
;
521 for (iter
= extra_input_methods
.begin(); iter
!= extra_input_methods
.end();
523 if (extension_ime_util::IsExtensionIME(iter
->first
))
524 result
->push_back(iter
->second
);
528 void InputMethodManagerImpl::StateImpl::SetEnabledExtensionImes(
529 std::vector
<std::string
>* ids
) {
530 enabled_extension_imes
.clear();
531 enabled_extension_imes
.insert(
532 enabled_extension_imes
.end(), ids
->begin(), ids
->end());
533 bool active_imes_changed
= false;
534 bool switch_to_pending
= false;
536 for (std::map
<std::string
, InputMethodDescriptor
>::iterator extra_iter
=
537 extra_input_methods
.begin();
538 extra_iter
!= extra_input_methods
.end();
540 if (extension_ime_util::IsComponentExtensionIME(extra_iter
->first
))
541 continue; // Do not filter component extension.
543 if (pending_input_method_id
== extra_iter
->first
)
544 switch_to_pending
= true;
546 std::vector
<std::string
>::iterator active_iter
=
547 std::find(active_input_method_ids
.begin(),
548 active_input_method_ids
.end(),
551 bool active
= active_iter
!= active_input_method_ids
.end();
552 bool enabled
= Contains(enabled_extension_imes
, extra_iter
->first
);
554 if (active
&& !enabled
)
555 active_input_method_ids
.erase(active_iter
);
557 if (!active
&& enabled
)
558 active_input_method_ids
.push_back(extra_iter
->first
);
560 if (active
== !enabled
)
561 active_imes_changed
= true;
564 if (IsActive() && active_imes_changed
) {
565 manager_
->MaybeInitializeCandidateWindowController();
567 if (switch_to_pending
) {
568 ChangeInputMethod(pending_input_method_id
, false);
569 pending_input_method_id
.clear();
571 // If |current_input_method| is no longer in |active_input_method_ids_|,
572 // switch to the first one in |active_input_method_ids_|.
573 ChangeInputMethod(current_input_method
.id(), false);
578 void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefaultFromVPD(
579 const std::string
& locale
,
580 const std::string
& oem_layout
) {
582 if (!oem_layout
.empty()) {
583 // If the OEM layout information is provided, use it.
586 // Otherwise, determine the hardware keyboard from the locale.
587 std::vector
<std::string
> input_method_ids
;
588 if (manager_
->util_
.GetInputMethodIdsFromLanguageCode(
590 chromeos::input_method::kKeyboardLayoutsOnly
,
591 &input_method_ids
)) {
592 // The output list |input_method_ids| is sorted by popularity, hence
593 // input_method_ids[0] now contains the most popular keyboard layout
594 // for the given locale.
595 DCHECK_GE(input_method_ids
.size(), 1U);
596 layout
= input_method_ids
[0];
603 std::vector
<std::string
> layouts
;
604 base::SplitString(layout
, ',', &layouts
);
605 manager_
->MigrateInputMethods(&layouts
);
607 PrefService
* prefs
= g_browser_process
->local_state();
608 prefs
->SetString(prefs::kHardwareKeyboardLayout
, JoinString(layouts
, ","));
610 // This asks the file thread to save the prefs (i.e. doesn't block).
611 // The latest values of Local State reside in memory so we can safely
612 // get the value of kHardwareKeyboardLayout even if the data is not
613 // yet saved to disk.
614 prefs
->CommitPendingWrite();
616 manager_
->util_
.UpdateHardwareLayoutCache();
618 EnableLoginLayouts(locale
, layouts
);
619 manager_
->LoadNecessaryComponentExtensions(this);
622 void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefault() {
623 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
624 // and US dvorak keyboard layouts.
625 if (g_browser_process
&& g_browser_process
->local_state()) {
626 const std::string locale
= g_browser_process
->GetApplicationLocale();
627 // If the preferred keyboard for the login screen has been saved, use it.
628 PrefService
* prefs
= g_browser_process
->local_state();
629 std::string initial_input_method_id
=
630 prefs
->GetString(chromeos::language_prefs::kPreferredKeyboardLayout
);
631 std::vector
<std::string
> input_methods_to_be_enabled
;
632 if (initial_input_method_id
.empty()) {
633 // If kPreferredKeyboardLayout is not specified, use the hardware layout.
634 input_methods_to_be_enabled
=
635 manager_
->util_
.GetHardwareLoginInputMethodIds();
637 input_methods_to_be_enabled
.push_back(initial_input_method_id
);
639 EnableLoginLayouts(locale
, input_methods_to_be_enabled
);
640 manager_
->LoadNecessaryComponentExtensions(this);
644 bool InputMethodManagerImpl::StateImpl::CanCycleInputMethod() {
646 if (active_input_method_ids
.empty()) {
647 DVLOG(1) << "active input method is empty";
651 if (current_input_method
.id().empty()) {
652 DVLOG(1) << "current_input_method is unknown";
656 // Do not consume key event if there is only one input method is enabled.
657 // Ctrl+Space or Alt+Shift may be used by other application.
658 return active_input_method_ids
.size() > 1;
661 void InputMethodManagerImpl::StateImpl::SwitchToNextInputMethod() {
662 DCHECK(CanCycleInputMethod());
663 if (!CanCycleInputMethod())
666 // Find the next input method and switch to it.
667 SwitchToNextInputMethodInternal(active_input_method_ids
,
668 current_input_method
.id());
669 InputMethodSwitchRecorder::Get()->RecordSwitch(false /* by_tray_menu*/);
672 void InputMethodManagerImpl::StateImpl::SwitchToPreviousInputMethod() {
673 DCHECK(CanCycleInputMethod());
674 if (!CanCycleInputMethod())
677 if (previous_input_method
.id().empty() ||
678 previous_input_method
.id() == current_input_method
.id()) {
679 SwitchToNextInputMethod();
683 std::vector
<std::string
>::const_iterator iter
=
684 std::find(active_input_method_ids
.begin(),
685 active_input_method_ids
.end(),
686 previous_input_method
.id());
687 if (iter
== active_input_method_ids
.end()) {
688 // previous_input_method is not supported.
689 SwitchToNextInputMethod();
692 ChangeInputMethod(*iter
, true);
693 InputMethodSwitchRecorder::Get()->RecordSwitch(false /* by_tray_menu*/);
696 bool InputMethodManagerImpl::StateImpl::CanSwitchInputMethod(
697 const ui::Accelerator
& accelerator
) {
698 // If none of the input methods associated with |accelerator| are active, we
699 // should ignore the accelerator. For example, we should just ignore
700 // VKEY_HANGUL when mozc-hangul is not active.
701 std::vector
<std::string
> candidate_ids
;
702 GetCandidateInputMethodsForAccelerator(accelerator
, &candidate_ids
);
703 return !candidate_ids
.empty();
706 void InputMethodManagerImpl::StateImpl::SwitchInputMethod(
707 const ui::Accelerator
& accelerator
) {
708 std::vector
<std::string
> candidate_ids
;
709 GetCandidateInputMethodsForAccelerator(accelerator
, &candidate_ids
);
710 DCHECK(!candidate_ids
.empty());
711 if (!candidate_ids
.empty()) {
712 SwitchToNextInputMethodInternal(candidate_ids
, current_input_method
.id());
713 InputMethodSwitchRecorder::Get()->RecordSwitch(false /* by_tray_menu*/);
717 void InputMethodManagerImpl::StateImpl::SwitchToNextInputMethodInternal(
718 const std::vector
<std::string
>& input_method_ids
,
719 const std::string
& current_input_methodid
) {
720 std::vector
<std::string
>::const_iterator iter
= std::find(
721 input_method_ids
.begin(), input_method_ids
.end(), current_input_methodid
);
722 if (iter
!= input_method_ids
.end())
724 if (iter
== input_method_ids
.end())
725 iter
= input_method_ids
.begin();
726 ChangeInputMethod(*iter
, true);
729 void InputMethodManagerImpl::StateImpl::GetCandidateInputMethodsForAccelerator(
730 const ui::Accelerator
& accelerator
,
731 std::vector
<std::string
>* out_candidate_ids
) {
732 out_candidate_ids
->clear();
735 if (active_input_method_ids
.empty()) {
736 DVLOG(1) << "active input method is empty";
740 std::vector
<std::string
> input_method_ids_to_switch
;
741 switch (accelerator
.key_code()) {
742 case ui::VKEY_CONVERT
: // Henkan key on JP106 keyboard
743 input_method_ids_to_switch
.push_back(
744 extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
746 case ui::VKEY_NONCONVERT
: // Muhenkan key on JP106 keyboard
747 input_method_ids_to_switch
.push_back(
748 extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
750 case ui::VKEY_DBE_SBCSCHAR
: // ZenkakuHankaku key on JP106 keyboard
751 case ui::VKEY_DBE_DBCSCHAR
:
752 input_method_ids_to_switch
.push_back(
753 extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
754 input_method_ids_to_switch
.push_back(
755 extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
761 if (input_method_ids_to_switch
.empty()) {
762 DVLOG(1) << "Unexpected VKEY: " << accelerator
.key_code();
766 // Obtain the intersection of input_method_ids_to_switch and
767 // active_input_method_ids.
768 for (size_t i
= 0; i
< input_method_ids_to_switch
.size(); ++i
) {
769 const std::string
& id
= input_method_ids_to_switch
[i
];
770 if (Contains(active_input_method_ids
, id
))
771 out_candidate_ids
->push_back(id
);
775 InputMethodDescriptor
InputMethodManagerImpl::StateImpl::GetCurrentInputMethod()
777 if (current_input_method
.id().empty())
778 return InputMethodUtil::GetFallbackInputMethodDescriptor();
780 return current_input_method
;
783 bool InputMethodManagerImpl::StateImpl::InputMethodIsActivated(
784 const std::string
& input_method_id
) const {
785 return Contains(active_input_method_ids
, input_method_id
);
788 // ------------------------ InputMethodManagerImpl
789 bool InputMethodManagerImpl::IsLoginKeyboard(
790 const std::string
& layout
) const {
791 return util_
.IsLoginKeyboard(layout
);
794 bool InputMethodManagerImpl::MigrateInputMethods(
795 std::vector
<std::string
>* input_method_ids
) {
796 return util_
.MigrateInputMethods(input_method_ids
);
799 // Starts or stops the system input method framework as needed.
800 void InputMethodManagerImpl::ReconfigureIMFramework(
801 InputMethodManagerImpl::StateImpl
* state
) {
802 LoadNecessaryComponentExtensions(state
);
804 // Initialize candidate window controller and widgets such as
805 // candidate window, infolist and mode indicator. Note, mode
806 // indicator is used by only keyboard layout input methods.
807 if (state_
.get() == state
)
808 MaybeInitializeCandidateWindowController();
811 void InputMethodManagerImpl::SetState(
812 scoped_refptr
<InputMethodManager::State
> state
) {
814 InputMethodManagerImpl::StateImpl
* new_impl_state
=
815 static_cast<InputMethodManagerImpl::StateImpl
*>(state
.get());
817 state_
= new_impl_state
;
819 if (state_
.get() && state_
->active_input_method_ids
.size()) {
820 // Initialize candidate window controller and widgets such as
821 // candidate window, infolist and mode indicator. Note, mode
822 // indicator is used by only keyboard layout input methods.
823 MaybeInitializeCandidateWindowController();
825 // Always call ChangeInputMethodInternal even when the input method id
826 // remain unchanged, because onActivate event needs to be sent to IME
827 // extension to update the current screen type correctly.
828 ChangeInputMethodInternal(state_
->current_input_method
,
829 false /* show_message */,
830 true /* notify_menu */);
834 scoped_refptr
<InputMethodManager::State
>
835 InputMethodManagerImpl::GetActiveIMEState() {
836 return scoped_refptr
<InputMethodManager::State
>(state_
.get());
839 InputMethodManagerImpl::InputMethodManagerImpl(
840 scoped_ptr
<InputMethodDelegate
> delegate
,
841 bool enable_extension_loading
)
842 : delegate_(delegate
.Pass()),
843 ui_session_(STATE_LOGIN_SCREEN
),
845 util_(delegate_
.get()),
846 component_extension_ime_manager_(new ComponentExtensionIMEManager()),
847 enable_extension_loading_(enable_extension_loading
) {
848 if (base::SysInfo::IsRunningOnChromeOS())
849 keyboard_
.reset(ImeKeyboard::Create());
851 keyboard_
.reset(new FakeImeKeyboard());
853 // Initializes the system IME list.
854 scoped_ptr
<ComponentExtensionIMEManagerDelegate
> comp_delegate(
855 new ComponentExtensionIMEManagerImpl());
856 component_extension_ime_manager_
->Initialize(comp_delegate
.Pass());
857 const InputMethodDescriptors
& descriptors
=
858 component_extension_ime_manager_
->GetAllIMEAsInputMethodDescriptor();
859 util_
.ResetInputMethods(descriptors
);
861 // Initializes the stat id map.
862 std::map
<int, std::vector
<std::string
> > buckets
;
863 for (InputMethodDescriptors::const_iterator it
= descriptors
.begin();
864 it
!= descriptors
.end(); ++it
) {
866 int cat_id
= static_cast<int>(
867 GetInputMethodCategory(it
->id(), &first_char
));
868 int key
= cat_id
* 1000 + first_char
;
869 buckets
[key
].push_back(it
->id());
871 for (std::map
<int, std::vector
<std::string
>>::iterator i
=
872 buckets
.begin(); i
!= buckets
.end(); ++i
) {
873 std::sort(i
->second
.begin(), i
->second
.end());
874 for (size_t j
= 0; j
< i
->second
.size() && j
< 100; ++j
) {
875 int key
= i
->first
* 100 + j
;
876 stat_id_map_
[i
->second
[j
]] = key
;
881 InputMethodManagerImpl::~InputMethodManagerImpl() {
882 if (candidate_window_controller_
.get())
883 candidate_window_controller_
->RemoveObserver(this);
886 void InputMethodManagerImpl::RecordInputMethodUsage(
887 std::string input_method_id
) {
888 UMA_HISTOGRAM_ENUMERATION("InputMethod.Category",
889 GetInputMethodCategory(input_method_id
),
890 INPUT_METHOD_CATEGORY_MAX
);
891 UMA_HISTOGRAM_SPARSE_SLOWLY("InputMethod.ID",
892 stat_id_map_
[input_method_id
]);
895 void InputMethodManagerImpl::AddObserver(
896 InputMethodManager::Observer
* observer
) {
897 observers_
.AddObserver(observer
);
900 void InputMethodManagerImpl::AddCandidateWindowObserver(
901 InputMethodManager::CandidateWindowObserver
* observer
) {
902 candidate_window_observers_
.AddObserver(observer
);
905 void InputMethodManagerImpl::RemoveObserver(
906 InputMethodManager::Observer
* observer
) {
907 observers_
.RemoveObserver(observer
);
910 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
911 InputMethodManager::CandidateWindowObserver
* observer
) {
912 candidate_window_observers_
.RemoveObserver(observer
);
915 InputMethodManager::UISessionState
InputMethodManagerImpl::GetUISessionState() {
919 void InputMethodManagerImpl::SetUISessionState(UISessionState new_ui_session
) {
920 ui_session_
= new_ui_session
;
921 switch (ui_session_
) {
922 case STATE_LOGIN_SCREEN
:
924 case STATE_BROWSER_SCREEN
:
926 case STATE_LOCK_SCREEN
:
928 case STATE_TERMINATING
: {
929 if (candidate_window_controller_
.get())
930 candidate_window_controller_
.reset();
936 scoped_ptr
<InputMethodDescriptors
>
937 InputMethodManagerImpl::GetSupportedInputMethods() const {
938 return scoped_ptr
<InputMethodDescriptors
>(new InputMethodDescriptors
).Pass();
941 const InputMethodDescriptor
* InputMethodManagerImpl::LookupInputMethod(
942 const std::string
& input_method_id
,
943 InputMethodManagerImpl::StateImpl
* state
) {
946 std::string input_method_id_to_switch
= input_method_id
;
949 if (!state
->InputMethodIsActivated(input_method_id
)) {
950 scoped_ptr
<InputMethodDescriptors
> input_methods(
951 state
->GetActiveInputMethods());
952 DCHECK(!input_methods
->empty());
953 input_method_id_to_switch
= input_methods
->at(0).id();
954 if (!input_method_id
.empty()) {
955 DVLOG(1) << "Can't change the current input method to "
956 << input_method_id
<< " since the engine is not enabled. "
957 << "Switch to " << input_method_id_to_switch
<< " instead.";
961 const InputMethodDescriptor
* descriptor
= NULL
;
962 if (extension_ime_util::IsExtensionIME(input_method_id_to_switch
)) {
963 DCHECK(state
->extra_input_methods
.find(input_method_id_to_switch
) !=
964 state
->extra_input_methods
.end());
965 descriptor
= &(state
->extra_input_methods
[input_method_id_to_switch
]);
968 util_
.GetInputMethodDescriptorFromId(input_method_id_to_switch
);
970 LOG(ERROR
) << "Unknown input method id: " << input_method_id_to_switch
;
976 void InputMethodManagerImpl::ChangeInputMethodInternal(
977 const InputMethodDescriptor
& descriptor
,
980 // No need to switch input method when terminating.
981 if (ui_session_
== STATE_TERMINATING
)
984 if (candidate_window_controller_
.get())
985 candidate_window_controller_
->Hide();
988 // Clear property list. Property list would be updated by
989 // extension IMEs via InputMethodEngine::(Set|Update)MenuItems.
990 // If the current input method is a keyboard layout, empty
991 // properties are sufficient.
992 const ui::ime::InputMethodMenuItemList empty_menu_item_list
;
993 ui::ime::InputMethodMenuManager
* input_method_menu_manager
=
994 ui::ime::InputMethodMenuManager::GetInstance();
995 input_method_menu_manager
->SetCurrentInputMethodMenuItemList(
996 empty_menu_item_list
);
999 // Disable the current engine handler.
1000 IMEEngineHandlerInterface
* engine
=
1001 IMEBridge::Get()->GetCurrentEngineHandler();
1005 // Configure the next engine handler.
1006 // This must be after |current_input_method| has been set to new input
1007 // method, because engine's Enable() method needs to access it.
1008 const std::string
& extension_id
=
1009 extension_ime_util::GetExtensionIDFromInputMethodID(descriptor
.id());
1010 const std::string
& component_id
=
1011 extension_ime_util::GetComponentIDByInputMethodID(descriptor
.id());
1012 engine
= engine_map_
[extension_id
];
1014 IMEBridge::Get()->SetCurrentEngineHandler(engine
);
1017 engine
->Enable(component_id
);
1019 // If no engine to enable, cancel the virtual keyboard url override so that
1020 // it can use the fallback system virtual keyboard UI.
1021 keyboard::SetOverrideContentUrl(GURL());
1022 keyboard::KeyboardController
* keyboard_controller
=
1023 keyboard::KeyboardController::GetInstance();
1024 if (keyboard_controller
)
1025 keyboard_controller
->Reload();
1028 // Change the keyboard layout to a preferred layout for the input method.
1029 if (!keyboard_
->SetCurrentKeyboardLayoutByName(
1030 descriptor
.GetPreferredKeyboardLayout())) {
1031 LOG(ERROR
) << "Failed to change keyboard layout to "
1032 << descriptor
.GetPreferredKeyboardLayout();
1035 // Update input method indicators (e.g. "US", "DV") in Chrome windows.
1036 FOR_EACH_OBSERVER(InputMethodManager::Observer
,
1038 InputMethodChanged(this, show_message
));
1041 void InputMethodManagerImpl::LoadNecessaryComponentExtensions(
1042 InputMethodManagerImpl::StateImpl
* state
) {
1043 // Load component extensions but also update |active_input_method_ids| as
1044 // some component extension IMEs may have been removed from the Chrome OS
1045 // image. If specified component extension IME no longer exists, falling back
1046 // to an existing IME.
1048 std::vector
<std::string
> unfiltered_input_method_ids
;
1049 unfiltered_input_method_ids
.swap(state
->active_input_method_ids
);
1050 for (size_t i
= 0; i
< unfiltered_input_method_ids
.size(); ++i
) {
1051 if (!extension_ime_util::IsComponentExtensionIME(
1052 unfiltered_input_method_ids
[i
])) {
1053 // Legacy IMEs or xkb layouts are alwayes active.
1054 state
->active_input_method_ids
.push_back(unfiltered_input_method_ids
[i
]);
1055 } else if (component_extension_ime_manager_
->IsWhitelisted(
1056 unfiltered_input_method_ids
[i
])) {
1057 if (enable_extension_loading_
) {
1058 component_extension_ime_manager_
->LoadComponentExtensionIME(
1059 state
->profile
, unfiltered_input_method_ids
[i
]);
1062 state
->active_input_method_ids
.push_back(unfiltered_input_method_ids
[i
]);
1067 void InputMethodManagerImpl::ActivateInputMethodMenuItem(
1068 const std::string
& key
) {
1069 DCHECK(!key
.empty());
1071 if (ui::ime::InputMethodMenuManager::GetInstance()->
1072 HasInputMethodMenuItemForKey(key
)) {
1073 IMEEngineHandlerInterface
* engine
=
1074 IMEBridge::Get()->GetCurrentEngineHandler();
1076 engine
->PropertyActivate(key
);
1080 DVLOG(1) << "ActivateInputMethodMenuItem: unknown key: " << key
;
1083 bool InputMethodManagerImpl::IsISOLevel5ShiftUsedByCurrentInputMethod() const {
1084 return keyboard_
->IsISOLevel5ShiftAvailable();
1087 bool InputMethodManagerImpl::IsAltGrUsedByCurrentInputMethod() const {
1088 return keyboard_
->IsAltGrAvailable();
1091 ImeKeyboard
* InputMethodManagerImpl::GetImeKeyboard() {
1092 return keyboard_
.get();
1095 InputMethodUtil
* InputMethodManagerImpl::GetInputMethodUtil() {
1099 ComponentExtensionIMEManager
*
1100 InputMethodManagerImpl::GetComponentExtensionIMEManager() {
1101 return component_extension_ime_manager_
.get();
1104 scoped_refptr
<InputMethodManager::State
> InputMethodManagerImpl::CreateNewState(
1106 StateImpl
* new_state
= new StateImpl(this, profile
);
1108 // Active IM should be set to owner's default.
1109 PrefService
* prefs
= g_browser_process
->local_state();
1110 const std::string initial_input_method_id
=
1111 prefs
->GetString(chromeos::language_prefs::kPreferredKeyboardLayout
);
1113 const InputMethodDescriptor
* descriptor
=
1114 GetInputMethodUtil()->GetInputMethodDescriptorFromId(
1115 initial_input_method_id
.empty()
1116 ? GetInputMethodUtil()->GetFallbackInputMethodDescriptor().id()
1117 : initial_input_method_id
);
1119 new_state
->active_input_method_ids
.push_back(descriptor
->id());
1120 new_state
->current_input_method
= *descriptor
;
1122 return scoped_refptr
<InputMethodManager::State
>(new_state
);
1125 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
1126 CandidateWindowController
* candidate_window_controller
) {
1127 candidate_window_controller_
.reset(candidate_window_controller
);
1128 candidate_window_controller_
->AddObserver(this);
1131 void InputMethodManagerImpl::SetImeKeyboardForTesting(ImeKeyboard
* keyboard
) {
1132 keyboard_
.reset(keyboard
);
1135 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
1136 scoped_ptr
<ComponentExtensionIMEManagerDelegate
> delegate
) {
1137 component_extension_ime_manager_
->Initialize(delegate
.Pass());
1138 util_
.ResetInputMethods(
1139 component_extension_ime_manager_
->GetAllIMEAsInputMethodDescriptor());
1142 void InputMethodManagerImpl::CandidateClicked(int index
) {
1143 IMEEngineHandlerInterface
* engine
=
1144 IMEBridge::Get()->GetCurrentEngineHandler();
1146 engine
->CandidateClicked(index
);
1149 void InputMethodManagerImpl::CandidateWindowOpened() {
1150 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver
,
1151 candidate_window_observers_
,
1152 CandidateWindowOpened(this));
1155 void InputMethodManagerImpl::CandidateWindowClosed() {
1156 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver
,
1157 candidate_window_observers_
,
1158 CandidateWindowClosed(this));
1161 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
1162 if (candidate_window_controller_
.get())
1165 candidate_window_controller_
.reset(
1166 CandidateWindowController::CreateCandidateWindowController());
1167 candidate_window_controller_
->AddObserver(this);
1170 } // namespace input_method
1171 } // namespace chromeos