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