• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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