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 <stdint.h>
8 
9 #include <algorithm>  // std::find
10 #include <cstdint>
11 #include <memory>
12 #include <set>
13 #include <sstream>
14 #include <utility>
15 
16 #include "ash/public/cpp/ash_features.h"
17 #include "base/bind.h"
18 #include "base/feature_list.h"
19 #include "base/hash/hash.h"
20 #include "base/location.h"
21 #include "base/metrics/histogram_functions.h"
22 #include "base/metrics/histogram_macros.h"
23 #include "base/stl_util.h"
24 #include "base/strings/string_split.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/system/sys_info.h"
28 #include "base/time/time.h"
29 #include "chrome/browser/browser_process.h"
30 #include "chrome/browser/browser_process_platform_part_chromeos.h"
31 #include "chrome/browser/chrome_notification_types.h"
32 #include "chrome/browser/chromeos/input_method/assistive_window_controller.h"
33 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h"
34 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_delegate_impl.h"
35 #include "chrome/browser/chromeos/input_method/ui/assistive_delegate.h"
36 #include "chrome/browser/chromeos/input_method/ui/input_method_menu_item.h"
37 #include "chrome/browser/chromeos/input_method/ui/input_method_menu_manager.h"
38 #include "chrome/browser/chromeos/language_preferences.h"
39 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
40 #include "chrome/browser/chromeos/profiles/profile_helper.h"
41 #include "chrome/browser/profiles/profile_manager.h"
42 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
43 #include "chrome/common/chrome_features.h"
44 #include "chrome/common/pref_names.h"
45 #include "chromeos/system/devicemode.h"
46 #include "components/prefs/pref_service.h"
47 #include "components/user_manager/user_manager.h"
48 #include "content/public/browser/notification_service.h"
49 #include "third_party/icu/source/common/unicode/uloc.h"
50 #include "ui/base/ime/chromeos/component_extension_ime_manager.h"
51 #include "ui/base/ime/chromeos/component_extension_ime_manager_delegate.h"
52 #include "ui/base/ime/chromeos/extension_ime_util.h"
53 #include "ui/base/ime/chromeos/fake_ime_keyboard.h"
54 #include "ui/base/ime/chromeos/ime_bridge.h"
55 #include "ui/base/ime/chromeos/ime_keyboard.h"
56 #include "ui/base/ime/chromeos/ime_keyboard_impl.h"
57 #include "ui/base/ime/chromeos/input_method_delegate.h"
58 #include "ui/ozone/public/ozone_platform.h"
59 
60 namespace chromeos {
61 namespace input_method {
62 
63 namespace {
64 
65 enum InputMethodCategory {
66   INPUT_METHOD_CATEGORY_UNKNOWN = 0,
67   INPUT_METHOD_CATEGORY_XKB,   // XKB input methods
68   INPUT_METHOD_CATEGORY_ZH,    // Chinese input methods
69   INPUT_METHOD_CATEGORY_JA,    // Japanese input methods
70   INPUT_METHOD_CATEGORY_KO,    // Korean input methods
71   INPUT_METHOD_CATEGORY_M17N,  // Multilingualization input methods
72   INPUT_METHOD_CATEGORY_T13N,  // Transliteration input methods
73   INPUT_METHOD_CATEGORY_ARC,   // ARC input methods
74   INPUT_METHOD_CATEGORY_MAX
75 };
76 
77 const chromeos::input_method::ImeKeyset kKeysets[] = {
78     chromeos::input_method::ImeKeyset::kEmoji,
79     chromeos::input_method::ImeKeyset::kHandwriting,
80     chromeos::input_method::ImeKeyset::kVoice};
81 
GetInputMethodCategory(const std::string & input_method_id)82 InputMethodCategory GetInputMethodCategory(const std::string& input_method_id) {
83   const std::string component_id =
84       extension_ime_util::GetComponentIDByInputMethodID(input_method_id);
85   InputMethodCategory category = INPUT_METHOD_CATEGORY_UNKNOWN;
86   if (base::StartsWith(component_id, "xkb:", base::CompareCase::SENSITIVE)) {
87     category = INPUT_METHOD_CATEGORY_XKB;
88   } else if (base::StartsWith(component_id, "zh-",
89                               base::CompareCase::SENSITIVE)) {
90     category = INPUT_METHOD_CATEGORY_ZH;
91   } else if (base::StartsWith(component_id, "nacl_mozc_",
92                               base::CompareCase::SENSITIVE)) {
93     category = INPUT_METHOD_CATEGORY_JA;
94   } else if (base::StartsWith(component_id, "hangul_",
95                               base::CompareCase::SENSITIVE) ||
96              component_id == "ko-t-i0-und") {
97     category = INPUT_METHOD_CATEGORY_KO;
98   } else if (base::StartsWith(component_id, "vkd_",
99                               base::CompareCase::SENSITIVE)) {
100     category = INPUT_METHOD_CATEGORY_M17N;
101   } else if (component_id.find("-t-i0-") != std::string::npos) {
102     category = INPUT_METHOD_CATEGORY_T13N;
103   } else if (extension_ime_util::IsArcIME(input_method_id)) {
104     category = INPUT_METHOD_CATEGORY_ARC;
105   }
106 
107   return category;
108 }
109 
KeysetToString(chromeos::input_method::ImeKeyset keyset)110 std::string KeysetToString(chromeos::input_method::ImeKeyset keyset) {
111   switch (keyset) {
112     case chromeos::input_method::ImeKeyset::kNone:
113       return "";
114     case chromeos::input_method::ImeKeyset::kEmoji:
115       return "emoji";
116     case chromeos::input_method::ImeKeyset::kHandwriting:
117       return "hwt";
118     case chromeos::input_method::ImeKeyset::kVoice:
119       return "voice";
120   }
121 }
122 
IsShuttingDown()123 bool IsShuttingDown() {
124   return !g_browser_process || g_browser_process->IsShuttingDown();
125 }
126 
127 }  // namespace
128 
129 // ------------------------ InputMethodManagerImpl::StateImpl
130 
StateImpl(InputMethodManagerImpl * manager,Profile * profile)131 InputMethodManagerImpl::StateImpl::StateImpl(InputMethodManagerImpl* manager,
132                                              Profile* profile)
133     : profile(profile), manager_(manager) {}
134 
135 InputMethodManagerImpl::StateImpl::~StateImpl() = default;
136 
InitFrom(const StateImpl & other)137 void InputMethodManagerImpl::StateImpl::InitFrom(const StateImpl& other) {
138   last_used_input_method = other.last_used_input_method;
139   current_input_method = other.current_input_method;
140 
141   active_input_method_ids = other.active_input_method_ids;
142   allowed_keyboard_layout_input_method_ids =
143       other.allowed_keyboard_layout_input_method_ids;
144 
145   pending_input_method_id = other.pending_input_method_id;
146 
147   enabled_extension_imes = other.enabled_extension_imes;
148   extra_input_methods = other.extra_input_methods;
149   menu_activated = other.menu_activated;
150   input_view_url = other.input_view_url;
151   input_view_url_overridden = other.input_view_url_overridden;
152   ui_style_ = other.ui_style_;
153 }
154 
IsActive() const155 bool InputMethodManagerImpl::StateImpl::IsActive() const {
156   return manager_->state_.get() == this;
157 }
158 
Dump() const159 std::string InputMethodManagerImpl::StateImpl::Dump() const {
160   std::ostringstream os;
161 
162   os << "################# "
163      << (profile ? profile->GetProfileUserName() : std::string("NULL"))
164      << " #################\n";
165 
166   os << "last_used_input_method: '"
167      << last_used_input_method.GetPreferredKeyboardLayout() << "'\n";
168   os << "current_input_method: '"
169      << current_input_method.GetPreferredKeyboardLayout() << "'\n";
170   os << "active_input_method_ids (size=" << active_input_method_ids.size()
171      << "):";
172   for (const auto& active_input_method_id : active_input_method_ids) {
173     os << " '" << active_input_method_id << "',";
174   }
175   os << "\n";
176   os << "allowed_keyboard_layout_input_method_ids (size="
177      << allowed_keyboard_layout_input_method_ids.size() << "):";
178   for (const auto& allowed_keyboard_layout_input_method_id :
179        allowed_keyboard_layout_input_method_ids) {
180     os << " '" << allowed_keyboard_layout_input_method_id << "',";
181   }
182   os << "\n";
183   os << "pending_input_method_id: '" << pending_input_method_id << "'\n";
184   os << "enabled_extension_imes (size=" << enabled_extension_imes.size()
185      << "):";
186   for (const auto& enabled_extension_ime : enabled_extension_imes) {
187     os << " '" << enabled_extension_ime << "'\n";
188   }
189   os << "\n";
190   os << "extra_input_methods (size=" << extra_input_methods.size() << "):";
191   for (const auto& entry : extra_input_methods) {
192     os << " '" << entry.first << "' => '" << entry.second.id() << "',\n";
193   }
194   os << "menu_activated: '" << menu_activated << "'\n";
195   os << "input_view_url: '" << input_view_url << "'\n";
196   os << "input_view_url_overridden: '" << input_view_url_overridden << "'\n";
197   os << "ui_style_: '" << static_cast<int>(ui_style_) << "'\n";
198 
199   return os.str();
200 }
201 
202 scoped_refptr<InputMethodManager::State>
Clone() const203 InputMethodManagerImpl::StateImpl::Clone() const {
204   scoped_refptr<StateImpl> new_state(new StateImpl(this->manager_, profile));
205   new_state->InitFrom(*this);
206   return scoped_refptr<InputMethodManager::State>(new_state.get());
207 }
208 
209 std::unique_ptr<InputMethodDescriptors>
GetActiveInputMethods() const210 InputMethodManagerImpl::StateImpl::GetActiveInputMethods() const {
211   std::unique_ptr<InputMethodDescriptors> result(new InputMethodDescriptors);
212   // Build the active input method descriptors from the active input
213   // methods cache |active_input_method_ids|.
214   for (const auto& input_method_id : active_input_method_ids) {
215     const InputMethodDescriptor* descriptor =
216         manager_->util_.GetInputMethodDescriptorFromId(input_method_id);
217     if (descriptor) {
218       result->push_back(*descriptor);
219     } else {
220       const auto ix = extra_input_methods.find(input_method_id);
221       if (ix != extra_input_methods.end())
222         result->push_back(ix->second);
223       else
224         DVLOG(1) << "Descriptor is not found for: " << input_method_id;
225     }
226   }
227   if (result->empty()) {
228     // Initially |active_input_method_ids| is empty. browser_tests might take
229     // this path.
230     result->push_back(
231         InputMethodUtil::GetFallbackInputMethodDescriptor());
232   }
233   return result;
234 }
235 
236 const std::vector<std::string>&
GetActiveInputMethodIds() const237 InputMethodManagerImpl::StateImpl::GetActiveInputMethodIds() const {
238   return active_input_method_ids;
239 }
240 
GetNumActiveInputMethods() const241 size_t InputMethodManagerImpl::StateImpl::GetNumActiveInputMethods() const {
242   return active_input_method_ids.size();
243 }
244 
245 const InputMethodDescriptor*
GetInputMethodFromId(const std::string & input_method_id) const246 InputMethodManagerImpl::StateImpl::GetInputMethodFromId(
247     const std::string& input_method_id) const {
248   const InputMethodDescriptor* ime =
249       manager_->util_.GetInputMethodDescriptorFromId(input_method_id);
250   if (!ime) {
251     const auto ix = extra_input_methods.find(input_method_id);
252     if (ix != extra_input_methods.end())
253       ime = &ix->second;
254   }
255   return ime;
256 }
257 
EnableLoginLayouts(const std::string & language_code,const std::vector<std::string> & initial_layouts)258 void InputMethodManagerImpl::StateImpl::EnableLoginLayouts(
259     const std::string& language_code,
260     const std::vector<std::string>& initial_layouts) {
261   if (IsShuttingDown())
262     return;
263 
264   // First, hardware keyboard layout should be shown.
265   std::vector<std::string> candidates =
266       manager_->util_.GetHardwareLoginInputMethodIds();
267 
268   // Second, locale based input method should be shown.
269   // Add input methods associated with the language.
270   std::vector<std::string> layouts_from_locale;
271   manager_->util_.GetInputMethodIdsFromLanguageCode(
272       language_code, kKeyboardLayoutsOnly, &layouts_from_locale);
273   candidates.insert(candidates.end(), layouts_from_locale.begin(),
274                     layouts_from_locale.end());
275 
276   std::vector<std::string> layouts;
277   // First, add the initial input method ID, if it's requested, to
278   // layouts, so it appears first on the list of active input
279   // methods at the input language status menu.
280   for (const auto& initial_layout : initial_layouts) {
281     if (manager_->util_.IsValidInputMethodId(initial_layout)) {
282       if (manager_->IsLoginKeyboard(initial_layout)) {
283         if (IsInputMethodAllowed(initial_layout)) {
284           layouts.push_back(initial_layout);
285         } else {
286           DVLOG(1) << "EnableLoginLayouts: ignoring layout disallowd by policy:"
287                    << initial_layout;
288         }
289       } else {
290         DVLOG(1)
291             << "EnableLoginLayouts: ignoring non-login initial keyboard layout:"
292             << initial_layout;
293       }
294     } else if (!initial_layout.empty()) {
295       DVLOG(1) << "EnableLoginLayouts: ignoring non-keyboard or invalid ID: "
296                << initial_layout;
297     }
298   }
299 
300   // Add candidates to layouts, while skipping duplicates.
301   for (const auto& candidate : candidates) {
302     // Not efficient, but should be fine, as the two vectors are very
303     // short (2-5 items).
304     if (!base::Contains(layouts, candidate) &&
305         manager_->IsLoginKeyboard(candidate) &&
306         IsInputMethodAllowed(candidate)) {
307       layouts.push_back(candidate);
308     }
309   }
310 
311   manager_->MigrateInputMethods(&layouts);
312   active_input_method_ids.swap(layouts);
313 
314   if (IsActive()) {
315     // Initialize candidate window controller and widgets such as
316     // candidate window, infolist and mode indicator.  Note, mode
317     // indicator is used by only keyboard layout input methods.
318     if (active_input_method_ids.size() > 1)
319       manager_->MaybeInitializeCandidateWindowController();
320 
321     // you can pass empty |initial_layout|.
322     ChangeInputMethod(initial_layouts.empty()
323                           ? std::string()
324                           : extension_ime_util::GetInputMethodIDByEngineID(
325                                 initial_layouts[0]),
326                       false);
327   }
328 }
329 
EnableLockScreenLayouts()330 void InputMethodManagerImpl::StateImpl::EnableLockScreenLayouts() {
331   std::set<std::string> added_ids;
332 
333   const std::vector<std::string>& hardware_keyboard_ids =
334       manager_->util_.GetHardwareLoginInputMethodIds();
335 
336   std::vector<std::string> new_active_input_method_ids;
337   for (const auto& input_method_id : active_input_method_ids) {
338     // Skip if it's not a keyboard layout. Drop input methods including
339     // extension ones. We need to keep all IMEs to support inputting on inline
340     // reply on a notification if notifications on lock screen is enabled.
341     if ((!ash::features::IsLockScreenInlineReplyEnabled() &&
342          !manager_->IsLoginKeyboard(input_method_id)) ||
343         added_ids.count(input_method_id)) {
344       continue;
345     }
346     new_active_input_method_ids.push_back(input_method_id);
347     added_ids.insert(input_method_id);
348   }
349 
350   // We'll add the hardware keyboard if it's not included in
351   // |active_input_method_ids| so that the user can always use the hardware
352   // keyboard on the screen locker.
353   for (const auto& hardware_keyboard_id : hardware_keyboard_ids) {
354     if (added_ids.count(hardware_keyboard_id))
355       continue;
356     new_active_input_method_ids.push_back(hardware_keyboard_id);
357     added_ids.insert(hardware_keyboard_id);
358   }
359 
360   active_input_method_ids.swap(new_active_input_method_ids);
361 
362   // Re-check current_input_method.
363   ChangeInputMethod(current_input_method.id(), false);
364 }
365 
366 // Adds new input method to given list.
EnableInputMethodImpl(const std::string & input_method_id,std::vector<std::string> * new_active_input_method_ids) const367 bool InputMethodManagerImpl::StateImpl::EnableInputMethodImpl(
368     const std::string& input_method_id,
369     std::vector<std::string>* new_active_input_method_ids) const {
370   if (!IsInputMethodAllowed(input_method_id)) {
371     DVLOG(1) << "EnableInputMethod: " << input_method_id << " is not allowed.";
372     return false;
373   }
374 
375   DCHECK(new_active_input_method_ids);
376   if (!manager_->util_.IsValidInputMethodId(input_method_id)) {
377     DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id;
378     return false;
379   }
380 
381   if (!base::Contains(*new_active_input_method_ids, input_method_id))
382     new_active_input_method_ids->push_back(input_method_id);
383 
384   return true;
385 }
386 
EnableInputMethod(const std::string & input_method_id)387 bool InputMethodManagerImpl::StateImpl::EnableInputMethod(
388     const std::string& input_method_id) {
389   if (!EnableInputMethodImpl(input_method_id, &active_input_method_ids))
390     return false;
391 
392   manager_->ReconfigureIMFramework(this);
393   return true;
394 }
395 
ReplaceEnabledInputMethods(const std::vector<std::string> & new_active_input_method_ids)396 bool InputMethodManagerImpl::StateImpl::ReplaceEnabledInputMethods(
397     const std::vector<std::string>& new_active_input_method_ids) {
398   if (IsShuttingDown())
399     return false;
400 
401   // Filter unknown or obsolete IDs.
402   std::vector<std::string> new_active_input_method_ids_filtered;
403 
404   for (const auto& new_active_input_method_id : new_active_input_method_ids)
405     EnableInputMethodImpl(new_active_input_method_id,
406                           &new_active_input_method_ids_filtered);
407 
408   if (new_active_input_method_ids_filtered.empty()) {
409     DVLOG(1) << "ReplaceEnabledInputMethods: No valid input method ID";
410     return false;
411   }
412 
413   // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to
414   // keep relative order of the extension input method IDs.
415   for (const auto& input_method_id : active_input_method_ids) {
416     if (extension_ime_util::IsExtensionIME(input_method_id))
417       new_active_input_method_ids_filtered.push_back(input_method_id);
418   }
419   active_input_method_ids.swap(new_active_input_method_ids_filtered);
420   manager_->MigrateInputMethods(&active_input_method_ids);
421 
422   manager_->ReconfigureIMFramework(this);
423 
424   // If |current_input_method| is no longer in |active_input_method_ids|,
425   // ChangeInputMethod() picks the first one in |active_input_method_ids|.
426   ChangeInputMethod(current_input_method.id(), false);
427 
428   // Record histogram for active input method count.
429   UMA_HISTOGRAM_COUNTS_1M("InputMethod.ActiveCount",
430                           active_input_method_ids.size());
431 
432   return true;
433 }
434 
SetAllowedInputMethods(const std::vector<std::string> & new_allowed_input_method_ids,bool enable_allowed_input_methods)435 bool InputMethodManagerImpl::StateImpl::SetAllowedInputMethods(
436     const std::vector<std::string>& new_allowed_input_method_ids,
437     bool enable_allowed_input_methods) {
438   allowed_keyboard_layout_input_method_ids.clear();
439   for (auto input_method_id : new_allowed_input_method_ids) {
440     std::string migrated_id =
441         manager_->util_.MigrateInputMethod(input_method_id);
442     if (manager_->util_.IsValidInputMethodId(migrated_id)) {
443       allowed_keyboard_layout_input_method_ids.push_back(migrated_id);
444     }
445   }
446 
447   if (allowed_keyboard_layout_input_method_ids.empty()) {
448     // None of the passed input methods were valid, so allow everything.
449     return false;
450   }
451 
452   std::vector<std::string> new_active_input_method_ids;
453   if (enable_allowed_input_methods) {
454     // Enable all allowed keyboard layout input methods. Leave all non-keyboard
455     // input methods enabled.
456     new_active_input_method_ids = allowed_keyboard_layout_input_method_ids;
457     for (auto active_input_method_id : active_input_method_ids) {
458       if (!manager_->util_.IsKeyboardLayout(active_input_method_id))
459         new_active_input_method_ids.push_back(active_input_method_id);
460     }
461   } else {
462     // Filter all currently active input methods and leave only non-keyboard or
463     // allowed keyboard layouts. If no input method remains, take a fallback
464     // keyboard layout.
465     bool has_keyboard_layout = false;
466     for (auto active_input_method_id : active_input_method_ids) {
467       if (IsInputMethodAllowed(active_input_method_id)) {
468         new_active_input_method_ids.push_back(active_input_method_id);
469         has_keyboard_layout |=
470             manager_->util_.IsKeyboardLayout(active_input_method_id);
471       }
472     }
473     if (!has_keyboard_layout)
474       new_active_input_method_ids.push_back(GetAllowedFallBackKeyboardLayout());
475   }
476   return ReplaceEnabledInputMethods(new_active_input_method_ids);
477 }
478 
479 const std::vector<std::string>&
GetAllowedInputMethods()480 InputMethodManagerImpl::StateImpl::GetAllowedInputMethods() {
481   return allowed_keyboard_layout_input_method_ids;
482 }
483 
IsInputMethodAllowed(const std::string & input_method_id) const484 bool InputMethodManagerImpl::StateImpl::IsInputMethodAllowed(
485     const std::string& input_method_id) const {
486   // Every input method is allowed if SetAllowedKeyboardLayoutInputMethods has
487   // not been called.
488   if (allowed_keyboard_layout_input_method_ids.empty())
489     return true;
490 
491   // We only restrict keyboard layouts.
492   if (!manager_->util_.IsKeyboardLayout(input_method_id) &&
493       !extension_ime_util::IsArcIME(input_method_id)) {
494     return true;
495   }
496 
497   return base::Contains(allowed_keyboard_layout_input_method_ids,
498                         input_method_id) ||
499          base::Contains(allowed_keyboard_layout_input_method_ids,
500                         manager_->util_.MigrateInputMethod(input_method_id));
501 }
502 
503 std::string
GetAllowedFallBackKeyboardLayout() const504 InputMethodManagerImpl::StateImpl::GetAllowedFallBackKeyboardLayout() const {
505   for (const std::string& hardware_id :
506        manager_->util_.GetHardwareInputMethodIds()) {
507     if (IsInputMethodAllowed(hardware_id))
508       return hardware_id;
509   }
510   return allowed_keyboard_layout_input_method_ids[0];
511 }
512 
ChangeInputMethod(const std::string & input_method_id,bool show_message)513 void InputMethodManagerImpl::StateImpl::ChangeInputMethod(
514     const std::string& input_method_id,
515     bool show_message) {
516   if (IsShuttingDown())
517     return;
518 
519   bool notify_menu = false;
520 
521   // Always lookup input method, even if it is the same as
522   // |current_input_method| because If it is no longer in
523   // |active_input_method_ids|, pick the first one in
524   // |active_input_method_ids|.
525   const InputMethodDescriptor* descriptor =
526       manager_->LookupInputMethod(input_method_id, this);
527   if (!descriptor) {
528     descriptor = manager_->LookupInputMethod(
529         manager_->util_.MigrateInputMethod(input_method_id), this);
530     if (!descriptor) {
531       LOG(ERROR) << "Can't find InputMethodDescriptor for \"" << input_method_id
532                  << "\"";
533       return;
534     }
535   }
536 
537   // For 3rd party IME, when the user just logged in, SetEnabledExtensionImes
538   // happens after activating the 3rd party IME.
539   // So here to record the 3rd party IME to be activated, and activate it
540   // when SetEnabledExtensionImes happens later.
541   if (MethodAwaitsExtensionLoad(input_method_id))
542     pending_input_method_id = input_method_id;
543 
544   if (descriptor->id() != current_input_method.id()) {
545     last_used_input_method = current_input_method;
546     current_input_method = *descriptor;
547     notify_menu = true;
548   }
549 
550   // Always change input method even if it is the same.
551   // TODO(komatsu): Revisit if this is necessary.
552   if (IsActive()) {
553     manager_->ChangeInputMethodInternalFromActiveState(show_message,
554                                                        notify_menu);
555   }
556 
557   manager_->RecordInputMethodUsage(current_input_method.id());
558 }
559 
ChangeInputMethodToJpKeyboard()560 void InputMethodManagerImpl::StateImpl::ChangeInputMethodToJpKeyboard() {
561   ChangeInputMethod(
562       extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn"), true);
563 }
564 
ChangeInputMethodToJpIme()565 void InputMethodManagerImpl::StateImpl::ChangeInputMethodToJpIme() {
566   ChangeInputMethod(
567       extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp"), true);
568 }
569 
ToggleInputMethodForJpIme()570 void InputMethodManagerImpl::StateImpl::ToggleInputMethodForJpIme() {
571   std::string jp_ime_id =
572       extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp");
573   ChangeInputMethod(
574       GetCurrentInputMethod().id() == jp_ime_id
575           ? extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn")
576           : jp_ime_id,
577       true);
578 }
579 
MethodAwaitsExtensionLoad(const std::string & input_method_id) const580 bool InputMethodManagerImpl::StateImpl::MethodAwaitsExtensionLoad(
581     const std::string& input_method_id) const {
582   // For 3rd party IME, when the user just logged in, SetEnabledExtensionImes
583   // happens after activating the 3rd party IME.
584   // So here to record the 3rd party IME to be activated, and activate it
585   // when SetEnabledExtensionImes happens later.
586   return !InputMethodIsActivated(input_method_id) &&
587          extension_ime_util::IsExtensionIME(input_method_id);
588 }
589 
AddInputMethodExtension(const std::string & extension_id,const InputMethodDescriptors & descriptors,ui::IMEEngineHandlerInterface * engine)590 void InputMethodManagerImpl::StateImpl::AddInputMethodExtension(
591     const std::string& extension_id,
592     const InputMethodDescriptors& descriptors,
593     ui::IMEEngineHandlerInterface* engine) {
594   if (IsShuttingDown())
595     return;
596 
597   DCHECK(engine);
598 
599   manager_->engine_map_[profile][extension_id] = engine;
600   VLOG(1) << "Add an engine for \"" << extension_id << "\"";
601 
602   bool contain = false;
603   for (const auto& descriptor : descriptors) {
604     const std::string& id = descriptor.id();
605     extra_input_methods[id] = descriptor;
606     if (base::Contains(enabled_extension_imes, id)) {
607       if (!base::Contains(active_input_method_ids, id)) {
608         active_input_method_ids.push_back(id);
609       } else {
610         DVLOG(1) << "AddInputMethodExtension: already added: " << id << ", "
611                  << descriptor.name();
612       }
613       contain = true;
614     }
615   }
616 
617   if (IsActive()) {
618     if (extension_id == extension_ime_util::GetExtensionIDFromInputMethodID(
619                             current_input_method.id())) {
620       ui::IMEBridge::Get()->SetCurrentEngineHandler(engine);
621       engine->Enable(extension_ime_util::GetComponentIDByInputMethodID(
622           current_input_method.id()));
623     }
624 
625     // Ensure that the input method daemon is running.
626     if (contain)
627       manager_->MaybeInitializeCandidateWindowController();
628   }
629 
630   manager_->NotifyImeMenuListChanged();
631   manager_->NotifyInputMethodExtensionAdded(extension_id);
632 }
633 
RemoveInputMethodExtension(const std::string & extension_id)634 void InputMethodManagerImpl::StateImpl::RemoveInputMethodExtension(
635     const std::string& extension_id) {
636   // Remove the active input methods with |extension_id|.
637   std::vector<std::string> new_active_input_method_ids;
638   for (const auto& active_input_method_id : active_input_method_ids) {
639     if (extension_id != extension_ime_util::GetExtensionIDFromInputMethodID(
640                             active_input_method_id))
641       new_active_input_method_ids.push_back(active_input_method_id);
642   }
643   active_input_method_ids.swap(new_active_input_method_ids);
644 
645   // Remove the extra input methods with |extension_id|.
646   std::map<std::string, InputMethodDescriptor> new_extra_input_methods;
647   for (const auto& entry : extra_input_methods) {
648     if (extension_id !=
649         extension_ime_util::GetExtensionIDFromInputMethodID(entry.first))
650       new_extra_input_methods[entry.first] = entry.second;
651   }
652   extra_input_methods.swap(new_extra_input_methods);
653 
654   if (IsActive()) {
655     if (ui::IMEBridge::Get()->GetCurrentEngineHandler() ==
656         manager_->engine_map_[profile][extension_id]) {
657       ui::IMEBridge::Get()->SetCurrentEngineHandler(NULL);
658     }
659     manager_->engine_map_[profile].erase(extension_id);
660   }
661 
662   // If |current_input_method| is no longer in |active_input_method_ids|,
663   // switch to the first one in |active_input_method_ids|.
664   ChangeInputMethod(current_input_method.id(), false);
665   manager_->NotifyInputMethodExtensionRemoved(extension_id);
666 }
667 
GetInputMethodExtensions(InputMethodDescriptors * result)668 void InputMethodManagerImpl::StateImpl::GetInputMethodExtensions(
669     InputMethodDescriptors* result) {
670   // Build the extension input method descriptors from the extra input
671   // methods cache |extra_input_methods|.
672   for (const auto& entry : extra_input_methods) {
673     if (extension_ime_util::IsExtensionIME(entry.first) ||
674         extension_ime_util::IsArcIME(entry.first)) {
675       result->push_back(entry.second);
676     }
677   }
678 }
679 
SetEnabledExtensionImes(std::vector<std::string> * ids)680 void InputMethodManagerImpl::StateImpl::SetEnabledExtensionImes(
681     std::vector<std::string>* ids) {
682   enabled_extension_imes.clear();
683   enabled_extension_imes.insert(
684       enabled_extension_imes.end(), ids->begin(), ids->end());
685   bool active_imes_changed = false;
686   bool switch_to_pending = false;
687 
688   for (const auto& entry : extra_input_methods) {
689     if (extension_ime_util::IsComponentExtensionIME(entry.first))
690       continue;  // Do not filter component extension.
691 
692     if (pending_input_method_id == entry.first)
693       switch_to_pending = true;
694 
695     const auto active_iter =
696         std::find(active_input_method_ids.begin(),
697                   active_input_method_ids.end(), entry.first);
698 
699     bool active = active_iter != active_input_method_ids.end();
700     bool enabled = base::Contains(enabled_extension_imes, entry.first);
701 
702     if (active && !enabled)
703       active_input_method_ids.erase(active_iter);
704 
705     if (!active && enabled)
706       active_input_method_ids.push_back(entry.first);
707 
708     if (active == !enabled)
709       active_imes_changed = true;
710   }
711 
712   if (IsActive() && active_imes_changed) {
713     manager_->MaybeInitializeCandidateWindowController();
714 
715     if (switch_to_pending) {
716       ChangeInputMethod(pending_input_method_id, false);
717       pending_input_method_id.clear();
718     } else {
719       // If |current_input_method| is no longer in |active_input_method_ids_|,
720       // switch to the first one in |active_input_method_ids_|.
721       ChangeInputMethod(current_input_method.id(), false);
722     }
723   }
724 }
725 
SetInputMethodLoginDefaultFromVPD(const std::string & locale,const std::string & oem_layout)726 void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefaultFromVPD(
727     const std::string& locale,
728     const std::string& oem_layout) {
729   std::string layout;
730   if (!oem_layout.empty()) {
731     // If the OEM layout information is provided, use it.
732     layout = oem_layout;
733   } else {
734     // Otherwise, determine the hardware keyboard from the locale.
735     std::vector<std::string> input_method_ids;
736     if (manager_->util_.GetInputMethodIdsFromLanguageCode(
737             locale,
738             chromeos::input_method::kKeyboardLayoutsOnly,
739             &input_method_ids)) {
740       // The output list |input_method_ids| is sorted by popularity, hence
741       // input_method_ids[0] now contains the most popular keyboard layout
742       // for the given locale.
743       DCHECK_GE(input_method_ids.size(), 1U);
744       layout = input_method_ids[0];
745     }
746   }
747 
748   if (layout.empty())
749     return;
750 
751   std::vector<std::string> layouts = base::SplitString(
752       layout, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
753   manager_->MigrateInputMethods(&layouts);
754 
755   PrefService* prefs = g_browser_process->local_state();
756   prefs->SetString(prefs::kHardwareKeyboardLayout,
757                    base::JoinString(layouts, ","));
758 
759   // This asks the file thread to save the prefs (i.e. doesn't block).
760   // The latest values of Local State reside in memory so we can safely
761   // get the value of kHardwareKeyboardLayout even if the data is not
762   // yet saved to disk.
763   prefs->CommitPendingWrite();
764 
765   manager_->util_.UpdateHardwareLayoutCache();
766 
767   EnableLoginLayouts(locale, layouts);
768   manager_->LoadNecessaryComponentExtensions(this);
769 }
770 
SetInputMethodLoginDefault()771 void InputMethodManagerImpl::StateImpl::SetInputMethodLoginDefault() {
772   // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty
773   // and US dvorak keyboard layouts.
774   if (g_browser_process && g_browser_process->local_state()) {
775     const std::string locale = g_browser_process->GetApplicationLocale();
776     std::vector<std::string> input_methods_to_be_enabled;
777     if (!GetAllowedInputMethods().empty()) {
778       // Prefer policy-set input methods.
779       input_methods_to_be_enabled = GetAllowedInputMethods();
780     } else {
781       // If the preferred keyboard for the login screen has been saved, use it.
782       PrefService* prefs = g_browser_process->local_state();
783       std::string initial_input_method_id =
784           prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
785       if (initial_input_method_id.empty()) {
786         // If kPreferredKeyboardLayout is not specified, use the hardware
787         // layout.
788         input_methods_to_be_enabled =
789             manager_->util_.GetHardwareLoginInputMethodIds();
790       } else {
791         input_methods_to_be_enabled.push_back(initial_input_method_id);
792       }
793     }
794     EnableLoginLayouts(locale, input_methods_to_be_enabled);
795     manager_->LoadNecessaryComponentExtensions(this);
796   }
797 }
798 
CanCycleInputMethod() const799 bool InputMethodManagerImpl::StateImpl::CanCycleInputMethod() const {
800   // Sanity checks.
801   if (active_input_method_ids.empty()) {
802     DVLOG(1) << "active input method is empty";
803     return false;
804   }
805 
806   if (current_input_method.id().empty()) {
807     DVLOG(1) << "current_input_method is unknown";
808     return false;
809   }
810 
811   return active_input_method_ids.size() > 1;
812 }
813 
SwitchToNextInputMethod()814 void InputMethodManagerImpl::StateImpl::SwitchToNextInputMethod() {
815   if (!CanCycleInputMethod())
816     return;
817 
818   // Find the next input method and switch to it.
819   SwitchToNextInputMethodInternal(active_input_method_ids,
820                                   current_input_method.id());
821 }
822 
SwitchToLastUsedInputMethod()823 void InputMethodManagerImpl::StateImpl::SwitchToLastUsedInputMethod() {
824   if (!CanCycleInputMethod())
825     return;
826 
827   if (last_used_input_method.id().empty() ||
828       last_used_input_method.id() == current_input_method.id()) {
829     SwitchToNextInputMethod();
830     return;
831   }
832 
833   const auto iter =
834       std::find(active_input_method_ids.begin(), active_input_method_ids.end(),
835                 last_used_input_method.id());
836   if (iter == active_input_method_ids.end()) {
837     // last_used_input_method is not supported.
838     SwitchToNextInputMethod();
839     return;
840   }
841   ChangeInputMethod(*iter, true);
842 }
843 
SwitchToNextInputMethodInternal(const std::vector<std::string> & input_method_ids,const std::string & current_input_method_id)844 void InputMethodManagerImpl::StateImpl::SwitchToNextInputMethodInternal(
845     const std::vector<std::string>& input_method_ids,
846     const std::string& current_input_method_id) {
847   auto iter = std::find(input_method_ids.begin(), input_method_ids.end(),
848                         current_input_method_id);
849   if (iter != input_method_ids.end())
850     ++iter;
851   if (iter == input_method_ids.end())
852     iter = input_method_ids.begin();
853   ChangeInputMethod(*iter, true);
854 }
855 
GetCurrentInputMethod() const856 InputMethodDescriptor InputMethodManagerImpl::StateImpl::GetCurrentInputMethod()
857     const {
858   if (current_input_method.id().empty())
859     return InputMethodUtil::GetFallbackInputMethodDescriptor();
860 
861   return current_input_method;
862 }
863 
InputMethodIsActivated(const std::string & input_method_id) const864 bool InputMethodManagerImpl::StateImpl::InputMethodIsActivated(
865     const std::string& input_method_id) const {
866   return base::Contains(active_input_method_ids, input_method_id);
867 }
868 
EnableInputView()869 void InputMethodManagerImpl::StateImpl::EnableInputView() {
870   if (!input_view_url_overridden) {
871     input_view_url = current_input_method.input_view_url();
872   }
873 }
874 
DisableInputView()875 void InputMethodManagerImpl::StateImpl::DisableInputView() {
876   input_view_url = GURL();
877 }
878 
GetInputViewUrl() const879 const GURL& InputMethodManagerImpl::StateImpl::GetInputViewUrl() const {
880   return input_view_url;
881 }
882 
GetUIStyle() const883 InputMethodManager::UIStyle InputMethodManagerImpl::StateImpl::GetUIStyle()
884     const {
885   return ui_style_;
886 }
887 
SetUIStyle(InputMethodManager::UIStyle ui_style)888 void InputMethodManagerImpl::StateImpl::SetUIStyle(
889     InputMethodManager::UIStyle ui_style) {
890   ui_style_ = ui_style;
891 }
892 
OverrideInputViewUrl(const GURL & url)893 void InputMethodManagerImpl::StateImpl::OverrideInputViewUrl(const GURL& url) {
894   input_view_url = url;
895   input_view_url_overridden = true;
896 }
897 
ResetInputViewUrl()898 void InputMethodManagerImpl::StateImpl::ResetInputViewUrl() {
899   input_view_url = current_input_method.input_view_url();
900   input_view_url_overridden = false;
901 }
902 
ConnectMojoManager(mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager> receiver)903 void InputMethodManagerImpl::StateImpl::ConnectMojoManager(
904     mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager> receiver) {
905   if (!ime_service_connector_) {
906     ime_service_connector_ = std::make_unique<ImeServiceConnector>(profile);
907   }
908   ime_service_connector_->SetupImeService(std::move(receiver));
909 }
910 
911 // ------------------------ InputMethodManagerImpl
IsLoginKeyboard(const std::string & layout) const912 bool InputMethodManagerImpl::IsLoginKeyboard(
913     const std::string& layout) const {
914   return util_.IsLoginKeyboard(layout);
915 }
916 
MigrateInputMethods(std::vector<std::string> * input_method_ids)917 bool InputMethodManagerImpl::MigrateInputMethods(
918     std::vector<std::string>* input_method_ids) {
919   return util_.MigrateInputMethods(input_method_ids);
920 }
921 
922 // Starts or stops the system input method framework as needed.
ReconfigureIMFramework(InputMethodManagerImpl::StateImpl * state)923 void InputMethodManagerImpl::ReconfigureIMFramework(
924     InputMethodManagerImpl::StateImpl* state) {
925   LoadNecessaryComponentExtensions(state);
926 
927   // Initialize candidate window controller and widgets such as
928   // candidate window, infolist and mode indicator.  Note, mode
929   // indicator is used by only keyboard layout input methods.
930   if (state_.get() == state) {
931     MaybeInitializeCandidateWindowController();
932     MaybeInitializeAssistiveWindowController();
933   }
934 }
935 
SetState(scoped_refptr<InputMethodManager::State> state)936 void InputMethodManagerImpl::SetState(
937     scoped_refptr<InputMethodManager::State> state) {
938   DCHECK(state.get());
939   auto* new_impl_state =
940       static_cast<InputMethodManagerImpl::StateImpl*>(state.get());
941 
942   state_ = new_impl_state;
943 
944   if (state_.get() && state_->active_input_method_ids.size()) {
945     // Initialize candidate window controller and widgets such as
946     // candidate window, infolist and mode indicator.  Note, mode
947     // indicator is used by only keyboard layout input methods.
948     MaybeInitializeCandidateWindowController();
949     MaybeInitializeAssistiveWindowController();
950 
951     // Always call ChangeInputMethodInternalFromActiveState even when the input
952     // method id remain unchanged, because onActivate event needs to be sent to
953     // IME extension to update the current screen type correctly.
954     ChangeInputMethodInternalFromActiveState(false /* show_message */,
955                                              true /* notify_menu */);
956   }
957 }
958 
959 scoped_refptr<InputMethodManager::State>
GetActiveIMEState()960 InputMethodManagerImpl::GetActiveIMEState() {
961   return scoped_refptr<InputMethodManager::State>(state_.get());
962 }
963 
InputMethodManagerImpl(std::unique_ptr<InputMethodDelegate> delegate,bool enable_extension_loading)964 InputMethodManagerImpl::InputMethodManagerImpl(
965     std::unique_ptr<InputMethodDelegate> delegate,
966     bool enable_extension_loading)
967     : delegate_(std::move(delegate)),
968       util_(delegate_.get()),
969       component_extension_ime_manager_(new ComponentExtensionIMEManager()),
970       enable_extension_loading_(enable_extension_loading),
971       features_enabled_state_(InputMethodManager::FEATURE_ALL) {
972   if (IsRunningAsSystemCompositor()) {
973     keyboard_ = std::make_unique<ImeKeyboardImpl>(
974         ui::OzonePlatform::GetInstance()->GetInputController());
975   } else {
976     keyboard_ = std::make_unique<FakeImeKeyboard>();
977   }
978   // Initializes the system IME list.
979   std::unique_ptr<ComponentExtensionIMEManagerDelegate> comp_delegate(
980       new ComponentExtensionIMEManagerDelegateImpl());
981   component_extension_ime_manager_->Initialize(std::move(comp_delegate));
982   const InputMethodDescriptors& descriptors =
983       component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor();
984   util_.ResetInputMethods(descriptors);
985 
986   // We should not use ALL_BROWSERS_CLOSING here since logout might be cancelled
987   // by JavaScript after ALL_BROWSERS_CLOSING is sent (crosbug.com/11055).
988   notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
989                               content::NotificationService::AllSources());
990 }
991 
~InputMethodManagerImpl()992 InputMethodManagerImpl::~InputMethodManagerImpl() {
993   if (candidate_window_controller_.get())
994     candidate_window_controller_->RemoveObserver(this);
995 }
996 
RecordInputMethodUsage(const std::string & input_method_id)997 void InputMethodManagerImpl::RecordInputMethodUsage(
998     const std::string& input_method_id) {
999   UMA_HISTOGRAM_ENUMERATION("InputMethod.Category",
1000                             GetInputMethodCategory(input_method_id),
1001                             INPUT_METHOD_CATEGORY_MAX);
1002   base::UmaHistogramSparse(
1003       "InputMethod.ID2",
1004       static_cast<int32_t>(base::PersistentHash(input_method_id)));
1005 }
1006 
AddObserver(InputMethodManager::Observer * observer)1007 void InputMethodManagerImpl::AddObserver(
1008     InputMethodManager::Observer* observer) {
1009   observers_.AddObserver(observer);
1010   observer->OnExtraInputEnabledStateChange(
1011       // TODO(shuchen): Remove this parameter - ex features::kEHVInputOnImeMenu
1012       true, features_enabled_state_ & InputMethodManager::FEATURE_EMOJI,
1013       features_enabled_state_ & InputMethodManager::FEATURE_HANDWRITING,
1014       features_enabled_state_ & InputMethodManager::FEATURE_VOICE);
1015 }
1016 
AddCandidateWindowObserver(InputMethodManager::CandidateWindowObserver * observer)1017 void InputMethodManagerImpl::AddCandidateWindowObserver(
1018     InputMethodManager::CandidateWindowObserver* observer) {
1019   candidate_window_observers_.AddObserver(observer);
1020 }
1021 
AddImeMenuObserver(InputMethodManager::ImeMenuObserver * observer)1022 void InputMethodManagerImpl::AddImeMenuObserver(
1023     InputMethodManager::ImeMenuObserver* observer) {
1024   ime_menu_observers_.AddObserver(observer);
1025 }
1026 
RemoveObserver(InputMethodManager::Observer * observer)1027 void InputMethodManagerImpl::RemoveObserver(
1028     InputMethodManager::Observer* observer) {
1029   observers_.RemoveObserver(observer);
1030 }
1031 
RemoveCandidateWindowObserver(InputMethodManager::CandidateWindowObserver * observer)1032 void InputMethodManagerImpl::RemoveCandidateWindowObserver(
1033     InputMethodManager::CandidateWindowObserver* observer) {
1034   candidate_window_observers_.RemoveObserver(observer);
1035 }
1036 
RemoveImeMenuObserver(InputMethodManager::ImeMenuObserver * observer)1037 void InputMethodManagerImpl::RemoveImeMenuObserver(
1038     InputMethodManager::ImeMenuObserver* observer) {
1039   ime_menu_observers_.RemoveObserver(observer);
1040 }
1041 
1042 std::unique_ptr<InputMethodDescriptors>
GetSupportedInputMethods() const1043 InputMethodManagerImpl::GetSupportedInputMethods() const {
1044   return std::unique_ptr<InputMethodDescriptors>(new InputMethodDescriptors);
1045 }
1046 
LookupInputMethod(const std::string & input_method_id,InputMethodManagerImpl::StateImpl * state)1047 const InputMethodDescriptor* InputMethodManagerImpl::LookupInputMethod(
1048     const std::string& input_method_id,
1049     InputMethodManagerImpl::StateImpl* state) {
1050   DCHECK(state);
1051 
1052   std::string input_method_id_to_switch = input_method_id;
1053 
1054   // Sanity check
1055   if (!state->InputMethodIsActivated(input_method_id)) {
1056     std::unique_ptr<InputMethodDescriptors> input_methods(
1057         state->GetActiveInputMethods());
1058     DCHECK(!input_methods->empty());
1059     input_method_id_to_switch = input_methods->at(0).id();
1060     if (!input_method_id.empty()) {
1061       DVLOG(1) << "Can't change the current input method to "
1062                << input_method_id << " since the engine is not enabled. "
1063                << "Switch to " << input_method_id_to_switch << " instead.";
1064     }
1065   }
1066 
1067   const InputMethodDescriptor* descriptor = NULL;
1068   if (extension_ime_util::IsExtensionIME(input_method_id_to_switch) ||
1069       extension_ime_util::IsArcIME(input_method_id_to_switch)) {
1070     DCHECK(state->extra_input_methods.find(input_method_id_to_switch) !=
1071            state->extra_input_methods.end());
1072     descriptor = &(state->extra_input_methods[input_method_id_to_switch]);
1073   } else {
1074     descriptor =
1075         util_.GetInputMethodDescriptorFromId(input_method_id_to_switch);
1076     if (!descriptor)
1077       LOG(ERROR) << "Unknown input method id: " << input_method_id_to_switch;
1078   }
1079   DCHECK(descriptor);
1080   return descriptor;
1081 }
1082 
ChangeInputMethodInternalFromActiveState(bool show_message,bool notify_menu)1083 void InputMethodManagerImpl::ChangeInputMethodInternalFromActiveState(
1084     bool show_message,
1085     bool notify_menu) {
1086   // No need to switch input method when terminating.
1087   if (IsShuttingDown()) {
1088     VLOG(1) << "No need to switch input method when terminating.";
1089     return;
1090   }
1091 
1092   if (candidate_window_controller_.get())
1093     candidate_window_controller_->Hide();
1094 
1095   if (notify_menu) {
1096     // Clear property list.  Property list would be updated by
1097     // extension IMEs via IMEEngineHandlerInterface::(Set|Update)MenuItems.
1098     // If the current input method is a keyboard layout, empty
1099     // properties are sufficient.
1100     const ui::ime::InputMethodMenuItemList empty_menu_item_list;
1101     ui::ime::InputMethodMenuManager* input_method_menu_manager =
1102         ui::ime::InputMethodMenuManager::GetInstance();
1103     input_method_menu_manager->SetCurrentInputMethodMenuItemList(
1104             empty_menu_item_list);
1105   }
1106 
1107   // Disable the current engine handler.
1108   ui::IMEEngineHandlerInterface* engine =
1109       ui::IMEBridge::Get()->GetCurrentEngineHandler();
1110   if (engine)
1111     engine->Disable();
1112 
1113   // Configure the next engine handler.
1114   // This must be after |current_input_method| has been set to new input
1115   // method, because engine's Enable() method needs to access it.
1116   const std::string& extension_id =
1117       extension_ime_util::GetExtensionIDFromInputMethodID(
1118           state_->current_input_method.id());
1119   const std::string& component_id =
1120       extension_ime_util::GetComponentIDByInputMethodID(
1121           state_->current_input_method.id());
1122   if (!engine_map_.count(state_->profile) ||
1123       !engine_map_[state_->profile].count(extension_id)) {
1124     LOG_IF(ERROR, base::SysInfo::IsRunningOnChromeOS())
1125         << "IMEEngine for \"" << extension_id << "\" is not registered";
1126   }
1127   engine = engine_map_[state_->profile][extension_id];
1128 
1129   ui::IMEBridge::Get()->SetCurrentEngineHandler(engine);
1130 
1131   if (engine) {
1132     engine->Enable(component_id);
1133   } else {
1134     // If no engine to enable, cancel the virtual keyboard url override so that
1135     // it can use the fallback system virtual keyboard UI.
1136     state_->DisableInputView();
1137     ReloadKeyboard();
1138   }
1139 
1140   // Change the keyboard layout to a preferred layout for the input method.
1141   if (!keyboard_->SetCurrentKeyboardLayoutByName(
1142           state_->current_input_method.GetPreferredKeyboardLayout())) {
1143     LOG(ERROR) << "Failed to change keyboard layout to "
1144                << state_->current_input_method.GetPreferredKeyboardLayout();
1145   }
1146 
1147   // Update input method indicators (e.g. "US", "DV") in Chrome windows.
1148   for (auto& observer : observers_)
1149     observer.InputMethodChanged(this, state_->profile, show_message);
1150   // Update the current input method in IME menu.
1151   NotifyImeMenuListChanged();
1152 }
1153 
LoadNecessaryComponentExtensions(InputMethodManagerImpl::StateImpl * state)1154 void InputMethodManagerImpl::LoadNecessaryComponentExtensions(
1155     InputMethodManagerImpl::StateImpl* state) {
1156   // Load component extensions but also update |active_input_method_ids| as
1157   // some component extension IMEs may have been removed from the Chrome OS
1158   // image. If specified component extension IME no longer exists, falling back
1159   // to an existing IME.
1160   DCHECK(state);
1161   std::vector<std::string> unfiltered_input_method_ids;
1162   unfiltered_input_method_ids.swap(state->active_input_method_ids);
1163   for (const auto& unfiltered_input_method_id : unfiltered_input_method_ids) {
1164     if (!extension_ime_util::IsComponentExtensionIME(
1165             unfiltered_input_method_id)) {
1166       // Legacy IMEs or xkb layouts are alwayes active.
1167       state->active_input_method_ids.push_back(unfiltered_input_method_id);
1168     } else if (component_extension_ime_manager_->IsAllowlisted(
1169                    unfiltered_input_method_id)) {
1170       if (enable_extension_loading_) {
1171         component_extension_ime_manager_->LoadComponentExtensionIME(
1172             state->profile, unfiltered_input_method_id);
1173       }
1174 
1175       state->active_input_method_ids.push_back(unfiltered_input_method_id);
1176     }
1177   }
1178 }
1179 
ActivateInputMethodMenuItem(const std::string & key)1180 void InputMethodManagerImpl::ActivateInputMethodMenuItem(
1181     const std::string& key) {
1182   DCHECK(!key.empty());
1183 
1184   if (ui::ime::InputMethodMenuManager::GetInstance()->
1185       HasInputMethodMenuItemForKey(key)) {
1186     ui::IMEEngineHandlerInterface* engine =
1187         ui::IMEBridge::Get()->GetCurrentEngineHandler();
1188     if (engine)
1189       engine->PropertyActivate(key);
1190     return;
1191   }
1192 
1193   DVLOG(1) << "ActivateInputMethodMenuItem: unknown key: " << key;
1194 }
1195 
ConnectInputEngineManager(mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager> receiver)1196 void InputMethodManagerImpl::ConnectInputEngineManager(
1197     mojo::PendingReceiver<chromeos::ime::mojom::InputEngineManager> receiver) {
1198   DCHECK(state_);
1199   state_->ConnectMojoManager(std::move(receiver));
1200 }
1201 
IsISOLevel5ShiftUsedByCurrentInputMethod() const1202 bool InputMethodManagerImpl::IsISOLevel5ShiftUsedByCurrentInputMethod() const {
1203   return keyboard_->IsISOLevel5ShiftAvailable();
1204 }
1205 
IsAltGrUsedByCurrentInputMethod() const1206 bool InputMethodManagerImpl::IsAltGrUsedByCurrentInputMethod() const {
1207   return keyboard_->IsAltGrAvailable();
1208 }
1209 
GetImeKeyboard()1210 ImeKeyboard* InputMethodManagerImpl::GetImeKeyboard() {
1211   return keyboard_.get();
1212 }
1213 
GetInputMethodUtil()1214 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() {
1215   return &util_;
1216 }
1217 
1218 ComponentExtensionIMEManager*
GetComponentExtensionIMEManager()1219     InputMethodManagerImpl::GetComponentExtensionIMEManager() {
1220   return component_extension_ime_manager_.get();
1221 }
1222 
CreateNewState(Profile * profile)1223 scoped_refptr<InputMethodManager::State> InputMethodManagerImpl::CreateNewState(
1224     Profile* profile) {
1225   auto* new_state = new StateImpl(this, profile);
1226 
1227   // Active IM should be set to owner/user's default.
1228   PrefService* prefs = g_browser_process->local_state();
1229   PrefService* user_prefs = profile ? profile->GetPrefs() : nullptr;
1230   std::string initial_input_method_id;
1231   if (user_prefs) {
1232     initial_input_method_id =
1233         user_prefs->GetString(prefs::kLanguageCurrentInputMethod);
1234   }
1235   if (initial_input_method_id.empty()) {
1236     initial_input_method_id =
1237         prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout);
1238   }
1239 
1240   const InputMethodDescriptor* descriptor =
1241       GetInputMethodUtil()->GetInputMethodDescriptorFromId(
1242           initial_input_method_id.empty()
1243               ? GetInputMethodUtil()->GetFallbackInputMethodDescriptor().id()
1244               : initial_input_method_id);
1245   if (descriptor) {
1246     new_state->active_input_method_ids.push_back(descriptor->id());
1247     new_state->current_input_method = *descriptor;
1248   }
1249   return scoped_refptr<InputMethodManager::State>(new_state);
1250 }
1251 
SetCandidateWindowControllerForTesting(CandidateWindowController * candidate_window_controller)1252 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting(
1253     CandidateWindowController* candidate_window_controller) {
1254   candidate_window_controller_.reset(candidate_window_controller);
1255   candidate_window_controller_->AddObserver(this);
1256 }
1257 
SetImeKeyboardForTesting(ImeKeyboard * keyboard)1258 void InputMethodManagerImpl::SetImeKeyboardForTesting(ImeKeyboard* keyboard) {
1259   keyboard_.reset(keyboard);
1260 }
1261 
InitializeComponentExtensionForTesting(std::unique_ptr<ComponentExtensionIMEManagerDelegate> delegate)1262 void InputMethodManagerImpl::InitializeComponentExtensionForTesting(
1263     std::unique_ptr<ComponentExtensionIMEManagerDelegate> delegate) {
1264   component_extension_ime_manager_->Initialize(std::move(delegate));
1265   util_.ResetInputMethods(
1266       component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor());
1267 }
1268 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)1269 void InputMethodManagerImpl::Observe(
1270     int type,
1271     const content::NotificationSource& source,
1272     const content::NotificationDetails& details) {
1273   DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
1274 
1275   if (candidate_window_controller_.get())
1276     candidate_window_controller_.reset();
1277 
1278   if (assistive_window_controller_.get()) {
1279     assistive_window_controller_.reset();
1280     ui::IMEBridge::Get()->SetAssistiveWindowHandler(nullptr);
1281   }
1282 }
1283 
CandidateClicked(int index)1284 void InputMethodManagerImpl::CandidateClicked(int index) {
1285   ui::IMEEngineHandlerInterface* engine =
1286       ui::IMEBridge::Get()->GetCurrentEngineHandler();
1287   if (engine)
1288     engine->CandidateClicked(index);
1289 }
1290 
CandidateWindowOpened()1291 void InputMethodManagerImpl::CandidateWindowOpened() {
1292   for (auto& observer : candidate_window_observers_)
1293     observer.CandidateWindowOpened(this);
1294 }
1295 
CandidateWindowClosed()1296 void InputMethodManagerImpl::CandidateWindowClosed() {
1297   for (auto& observer : candidate_window_observers_)
1298     observer.CandidateWindowClosed(this);
1299 }
1300 
AssistiveWindowButtonClicked(const ui::ime::AssistiveWindowButton & button) const1301 void InputMethodManagerImpl::AssistiveWindowButtonClicked(
1302     const ui::ime::AssistiveWindowButton& button) const {
1303   ui::IMEEngineHandlerInterface* engine =
1304       ui::IMEBridge::Get()->GetCurrentEngineHandler();
1305   if (engine)
1306     engine->AssistiveWindowButtonClicked(button);
1307 }
1308 
ImeMenuActivationChanged(bool is_active)1309 void InputMethodManagerImpl::ImeMenuActivationChanged(bool is_active) {
1310   // Saves the state that whether the expanded IME menu has been activated by
1311   // users. This method is only called when the preference is changing.
1312   state_->menu_activated = is_active;
1313   MaybeNotifyImeMenuActivationChanged();
1314 }
1315 
NotifyInputMethodExtensionAdded(const std::string & extension_id)1316 void InputMethodManagerImpl::NotifyInputMethodExtensionAdded(
1317     const std::string& extension_id) {
1318   for (auto& observer : observers_)
1319     observer.OnInputMethodExtensionAdded(extension_id);
1320 }
1321 
NotifyInputMethodExtensionRemoved(const std::string & extension_id)1322 void InputMethodManagerImpl::NotifyInputMethodExtensionRemoved(
1323     const std::string& extension_id) {
1324   for (auto& observer : observers_)
1325     observer.OnInputMethodExtensionRemoved(extension_id);
1326 }
1327 
NotifyImeMenuListChanged()1328 void InputMethodManagerImpl::NotifyImeMenuListChanged() {
1329   for (auto& observer : ime_menu_observers_)
1330     observer.ImeMenuListChanged();
1331 }
1332 
MaybeInitializeCandidateWindowController()1333 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() {
1334   if (candidate_window_controller_.get())
1335     return;
1336 
1337   candidate_window_controller_.reset(
1338       CandidateWindowController::CreateCandidateWindowController());
1339   candidate_window_controller_->AddObserver(this);
1340 }
1341 
MaybeInitializeAssistiveWindowController()1342 void InputMethodManagerImpl::MaybeInitializeAssistiveWindowController() {
1343   if (assistive_window_controller_.get())
1344     return;
1345 
1346   assistive_window_controller_ =
1347       std::make_unique<AssistiveWindowController>(this, state_->profile);
1348   ui::IMEBridge::Get()->SetAssistiveWindowHandler(
1349       assistive_window_controller_.get());
1350 }
1351 
NotifyImeMenuItemsChanged(const std::string & engine_id,const std::vector<InputMethodManager::MenuItem> & items)1352 void InputMethodManagerImpl::NotifyImeMenuItemsChanged(
1353     const std::string& engine_id,
1354     const std::vector<InputMethodManager::MenuItem>& items) {
1355   for (auto& observer : ime_menu_observers_)
1356     observer.ImeMenuItemsChanged(engine_id, items);
1357 }
1358 
MaybeNotifyImeMenuActivationChanged()1359 void InputMethodManagerImpl::MaybeNotifyImeMenuActivationChanged() {
1360   if (is_ime_menu_activated_ == state_->menu_activated)
1361     return;
1362 
1363   is_ime_menu_activated_ = state_->menu_activated;
1364   for (auto& observer : ime_menu_observers_)
1365     observer.ImeMenuActivationChanged(is_ime_menu_activated_);
1366   UMA_HISTOGRAM_BOOLEAN("InputMethod.ImeMenu.ActivationChanged",
1367                         is_ime_menu_activated_);
1368 }
1369 
OverrideKeyboardKeyset(chromeos::input_method::ImeKeyset keyset)1370 void InputMethodManagerImpl::OverrideKeyboardKeyset(
1371     chromeos::input_method::ImeKeyset keyset) {
1372   GURL url = state_->GetInputViewUrl();
1373 
1374   // If fails to find ref or tag "id" in the ref, it means the current IME is
1375   // not system IME, and we don't support show emoji, handwriting or voice
1376   // input for such IME extension.
1377   if (!url.has_ref())
1378     return;
1379   std::string overridden_ref = url.ref();
1380 
1381   auto id_start = overridden_ref.find("id=");
1382   if (id_start == std::string::npos)
1383     return;
1384 
1385   if (keyset == chromeos::input_method::ImeKeyset::kNone) {
1386     // Resets the url as the input method default url and notify the hash
1387     // changed to VK.
1388     state_->ResetInputViewUrl();
1389     ReloadKeyboard();
1390     return;
1391   }
1392 
1393   // For IME component extension, the input view url is overridden as:
1394   // chrome-extension://${extension_id}/inputview.html#id=us.compact.qwerty
1395   // &language=en-US&passwordLayout=us.compact.qwerty&name=keyboard_us
1396   // For emoji, handwriting and voice input, we append the keyset to the end of
1397   // id like: id=${keyset}.emoji/hwt/voice.
1398   auto id_end = overridden_ref.find("&", id_start + 1);
1399   std::string id_string = overridden_ref.substr(id_start, id_end - id_start);
1400   // Remove existing keyset string.
1401   for (const chromeos::input_method::ImeKeyset keyset : kKeysets) {
1402     std::string keyset_string = KeysetToString(keyset);
1403     auto keyset_start = id_string.find("." + keyset_string);
1404     if (keyset_start != std::string::npos) {
1405       id_string.replace(keyset_start, keyset_string.length() + 1, "");
1406     }
1407   }
1408   id_string += "." + KeysetToString(keyset);
1409   overridden_ref.replace(id_start, id_end - id_start, id_string);
1410 
1411   // Always add a timestamp tag to make sure the hash tags are changed, so that
1412   // the frontend will reload.
1413   auto ts_start = overridden_ref.find("&ts=");
1414   std::string ts_tag =
1415       base::StringPrintf("&ts=%" PRId64, base::Time::NowFromSystemTime()
1416                                              .ToDeltaSinceWindowsEpoch()
1417                                              .InMicroseconds());
1418   if (ts_start == std::string::npos) {
1419     overridden_ref += ts_tag;
1420   } else {
1421     auto ts_end = overridden_ref.find("&", ts_start + 1);
1422     if (ts_end == std::string::npos) {
1423       overridden_ref.replace(ts_start, overridden_ref.length() - ts_start,
1424                              ts_tag);
1425     } else {
1426       overridden_ref.replace(ts_start, ts_end - ts_start, ts_tag);
1427     }
1428   }
1429 
1430   GURL::Replacements replacements;
1431   replacements.SetRefStr(overridden_ref);
1432   state_->OverrideInputViewUrl(url.ReplaceComponents(replacements));
1433   ReloadKeyboard();
1434 }
1435 
SetImeMenuFeatureEnabled(ImeMenuFeature feature,bool enabled)1436 void InputMethodManagerImpl::SetImeMenuFeatureEnabled(ImeMenuFeature feature,
1437                                                       bool enabled) {
1438   const uint32_t original_state = features_enabled_state_;
1439   if (enabled)
1440     features_enabled_state_ |= feature;
1441   else
1442     features_enabled_state_ &= ~feature;
1443   if (original_state != features_enabled_state_)
1444     NotifyObserversImeExtraInputStateChange();
1445 }
1446 
GetImeMenuFeatureEnabled(ImeMenuFeature feature) const1447 bool InputMethodManagerImpl::GetImeMenuFeatureEnabled(
1448     ImeMenuFeature feature) const {
1449   return features_enabled_state_ & feature;
1450 }
1451 
NotifyObserversImeExtraInputStateChange()1452 void InputMethodManagerImpl::NotifyObserversImeExtraInputStateChange() {
1453   for (auto& observer : observers_) {
1454     const bool is_emoji_enabled =
1455         (features_enabled_state_ & InputMethodManager::FEATURE_EMOJI);
1456     const bool is_handwriting_enabled =
1457         (features_enabled_state_ & InputMethodManager::FEATURE_HANDWRITING);
1458     const bool is_voice_enabled =
1459         (features_enabled_state_ & InputMethodManager::FEATURE_VOICE);
1460     observer.OnExtraInputEnabledStateChange(
1461         true, is_emoji_enabled, is_handwriting_enabled, is_voice_enabled);
1462   }
1463 }
1464 
1465 ui::InputMethodKeyboardController*
GetInputMethodKeyboardController()1466 InputMethodManagerImpl::GetInputMethodKeyboardController() {
1467   ui::IMEEngineHandlerInterface* engine =
1468       ui::IMEBridge::Get()->GetCurrentEngineHandler();
1469   if (!engine)
1470     return nullptr;
1471   return engine->GetInputMethodKeyboardController();
1472 }
1473 
ReloadKeyboard()1474 void InputMethodManagerImpl::ReloadKeyboard() {
1475   auto* keyboard_client = ChromeKeyboardControllerClient::Get();
1476   if (keyboard_client->is_keyboard_enabled())
1477     keyboard_client->ReloadKeyboardIfNeeded();
1478 }
1479 
1480 }  // namespace input_method
1481 }  // namespace chromeos
1482