1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/input_method/input_method_manager_impl.h"
6
7 #include <algorithm> // std::find
8
9 #include "ash/ime/input_method_menu_item.h"
10 #include "ash/ime/input_method_menu_manager.h"
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/prefs/pref_service.h"
16 #include "base/strings/string_split.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/sys_info.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
22 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h"
23 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
24 #include "chrome/browser/chromeos/language_preferences.h"
25 #include "chrome/browser/chromeos/login/users/user_manager.h"
26 #include "chrome/browser/profiles/profile_manager.h"
27 #include "chrome/common/pref_names.h"
28 #include "chromeos/ime/component_extension_ime_manager.h"
29 #include "chromeos/ime/extension_ime_util.h"
30 #include "chromeos/ime/fake_ime_keyboard.h"
31 #include "chromeos/ime/ime_keyboard.h"
32 #include "chromeos/ime/input_method_delegate.h"
33 #include "third_party/icu/source/common/unicode/uloc.h"
34 #include "ui/base/accelerators/accelerator.h"
35
36 namespace chromeos {
37 namespace input_method {
38
39 namespace {
40
Contains(const std::vector<std::string> & container,const std::string & value)41 bool Contains(const std::vector<std::string>& container,
42 const std::string& value) {
43 return std::find(container.begin(), container.end(), value) !=
44 container.end();
45 }
46
47 } // namespace
48
IsLoginKeyboard(const std::string & layout) const49 bool InputMethodManagerImpl::IsLoginKeyboard(
50 const std::string& layout) const {
51 return util_.IsLoginKeyboard(layout);
52 }
53
MigrateInputMethods(std::vector<std::string> * input_method_ids)54 bool InputMethodManagerImpl::MigrateInputMethods(
55 std::vector<std::string>* input_method_ids) {
56 return util_.MigrateInputMethods(input_method_ids);
57 }
58
InputMethodManagerImpl(scoped_ptr<InputMethodDelegate> delegate)59 InputMethodManagerImpl::InputMethodManagerImpl(
60 scoped_ptr<InputMethodDelegate> delegate)
61 : delegate_(delegate.Pass()),
62 state_(STATE_LOGIN_SCREEN),
63 util_(delegate_.get(), whitelist_.GetSupportedInputMethods()),
64 component_extension_ime_manager_(new ComponentExtensionIMEManager()),
65 weak_ptr_factory_(this) {
66 }
67
~InputMethodManagerImpl()68 InputMethodManagerImpl::~InputMethodManagerImpl() {
69 if (candidate_window_controller_.get())
70 candidate_window_controller_->RemoveObserver(this);
71 }
72
AddObserver(InputMethodManager::Observer * observer)73 void InputMethodManagerImpl::AddObserver(
74 InputMethodManager::Observer* observer) {
75 observers_.AddObserver(observer);
76 }
77
AddCandidateWindowObserver(InputMethodManager::CandidateWindowObserver * observer)78 void InputMethodManagerImpl::AddCandidateWindowObserver(
79 InputMethodManager::CandidateWindowObserver* observer) {
80 candidate_window_observers_.AddObserver(observer);
81 }
82
RemoveObserver(InputMethodManager::Observer * observer)83 void InputMethodManagerImpl::RemoveObserver(
84 InputMethodManager::Observer* observer) {
85 observers_.RemoveObserver(observer);
86 }
87
RemoveCandidateWindowObserver(InputMethodManager::CandidateWindowObserver * observer)88 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
89 InputMethodManager::CandidateWindowObserver* observer) {
90 candidate_window_observers_.RemoveObserver(observer);
91 }
92
SetState(State new_state)93 void InputMethodManagerImpl::SetState(State new_state) {
94 const State old_state = state_;
95 state_ = new_state;
96 switch (state_) {
97 case STATE_LOGIN_SCREEN:
98 break;
99 case STATE_BROWSER_SCREEN:
100 if (old_state == STATE_LOCK_SCREEN)
101 OnScreenUnlocked();
102 break;
103 case STATE_LOCK_SCREEN:
104 OnScreenLocked();
105 break;
106 case STATE_TERMINATING: {
107 if (candidate_window_controller_.get())
108 candidate_window_controller_.reset();
109 break;
110 }
111 }
112 }
113
114 scoped_ptr<InputMethodDescriptors>
GetSupportedInputMethods() const115 InputMethodManagerImpl::GetSupportedInputMethods() const {
116 if (!IsXkbComponentExtensionAvailable())
117 return whitelist_.GetSupportedInputMethods().Pass();
118 return scoped_ptr<InputMethodDescriptors>(new InputMethodDescriptors).Pass();
119 }
120
121 scoped_ptr<InputMethodDescriptors>
GetActiveInputMethods() const122 InputMethodManagerImpl::GetActiveInputMethods() const {
123 scoped_ptr<InputMethodDescriptors> result(new InputMethodDescriptors);
124 // Build the active input method descriptors from the active input
125 // methods cache |active_input_method_ids_|.
126 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
127 const std::string& input_method_id = active_input_method_ids_[i];
128 const InputMethodDescriptor* descriptor =
129 util_.GetInputMethodDescriptorFromId(input_method_id);
130 if (descriptor) {
131 result->push_back(*descriptor);
132 } else {
133 std::map<std::string, InputMethodDescriptor>::const_iterator ix =
134 extra_input_methods_.find(input_method_id);
135 if (ix != extra_input_methods_.end())
136 result->push_back(ix->second);
137 else
138 DVLOG(1) << "Descriptor is not found for: " << input_method_id;
139 }
140 }
141 if (result->empty()) {
142 // Initially |active_input_method_ids_| is empty. browser_tests might take
143 // this path.
144 result->push_back(
145 InputMethodUtil::GetFallbackInputMethodDescriptor());
146 }
147 return result.Pass();
148 }
149
150 const std::vector<std::string>&
GetActiveInputMethodIds() const151 InputMethodManagerImpl::GetActiveInputMethodIds() const {
152 return active_input_method_ids_;
153 }
154
GetNumActiveInputMethods() const155 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const {
156 return active_input_method_ids_.size();
157 }
158
GetInputMethodFromId(const std::string & input_method_id) const159 const InputMethodDescriptor* InputMethodManagerImpl::GetInputMethodFromId(
160 const std::string& input_method_id) const {
161 const InputMethodDescriptor* ime = util_.GetInputMethodDescriptorFromId(
162 input_method_id);
163 if (!ime) {
164 std::map<std::string, InputMethodDescriptor>::const_iterator ix =
165 extra_input_methods_.find(input_method_id);
166 if (ix != extra_input_methods_.end())
167 ime = &ix->second;
168 }
169 return ime;
170 }
171
EnableLoginLayouts(const std::string & language_code,const std::vector<std::string> & initial_layouts)172 void InputMethodManagerImpl::EnableLoginLayouts(
173 const std::string& language_code,
174 const std::vector<std::string>& initial_layouts) {
175 if (state_ == STATE_TERMINATING)
176 return;
177
178 // First, hardware keyboard layout should be shown.
179 std::vector<std::string> candidates =
180 util_.GetHardwareLoginInputMethodIds();
181
182 // Seocnd, locale based input method should be shown.
183 // Add input methods associated with the language.
184 std::vector<std::string> layouts_from_locale;
185 util_.GetInputMethodIdsFromLanguageCode(language_code,
186 kKeyboardLayoutsOnly,
187 &layouts_from_locale);
188 candidates.insert(candidates.end(), layouts_from_locale.begin(),
189 layouts_from_locale.end());
190
191 std::vector<std::string> layouts;
192 // First, add the initial input method ID, if it's requested, to
193 // layouts, so it appears first on the list of active input
194 // methods at the input language status menu.
195 for (size_t i = 0; i < initial_layouts.size(); ++i) {
196 if (util_.IsValidInputMethodId(initial_layouts[i])) {
197 if (IsLoginKeyboard(initial_layouts[i])) {
198 layouts.push_back(initial_layouts[i]);
199 } else {
200 DVLOG(1)
201 << "EnableLoginLayouts: ignoring non-login initial keyboard layout:"
202 << initial_layouts[i];
203 }
204 } else if (!initial_layouts[i].empty()) {
205 DVLOG(1) << "EnableLoginLayouts: ignoring non-keyboard or invalid ID: "
206 << initial_layouts[i];
207 }
208 }
209
210 // Add candidates to layouts, while skipping duplicates.
211 for (size_t i = 0; i < candidates.size(); ++i) {
212 const std::string& candidate = candidates[i];
213 // Not efficient, but should be fine, as the two vectors are very
214 // short (2-5 items).
215 if (!Contains(layouts, candidate) && IsLoginKeyboard(candidate))
216 layouts.push_back(candidate);
217 }
218
219 MigrateInputMethods(&layouts);
220 active_input_method_ids_.swap(layouts);
221
222 // Initialize candidate window controller and widgets such as
223 // candidate window, infolist and mode indicator. Note, mode
224 // indicator is used by only keyboard layout input methods.
225 if (active_input_method_ids_.size() > 1)
226 MaybeInitializeCandidateWindowController();
227
228 // you can pass empty |initial_layout|.
229 ChangeInputMethod(initial_layouts.empty() ? "" :
230 extension_ime_util::GetInputMethodIDByEngineID(initial_layouts[0]));
231 }
232
233 // Adds new input method to given list.
EnableInputMethodImpl(const std::string & input_method_id,std::vector<std::string> * new_active_input_method_ids) const234 bool InputMethodManagerImpl::EnableInputMethodImpl(
235 const std::string& input_method_id,
236 std::vector<std::string>* new_active_input_method_ids) const {
237 DCHECK(new_active_input_method_ids);
238 if (!util_.IsValidInputMethodId(input_method_id)) {
239 DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id;
240 return false;
241 }
242
243 if (!Contains(*new_active_input_method_ids, input_method_id))
244 new_active_input_method_ids->push_back(input_method_id);
245
246 return true;
247 }
248
249 // Starts or stops the system input method framework as needed.
ReconfigureIMFramework()250 void InputMethodManagerImpl::ReconfigureIMFramework() {
251 LoadNecessaryComponentExtensions();
252
253 // Initialize candidate window controller and widgets such as
254 // candidate window, infolist and mode indicator. Note, mode
255 // indicator is used by only keyboard layout input methods.
256 MaybeInitializeCandidateWindowController();
257 }
258
EnableInputMethod(const std::string & input_method_id)259 bool InputMethodManagerImpl::EnableInputMethod(
260 const std::string& input_method_id) {
261 if (!EnableInputMethodImpl(input_method_id, &active_input_method_ids_))
262 return false;
263
264 ReconfigureIMFramework();
265 return true;
266 }
267
ReplaceEnabledInputMethods(const std::vector<std::string> & new_active_input_method_ids)268 bool InputMethodManagerImpl::ReplaceEnabledInputMethods(
269 const std::vector<std::string>& new_active_input_method_ids) {
270 if (state_ == STATE_TERMINATING)
271 return false;
272
273 // Filter unknown or obsolete IDs.
274 std::vector<std::string> new_active_input_method_ids_filtered;
275
276 for (size_t i = 0; i < new_active_input_method_ids.size(); ++i)
277 EnableInputMethodImpl(new_active_input_method_ids[i],
278 &new_active_input_method_ids_filtered);
279
280 if (new_active_input_method_ids_filtered.empty()) {
281 DVLOG(1) << "ReplaceEnabledInputMethods: No valid input method ID";
282 return false;
283 }
284
285 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
286 // keep relative order of the extension input method IDs.
287 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
288 const std::string& input_method_id = active_input_method_ids_[i];
289 if (extension_ime_util::IsExtensionIME(input_method_id))
290 new_active_input_method_ids_filtered.push_back(input_method_id);
291 }
292 active_input_method_ids_.swap(new_active_input_method_ids_filtered);
293 MigrateInputMethods(&active_input_method_ids_);
294
295 ReconfigureIMFramework();
296
297 // If |current_input_method| is no longer in |active_input_method_ids_|,
298 // ChangeInputMethod() picks the first one in |active_input_method_ids_|.
299 ChangeInputMethod(current_input_method_.id());
300 return true;
301 }
302
ChangeInputMethod(const std::string & input_method_id)303 void InputMethodManagerImpl::ChangeInputMethod(
304 const std::string& input_method_id) {
305 ChangeInputMethodInternal(input_method_id, false);
306 }
307
ChangeInputMethodInternal(const std::string & input_method_id,bool show_message)308 bool InputMethodManagerImpl::ChangeInputMethodInternal(
309 const std::string& input_method_id,
310 bool show_message) {
311 if (state_ == STATE_TERMINATING)
312 return false;
313
314 std::string input_method_id_to_switch = input_method_id;
315
316 // Sanity check.
317 if (!InputMethodIsActivated(input_method_id)) {
318 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
319 DCHECK(!input_methods->empty());
320 input_method_id_to_switch = input_methods->at(0).id();
321 if (!input_method_id.empty()) {
322 DVLOG(1) << "Can't change the current input method to "
323 << input_method_id << " since the engine is not enabled. "
324 << "Switch to " << input_method_id_to_switch << " instead.";
325 }
326 }
327
328 if (!component_extension_ime_manager_->IsInitialized() &&
329 !InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) {
330 // We can't change input method before the initialization of
331 // component extension ime manager. ChangeInputMethod will be
332 // called with |pending_input_method_| when the initialization is
333 // done.
334 pending_input_method_ = input_method_id_to_switch;
335 return false;
336 }
337 pending_input_method_.clear();
338
339 // Hide candidate window and info list.
340 if (candidate_window_controller_.get())
341 candidate_window_controller_->Hide();
342
343 // Disable the current engine handler.
344 IMEEngineHandlerInterface* engine =
345 IMEBridge::Get()->GetCurrentEngineHandler();
346 if (engine)
347 engine->Disable();
348
349 // Configure the next engine handler.
350 if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch) &&
351 !extension_ime_util::IsKeyboardLayoutExtension(
352 input_method_id_to_switch)) {
353 IMEBridge::Get()->SetCurrentEngineHandler(NULL);
354 } else {
355 IMEEngineHandlerInterface* next_engine =
356 profile_engine_map_[GetProfile()][input_method_id_to_switch];
357 if (next_engine) {
358 IMEBridge::Get()->SetCurrentEngineHandler(next_engine);
359 next_engine->Enable();
360 }
361 }
362
363 // TODO(komatsu): Check if it is necessary to perform the above routine
364 // when the current input method is equal to |input_method_id_to_swich|.
365 if (current_input_method_.id() != input_method_id_to_switch) {
366 // Clear property list. Property list would be updated by
367 // extension IMEs via InputMethodEngine::(Set|Update)MenuItems.
368 // If the current input method is a keyboard layout, empty
369 // properties are sufficient.
370 const ash::ime::InputMethodMenuItemList empty_menu_item_list;
371 ash::ime::InputMethodMenuManager* input_method_menu_manager =
372 ash::ime::InputMethodMenuManager::GetInstance();
373 input_method_menu_manager->SetCurrentInputMethodMenuItemList(
374 empty_menu_item_list);
375
376 const InputMethodDescriptor* descriptor = NULL;
377 if (extension_ime_util::IsExtensionIME(input_method_id_to_switch)) {
378 DCHECK(extra_input_methods_.find(input_method_id_to_switch) !=
379 extra_input_methods_.end());
380 descriptor = &(extra_input_methods_[input_method_id_to_switch]);
381 } else {
382 descriptor =
383 util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
384 if (!descriptor)
385 LOG(ERROR) << "Unknown input method id: " << input_method_id_to_switch;
386 }
387 DCHECK(descriptor);
388
389 previous_input_method_ = current_input_method_;
390 current_input_method_ = *descriptor;
391 }
392
393 // Change the keyboard layout to a preferred layout for the input method.
394 if (!keyboard_->SetCurrentKeyboardLayoutByName(
395 current_input_method_.GetPreferredKeyboardLayout())) {
396 LOG(ERROR) << "Failed to change keyboard layout to "
397 << current_input_method_.GetPreferredKeyboardLayout();
398 }
399
400 // Update input method indicators (e.g. "US", "DV") in Chrome windows.
401 FOR_EACH_OBSERVER(InputMethodManager::Observer,
402 observers_,
403 InputMethodChanged(this, show_message));
404 return true;
405 }
406
IsXkbComponentExtensionAvailable() const407 bool InputMethodManagerImpl::IsXkbComponentExtensionAvailable() const {
408 if (!component_extension_ime_manager_->IsInitialized())
409 return false;
410 InputMethodDescriptors imes =
411 component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor();
412 for (size_t i = 0; i < imes.size(); ++i) {
413 if (StartsWithASCII(extension_ime_util::MaybeGetLegacyXkbId(
414 imes[i].id()), "xkb:", true))
415 return true;
416 }
417 return false;
418 }
419
OnComponentExtensionInitialized(scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate)420 void InputMethodManagerImpl::OnComponentExtensionInitialized(
421 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
422 DCHECK(thread_checker_.CalledOnValidThread());
423 component_extension_ime_manager_->Initialize(delegate.Pass());
424 InputMethodDescriptors imes =
425 component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor();
426 // In case of XKB extension is not available (e.g. linux_chromeos), don't
427 // reset the input methods in InputMethodUtil, Instead append input methods.
428 if (IsXkbComponentExtensionAvailable())
429 util_.ResetInputMethods(imes);
430 else
431 util_.AppendInputMethods(imes);
432
433 LoadNecessaryComponentExtensions();
434
435 if (!pending_input_method_.empty())
436 ChangeInputMethodInternal(pending_input_method_, false);
437 }
438
LoadNecessaryComponentExtensions()439 void InputMethodManagerImpl::LoadNecessaryComponentExtensions() {
440 if (!component_extension_ime_manager_->IsInitialized())
441 return;
442 // Load component extensions but also update |active_input_method_ids_| as
443 // some component extension IMEs may have been removed from the Chrome OS
444 // image. If specified component extension IME no longer exists, falling back
445 // to an existing IME.
446 std::vector<std::string> unfiltered_input_method_ids;
447 unfiltered_input_method_ids.swap(active_input_method_ids_);
448 for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) {
449 if (!extension_ime_util::IsComponentExtensionIME(
450 unfiltered_input_method_ids[i])) {
451 // Legacy IMEs or xkb layouts are alwayes active.
452 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
453 } else if (component_extension_ime_manager_->IsWhitelisted(
454 unfiltered_input_method_ids[i])) {
455 component_extension_ime_manager_->LoadComponentExtensionIME(
456 unfiltered_input_method_ids[i]);
457 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]);
458 }
459 }
460 // TODO(shuchen): move this call in ComponentExtensionIMEManager.
461 component_extension_ime_manager_->NotifyInitialized();
462 }
463
ActivateInputMethodMenuItem(const std::string & key)464 void InputMethodManagerImpl::ActivateInputMethodMenuItem(
465 const std::string& key) {
466 DCHECK(!key.empty());
467
468 if (ash::ime::InputMethodMenuManager::GetInstance()->
469 HasInputMethodMenuItemForKey(key)) {
470 IMEEngineHandlerInterface* engine =
471 IMEBridge::Get()->GetCurrentEngineHandler();
472 if (engine)
473 engine->PropertyActivate(key);
474 return;
475 }
476
477 DVLOG(1) << "ActivateInputMethodMenuItem: unknown key: " << key;
478 }
479
AddInputMethodExtension(Profile * profile,const std::string & id,InputMethodEngineInterface * engine)480 void InputMethodManagerImpl::AddInputMethodExtension(
481 Profile* profile,
482 const std::string& id,
483 InputMethodEngineInterface* engine) {
484 if (state_ == STATE_TERMINATING)
485 return;
486
487 DCHECK(engine);
488
489 profile_engine_map_[profile][id] = engine;
490
491 if (id == current_input_method_.id()) {
492 IMEBridge::Get()->SetCurrentEngineHandler(engine);
493 engine->Enable();
494 }
495
496 if (extension_ime_util::IsComponentExtensionIME(id))
497 return;
498
499 CHECK(extension_ime_util::IsExtensionIME(id))
500 << id << "is not a valid extension input method ID";
501
502 const InputMethodDescriptor& descriptor = engine->GetDescriptor();
503 extra_input_methods_[id] = descriptor;
504
505 if (Contains(enabled_extension_imes_, id)) {
506 if (!Contains(active_input_method_ids_, id)) {
507 active_input_method_ids_.push_back(id);
508 } else {
509 DVLOG(1) << "AddInputMethodExtension: alread added: "
510 << id << ", " << descriptor.name();
511 }
512
513 // Ensure that the input method daemon is running.
514 MaybeInitializeCandidateWindowController();
515 }
516 }
517
RemoveInputMethodExtension(Profile * profile,const std::string & id)518 void InputMethodManagerImpl::RemoveInputMethodExtension(Profile* profile,
519 const std::string& id) {
520 if (!extension_ime_util::IsExtensionIME(id))
521 DVLOG(1) << id << " is not a valid extension input method ID.";
522
523 std::vector<std::string>::iterator i = std::find(
524 active_input_method_ids_.begin(), active_input_method_ids_.end(), id);
525 if (i != active_input_method_ids_.end())
526 active_input_method_ids_.erase(i);
527 extra_input_methods_.erase(id);
528
529 EngineMap& engine_map = profile_engine_map_[profile];
530 if (IMEBridge::Get()->GetCurrentEngineHandler() == engine_map[id])
531 IMEBridge::Get()->SetCurrentEngineHandler(NULL);
532 engine_map.erase(id);
533
534 // No need to switch input method when terminating.
535 if (state_ != STATE_TERMINATING) {
536 // If |current_input_method| is no longer in |active_input_method_ids_|,
537 // switch to the first one in |active_input_method_ids_|.
538 ChangeInputMethod(current_input_method_.id());
539 }
540 }
541
GetInputMethodExtensions(InputMethodDescriptors * result)542 void InputMethodManagerImpl::GetInputMethodExtensions(
543 InputMethodDescriptors* result) {
544 // Build the extension input method descriptors from the extra input
545 // methods cache |extra_input_methods_|.
546 std::map<std::string, InputMethodDescriptor>::iterator iter;
547 for (iter = extra_input_methods_.begin(); iter != extra_input_methods_.end();
548 ++iter) {
549 if (extension_ime_util::IsExtensionIME(iter->first))
550 result->push_back(iter->second);
551 }
552 }
553
SetEnabledExtensionImes(std::vector<std::string> * ids)554 void InputMethodManagerImpl::SetEnabledExtensionImes(
555 std::vector<std::string>* ids) {
556 enabled_extension_imes_.clear();
557 enabled_extension_imes_.insert(enabled_extension_imes_.end(),
558 ids->begin(),
559 ids->end());
560
561 bool active_imes_changed = false;
562
563 for (std::map<std::string, InputMethodDescriptor>::iterator extra_iter =
564 extra_input_methods_.begin(); extra_iter != extra_input_methods_.end();
565 ++extra_iter) {
566 if (extension_ime_util::IsComponentExtensionIME(
567 extra_iter->first))
568 continue; // Do not filter component extension.
569 std::vector<std::string>::iterator active_iter = std::find(
570 active_input_method_ids_.begin(), active_input_method_ids_.end(),
571 extra_iter->first);
572
573 bool active = active_iter != active_input_method_ids_.end();
574 bool enabled = Contains(enabled_extension_imes_, extra_iter->first);
575
576 if (active && !enabled)
577 active_input_method_ids_.erase(active_iter);
578
579 if (!active && enabled)
580 active_input_method_ids_.push_back(extra_iter->first);
581
582 if (active == !enabled)
583 active_imes_changed = true;
584 }
585
586 if (active_imes_changed) {
587 MaybeInitializeCandidateWindowController();
588
589 // If |current_input_method| is no longer in |active_input_method_ids_|,
590 // switch to the first one in |active_input_method_ids_|.
591 ChangeInputMethod(current_input_method_.id());
592 }
593 }
594
SetInputMethodLoginDefaultFromVPD(const std::string & locale,const std::string & oem_layout)595 void InputMethodManagerImpl::SetInputMethodLoginDefaultFromVPD(
596 const std::string& locale, const std::string& oem_layout) {
597 std::string layout;
598 if (!oem_layout.empty()) {
599 // If the OEM layout information is provided, use it.
600 layout = oem_layout;
601 } else {
602 // Otherwise, determine the hardware keyboard from the locale.
603 std::vector<std::string> input_method_ids;
604 if (util_.GetInputMethodIdsFromLanguageCode(
605 locale,
606 chromeos::input_method::kKeyboardLayoutsOnly,
607 &input_method_ids)) {
608 // The output list |input_method_ids| is sorted by popularity, hence
609 // input_method_ids[0] now contains the most popular keyboard layout
610 // for the given locale.
611 DCHECK_GE(input_method_ids.size(), 1U);
612 layout = input_method_ids[0];
613 }
614 }
615
616 if (layout.empty())
617 return;
618
619 std::vector<std::string> layouts;
620 base::SplitString(layout, ',', &layouts);
621 MigrateInputMethods(&layouts);
622
623 PrefService* prefs = g_browser_process->local_state();
624 prefs->SetString(prefs::kHardwareKeyboardLayout, JoinString(layouts, ","));
625
626 // This asks the file thread to save the prefs (i.e. doesn't block).
627 // The latest values of Local State reside in memory so we can safely
628 // get the value of kHardwareKeyboardLayout even if the data is not
629 // yet saved to disk.
630 prefs->CommitPendingWrite();
631
632 util_.UpdateHardwareLayoutCache();
633
634 EnableLoginLayouts(locale, layouts);
635 }
636
SetInputMethodLoginDefault()637 void InputMethodManagerImpl::SetInputMethodLoginDefault() {
638 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
639 // and US dvorak keyboard layouts.
640 if (g_browser_process && g_browser_process->local_state()) {
641 const std::string locale = g_browser_process->GetApplicationLocale();
642 // If the preferred keyboard for the login screen has been saved, use it.
643 PrefService* prefs = g_browser_process->local_state();
644 std::string initial_input_method_id =
645 prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
646 std::vector<std::string> input_methods_to_be_enabled;
647 if (initial_input_method_id.empty()) {
648 // If kPreferredKeyboardLayout is not specified, use the hardware layout.
649 input_methods_to_be_enabled = util_.GetHardwareLoginInputMethodIds();
650 } else {
651 input_methods_to_be_enabled.push_back(initial_input_method_id);
652 }
653 EnableLoginLayouts(locale, input_methods_to_be_enabled);
654 }
655 }
656
SwitchToNextInputMethod()657 bool InputMethodManagerImpl::SwitchToNextInputMethod() {
658 // Sanity checks.
659 if (active_input_method_ids_.empty()) {
660 DVLOG(1) << "active input method is empty";
661 return false;
662 }
663
664 if (current_input_method_.id().empty()) {
665 DVLOG(1) << "current_input_method_ is unknown";
666 return false;
667 }
668
669 // Do not consume key event if there is only one input method is enabled.
670 // Ctrl+Space or Alt+Shift may be used by other application.
671 if (active_input_method_ids_.size() == 1)
672 return false;
673
674 // Find the next input method and switch to it.
675 SwitchToNextInputMethodInternal(active_input_method_ids_,
676 current_input_method_.id());
677 return true;
678 }
679
SwitchToPreviousInputMethod(const ui::Accelerator & accelerator)680 bool InputMethodManagerImpl::SwitchToPreviousInputMethod(
681 const ui::Accelerator& accelerator) {
682 // Sanity check.
683 if (active_input_method_ids_.empty()) {
684 DVLOG(1) << "active input method is empty";
685 return false;
686 }
687
688 // Do not consume key event if there is only one input method is enabled.
689 // Ctrl+Space or Alt+Shift may be used by other application.
690 if (active_input_method_ids_.size() == 1)
691 return false;
692
693 if (accelerator.type() == ui::ET_KEY_RELEASED)
694 return true;
695
696 if (previous_input_method_.id().empty() ||
697 previous_input_method_.id() == current_input_method_.id()) {
698 return SwitchToNextInputMethod();
699 }
700
701 std::vector<std::string>::const_iterator iter =
702 std::find(active_input_method_ids_.begin(),
703 active_input_method_ids_.end(),
704 previous_input_method_.id());
705 if (iter == active_input_method_ids_.end()) {
706 // previous_input_method_ is not supported.
707 return SwitchToNextInputMethod();
708 }
709 ChangeInputMethodInternal(*iter, true);
710 return true;
711 }
712
SwitchInputMethod(const ui::Accelerator & accelerator)713 bool InputMethodManagerImpl::SwitchInputMethod(
714 const ui::Accelerator& accelerator) {
715 // Sanity check.
716 if (active_input_method_ids_.empty()) {
717 DVLOG(1) << "active input method is empty";
718 return false;
719 }
720
721 // Get the list of input method ids for the |accelerator|. For example, get
722 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
723 std::vector<std::string> input_method_ids_to_switch;
724 switch (accelerator.key_code()) {
725 case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard
726 input_method_ids_to_switch.push_back(
727 extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
728 break;
729 case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard
730 input_method_ids_to_switch.push_back(
731 extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
732 break;
733 case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard
734 case ui::VKEY_DBE_DBCSCHAR:
735 input_method_ids_to_switch.push_back(
736 extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"));
737 input_method_ids_to_switch.push_back(
738 extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"));
739 break;
740 default:
741 NOTREACHED();
742 break;
743 }
744 if (input_method_ids_to_switch.empty()) {
745 DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code();
746 return false;
747 }
748
749 // Obtain the intersection of input_method_ids_to_switch and
750 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
751 // preserved.
752 std::vector<std::string> ids;
753 for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
754 const std::string& id = input_method_ids_to_switch[i];
755 if (Contains(active_input_method_ids_, id))
756 ids.push_back(id);
757 }
758 if (ids.empty()) {
759 // No input method for the accelerator is active. For example, we should
760 // just ignore VKEY_HANGUL when mozc-hangul is not active.
761 return false;
762 }
763
764 SwitchToNextInputMethodInternal(ids, current_input_method_.id());
765 return true; // consume the accelerator.
766 }
767
SwitchToNextInputMethodInternal(const std::vector<std::string> & input_method_ids,const std::string & current_input_method_id)768 void InputMethodManagerImpl::SwitchToNextInputMethodInternal(
769 const std::vector<std::string>& input_method_ids,
770 const std::string& current_input_method_id) {
771 std::vector<std::string>::const_iterator iter =
772 std::find(input_method_ids.begin(),
773 input_method_ids.end(),
774 current_input_method_id);
775 if (iter != input_method_ids.end())
776 ++iter;
777 if (iter == input_method_ids.end())
778 iter = input_method_ids.begin();
779 ChangeInputMethodInternal(*iter, true);
780 }
781
GetCurrentInputMethod() const782 InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const {
783 if (current_input_method_.id().empty())
784 return InputMethodUtil::GetFallbackInputMethodDescriptor();
785
786 return current_input_method_;
787 }
788
IsISOLevel5ShiftUsedByCurrentInputMethod() const789 bool InputMethodManagerImpl::IsISOLevel5ShiftUsedByCurrentInputMethod() const {
790 return keyboard_->IsISOLevel5ShiftAvailable();
791 }
792
IsAltGrUsedByCurrentInputMethod() const793 bool InputMethodManagerImpl::IsAltGrUsedByCurrentInputMethod() const {
794 return keyboard_->IsAltGrAvailable();
795 }
796
GetImeKeyboard()797 ImeKeyboard* InputMethodManagerImpl::GetImeKeyboard() {
798 return keyboard_.get();
799 }
800
GetInputMethodUtil()801 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
802 return &util_;
803 }
804
805 ComponentExtensionIMEManager*
GetComponentExtensionIMEManager()806 InputMethodManagerImpl::GetComponentExtensionIMEManager() {
807 DCHECK(thread_checker_.CalledOnValidThread());
808 return component_extension_ime_manager_.get();
809 }
810
InitializeComponentExtension()811 void InputMethodManagerImpl::InitializeComponentExtension() {
812 ComponentExtensionIMEManagerImpl* impl =
813 new ComponentExtensionIMEManagerImpl();
814 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate(impl);
815 impl->InitializeAsync(base::Bind(
816 &InputMethodManagerImpl::OnComponentExtensionInitialized,
817 weak_ptr_factory_.GetWeakPtr(),
818 base::Passed(&delegate)));
819 }
820
Init(base::SequencedTaskRunner * ui_task_runner)821 void InputMethodManagerImpl::Init(base::SequencedTaskRunner* ui_task_runner) {
822 DCHECK(thread_checker_.CalledOnValidThread());
823
824 if (base::SysInfo::IsRunningOnChromeOS())
825 keyboard_.reset(ImeKeyboard::Create());
826 else
827 keyboard_.reset(new FakeImeKeyboard());
828
829 // We can't call impl->Initialize here, because file thread is not available
830 // at this moment.
831 ui_task_runner->PostTask(
832 FROM_HERE,
833 base::Bind(&InputMethodManagerImpl::InitializeComponentExtension,
834 weak_ptr_factory_.GetWeakPtr()));
835 }
836
SetCandidateWindowControllerForTesting(CandidateWindowController * candidate_window_controller)837 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
838 CandidateWindowController* candidate_window_controller) {
839 candidate_window_controller_.reset(candidate_window_controller);
840 candidate_window_controller_->AddObserver(this);
841 }
842
SetImeKeyboardForTesting(ImeKeyboard * keyboard)843 void InputMethodManagerImpl::SetImeKeyboardForTesting(ImeKeyboard* keyboard) {
844 keyboard_.reset(keyboard);
845 }
846
InitializeComponentExtensionForTesting(scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate)847 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
848 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
849 OnComponentExtensionInitialized(delegate.Pass());
850 }
851
CandidateClicked(int index)852 void InputMethodManagerImpl::CandidateClicked(int index) {
853 IMEEngineHandlerInterface* engine =
854 IMEBridge::Get()->GetCurrentEngineHandler();
855 if (engine)
856 engine->CandidateClicked(index);
857 }
858
CandidateWindowOpened()859 void InputMethodManagerImpl::CandidateWindowOpened() {
860 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
861 candidate_window_observers_,
862 CandidateWindowOpened(this));
863 }
864
CandidateWindowClosed()865 void InputMethodManagerImpl::CandidateWindowClosed() {
866 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
867 candidate_window_observers_,
868 CandidateWindowClosed(this));
869 }
870
OnScreenLocked()871 void InputMethodManagerImpl::OnScreenLocked() {
872 saved_previous_input_method_ = previous_input_method_;
873 saved_current_input_method_ = current_input_method_;
874 saved_active_input_method_ids_ = active_input_method_ids_;
875
876 std::set<std::string> added_ids_;
877
878 const std::vector<std::string>& hardware_keyboard_ids =
879 util_.GetHardwareLoginInputMethodIds();
880
881 active_input_method_ids_.clear();
882 for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) {
883 const std::string& input_method_id = saved_active_input_method_ids_[i];
884 // Skip if it's not a keyboard layout. Drop input methods including
885 // extension ones.
886 if (!IsLoginKeyboard(input_method_id) ||
887 added_ids_.find(input_method_id) != added_ids_.end())
888 continue;
889 active_input_method_ids_.push_back(input_method_id);
890 added_ids_.insert(input_method_id);
891 }
892
893 // We'll add the hardware keyboard if it's not included in
894 // |active_input_method_ids_| so that the user can always use the hardware
895 // keyboard on the screen locker.
896 for (size_t i = 0; i < hardware_keyboard_ids.size(); ++i) {
897 if (added_ids_.find(hardware_keyboard_ids[i]) == added_ids_.end()) {
898 active_input_method_ids_.push_back(hardware_keyboard_ids[i]);
899 added_ids_.insert(hardware_keyboard_ids[i]);
900 }
901 }
902
903 ChangeInputMethod(current_input_method_.id());
904 }
905
OnScreenUnlocked()906 void InputMethodManagerImpl::OnScreenUnlocked() {
907 previous_input_method_ = saved_previous_input_method_;
908 current_input_method_ = saved_current_input_method_;
909 active_input_method_ids_ = saved_active_input_method_ids_;
910
911 ChangeInputMethod(current_input_method_.id());
912 }
913
InputMethodIsActivated(const std::string & input_method_id)914 bool InputMethodManagerImpl::InputMethodIsActivated(
915 const std::string& input_method_id) {
916 return Contains(active_input_method_ids_, input_method_id);
917 }
918
MaybeInitializeCandidateWindowController()919 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
920 if (candidate_window_controller_.get())
921 return;
922
923 candidate_window_controller_.reset(
924 CandidateWindowController::CreateCandidateWindowController());
925 candidate_window_controller_->AddObserver(this);
926 }
927
GetProfile() const928 Profile* InputMethodManagerImpl::GetProfile() const {
929 return ProfileManager::GetActiveUserProfile();
930 }
931
932 } // namespace input_method
933 } // namespace chromeos
934