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/extensions/input_method_api.h"
6
7 #include <stddef.h>
8 #include <memory>
9 #include <set>
10 #include <string>
11 #include <utility>
12
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/lazy_instance.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/values.h"
19 #include "build/build_config.h"
20 #include "chrome/browser/chromeos/extensions/dictionary_event_router.h"
21 #include "chrome/browser/chromeos/extensions/ime_menu_event_router.h"
22 #include "chrome/browser/chromeos/extensions/input_method_event_router.h"
23 #include "chrome/browser/chromeos/input_method/autocorrect_manager.h"
24 #include "chrome/browser/chromeos/input_method/native_input_method_engine.h"
25 #include "chrome/browser/extensions/api/input_ime/input_ime_api.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/spellchecker/spellcheck_factory.h"
28 #include "chrome/browser/spellchecker/spellcheck_service.h"
29 #include "chrome/browser/sync/profile_sync_service_factory.h"
30 #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
31 #include "chrome/browser/ui/browser.h"
32 #include "chrome/browser/ui/browser_finder.h"
33 #include "chrome/common/extensions/api/input_method_private.h"
34 #include "chrome/common/pref_names.h"
35 #include "components/prefs/pref_service.h"
36 #include "components/prefs/scoped_user_pref_update.h"
37 #include "components/sync/driver/sync_service.h"
38 #include "components/sync/driver/sync_user_settings.h"
39 #include "extensions/browser/extension_function_registry.h"
40 #include "extensions/browser/extension_system.h"
41 #include "ui/base/ime/chromeos/extension_ime_util.h"
42 #include "ui/base/ime/chromeos/ime_bridge.h"
43 #include "ui/base/ime/chromeos/ime_keyboard.h"
44 #include "ui/base/ime/chromeos/input_method_descriptor.h"
45 #include "ui/base/ime/chromeos/input_method_manager.h"
46 #include "ui/base/ime/chromeos/input_method_util.h"
47
48 namespace input_method_private = extensions::api::input_method_private;
49 namespace AddWordToDictionary =
50 extensions::api::input_method_private::AddWordToDictionary;
51 namespace SetCurrentInputMethod =
52 extensions::api::input_method_private::SetCurrentInputMethod;
53 namespace SetXkbLayout = extensions::api::input_method_private::SetXkbLayout;
54 namespace OpenOptionsPage =
55 extensions::api::input_method_private::OpenOptionsPage;
56 namespace OnChanged = extensions::api::input_method_private::OnChanged;
57 namespace OnDictionaryChanged =
58 extensions::api::input_method_private::OnDictionaryChanged;
59 namespace OnDictionaryLoaded =
60 extensions::api::input_method_private::OnDictionaryLoaded;
61 namespace OnImeMenuActivationChanged =
62 extensions::api::input_method_private::OnImeMenuActivationChanged;
63 namespace OnImeMenuListChanged =
64 extensions::api::input_method_private::OnImeMenuListChanged;
65 namespace OnImeMenuItemsChanged =
66 extensions::api::input_method_private::OnImeMenuItemsChanged;
67 namespace GetSurroundingText =
68 extensions::api::input_method_private::GetSurroundingText;
69 namespace GetSettings = extensions::api::input_method_private::GetSettings;
70 namespace SetSettings = extensions::api::input_method_private::SetSettings;
71 namespace SetCompositionRange =
72 extensions::api::input_method_private::SetCompositionRange;
73 namespace SetComposingRange =
74 extensions::api::input_method_private::SetComposingRange;
75 namespace GetAutocorrectRange =
76 extensions::api::input_method_private::GetAutocorrectRange;
77 namespace GetAutocorrectCharacterBounds =
78 extensions::api::input_method_private::GetAutocorrectCharacterBounds;
79 namespace SetAutocorrectRange =
80 extensions::api::input_method_private::SetAutocorrectRange;
81 namespace SetSelectionRange =
82 extensions::api::input_method_private::SetSelectionRange;
83 namespace OnInputMethodOptionsChanged =
84 extensions::api::input_method_private::OnInputMethodOptionsChanged;
85 namespace OnAutocorrect = extensions::api::input_method_private::OnAutocorrect;
86
87 using chromeos::InputMethodEngineBase;
88
89 namespace {
90
91 // Prefix, which is used by XKB.
92 const char kXkbPrefix[] = "xkb:";
93 const char kErrorFailToShowInputView[] =
94 "Unable to show the input view window because the keyboard is not enabled.";
95 const char kErrorFailToHideInputView[] =
96 "Unable to hide the input view window because the keyboard is not enabled.";
97 const char kErrorRouterNotAvailable[] = "The router is not available.";
98 const char kErrorInvalidInputMethod[] = "Input method not found.";
99 const char kErrorSpellCheckNotAvailable[] =
100 "Spellcheck service is not available.";
101 const char kErrorCustomDictionaryNotLoaded[] =
102 "Custom dictionary is not loaded yet.";
103 const char kErrorInvalidWord[] = "Unable to add invalid word to dictionary.";
104 const char kErrorSyncServiceNotReady[] =
105 "Sync service is not ready for current profile.";
106 const char kErrorInputContextHandlerNotAvailable[] =
107 "Input context handler is not available.";
108 const char kErrorInvalidParametersForGetSurroundingText[] =
109 "Invalid negative parameters for GetSurroundingText.";
110
GetEngineIfActive(content::BrowserContext * browser_context,const std::string & extension_id,std::string * error)111 InputMethodEngineBase* GetEngineIfActive(
112 content::BrowserContext* browser_context,
113 const std::string& extension_id,
114 std::string* error) {
115 Profile* profile = Profile::FromBrowserContext(browser_context);
116 extensions::InputImeEventRouter* event_router =
117 extensions::GetInputImeEventRouter(profile);
118 DCHECK(event_router) << kErrorRouterNotAvailable;
119 InputMethodEngineBase* engine =
120 event_router->GetEngineIfActive(extension_id, error);
121 return engine;
122 }
123
124 } // namespace
125
126 namespace extensions {
127
128 ExtensionFunction::ResponseAction
Run()129 InputMethodPrivateGetInputMethodConfigFunction::Run() {
130 std::unique_ptr<base::DictionaryValue> output(new base::DictionaryValue());
131 output->SetBoolean("isPhysicalKeyboardAutocorrectEnabled", true);
132 output->SetBoolean("isImeMenuActivated",
133 Profile::FromBrowserContext(browser_context())
134 ->GetPrefs()
135 ->GetBoolean(prefs::kLanguageImeMenuActivated));
136 return RespondNow(
137 OneArgument(base::Value::FromUniquePtrValue(std::move(output))));
138 }
139
140 ExtensionFunction::ResponseAction
Run()141 InputMethodPrivateGetCurrentInputMethodFunction::Run() {
142 chromeos::input_method::InputMethodManager* manager =
143 chromeos::input_method::InputMethodManager::Get();
144 return RespondNow(OneArgument(
145 base::Value(manager->GetActiveIMEState()->GetCurrentInputMethod().id())));
146 }
147
148 ExtensionFunction::ResponseAction
Run()149 InputMethodPrivateSetCurrentInputMethodFunction::Run() {
150 std::unique_ptr<SetCurrentInputMethod::Params> params(
151 SetCurrentInputMethod::Params::Create(*args_));
152 EXTENSION_FUNCTION_VALIDATE(params.get());
153 scoped_refptr<chromeos::input_method::InputMethodManager::State> ime_state =
154 chromeos::input_method::InputMethodManager::Get()->GetActiveIMEState();
155 const std::vector<std::string>& input_methods =
156 ime_state->GetActiveInputMethodIds();
157 for (size_t i = 0; i < input_methods.size(); ++i) {
158 const std::string& input_method = input_methods[i];
159 if (input_method == params->input_method_id) {
160 ime_state->ChangeInputMethod(params->input_method_id,
161 false /* show_message */);
162 return RespondNow(NoArguments());
163 }
164 }
165 return RespondNow(Error(InformativeError(
166 base::StringPrintf("%s Input Method: %s", kErrorInvalidInputMethod,
167 params->input_method_id.c_str()),
168 static_function_name())));
169 }
170
171 ExtensionFunction::ResponseAction
Run()172 InputMethodPrivateGetInputMethodsFunction::Run() {
173 std::unique_ptr<base::ListValue> output(new base::ListValue());
174 chromeos::input_method::InputMethodManager* manager =
175 chromeos::input_method::InputMethodManager::Get();
176 chromeos::input_method::InputMethodUtil* util = manager->GetInputMethodUtil();
177 scoped_refptr<chromeos::input_method::InputMethodManager::State> ime_state =
178 manager->GetActiveIMEState();
179 std::unique_ptr<chromeos::input_method::InputMethodDescriptors>
180 input_methods = ime_state->GetActiveInputMethods();
181 for (size_t i = 0; i < input_methods->size(); ++i) {
182 const chromeos::input_method::InputMethodDescriptor& input_method =
183 (*input_methods)[i];
184 auto val = std::make_unique<base::DictionaryValue>();
185 val->SetString("id", input_method.id());
186 val->SetString("name", util->GetInputMethodLongName(input_method));
187 val->SetString("indicator", util->GetInputMethodShortName(input_method));
188 output->Append(std::move(val));
189 }
190 return RespondNow(
191 OneArgument(base::Value::FromUniquePtrValue(std::move(output))));
192 }
193
194 ExtensionFunction::ResponseAction
Run()195 InputMethodPrivateFetchAllDictionaryWordsFunction::Run() {
196 SpellcheckService* spellcheck =
197 SpellcheckServiceFactory::GetForContext(browser_context());
198 if (!spellcheck) {
199 return RespondNow(Error(InformativeError(kErrorSpellCheckNotAvailable,
200 static_function_name())));
201 }
202 SpellcheckCustomDictionary* dictionary = spellcheck->GetCustomDictionary();
203 if (!dictionary->IsLoaded()) {
204 return RespondNow(Error(InformativeError(kErrorCustomDictionaryNotLoaded,
205 static_function_name())));
206 }
207
208 const std::set<std::string>& words = dictionary->GetWords();
209 std::unique_ptr<base::ListValue> output(new base::ListValue());
210 for (auto it = words.begin(); it != words.end(); ++it) {
211 output->AppendString(*it);
212 }
213 return RespondNow(
214 OneArgument(base::Value::FromUniquePtrValue(std::move(output))));
215 }
216
217 ExtensionFunction::ResponseAction
Run()218 InputMethodPrivateAddWordToDictionaryFunction::Run() {
219 std::unique_ptr<AddWordToDictionary::Params> params(
220 AddWordToDictionary::Params::Create(*args_));
221 EXTENSION_FUNCTION_VALIDATE(params.get());
222 SpellcheckService* spellcheck =
223 SpellcheckServiceFactory::GetForContext(browser_context());
224 if (!spellcheck) {
225 return RespondNow(Error(InformativeError(kErrorSpellCheckNotAvailable,
226 static_function_name())));
227 }
228 SpellcheckCustomDictionary* dictionary = spellcheck->GetCustomDictionary();
229 if (!dictionary->IsLoaded()) {
230 return RespondNow(Error(InformativeError(kErrorCustomDictionaryNotLoaded,
231 static_function_name())));
232 }
233
234 if (dictionary->AddWord(params->word))
235 return RespondNow(NoArguments());
236 // Invalid words:
237 // - Already in the dictionary.
238 // - Not a UTF8 string.
239 // - Longer than 99 bytes (kMaxCustomDictionaryWordBytes).
240 // - Leading/trailing whitespace.
241 // - Empty.
242 return RespondNow(Error(
243 InformativeError(base::StringPrintf("%s. Word: %s", kErrorInvalidWord,
244 params->word.c_str()),
245 static_function_name())));
246 }
247
248 ExtensionFunction::ResponseAction
Run()249 InputMethodPrivateGetEncryptSyncEnabledFunction::Run() {
250 syncer::SyncService* sync_service = ProfileSyncServiceFactory::GetForProfile(
251 Profile::FromBrowserContext(browser_context()));
252 if (!sync_service)
253 return RespondNow(Error(
254 InformativeError(kErrorSyncServiceNotReady, static_function_name())));
255 std::unique_ptr<base::Value> ret(new base::Value(
256 sync_service->GetUserSettings()->IsEncryptEverythingEnabled()));
257 return RespondNow(
258 OneArgument(base::Value::FromUniquePtrValue(std::move(ret))));
259 }
260
261 ExtensionFunction::ResponseAction
Run()262 InputMethodPrivateSetXkbLayoutFunction::Run() {
263 std::unique_ptr<SetXkbLayout::Params> params(
264 SetXkbLayout::Params::Create(*args_));
265 EXTENSION_FUNCTION_VALIDATE(params.get());
266 chromeos::input_method::InputMethodManager* manager =
267 chromeos::input_method::InputMethodManager::Get();
268 chromeos::input_method::ImeKeyboard* keyboard = manager->GetImeKeyboard();
269 keyboard->SetCurrentKeyboardLayoutByName(params->xkb_name);
270 return RespondNow(NoArguments());
271 }
272
273 ExtensionFunction::ResponseAction
Run()274 InputMethodPrivateShowInputViewFunction::Run() {
275 auto* keyboard_client = ChromeKeyboardControllerClient::Get();
276 if (!keyboard_client->is_keyboard_enabled()) {
277 return RespondNow(Error(kErrorFailToShowInputView));
278 }
279
280 keyboard_client->ShowKeyboard();
281 return RespondNow(NoArguments());
282 }
283
284 ExtensionFunction::ResponseAction
Run()285 InputMethodPrivateHideInputViewFunction::Run() {
286 auto* keyboard_client = ChromeKeyboardControllerClient::Get();
287 if (!keyboard_client->is_keyboard_enabled()) {
288 return RespondNow(Error(kErrorFailToHideInputView));
289 }
290
291 keyboard_client->HideKeyboard(ash::HideReason::kUser);
292 return RespondNow(NoArguments());
293 }
294
295 ExtensionFunction::ResponseAction
Run()296 InputMethodPrivateOpenOptionsPageFunction::Run() {
297 std::unique_ptr<OpenOptionsPage::Params> params(
298 OpenOptionsPage::Params::Create(*args_));
299 EXTENSION_FUNCTION_VALIDATE(params.get());
300 scoped_refptr<chromeos::input_method::InputMethodManager::State> ime_state =
301 chromeos::input_method::InputMethodManager::Get()->GetActiveIMEState();
302 const chromeos::input_method::InputMethodDescriptor* ime =
303 ime_state->GetInputMethodFromId(params->input_method_id);
304 if (!ime)
305 return RespondNow(Error(InformativeError(
306 base::StringPrintf("%s Input Method: %s", kErrorInvalidInputMethod,
307 params->input_method_id.c_str()),
308 static_function_name())));
309
310 content::WebContents* web_contents = GetSenderWebContents();
311 if (web_contents) {
312 const GURL& options_page_url = ime->options_page_url();
313 if (!options_page_url.is_empty()) {
314 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
315 content::OpenURLParams url_params(options_page_url, content::Referrer(),
316 WindowOpenDisposition::SINGLETON_TAB,
317 ui::PAGE_TRANSITION_LINK, false);
318 browser->OpenURL(url_params);
319 }
320 }
321 return RespondNow(NoArguments());
322 }
323
324 ExtensionFunction::ResponseAction
Run()325 InputMethodPrivateGetSurroundingTextFunction::Run() {
326 ui::IMEInputContextHandlerInterface* input_context =
327 ui::IMEBridge::Get()->GetInputContextHandler();
328 if (!input_context)
329 return RespondNow(Error(InformativeError(
330 kErrorInputContextHandlerNotAvailable, static_function_name())));
331
332 std::unique_ptr<GetSurroundingText::Params> params(
333 GetSurroundingText::Params::Create(*args_));
334 if (params->before_length < 0 || params->after_length < 0)
335 return RespondNow(Error(InformativeError(
336 base::StringPrintf("%s before_length = %d, after_length = %d.",
337 kErrorInvalidParametersForGetSurroundingText,
338 params->before_length, params->after_length),
339 static_function_name())));
340
341 uint32_t param_before_length = (uint32_t)params->before_length;
342 uint32_t param_after_length = (uint32_t)params->after_length;
343
344 ui::SurroundingTextInfo info = input_context->GetSurroundingTextInfo();
345 if (!info.selection_range.IsValid())
346 return RespondNow(OneArgument(base::Value()));
347
348 auto ret = std::make_unique<base::DictionaryValue>();
349 uint32_t selection_start = info.selection_range.start();
350 uint32_t selection_end = info.selection_range.end();
351 // Makes sure |selection_start| is less or equals to |selection_end|.
352 if (selection_start > selection_end)
353 std::swap(selection_start, selection_end);
354
355 uint32_t text_before_end = selection_start;
356 uint32_t text_before_start = text_before_end > param_before_length
357 ? text_before_end - param_before_length
358 : 0;
359 uint32_t text_after_start = selection_end;
360 uint32_t text_after_end =
361 text_after_start + param_after_length < info.surrounding_text.length()
362 ? text_after_start + param_after_length
363 : info.surrounding_text.length();
364
365 ret->SetString("before",
366 info.surrounding_text.substr(
367 text_before_start, text_before_end - text_before_start));
368 ret->SetString("selected",
369 info.surrounding_text.substr(
370 text_before_end, text_after_start - text_before_end));
371 ret->SetString(
372 "after", info.surrounding_text.substr(text_after_start,
373 text_after_end - text_after_start));
374
375 return RespondNow(
376 OneArgument(base::Value::FromUniquePtrValue(std::move(ret))));
377 }
378
Run()379 ExtensionFunction::ResponseAction InputMethodPrivateGetSettingsFunction::Run() {
380 const auto params = GetSettings::Params::Create(*args_);
381 EXTENSION_FUNCTION_VALIDATE(params.get());
382
383 const base::DictionaryValue* input_methods =
384 Profile::FromBrowserContext(browser_context())
385 ->GetPrefs()
386 ->GetDictionary(prefs::kLanguageInputMethodSpecificSettings);
387 const base::Value* engine_result = input_methods->FindPath(params->engine_id);
388 base::Value result;
389 if (engine_result)
390 result = engine_result->Clone();
391 return RespondNow(OneArgument(std::move(result)));
392 }
393
Run()394 ExtensionFunction::ResponseAction InputMethodPrivateSetSettingsFunction::Run() {
395 const auto params = SetSettings::Params::Create(*args_);
396 EXTENSION_FUNCTION_VALIDATE(params.get());
397
398 DictionaryPrefUpdate update(
399 Profile::FromBrowserContext(browser_context())->GetPrefs(),
400 prefs::kLanguageInputMethodSpecificSettings);
401 update->SetPath(params->engine_id, params->settings.ToValue()->Clone());
402
403 // The router will only send the event to extensions that are listening.
404 extensions::EventRouter* router =
405 extensions::EventRouter::Get(browser_context());
406 if (router->HasEventListener(OnInputMethodOptionsChanged::kEventName)) {
407 auto event = std::make_unique<extensions::Event>(
408 extensions::events::INPUT_IME_ON_INPUT_METHOD_OPTIONS_CHANGED,
409 OnInputMethodOptionsChanged::kEventName,
410 OnInputMethodOptionsChanged::Create(params->engine_id),
411 browser_context());
412 router->BroadcastEvent(std::move(event));
413 }
414
415 return RespondNow(NoArguments());
416 }
417
418 ExtensionFunction::ResponseAction
Run()419 InputMethodPrivateSetCompositionRangeFunction::Run() {
420 std::string error;
421 InputMethodEngineBase* engine =
422 GetEngineIfActive(browser_context(), extension_id(), &error);
423 if (!engine)
424 return RespondNow(Error(InformativeError(error, static_function_name())));
425
426 const auto parent_params = SetCompositionRange::Params::Create(*args_);
427 const auto& params = parent_params->parameters;
428 std::vector<InputMethodEngineBase::SegmentInfo> segments;
429 if (params.segments) {
430 for (const auto& segments_arg : *params.segments) {
431 InputMethodEngineBase::SegmentInfo segment_info;
432 segment_info.start = segments_arg.start;
433 segment_info.end = segments_arg.end;
434 switch (segments_arg.style) {
435 case input_method_private::UNDERLINE_STYLE_UNDERLINE:
436 segment_info.style = InputMethodEngineBase::SEGMENT_STYLE_UNDERLINE;
437 break;
438 case input_method_private::UNDERLINE_STYLE_DOUBLEUNDERLINE:
439 segment_info.style =
440 InputMethodEngineBase::SEGMENT_STYLE_DOUBLE_UNDERLINE;
441 break;
442 case input_method_private::UNDERLINE_STYLE_NOUNDERLINE:
443 segment_info.style =
444 InputMethodEngineBase::SEGMENT_STYLE_NO_UNDERLINE;
445 break;
446 case input_method_private::UNDERLINE_STYLE_NONE:
447 EXTENSION_FUNCTION_VALIDATE(false);
448 break;
449 }
450 segments.push_back(segment_info);
451 }
452 } else {
453 // Default to a single segment that spans the entire range.
454 InputMethodEngineBase::SegmentInfo segment_info;
455 segment_info.start = 0;
456 segment_info.end = params.selection_before + params.selection_after;
457 segment_info.style = InputMethodEngineBase::SEGMENT_STYLE_UNDERLINE;
458 segments.push_back(segment_info);
459 }
460
461 if (!engine->SetCompositionRange(params.context_id, params.selection_before,
462 params.selection_after, segments, &error)) {
463 return RespondNow(Error(InformativeError(error, static_function_name())));
464 }
465 return RespondNow(OneArgument(base::Value(true)));
466 }
467
468 ExtensionFunction::ResponseAction
Run()469 InputMethodPrivateSetComposingRangeFunction::Run() {
470 std::string error;
471 InputMethodEngineBase* engine =
472 GetEngineIfActive(browser_context(), extension_id(), &error);
473 if (!engine)
474 return RespondNow(Error(InformativeError(error, static_function_name())));
475
476 const auto parent_params = SetComposingRange::Params::Create(*args_);
477 const auto& params = parent_params->parameters;
478 std::vector<InputMethodEngineBase::SegmentInfo> segments;
479 if (params.segments) {
480 for (const auto& segments_arg : *params.segments) {
481 InputMethodEngineBase::SegmentInfo segment_info;
482 segment_info.start = segments_arg.start;
483 segment_info.end = segments_arg.end;
484 switch (segments_arg.style) {
485 case input_method_private::UNDERLINE_STYLE_UNDERLINE:
486 segment_info.style = InputMethodEngineBase::SEGMENT_STYLE_UNDERLINE;
487 break;
488 case input_method_private::UNDERLINE_STYLE_DOUBLEUNDERLINE:
489 segment_info.style =
490 InputMethodEngineBase::SEGMENT_STYLE_DOUBLE_UNDERLINE;
491 break;
492 case input_method_private::UNDERLINE_STYLE_NOUNDERLINE:
493 segment_info.style =
494 InputMethodEngineBase::SEGMENT_STYLE_NO_UNDERLINE;
495 break;
496 case input_method_private::UNDERLINE_STYLE_NONE:
497 EXTENSION_FUNCTION_VALIDATE(false);
498 break;
499 }
500 segments.push_back(segment_info);
501 }
502 } else {
503 // Default to a single segment that spans the entire composing range.
504 InputMethodEngineBase::SegmentInfo segment_info;
505 segment_info.start = 0;
506 segment_info.end = params.end - params.start;
507 segment_info.style = InputMethodEngineBase::SEGMENT_STYLE_UNDERLINE;
508 segments.push_back(segment_info);
509 }
510
511 if (!engine->SetComposingRange(params.context_id, params.start, params.end,
512 segments, &error)) {
513 return RespondNow(Error(InformativeError(error, static_function_name())));
514 }
515 return RespondNow(NoArguments());
516 }
517
518 ExtensionFunction::ResponseAction
Run()519 InputMethodPrivateGetAutocorrectRangeFunction::Run() {
520 std::string error;
521 InputMethodEngineBase* engine =
522 GetEngineIfActive(browser_context(), extension_id(), &error);
523 if (!engine)
524 return RespondNow(Error(InformativeError(error, static_function_name())));
525
526 const auto parent_params = GetAutocorrectRange::Params::Create(*args_);
527 const auto& params = parent_params->parameters;
528 const gfx::Range range =
529 engine->GetAutocorrectRange(params.context_id, &error);
530 auto ret = std::make_unique<base::DictionaryValue>();
531 ret->SetInteger("start", range.is_empty() ? 0 : range.start());
532 ret->SetInteger("end", range.is_empty() ? 0 : range.end());
533 return RespondNow(
534 OneArgument(base::Value::FromUniquePtrValue(std::move(ret))));
535 }
536
537 ExtensionFunction::ResponseAction
Run()538 InputMethodPrivateGetAutocorrectCharacterBoundsFunction::Run() {
539 std::string error;
540 InputMethodEngineBase* engine =
541 GetEngineIfActive(browser_context(), extension_id(), &error);
542 if (!engine)
543 return RespondNow(Error(InformativeError(error, static_function_name())));
544
545 const auto parent_params =
546 GetAutocorrectCharacterBounds::Params::Create(*args_);
547 const auto& params = parent_params->parameters;
548 const gfx::Rect rect =
549 engine->GetAutocorrectCharacterBounds(params.context_id, &error);
550 if (rect.IsEmpty()) {
551 return RespondNow(Error(InformativeError(error, static_function_name())));
552 }
553 auto ret = std::make_unique<base::DictionaryValue>();
554 ret->SetInteger("x", rect.x());
555 ret->SetInteger("y", rect.y());
556 ret->SetInteger("width", rect.width());
557 ret->SetInteger("height", rect.height());
558 return RespondNow(
559 OneArgument(base::Value::FromUniquePtrValue(std::move(ret))));
560 }
561
562 ExtensionFunction::ResponseAction
Run()563 InputMethodPrivateSetAutocorrectRangeFunction::Run() {
564 std::string error;
565 InputMethodEngineBase* engine =
566 GetEngineIfActive(browser_context(), extension_id(), &error);
567 if (!engine)
568 return RespondNow(Error(InformativeError(error, static_function_name())));
569
570 const auto parent_params = SetAutocorrectRange::Params::Create(*args_);
571 const auto& params = parent_params->parameters;
572 if (!engine->SetAutocorrectRange(
573 params.context_id, base::UTF8ToUTF16(params.autocorrect_string),
574 params.selection_start, params.selection_end, &error)) {
575 auto results = std::make_unique<base::ListValue>();
576 results->Append(std::make_unique<base::Value>(false));
577 return RespondNow(Error(InformativeError(error, static_function_name())));
578 }
579 return RespondNow(NoArguments());
580 }
581
582 ExtensionFunction::ResponseAction
Run()583 InputMethodPrivateSetSelectionRangeFunction::Run() {
584 std::string error;
585 InputMethodEngineBase* engine =
586 GetEngineIfActive(browser_context(), extension_id(), &error);
587 if (!engine)
588 return RespondNow(Error(InformativeError(error, static_function_name())));
589
590 std::unique_ptr<SetSelectionRange::Params> parent_params(
591 SetSelectionRange::Params::Create(*args_));
592 const SetSelectionRange::Params::Parameters& params =
593 parent_params->parameters;
594
595 if (!engine->SetSelectionRange(params.context_id, *params.selection_start,
596 *params.selection_end, &error)) {
597 auto results = std::make_unique<base::ListValue>();
598 results->Append(std::make_unique<base::Value>(false));
599 return RespondNow(ErrorWithArguments(
600 std::move(results), InformativeError(error, static_function_name())));
601 }
602 return RespondNow(OneArgument(base::Value(true)));
603 }
604
Run()605 ExtensionFunction::ResponseAction InputMethodPrivateResetFunction::Run() {
606 std::string error;
607 InputMethodEngineBase* engine =
608 GetEngineIfActive(browser_context(), extension_id(), &error);
609 if (!engine)
610 return RespondNow(Error(InformativeError(error, static_function_name())));
611
612 engine->Reset();
613 return RespondNow(NoArguments());
614 }
615
616 ExtensionFunction::ResponseAction
Run()617 InputMethodPrivateOnAutocorrectFunction::Run() {
618 std::unique_ptr<OnAutocorrect::Params> parent_params(
619 OnAutocorrect::Params::Create(*args_));
620 const OnAutocorrect::Params::Parameters& params = parent_params->parameters;
621 std::string error;
622 chromeos::NativeInputMethodEngine* engine =
623 static_cast<chromeos::NativeInputMethodEngine*>(
624 GetEngineIfActive(Profile::FromBrowserContext(browser_context()),
625 extension_id(), &error));
626 if (!engine)
627 return RespondNow(Error(InformativeError(error, static_function_name())));
628
629 engine->OnAutocorrect(params.typed_word, params.corrected_word,
630 params.start_index);
631 return RespondNow(NoArguments());
632 }
633
InputMethodAPI(content::BrowserContext * context)634 InputMethodAPI::InputMethodAPI(content::BrowserContext* context)
635 : context_(context) {
636 EventRouter::Get(context_)->RegisterObserver(this, OnChanged::kEventName);
637 EventRouter::Get(context_)
638 ->RegisterObserver(this, OnDictionaryChanged::kEventName);
639 EventRouter::Get(context_)
640 ->RegisterObserver(this, OnDictionaryLoaded::kEventName);
641 EventRouter::Get(context_)
642 ->RegisterObserver(this, OnImeMenuActivationChanged::kEventName);
643 EventRouter::Get(context_)
644 ->RegisterObserver(this, OnImeMenuListChanged::kEventName);
645 EventRouter::Get(context_)
646 ->RegisterObserver(this, OnImeMenuItemsChanged::kEventName);
647 ExtensionFunctionRegistry& registry =
648 ExtensionFunctionRegistry::GetInstance();
649 registry.RegisterFunction<InputMethodPrivateGetInputMethodConfigFunction>();
650 registry.RegisterFunction<InputMethodPrivateGetCurrentInputMethodFunction>();
651 registry.RegisterFunction<InputMethodPrivateSetCurrentInputMethodFunction>();
652 registry.RegisterFunction<InputMethodPrivateGetInputMethodsFunction>();
653 registry
654 .RegisterFunction<InputMethodPrivateFetchAllDictionaryWordsFunction>();
655 registry.RegisterFunction<InputMethodPrivateAddWordToDictionaryFunction>();
656 registry.RegisterFunction<InputMethodPrivateGetEncryptSyncEnabledFunction>();
657 registry
658 .RegisterFunction<InputMethodPrivateNotifyImeMenuItemActivatedFunction>();
659 registry.RegisterFunction<InputMethodPrivateOpenOptionsPageFunction>();
660 }
661
~InputMethodAPI()662 InputMethodAPI::~InputMethodAPI() {
663 }
664
665 // static
GetInputMethodForXkb(const std::string & xkb_id)666 std::string InputMethodAPI::GetInputMethodForXkb(const std::string& xkb_id) {
667 std::string xkb_prefix =
668 chromeos::extension_ime_util::GetInputMethodIDByEngineID(kXkbPrefix);
669 size_t prefix_length = xkb_prefix.length();
670 DCHECK(xkb_id.substr(0, prefix_length) == xkb_prefix);
671 return xkb_id.substr(prefix_length);
672 }
673
Shutdown()674 void InputMethodAPI::Shutdown() {
675 EventRouter::Get(context_)->UnregisterObserver(this);
676 }
677
OnListenerAdded(const extensions::EventListenerInfo & details)678 void InputMethodAPI::OnListenerAdded(
679 const extensions::EventListenerInfo& details) {
680 if (details.event_name == OnChanged::kEventName &&
681 !input_method_event_router_.get()) {
682 input_method_event_router_.reset(
683 new chromeos::ExtensionInputMethodEventRouter(context_));
684 } else if (details.event_name == OnDictionaryChanged::kEventName ||
685 details.event_name == OnDictionaryLoaded::kEventName) {
686 if (!dictionary_event_router_.get()) {
687 dictionary_event_router_.reset(
688 new chromeos::ExtensionDictionaryEventRouter(context_));
689 }
690 if (details.event_name == OnDictionaryLoaded::kEventName) {
691 dictionary_event_router_->DispatchLoadedEventIfLoaded();
692 }
693 } else if ((details.event_name == OnImeMenuActivationChanged::kEventName ||
694 details.event_name == OnImeMenuListChanged::kEventName ||
695 details.event_name == OnImeMenuItemsChanged::kEventName) &&
696 !ime_menu_event_router_.get()) {
697 ime_menu_event_router_.reset(
698 new chromeos::ExtensionImeMenuEventRouter(context_));
699 }
700 }
701
702 static base::LazyInstance<
703 BrowserContextKeyedAPIFactory<InputMethodAPI>>::DestructorAtExit g_factory =
704 LAZY_INSTANCE_INITIALIZER;
705
706 // static
707 BrowserContextKeyedAPIFactory<InputMethodAPI>*
GetFactoryInstance()708 InputMethodAPI::GetFactoryInstance() {
709 return g_factory.Pointer();
710 }
711
712 } // namespace extensions
713