1 // Copyright 2015 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/extensions/api/input_ime/input_ime_api.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <utility>
11
12 #include "ash/public/cpp/keyboard/keyboard_config.h"
13 #include "base/feature_list.h"
14 #include "base/macros.h"
15 #include "base/strings/stringprintf.h"
16 #include "chrome/browser/chromeos/input_method/assistive_window_properties.h"
17 #include "chrome/browser/chromeos/input_method/input_host_helper.h"
18 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
19 #include "chrome/browser/chromeos/input_method/native_input_method_engine.h"
20 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
21 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
22 #include "chrome/browser/chromeos/profiles/profile_helper.h"
23 #include "chrome/browser/profiles/profile_manager.h"
24 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
25 #include "chrome/common/extensions/api/input_ime.h"
26 #include "chrome/common/extensions/api/input_method_private.h"
27 #include "chromeos/constants/chromeos_features.h"
28 #include "extensions/browser/extension_registry.h"
29 #include "extensions/browser/process_manager.h"
30 #include "extensions/common/manifest_handlers/background_info.h"
31 #include "ui/base/ime/chromeos/component_extension_ime_manager.h"
32 #include "ui/base/ime/chromeos/extension_ime_util.h"
33 #include "ui/base/ime/chromeos/ime_engine_handler_interface.h"
34 #include "ui/base/ime/chromeos/input_method_manager.h"
35 #include "ui/base/ui_base_features.h"
36
37 namespace input_ime = extensions::api::input_ime;
38 namespace input_method_private = extensions::api::input_method_private;
39 namespace DeleteSurroundingText =
40 extensions::api::input_ime::DeleteSurroundingText;
41 namespace UpdateMenuItems = extensions::api::input_ime::UpdateMenuItems;
42 namespace HideInputView = extensions::api::input_ime::HideInputView;
43 namespace SetMenuItems = extensions::api::input_ime::SetMenuItems;
44 namespace SetCursorPosition = extensions::api::input_ime::SetCursorPosition;
45 namespace SetCandidates = extensions::api::input_ime::SetCandidates;
46 namespace SetCandidateWindowProperties =
47 extensions::api::input_ime::SetCandidateWindowProperties;
48 namespace SetAssistiveWindowProperties =
49 extensions::api::input_ime::SetAssistiveWindowProperties;
50 namespace SetAssistiveWindowButtonHighlighted =
51 extensions::api::input_ime::SetAssistiveWindowButtonHighlighted;
52 namespace ClearComposition = extensions::api::input_ime::ClearComposition;
53 namespace OnCompositionBoundsChanged =
54 extensions::api::input_method_private::OnCompositionBoundsChanged;
55 namespace NotifyImeMenuItemActivated =
56 extensions::api::input_method_private::NotifyImeMenuItemActivated;
57 namespace OnScreenProjectionChanged =
58 extensions::api::input_method_private::OnScreenProjectionChanged;
59 namespace SetSelectionRange =
60 extensions::api::input_method_private::SetSelectionRange;
61 namespace FinishComposingText =
62 extensions::api::input_method_private::FinishComposingText;
63 using chromeos::InputMethodEngine;
64 using chromeos::InputMethodEngineBase;
65 using ui::IMEEngineHandlerInterface;
66
67 namespace {
68 const char kErrorEngineNotAvailable[] = "The engine is not available.";
69 const char kErrorSetMenuItemsFail[] = "Could not create menu items.";
70 const char kErrorUpdateMenuItemsFail[] = "Could not update menu items.";
71 const char kErrorEngineNotActive[] = "The engine is not active.";
72 const char kErrorRouterNotAvailable[] = "The router is not available.";
73
SetMenuItemToMenu(const input_ime::MenuItem & input,chromeos::input_method::InputMethodManager::MenuItem * out)74 void SetMenuItemToMenu(
75 const input_ime::MenuItem& input,
76 chromeos::input_method::InputMethodManager::MenuItem* out) {
77 out->modified = 0;
78 out->id = input.id;
79 if (input.label) {
80 out->modified |= InputMethodEngine::MENU_ITEM_MODIFIED_LABEL;
81 out->label = *input.label;
82 }
83
84 if (input.style != input_ime::MENU_ITEM_STYLE_NONE) {
85 out->modified |= InputMethodEngine::MENU_ITEM_MODIFIED_STYLE;
86 out->style =
87 static_cast<chromeos::input_method::InputMethodManager::MenuItemStyle>(
88 input.style);
89 }
90
91 if (input.visible)
92 out->modified |= InputMethodEngine::MENU_ITEM_MODIFIED_VISIBLE;
93 out->visible = input.visible ? *input.visible : true;
94
95 if (input.checked)
96 out->modified |= InputMethodEngine::MENU_ITEM_MODIFIED_CHECKED;
97 out->checked = input.checked ? *input.checked : false;
98
99 if (input.enabled)
100 out->modified |= InputMethodEngine::MENU_ITEM_MODIFIED_ENABLED;
101 out->enabled = input.enabled ? *input.enabled : true;
102 }
103
GetKeyboardConfig()104 keyboard::KeyboardConfig GetKeyboardConfig() {
105 return ChromeKeyboardControllerClient::Get()->GetKeyboardConfig();
106 }
107
ConvertAssistiveWindowType(input_ime::AssistiveWindowType type)108 ui::ime::AssistiveWindowType ConvertAssistiveWindowType(
109 input_ime::AssistiveWindowType type) {
110 switch (type) {
111 case input_ime::ASSISTIVE_WINDOW_TYPE_NONE:
112 return ui::ime::AssistiveWindowType::kNone;
113 case input_ime::ASSISTIVE_WINDOW_TYPE_UNDO:
114 return ui::ime::AssistiveWindowType::kUndoWindow;
115 }
116 }
117
ConvertAssistiveWindowButtonId(input_ime::AssistiveWindowButton id)118 ui::ime::ButtonId ConvertAssistiveWindowButtonId(
119 input_ime::AssistiveWindowButton id) {
120 switch (id) {
121 case input_ime::ASSISTIVE_WINDOW_BUTTON_ADDTODICTIONARY:
122 return ui::ime::ButtonId::kAddToDictionary;
123 case input_ime::ASSISTIVE_WINDOW_BUTTON_UNDO:
124 return ui::ime::ButtonId::kUndo;
125 case input_ime::ASSISTIVE_WINDOW_BUTTON_NONE:
126 return ui::ime::ButtonId::kNone;
127 }
128 }
129
ConvertAssistiveWindowButton(const ui::ime::ButtonId id)130 input_ime::AssistiveWindowButton ConvertAssistiveWindowButton(
131 const ui::ime::ButtonId id) {
132 switch (id) {
133 case ui::ime::ButtonId::kNone:
134 case ui::ime::ButtonId::kSmartInputsSettingLink:
135 case ui::ime::ButtonId::kSuggestion:
136 case ui::ime::ButtonId::kLearnMore:
137 return input_ime::ASSISTIVE_WINDOW_BUTTON_NONE;
138 case ui::ime::ButtonId::kUndo:
139 return input_ime::ASSISTIVE_WINDOW_BUTTON_UNDO;
140 case ui::ime::ButtonId::kAddToDictionary:
141 return input_ime::ASSISTIVE_WINDOW_BUTTON_ADDTODICTIONARY;
142 }
143 }
144
ConvertAssistiveWindowType(const ui::ime::AssistiveWindowType & type)145 input_ime::AssistiveWindowType ConvertAssistiveWindowType(
146 const ui::ime::AssistiveWindowType& type) {
147 switch (type) {
148 case ui::ime::AssistiveWindowType::kNone:
149 case ui::ime::AssistiveWindowType::kEmojiSuggestion:
150 case ui::ime::AssistiveWindowType::kPersonalInfoSuggestion:
151 return input_ime::AssistiveWindowType::ASSISTIVE_WINDOW_TYPE_NONE;
152 case ui::ime::AssistiveWindowType::kUndoWindow:
153 return input_ime::AssistiveWindowType::ASSISTIVE_WINDOW_TYPE_UNDO;
154 }
155 }
156
157 class ImeObserverChromeOS : public ui::ImeObserver {
158 public:
ImeObserverChromeOS(const std::string & extension_id,Profile * profile)159 ImeObserverChromeOS(const std::string& extension_id, Profile* profile)
160 : ImeObserver(extension_id, profile) {}
161
162 ~ImeObserverChromeOS() override = default;
163
164 // chromeos::InputMethodEngineBase::Observer overrides.
OnInputContextUpdate(const IMEEngineHandlerInterface::InputContext & context)165 void OnInputContextUpdate(
166 const IMEEngineHandlerInterface::InputContext& context) override {
167 if (extension_id_.empty() ||
168 !HasListener(input_ime::OnInputContextUpdate::kEventName))
169 return;
170
171 input_ime::InputContext context_value;
172 context_value.context_id = context.id;
173 context_value.type =
174 input_ime::ParseInputContextType(ConvertInputContextType(context));
175
176 std::unique_ptr<base::ListValue> args(
177 input_ime::OnInputContextUpdate::Create(context_value));
178
179 DispatchEventToExtension(
180 extensions::events::INPUT_IME_ON_INPUT_CONTEXT_UPDATE,
181 input_ime::OnInputContextUpdate::kEventName, std::move(args));
182 }
183
OnCandidateClicked(const std::string & component_id,int candidate_id,InputMethodEngineBase::MouseButtonEvent button)184 void OnCandidateClicked(
185 const std::string& component_id,
186 int candidate_id,
187 InputMethodEngineBase::MouseButtonEvent button) override {
188 if (extension_id_.empty() ||
189 !HasListener(input_ime::OnCandidateClicked::kEventName))
190 return;
191
192 input_ime::MouseButton button_enum = input_ime::MOUSE_BUTTON_NONE;
193 switch (button) {
194 case InputMethodEngineBase::MOUSE_BUTTON_MIDDLE:
195 button_enum = input_ime::MOUSE_BUTTON_MIDDLE;
196 break;
197
198 case InputMethodEngineBase::MOUSE_BUTTON_RIGHT:
199 button_enum = input_ime::MOUSE_BUTTON_RIGHT;
200 break;
201
202 case InputMethodEngineBase::MOUSE_BUTTON_LEFT:
203 // Default to left.
204 default:
205 button_enum = input_ime::MOUSE_BUTTON_LEFT;
206 break;
207 }
208
209 std::unique_ptr<base::ListValue> args(input_ime::OnCandidateClicked::Create(
210 component_id, candidate_id, button_enum));
211
212 DispatchEventToExtension(extensions::events::INPUT_IME_ON_CANDIDATE_CLICKED,
213 input_ime::OnCandidateClicked::kEventName,
214 std::move(args));
215 }
216
OnMenuItemActivated(const std::string & component_id,const std::string & menu_id)217 void OnMenuItemActivated(const std::string& component_id,
218 const std::string& menu_id) override {
219 if (extension_id_.empty() ||
220 !HasListener(input_ime::OnMenuItemActivated::kEventName))
221 return;
222
223 std::unique_ptr<base::ListValue> args(
224 input_ime::OnMenuItemActivated::Create(component_id, menu_id));
225
226 DispatchEventToExtension(
227 extensions::events::INPUT_IME_ON_MENU_ITEM_ACTIVATED,
228 input_ime::OnMenuItemActivated::kEventName, std::move(args));
229 }
230
OnScreenProjectionChanged(bool is_projected)231 void OnScreenProjectionChanged(bool is_projected) override {
232 if (extension_id_.empty() ||
233 !HasListener(OnScreenProjectionChanged::kEventName)) {
234 return;
235 }
236 // Note: this is a private API event.
237 std::unique_ptr<base::ListValue> args(new base::ListValue());
238 args->Append(std::make_unique<base::Value>(is_projected));
239
240 DispatchEventToExtension(
241 extensions::events::INPUT_METHOD_PRIVATE_ON_SCREEN_PROJECTION_CHANGED,
242 OnScreenProjectionChanged::kEventName, std::move(args));
243 }
244
OnCompositionBoundsChanged(const std::vector<gfx::Rect> & bounds)245 void OnCompositionBoundsChanged(
246 const std::vector<gfx::Rect>& bounds) override {
247 if (extension_id_.empty() ||
248 !HasListener(OnCompositionBoundsChanged::kEventName))
249 return;
250
251 // Note: this is a private API event.
252 auto bounds_list = std::make_unique<base::ListValue>();
253 for (auto bound : bounds) {
254 auto bounds_value = std::make_unique<base::DictionaryValue>();
255 bounds_value->SetInteger("x", bound.x());
256 bounds_value->SetInteger("y", bound.y());
257 bounds_value->SetInteger("w", bound.width());
258 bounds_value->SetInteger("h", bound.height());
259 bounds_list->Append(std::move(bounds_value));
260 }
261
262 if (bounds_list->GetSize() <= 0)
263 return;
264 std::unique_ptr<base::ListValue> args(new base::ListValue());
265
266 // The old extension code uses the first parameter to get the bounds of the
267 // first composition character, so for backward compatibility, add it here.
268 base::Value* first_value = NULL;
269 if (bounds_list->Get(0, &first_value))
270 args->Append(first_value->CreateDeepCopy());
271 args->Append(std::move(bounds_list));
272
273 DispatchEventToExtension(
274 extensions::events::INPUT_METHOD_PRIVATE_ON_COMPOSITION_BOUNDS_CHANGED,
275 OnCompositionBoundsChanged::kEventName, std::move(args));
276 }
277
OnFocus(const IMEEngineHandlerInterface::InputContext & context)278 void OnFocus(
279 const IMEEngineHandlerInterface::InputContext& context) override {
280 if (extension_id_.empty())
281 return;
282
283 // There is both a public and private OnFocus event. The private OnFocus
284 // event is only for ChromeOS and contains additional information about pen
285 // inputs. We ensure that we only trigger one OnFocus event.
286 if (ExtensionHasListener(input_method_private::OnFocus::kEventName)) {
287 input_method_private::InputContext input_context;
288 input_context.context_id = context.id;
289 input_context.type = input_method_private::ParseInputContextType(
290 ConvertInputContextType(context));
291 input_context.mode = input_method_private::ParseInputModeType(
292 ConvertInputContextMode(context));
293 input_context.auto_correct = ConvertInputContextAutoCorrect(context);
294 input_context.auto_complete = ConvertInputContextAutoComplete(context);
295 input_context.auto_capitalize =
296 ConvertInputContextAutoCapitalize(context.flags);
297 input_context.spell_check = ConvertInputContextSpellCheck(context);
298 input_context.has_been_password = ConvertHasBeenPassword(context);
299 input_context.should_do_learning = context.should_do_learning;
300 input_context.focus_reason = input_method_private::ParseFocusReason(
301 ConvertInputContextFocusReason(context));
302
303 // Populate app key for private OnFocus.
304 // TODO(b/163645900): Add app type later.
305 chromeos::input_host_helper::InputAssociatedHost host;
306 chromeos::input_host_helper::PopulateInputHost(&host);
307 input_context.app_key = std::make_unique<std::string>(host.app_key);
308
309 std::unique_ptr<base::ListValue> args(
310 input_method_private::OnFocus::Create(input_context));
311
312 DispatchEventToExtension(
313 extensions::events::INPUT_METHOD_PRIVATE_ON_FOCUS,
314 input_method_private::OnFocus::kEventName, std::move(args));
315 return;
316 }
317
318 ImeObserver::OnFocus(context);
319 }
320
OnAssistiveWindowButtonClicked(const ui::ime::AssistiveWindowButton & button)321 void OnAssistiveWindowButtonClicked(
322 const ui::ime::AssistiveWindowButton& button) override {
323 if (extension_id_.empty() ||
324 !HasListener(input_ime::OnAssistiveWindowButtonClicked::kEventName)) {
325 return;
326 }
327 input_ime::OnAssistiveWindowButtonClicked::Details details;
328 details.button_id = ConvertAssistiveWindowButton(button.id);
329 details.window_type = ConvertAssistiveWindowType(button.window_type);
330
331 std::unique_ptr<base::ListValue> args(
332 input_ime::OnAssistiveWindowButtonClicked::Create(details));
333 DispatchEventToExtension(
334 extensions::events::INPUT_IME_ON_ASSISTIVE_WINDOW_BUTTON_CLICKED,
335 input_ime::OnAssistiveWindowButtonClicked::kEventName, std::move(args));
336 }
337
OnSuggestionsChanged(const std::vector<std::string> & suggestions)338 void OnSuggestionsChanged(
339 const std::vector<std::string>& suggestions) override {
340 std::unique_ptr<base::ListValue> args(
341 input_method_private::OnSuggestionsChanged::Create(suggestions));
342 DispatchEventToExtension(
343 extensions::events::INPUT_IME_ON_SUGGESTIONS_CHANGED,
344 input_method_private::OnSuggestionsChanged::kEventName,
345 std::move(args));
346 }
347
OnInputMethodOptionsChanged(const std::string & engine_id)348 void OnInputMethodOptionsChanged(const std::string& engine_id) override {
349 std::unique_ptr<base::ListValue> args(
350 input_method_private::OnInputMethodOptionsChanged::Create(engine_id));
351 DispatchEventToExtension(
352 extensions::events::INPUT_IME_ON_INPUT_METHOD_OPTIONS_CHANGED,
353 input_method_private::OnInputMethodOptionsChanged::kEventName,
354 std::move(args));
355 }
356
357 private:
358 // ui::ImeObserver overrides.
DispatchEventToExtension(extensions::events::HistogramValue histogram_value,const std::string & event_name,std::unique_ptr<base::ListValue> args)359 void DispatchEventToExtension(
360 extensions::events::HistogramValue histogram_value,
361 const std::string& event_name,
362 std::unique_ptr<base::ListValue> args) override {
363 if (event_name == input_ime::OnActivate::kEventName) {
364 // Send onActivate event regardless of it's listened by the IME.
365 auto event = std::make_unique<extensions::Event>(
366 histogram_value, event_name, std::move(args), profile_);
367 extensions::EventRouter::Get(profile_)->DispatchEventWithLazyListener(
368 extension_id_, std::move(event));
369 return;
370 }
371
372 // For suspended IME extension (e.g. XKB extension), don't awake it by IME
373 // events except onActivate. The IME extension should be awake by other
374 // events (e.g. runtime.onMessage) from its other pages.
375 // This is to save memory for steady state Chrome OS on which the users
376 // don't want any IME features.
377 extensions::ExtensionRegistry* extension_registry =
378 extensions::ExtensionRegistry::Get(profile_);
379 if (extension_registry) {
380 const extensions::Extension* extension =
381 extension_registry->GetExtensionById(
382 extension_id_, extensions::ExtensionRegistry::ENABLED);
383 if (!extension)
384 return;
385 extensions::ProcessManager* process_manager =
386 extensions::ProcessManager::Get(profile_);
387 if (extensions::BackgroundInfo::HasBackgroundPage(extension) &&
388 !process_manager->GetBackgroundHostForExtension(extension_id_)) {
389 return;
390 }
391 }
392
393 auto event = std::make_unique<extensions::Event>(
394 histogram_value, event_name, std::move(args), profile_);
395 extensions::EventRouter::Get(profile_)
396 ->DispatchEventToExtension(extension_id_, std::move(event));
397 }
398
399 // The component IME extensions need to know the current screen type (e.g.
400 // lock screen, login screen, etc.) so that its on-screen keyboard page
401 // won't open new windows/pages. See crbug.com/395621.
GetCurrentScreenType()402 std::string GetCurrentScreenType() override {
403 switch (chromeos::input_method::InputMethodManager::Get()
404 ->GetActiveIMEState()
405 ->GetUIStyle()) {
406 case chromeos::input_method::InputMethodManager::UIStyle::kLogin:
407 return "login";
408 case chromeos::input_method::InputMethodManager::UIStyle::kSecondaryLogin:
409 return "secondary-login";
410 case chromeos::input_method::InputMethodManager::UIStyle::kLock:
411 return "lock";
412 case chromeos::input_method::InputMethodManager::UIStyle::kNormal:
413 return "normal";
414 }
415 }
416
ConvertInputContextFocusReason(ui::IMEEngineHandlerInterface::InputContext input_context)417 std::string ConvertInputContextFocusReason(
418 ui::IMEEngineHandlerInterface::InputContext input_context) {
419 switch (input_context.focus_reason) {
420 case ui::TextInputClient::FOCUS_REASON_NONE:
421 return "";
422 case ui::TextInputClient::FOCUS_REASON_MOUSE:
423 return "mouse";
424 case ui::TextInputClient::FOCUS_REASON_TOUCH:
425 return "touch";
426 case ui::TextInputClient::FOCUS_REASON_PEN:
427 return "pen";
428 case ui::TextInputClient::FOCUS_REASON_OTHER:
429 return "other";
430 }
431 }
432
ConvertInputContextAutoCorrect(ui::IMEEngineHandlerInterface::InputContext input_context)433 bool ConvertInputContextAutoCorrect(
434 ui::IMEEngineHandlerInterface::InputContext input_context) override {
435 if (!GetKeyboardConfig().auto_correct)
436 return false;
437 return ImeObserver::ConvertInputContextAutoCorrect(input_context);
438 }
439
ConvertInputContextAutoComplete(ui::IMEEngineHandlerInterface::InputContext input_context)440 bool ConvertInputContextAutoComplete(
441 ui::IMEEngineHandlerInterface::InputContext input_context) override {
442 if (!GetKeyboardConfig().auto_complete)
443 return false;
444 return ImeObserver::ConvertInputContextAutoComplete(input_context);
445 }
446
ConvertInputContextAutoCapitalize(int flags)447 input_method_private::AutoCapitalizeType ConvertInputContextAutoCapitalize(
448 int flags) {
449 if (!GetKeyboardConfig().auto_capitalize)
450 return input_method_private::AUTO_CAPITALIZE_TYPE_OFF;
451 if (flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_NONE)
452 return input_method_private::AUTO_CAPITALIZE_TYPE_OFF;
453 if (flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_CHARACTERS)
454 return input_method_private::AUTO_CAPITALIZE_TYPE_CHARACTERS;
455 if (flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_WORDS)
456 return input_method_private::AUTO_CAPITALIZE_TYPE_WORDS;
457 if (flags & ui::TEXT_INPUT_FLAG_AUTOCAPITALIZE_SENTENCES)
458 return input_method_private::AUTO_CAPITALIZE_TYPE_SENTENCES;
459
460 // Autocapitalize flag may be missing for native text fields.
461 // See https://crbug.com/1002713.
462 return input_method_private::AUTO_CAPITALIZE_TYPE_NONE;
463 }
464
ConvertInputContextSpellCheck(ui::IMEEngineHandlerInterface::InputContext input_context)465 bool ConvertInputContextSpellCheck(
466 ui::IMEEngineHandlerInterface::InputContext input_context) override {
467 if (!GetKeyboardConfig().spell_check)
468 return false;
469 return ImeObserver::ConvertInputContextSpellCheck(input_context);
470 }
471
ConvertHasBeenPassword(ui::IMEEngineHandlerInterface::InputContext input_context)472 bool ConvertHasBeenPassword(
473 ui::IMEEngineHandlerInterface::InputContext input_context) {
474 return input_context.flags & ui::TEXT_INPUT_FLAG_HAS_BEEN_PASSWORD;
475 }
476
ConvertInputContextMode(ui::IMEEngineHandlerInterface::InputContext input_context)477 std::string ConvertInputContextMode(
478 ui::IMEEngineHandlerInterface::InputContext input_context) {
479 std::string input_mode_type = "none"; // default to nothing
480 switch (input_context.mode) {
481 case ui::TEXT_INPUT_MODE_SEARCH:
482 input_mode_type = "search";
483 break;
484 case ui::TEXT_INPUT_MODE_TEL:
485 input_mode_type = "tel";
486 break;
487 case ui::TEXT_INPUT_MODE_URL:
488 input_mode_type = "url";
489 break;
490 case ui::TEXT_INPUT_MODE_EMAIL:
491 input_mode_type = "email";
492 break;
493 case ui::TEXT_INPUT_MODE_NUMERIC:
494 input_mode_type = "numeric";
495 break;
496 case ui::TEXT_INPUT_MODE_DECIMAL:
497 input_mode_type = "decimal";
498 break;
499 case ui::TEXT_INPUT_MODE_NONE:
500 input_mode_type = "noKeyboard";
501 break;
502 case ui::TEXT_INPUT_MODE_TEXT:
503 input_mode_type = "text";
504 break;
505 default:
506 input_mode_type = "";
507 break;
508 }
509 return input_mode_type;
510 }
511
512 DISALLOW_COPY_AND_ASSIGN(ImeObserverChromeOS);
513 };
514
515 } // namespace
516
517 namespace extensions {
518
GetEngineIfActive(Profile * profile,const std::string & extension_id,std::string * error)519 InputMethodEngine* GetEngineIfActive(Profile* profile,
520 const std::string& extension_id,
521 std::string* error) {
522 InputImeEventRouter* event_router = GetInputImeEventRouter(profile);
523 DCHECK(event_router) << kErrorRouterNotAvailable;
524 InputMethodEngine* engine = static_cast<InputMethodEngine*>(
525 event_router->GetEngineIfActive(extension_id, error));
526 return engine;
527 }
528
GetEngine(content::BrowserContext * browser_context,const std::string & extension_id,std::string * error)529 InputMethodEngine* GetEngine(content::BrowserContext* browser_context,
530 const std::string& extension_id,
531 std::string* error) {
532 Profile* profile = Profile::FromBrowserContext(browser_context);
533 InputImeEventRouter* event_router = GetInputImeEventRouter(profile);
534 DCHECK(event_router) << kErrorRouterNotAvailable;
535 InputMethodEngine* engine =
536 static_cast<InputMethodEngine*>(event_router->GetEngine(extension_id));
537 DCHECK(engine) << kErrorEngineNotAvailable;
538 if (!engine)
539 *error = kErrorEngineNotAvailable;
540 return engine;
541 }
542
InputImeEventRouter(Profile * profile)543 InputImeEventRouter::InputImeEventRouter(Profile* profile)
544 : InputImeEventRouterBase(profile) {}
545
546 InputImeEventRouter::~InputImeEventRouter() = default;
547
RegisterImeExtension(const std::string & extension_id,const std::vector<InputComponentInfo> & input_components)548 bool InputImeEventRouter::RegisterImeExtension(
549 const std::string& extension_id,
550 const std::vector<InputComponentInfo>& input_components) {
551 VLOG(1) << "RegisterImeExtension: " << extension_id;
552
553 if (engine_map_[extension_id])
554 return false;
555
556 chromeos::input_method::InputMethodManager* manager =
557 chromeos::input_method::InputMethodManager::Get();
558 chromeos::ComponentExtensionIMEManager* comp_ext_ime_manager =
559 manager->GetComponentExtensionIMEManager();
560
561 chromeos::input_method::InputMethodDescriptors descriptors;
562 // Only creates descriptors for 3rd party IME extension, because the
563 // descriptors for component IME extensions are managed by InputMethodUtil.
564 if (!comp_ext_ime_manager->IsAllowlistedExtension(extension_id)) {
565 for (const auto& component : input_components) {
566 DCHECK(component.type == INPUT_COMPONENT_TYPE_IME);
567
568 std::vector<std::string> layouts;
569 layouts.assign(component.layouts.begin(), component.layouts.end());
570 std::vector<std::string> languages;
571 languages.assign(component.languages.begin(), component.languages.end());
572
573 const std::string& input_method_id =
574 chromeos::extension_ime_util::GetInputMethodID(extension_id,
575 component.id);
576 descriptors.push_back(chromeos::input_method::InputMethodDescriptor(
577 input_method_id,
578 component.name,
579 std::string(), // TODO(uekawa): Set short name.
580 layouts,
581 languages,
582 false, // 3rd party IMEs are always not for login.
583 component.options_page_url,
584 component.input_view_url));
585 }
586 }
587
588 Profile* profile = GetProfile();
589 // TODO(https://crbug.com/1140236): Investigate whether profile selection
590 // is really needed.
591 bool is_login = false;
592 // When Chrome starts with the Login screen, sometimes active IME State was
593 // not set yet. It's asynchronous process. So we need a fallback for that.
594 scoped_refptr<chromeos::input_method::InputMethodManager::State>
595 active_state = chromeos::input_method::InputMethodManager::Get()
596 ->GetActiveIMEState();
597 if (active_state) {
598 is_login = active_state->GetUIStyle() ==
599 chromeos::input_method::InputMethodManager::UIStyle::kLogin;
600 } else {
601 is_login = chromeos::ProfileHelper::IsSigninProfile(profile);
602 }
603
604 if (is_login && profile->HasPrimaryOTRProfile()) {
605 profile = profile->GetPrimaryOTRProfile();
606 }
607
608 auto observer = std::make_unique<ImeObserverChromeOS>(extension_id, profile);
609 auto engine = extension_id == "jkghodnilhceideoidjikpgommlajknk"
610 ? std::make_unique<chromeos::NativeInputMethodEngine>()
611 : std::make_unique<chromeos::InputMethodEngine>();
612 engine->Initialize(std::move(observer), extension_id.c_str(), profile);
613 engine_map_[extension_id] = std::move(engine);
614
615 chromeos::UserSessionManager::GetInstance()
616 ->GetDefaultIMEState(profile)
617 ->AddInputMethodExtension(extension_id, descriptors,
618 engine_map_[extension_id].get());
619
620 return true;
621 }
622
UnregisterAllImes(const std::string & extension_id)623 void InputImeEventRouter::UnregisterAllImes(const std::string& extension_id) {
624 auto it = engine_map_.find(extension_id);
625 if (it != engine_map_.end()) {
626 chromeos::input_method::InputMethodManager::Get()
627 ->GetActiveIMEState()
628 ->RemoveInputMethodExtension(extension_id);
629 engine_map_.erase(it);
630 }
631 }
632
GetEngine(const std::string & extension_id)633 InputMethodEngine* InputImeEventRouter::GetEngine(
634 const std::string& extension_id) {
635 auto it = engine_map_.find(extension_id);
636 if (it == engine_map_.end()) {
637 LOG(ERROR) << kErrorEngineNotAvailable << " extension id: " << extension_id;
638 return nullptr;
639 } else {
640 return it->second.get();
641 }
642 }
643
GetEngineIfActive(const std::string & extension_id,std::string * error)644 InputMethodEngineBase* InputImeEventRouter::GetEngineIfActive(
645 const std::string& extension_id,
646 std::string* error) {
647 auto it = engine_map_.find(extension_id);
648 if (it == engine_map_.end()) {
649 LOG(ERROR) << kErrorEngineNotAvailable << " extension id: " << extension_id;
650 *error = kErrorEngineNotAvailable;
651 return nullptr;
652 } else if (it->second->IsActive()) {
653 return it->second.get();
654 } else {
655 LOG(WARNING) << kErrorEngineNotActive << " extension id: " << extension_id;
656 *error = kErrorEngineNotActive;
657 return nullptr;
658 }
659 }
660
Run()661 ExtensionFunction::ResponseAction InputImeClearCompositionFunction::Run() {
662 std::string error;
663 InputMethodEngine* engine = GetEngineIfActive(
664 Profile::FromBrowserContext(browser_context()), extension_id(), &error);
665 if (!engine) {
666 return RespondNow(Error(InformativeError(error, static_function_name())));
667 }
668
669 std::unique_ptr<ClearComposition::Params> parent_params(
670 ClearComposition::Params::Create(*args_));
671 const ClearComposition::Params::Parameters& params =
672 parent_params->parameters;
673
674 bool success = engine->ClearComposition(params.context_id, &error);
675 std::unique_ptr<base::ListValue> results =
676 std::make_unique<base::ListValue>();
677 results->Append(std::make_unique<base::Value>(success));
678 return RespondNow(success
679 ? ArgumentList(std::move(results))
680 : ErrorWithArguments(
681 std::move(results),
682 InformativeError(error, static_function_name())));
683 }
684
Run()685 ExtensionFunction::ResponseAction InputImeHideInputViewFunction::Run() {
686 std::string error;
687 InputMethodEngine* engine = GetEngineIfActive(
688 Profile::FromBrowserContext(browser_context()), extension_id(), &error);
689 if (!engine)
690 return RespondNow(Error(InformativeError(error, static_function_name())));
691 engine->HideInputView();
692 return RespondNow(NoArguments());
693 }
694
695 ExtensionFunction::ResponseAction
Run()696 InputImeSetAssistiveWindowPropertiesFunction::Run() {
697 std::string error;
698 InputMethodEngine* engine = GetEngineIfActive(
699 Profile::FromBrowserContext(browser_context()), extension_id(), &error);
700 if (!engine) {
701 return RespondNow(Error(InformativeError(error, static_function_name())));
702 }
703 std::unique_ptr<SetAssistiveWindowProperties::Params> parent_params(
704 SetAssistiveWindowProperties::Params::Create(*args_));
705 const SetAssistiveWindowProperties::Params::Parameters& params =
706 parent_params->parameters;
707 const input_ime::AssistiveWindowProperties& window = params.properties;
708 chromeos::AssistiveWindowProperties assistive_window;
709
710 assistive_window.visible = window.visible;
711 assistive_window.type = ConvertAssistiveWindowType(window.type);
712 if (window.announce_string)
713 assistive_window.announce_string = *window.announce_string;
714
715 engine->SetAssistiveWindowProperties(params.context_id, assistive_window,
716 &error);
717 if (!error.empty())
718 return RespondNow(Error(InformativeError(error, static_function_name())));
719 return RespondNow(OneArgument(base::Value(true)));
720 }
721
722 ExtensionFunction::ResponseAction
Run()723 InputImeSetAssistiveWindowButtonHighlightedFunction::Run() {
724 std::string error;
725 InputMethodEngine* engine = GetEngineIfActive(
726 Profile::FromBrowserContext(browser_context()), extension_id(), &error);
727 if (!engine) {
728 return RespondNow(Error(InformativeError(error, static_function_name())));
729 }
730 std::unique_ptr<SetAssistiveWindowButtonHighlighted::Params> parent_params(
731 SetAssistiveWindowButtonHighlighted::Params::Create(*args_));
732 const SetAssistiveWindowButtonHighlighted::Params::Parameters& params =
733 parent_params->parameters;
734 ui::ime::AssistiveWindowButton button;
735
736 button.id = ConvertAssistiveWindowButtonId(params.button_id);
737 button.window_type = ConvertAssistiveWindowType(params.window_type);
738 if (params.announce_string)
739 button.announce_string = *params.announce_string;
740
741 engine->SetButtonHighlighted(params.context_id, button, params.highlighted,
742 &error);
743 if (!error.empty())
744 return RespondNow(Error(InformativeError(error, static_function_name())));
745
746 return RespondNow(NoArguments());
747 }
748
749 ExtensionFunction::ResponseAction
Run()750 InputImeSetCandidateWindowPropertiesFunction::Run() {
751 std::unique_ptr<SetCandidateWindowProperties::Params> parent_params(
752 SetCandidateWindowProperties::Params::Create(*args_));
753 const SetCandidateWindowProperties::Params::Parameters&
754 params = parent_params->parameters;
755
756 std::string error;
757 InputMethodEngine* engine =
758 GetEngine(browser_context(), extension_id(), &error);
759 if (!engine) {
760 return RespondNow(Error(InformativeError(error, static_function_name())));
761 }
762
763 const SetCandidateWindowProperties::Params::Parameters::Properties&
764 properties = params.properties;
765
766 if (properties.visible &&
767 !engine->SetCandidateWindowVisible(*properties.visible, &error)) {
768 std::unique_ptr<base::ListValue> results =
769 std::make_unique<base::ListValue>();
770 results->Append(std::make_unique<base::Value>(false));
771 return RespondNow(ErrorWithArguments(
772 std::move(results), InformativeError(error, static_function_name())));
773 }
774
775 InputMethodEngine::CandidateWindowProperty properties_out =
776 engine->GetCandidateWindowProperty(params.engine_id);
777 bool modified = false;
778
779 if (properties.cursor_visible) {
780 properties_out.is_cursor_visible = *properties.cursor_visible;
781 modified = true;
782 }
783
784 if (properties.vertical) {
785 properties_out.is_vertical = *properties.vertical;
786 modified = true;
787 }
788
789 if (properties.page_size) {
790 properties_out.page_size = *properties.page_size;
791 modified = true;
792 }
793
794 if (properties.window_position == input_ime::WINDOW_POSITION_COMPOSITION) {
795 properties_out.show_window_at_composition = true;
796 modified = true;
797 } else if (properties.window_position == input_ime::WINDOW_POSITION_CURSOR) {
798 properties_out.show_window_at_composition = false;
799 modified = true;
800 }
801
802 if (properties.auxiliary_text) {
803 properties_out.auxiliary_text = *properties.auxiliary_text;
804 modified = true;
805 }
806
807 if (properties.auxiliary_text_visible) {
808 properties_out.is_auxiliary_text_visible =
809 *properties.auxiliary_text_visible;
810 modified = true;
811 }
812
813 if (properties.current_candidate_index) {
814 properties_out.current_candidate_index =
815 *properties.current_candidate_index;
816 modified = true;
817 }
818
819 if (properties.total_candidates) {
820 properties_out.total_candidates = *properties.total_candidates;
821 modified = true;
822 }
823
824 if (modified) {
825 engine->SetCandidateWindowProperty(params.engine_id, properties_out);
826 }
827
828 return RespondNow(OneArgument(base::Value(true)));
829 }
830
Run()831 ExtensionFunction::ResponseAction InputImeSetCandidatesFunction::Run() {
832 std::string error;
833 InputMethodEngine* engine = GetEngineIfActive(
834 Profile::FromBrowserContext(browser_context()), extension_id(), &error);
835 if (!engine) {
836 return RespondNow(Error(InformativeError(error, static_function_name())));
837 }
838
839 std::unique_ptr<SetCandidates::Params> parent_params(
840 SetCandidates::Params::Create(*args_));
841 const SetCandidates::Params::Parameters& params =
842 parent_params->parameters;
843
844 std::vector<InputMethodEngine::Candidate> candidates_out;
845 for (const auto& candidate_in : params.candidates) {
846 candidates_out.emplace_back();
847 candidates_out.back().value = candidate_in.candidate;
848 candidates_out.back().id = candidate_in.id;
849 if (candidate_in.label)
850 candidates_out.back().label = *candidate_in.label;
851 if (candidate_in.annotation)
852 candidates_out.back().annotation = *candidate_in.annotation;
853 if (candidate_in.usage) {
854 candidates_out.back().usage.title = candidate_in.usage->title;
855 candidates_out.back().usage.body = candidate_in.usage->body;
856 }
857 }
858
859 bool success =
860 engine->SetCandidates(params.context_id, candidates_out, &error);
861 std::unique_ptr<base::ListValue> results =
862 std::make_unique<base::ListValue>();
863 results->Append(std::make_unique<base::Value>(success));
864 return RespondNow(success
865 ? ArgumentList(std::move(results))
866 : ErrorWithArguments(
867 std::move(results),
868 InformativeError(error, static_function_name())));
869 }
870
Run()871 ExtensionFunction::ResponseAction InputImeSetCursorPositionFunction::Run() {
872 std::string error;
873 InputMethodEngine* engine = GetEngineIfActive(
874 Profile::FromBrowserContext(browser_context()), extension_id(), &error);
875 if (!engine) {
876 return RespondNow(Error(InformativeError(error, static_function_name())));
877 }
878
879 std::unique_ptr<SetCursorPosition::Params> parent_params(
880 SetCursorPosition::Params::Create(*args_));
881 const SetCursorPosition::Params::Parameters& params =
882 parent_params->parameters;
883
884 bool success =
885 engine->SetCursorPosition(params.context_id, params.candidate_id, &error);
886 std::unique_ptr<base::ListValue> results =
887 std::make_unique<base::ListValue>();
888 results->Append(std::make_unique<base::Value>(success));
889 return RespondNow(success
890 ? ArgumentList(std::move(results))
891 : ErrorWithArguments(
892 std::move(results),
893 InformativeError(error, static_function_name())));
894 }
895
Run()896 ExtensionFunction::ResponseAction InputImeSetMenuItemsFunction::Run() {
897 std::unique_ptr<SetMenuItems::Params> parent_params(
898 SetMenuItems::Params::Create(*args_));
899 const input_ime::MenuParameters& params = parent_params->parameters;
900
901 std::string error;
902 InputMethodEngine* engine =
903 GetEngine(browser_context(), extension_id(), &error);
904 if (!engine) {
905 return RespondNow(Error(InformativeError(error, static_function_name())));
906 }
907
908 std::vector<chromeos::input_method::InputMethodManager::MenuItem> items_out;
909 for (const input_ime::MenuItem& item_in : params.items) {
910 items_out.emplace_back();
911 SetMenuItemToMenu(item_in, &items_out.back());
912 }
913
914 if (!engine->SetMenuItems(items_out, &error)) {
915 return RespondNow(Error(InformativeError(
916 base::StringPrintf("%s %s", kErrorSetMenuItemsFail, error.c_str()),
917 static_function_name())));
918 }
919 return RespondNow(NoArguments());
920 }
921
Run()922 ExtensionFunction::ResponseAction InputImeUpdateMenuItemsFunction::Run() {
923 std::unique_ptr<UpdateMenuItems::Params> parent_params(
924 UpdateMenuItems::Params::Create(*args_));
925 const input_ime::MenuParameters& params = parent_params->parameters;
926
927 std::string error;
928 InputMethodEngine* engine =
929 GetEngine(browser_context(), extension_id(), &error);
930 if (!engine) {
931 return RespondNow(Error(InformativeError(error, static_function_name())));
932 }
933
934 std::vector<chromeos::input_method::InputMethodManager::MenuItem> items_out;
935 for (const input_ime::MenuItem& item_in : params.items) {
936 items_out.emplace_back();
937 SetMenuItemToMenu(item_in, &items_out.back());
938 }
939
940 if (!engine->UpdateMenuItems(items_out, &error)) {
941 return RespondNow(Error(InformativeError(
942 base::StringPrintf("%s %s", kErrorUpdateMenuItemsFail, error.c_str()),
943 static_function_name())));
944 }
945 return RespondNow(NoArguments());
946 }
947
Run()948 ExtensionFunction::ResponseAction InputImeDeleteSurroundingTextFunction::Run() {
949 std::unique_ptr<DeleteSurroundingText::Params> parent_params(
950 DeleteSurroundingText::Params::Create(*args_));
951 const DeleteSurroundingText::Params::Parameters& params =
952 parent_params->parameters;
953
954 std::string error;
955 InputMethodEngine* engine =
956 GetEngine(browser_context(), extension_id(), &error);
957 if (!engine) {
958 return RespondNow(Error(InformativeError(error, static_function_name())));
959 }
960
961 engine->DeleteSurroundingText(params.context_id, params.offset, params.length,
962 &error);
963 return RespondNow(
964 error.empty() ? NoArguments()
965 : Error(InformativeError(error, static_function_name())));
966 }
967
968 ExtensionFunction::ResponseAction
Run()969 InputMethodPrivateFinishComposingTextFunction::Run() {
970 std::string error;
971 InputMethodEngine* engine = GetEngineIfActive(
972 Profile::FromBrowserContext(browser_context()), extension_id(), &error);
973 if (!engine)
974 return RespondNow(Error(InformativeError(error, static_function_name())));
975 std::unique_ptr<FinishComposingText::Params> parent_params(
976 FinishComposingText::Params::Create(*args_));
977 const FinishComposingText::Params::Parameters& params =
978 parent_params->parameters;
979 engine->FinishComposingText(params.context_id, &error);
980 return RespondNow(
981 error.empty() ? NoArguments()
982 : Error(InformativeError(error, static_function_name())));
983 }
984
985 ExtensionFunction::ResponseAction
Run()986 InputMethodPrivateNotifyImeMenuItemActivatedFunction::Run() {
987 chromeos::input_method::InputMethodDescriptor current_input_method =
988 chromeos::input_method::InputMethodManager::Get()
989 ->GetActiveIMEState()
990 ->GetCurrentInputMethod();
991 std::string active_extension_id =
992 chromeos::extension_ime_util::GetExtensionIDFromInputMethodID(
993 current_input_method.id());
994 std::string error;
995 InputMethodEngine* engine =
996 GetEngineIfActive(Profile::FromBrowserContext(browser_context()),
997 active_extension_id, &error);
998 if (!engine)
999 return RespondNow(Error(InformativeError(error, static_function_name())));
1000
1001 std::unique_ptr<NotifyImeMenuItemActivated::Params> params(
1002 NotifyImeMenuItemActivated::Params::Create(*args_));
1003 if (params->engine_id != engine->GetActiveComponentId())
1004 return RespondNow(
1005 Error(InformativeError(kErrorEngineNotActive, static_function_name())));
1006 engine->PropertyActivate(params->name);
1007 return RespondNow(NoArguments());
1008 }
1009
1010 ExtensionFunction::ResponseAction
Run()1011 InputMethodPrivateGetCompositionBoundsFunction::Run() {
1012 std::string error;
1013 InputMethodEngine* engine = GetEngineIfActive(
1014 Profile::FromBrowserContext(browser_context()), extension_id(), &error);
1015 if (!engine)
1016 return RespondNow(Error(InformativeError(error, static_function_name())));
1017
1018 auto bounds_list = std::make_unique<base::ListValue>();
1019 for (const auto& bounds : engine->composition_bounds()) {
1020 auto bounds_value = std::make_unique<base::DictionaryValue>();
1021 bounds_value->SetInteger("x", bounds.x());
1022 bounds_value->SetInteger("y", bounds.y());
1023 bounds_value->SetInteger("w", bounds.width());
1024 bounds_value->SetInteger("h", bounds.height());
1025 bounds_list->Append(std::move(bounds_value));
1026 }
1027
1028 return RespondNow(
1029 OneArgument(base::Value::FromUniquePtrValue(std::move(bounds_list))));
1030 }
1031
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)1032 void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
1033 const Extension* extension) {
1034 const std::vector<InputComponentInfo>* input_components =
1035 InputComponents::GetInputComponents(extension);
1036 InputImeEventRouter* event_router =
1037 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context));
1038 if (input_components && event_router) {
1039 if (extension->id() == event_router->GetUnloadedExtensionId()) {
1040 // After the 1st-party IME extension being unloaded unexpectedly,
1041 // we don't unregister the IME entries so after the extension being
1042 // reloaded we should reactivate the engine so that the IME extension
1043 // can receive the onActivate event to recover itself upon the
1044 // unexpected unload.
1045 std::string error;
1046 InputMethodEngineBase* engine =
1047 event_router->GetEngineIfActive(extension->id(), &error);
1048 DCHECK(engine) << error;
1049 // When extension is unloaded unexpectedly and reloaded, OS doesn't pass
1050 // details.browser_context value in OnListenerAdded callback. So we need
1051 // to reactivate engine here.
1052 if (engine)
1053 engine->Enable(engine->GetActiveComponentId());
1054 event_router->SetUnloadedExtensionId("");
1055 } else {
1056 event_router->RegisterImeExtension(extension->id(), *input_components);
1057 }
1058 }
1059 }
1060
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionReason reason)1061 void InputImeAPI::OnExtensionUnloaded(content::BrowserContext* browser_context,
1062 const Extension* extension,
1063 UnloadedExtensionReason reason) {
1064 const std::vector<InputComponentInfo>* input_components =
1065 InputComponents::GetInputComponents(extension);
1066 if (!input_components || input_components->empty())
1067 return;
1068 InputImeEventRouter* event_router =
1069 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context));
1070 if (!event_router)
1071 return;
1072 chromeos::input_method::InputMethodManager* manager =
1073 chromeos::input_method::InputMethodManager::Get();
1074 chromeos::ComponentExtensionIMEManager* comp_ext_ime_manager =
1075 manager->GetComponentExtensionIMEManager();
1076
1077 if (comp_ext_ime_manager->IsAllowlistedExtension(extension->id())) {
1078 // Since the first party ime is not allow to uninstall, and when it's
1079 // unloaded unexpectedly, OS will recover the extension at once.
1080 // So should not unregister the IMEs. Otherwise the IME icons on the
1081 // desktop shelf will disappear. see bugs: 775507,788247,786273,761714.
1082 // But still need to unload keyboard container document. Since ime extension
1083 // need to re-render the document when it's recovered.
1084 auto* keyboard_client = ChromeKeyboardControllerClient::Get();
1085 if (keyboard_client->is_keyboard_enabled()) {
1086 // Keyboard controller "Reload" method only reload current page when the
1087 // url is changed. So we need unload the current page first. Then next
1088 // engine->Enable() can refresh the inputview page correctly.
1089 // Empties the content url and reload the controller to unload the
1090 // current page.
1091 // TODO(wuyingbing): Should add a new method to unload the document.
1092 manager->GetActiveIMEState()->DisableInputView();
1093 keyboard_client->ReloadKeyboardIfNeeded();
1094 }
1095 event_router->SetUnloadedExtensionId(extension->id());
1096 } else {
1097 event_router->UnregisterAllImes(extension->id());
1098 }
1099 }
1100
OnListenerAdded(const EventListenerInfo & details)1101 void InputImeAPI::OnListenerAdded(const EventListenerInfo& details) {
1102 if (!details.browser_context)
1103 return;
1104 std::string error;
1105 InputMethodEngine* engine =
1106 GetEngineIfActive(Profile::FromBrowserContext(details.browser_context),
1107 details.extension_id, &error);
1108 // Notifies the IME extension for IME ready with onActivate/onFocus events.
1109 if (engine)
1110 engine->Enable(engine->GetActiveComponentId());
1111 }
1112
1113 } // namespace extensions
1114