blob: 8151083587116243c27745abd3522f5cb8cbde2a [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/extensions/input_method_api.h"
#include <stddef.h>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/lazy_instance.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/ash/input_method/autocorrect_manager.h"
#include "chrome/browser/ash/input_method/native_input_method_engine.h"
#include "chrome/browser/chromeos/extensions/dictionary_event_router.h"
#include "chrome/browser/chromeos/extensions/ime_menu_event_router.h"
#include "chrome/browser/chromeos/extensions/input_method_event_router.h"
#include "chrome/browser/extensions/api/input_ime/input_ime_api.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/spellchecker/spellcheck_factory.h"
#include "chrome/browser/spellchecker/spellcheck_service.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/common/extensions/api/input_method_private.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/driver/sync_user_settings.h"
#include "extensions/browser/extension_function_registry.h"
#include "extensions/browser/extension_system.h"
#include "ui/base/ime/ash/extension_ime_util.h"
#include "ui/base/ime/ash/ime_bridge.h"
#include "ui/base/ime/ash/ime_keyboard.h"
#include "ui/base/ime/ash/input_method_descriptor.h"
#include "ui/base/ime/ash/input_method_manager.h"
#include "ui/base/ime/ash/input_method_util.h"
namespace {
namespace input_method_private = extensions::api::input_method_private;
namespace AddWordToDictionary =
extensions::api::input_method_private::AddWordToDictionary;
namespace SetCurrentInputMethod =
extensions::api::input_method_private::SetCurrentInputMethod;
namespace SetXkbLayout = extensions::api::input_method_private::SetXkbLayout;
namespace OpenOptionsPage =
extensions::api::input_method_private::OpenOptionsPage;
namespace OnChanged = extensions::api::input_method_private::OnChanged;
namespace OnDictionaryChanged =
extensions::api::input_method_private::OnDictionaryChanged;
namespace OnDictionaryLoaded =
extensions::api::input_method_private::OnDictionaryLoaded;
namespace OnImeMenuActivationChanged =
extensions::api::input_method_private::OnImeMenuActivationChanged;
namespace OnImeMenuListChanged =
extensions::api::input_method_private::OnImeMenuListChanged;
namespace OnImeMenuItemsChanged =
extensions::api::input_method_private::OnImeMenuItemsChanged;
namespace GetSurroundingText =
extensions::api::input_method_private::GetSurroundingText;
namespace GetSettings = extensions::api::input_method_private::GetSettings;
namespace SetSettings = extensions::api::input_method_private::SetSettings;
namespace SetCompositionRange =
extensions::api::input_method_private::SetCompositionRange;
namespace SetComposingRange =
extensions::api::input_method_private::SetComposingRange;
namespace GetAutocorrectRange =
extensions::api::input_method_private::GetAutocorrectRange;
namespace GetAutocorrectCharacterBounds =
extensions::api::input_method_private::GetAutocorrectCharacterBounds;
namespace SetAutocorrectRange =
extensions::api::input_method_private::SetAutocorrectRange;
namespace SetSelectionRange =
extensions::api::input_method_private::SetSelectionRange;
namespace OnInputMethodOptionsChanged =
extensions::api::input_method_private::OnInputMethodOptionsChanged;
namespace OnAutocorrect = extensions::api::input_method_private::OnAutocorrect;
namespace GetTextFieldBounds =
extensions::api::input_method_private::GetTextFieldBounds;
using ::ash::input_method::InputMethodEngine;
// Prefix, which is used by XKB.
const char kXkbPrefix[] = "xkb:";
const char kErrorFailToShowInputView[] =
"Unable to show the input view window because the keyboard is not enabled.";
const char kErrorFailToHideInputView[] =
"Unable to hide the input view window because the keyboard is not enabled.";
const char kErrorRouterNotAvailable[] = "The router is not available.";
const char kErrorInvalidInputMethod[] = "Input method not found.";
const char kErrorSpellCheckNotAvailable[] =
"Spellcheck service is not available.";
const char kErrorCustomDictionaryNotLoaded[] =
"Custom dictionary is not loaded yet.";
const char kErrorInvalidWord[] = "Unable to add invalid word to dictionary.";
const char kErrorSyncServiceNotReady[] =
"Sync service is not ready for current profile.";
const char kErrorInputContextHandlerNotAvailable[] =
"Input context handler is not available.";
const char kErrorInvalidParametersForGetSurroundingText[] =
"Invalid negative parameters for GetSurroundingText.";
InputMethodEngine* GetEngineIfActive(content::BrowserContext* browser_context,
const std::string& extension_id,
std::string* error) {
Profile* profile = Profile::FromBrowserContext(browser_context);
extensions::InputImeEventRouter* event_router =
extensions::GetInputImeEventRouter(profile);
DCHECK(event_router) << kErrorRouterNotAvailable;
InputMethodEngine* engine =
event_router->GetEngineIfActive(extension_id, error);
return engine;
}
} // namespace
namespace extensions {
ExtensionFunction::ResponseAction
InputMethodPrivateGetInputMethodConfigFunction::Run() {
std::unique_ptr<base::DictionaryValue> output(new base::DictionaryValue());
output->SetBoolean("isPhysicalKeyboardAutocorrectEnabled", true);
output->SetBoolean("isImeMenuActivated",
Profile::FromBrowserContext(browser_context())
->GetPrefs()
->GetBoolean(prefs::kLanguageImeMenuActivated));
return RespondNow(
OneArgument(base::Value::FromUniquePtrValue(std::move(output))));
}
ExtensionFunction::ResponseAction
InputMethodPrivateGetCurrentInputMethodFunction::Run() {
auto* manager = ash::input_method::InputMethodManager::Get();
return RespondNow(OneArgument(
base::Value(manager->GetActiveIMEState()->GetCurrentInputMethod().id())));
}
ExtensionFunction::ResponseAction
InputMethodPrivateSetCurrentInputMethodFunction::Run() {
std::unique_ptr<SetCurrentInputMethod::Params> params(
SetCurrentInputMethod::Params::Create(args()));
EXTENSION_FUNCTION_VALIDATE(params.get());
scoped_refptr<ash::input_method::InputMethodManager::State> ime_state =
ash::input_method::InputMethodManager::Get()->GetActiveIMEState();
const std::vector<std::string>& input_methods =
ime_state->GetEnabledInputMethodIds();
for (size_t i = 0; i < input_methods.size(); ++i) {
const std::string& input_method = input_methods[i];
if (input_method == params->input_method_id) {
ime_state->ChangeInputMethod(params->input_method_id,
false /* show_message */);
return RespondNow(NoArguments());
}
}
return RespondNow(Error(InformativeError(
base::StringPrintf("%s Input Method: %s", kErrorInvalidInputMethod,
params->input_method_id.c_str()),
static_function_name())));
}
ExtensionFunction::ResponseAction
InputMethodPrivateGetInputMethodsFunction::Run() {
std::unique_ptr<base::ListValue> output(new base::ListValue());
auto* manager = ash::input_method::InputMethodManager::Get();
ash::input_method::InputMethodUtil* util = manager->GetInputMethodUtil();
scoped_refptr<ash::input_method::InputMethodManager::State> ime_state =
manager->GetActiveIMEState();
std::unique_ptr<ash::input_method::InputMethodDescriptors> input_methods =
ime_state->GetEnabledInputMethodsSortedByLocalizedDisplayNames();
for (size_t i = 0; i < input_methods->size(); ++i) {
const ash::input_method::InputMethodDescriptor& input_method =
(*input_methods)[i];
auto val = std::make_unique<base::DictionaryValue>();
val->SetString("id", input_method.id());
val->SetString("name", util->GetInputMethodLongName(input_method));
val->SetString("indicator", input_method.GetIndicator());
output->Append(std::move(val));
}
return RespondNow(
OneArgument(base::Value::FromUniquePtrValue(std::move(output))));
}
ExtensionFunction::ResponseAction
InputMethodPrivateFetchAllDictionaryWordsFunction::Run() {
SpellcheckService* spellcheck =
SpellcheckServiceFactory::GetForContext(browser_context());
if (!spellcheck) {
return RespondNow(Error(InformativeError(kErrorSpellCheckNotAvailable,
static_function_name())));
}
SpellcheckCustomDictionary* dictionary = spellcheck->GetCustomDictionary();
if (!dictionary->IsLoaded()) {
return RespondNow(Error(InformativeError(kErrorCustomDictionaryNotLoaded,
static_function_name())));
}
const std::set<std::string>& words = dictionary->GetWords();
std::unique_ptr<base::ListValue> output(new base::ListValue());
for (auto it = words.begin(); it != words.end(); ++it) {
output->Append(*it);
}
return RespondNow(
OneArgument(base::Value::FromUniquePtrValue(std::move(output))));
}
ExtensionFunction::ResponseAction
InputMethodPrivateAddWordToDictionaryFunction::Run() {
std::unique_ptr<AddWordToDictionary::Params> params(
AddWordToDictionary::Params::Create(args()));
EXTENSION_FUNCTION_VALIDATE(params.get());
SpellcheckService* spellcheck =
SpellcheckServiceFactory::GetForContext(browser_context());
if (!spellcheck) {
return RespondNow(Error(InformativeError(kErrorSpellCheckNotAvailable,
static_function_name())));
}
SpellcheckCustomDictionary* dictionary = spellcheck->GetCustomDictionary();
if (!dictionary->IsLoaded()) {
return RespondNow(Error(InformativeError(kErrorCustomDictionaryNotLoaded,
static_function_name())));
}
if (dictionary->AddWord(params->word))
return RespondNow(NoArguments());
// Invalid words:
// - Already in the dictionary.
// - Not a UTF8 string.
// - Longer than 99 bytes (kMaxCustomDictionaryWordBytes).
// - Leading/trailing whitespace.
// - Empty.
return RespondNow(Error(
InformativeError(base::StringPrintf("%s. Word: %s", kErrorInvalidWord,
params->word.c_str()),
static_function_name())));
}
ExtensionFunction::ResponseAction
InputMethodPrivateGetEncryptSyncEnabledFunction::Run() {
syncer::SyncService* sync_service = SyncServiceFactory::GetForProfile(
Profile::FromBrowserContext(browser_context()));
if (!sync_service)
return RespondNow(Error(
InformativeError(kErrorSyncServiceNotReady, static_function_name())));
std::unique_ptr<base::Value> ret(new base::Value(
sync_service->GetUserSettings()->IsEncryptEverythingEnabled()));
return RespondNow(
OneArgument(base::Value::FromUniquePtrValue(std::move(ret))));
}
ExtensionFunction::ResponseAction
InputMethodPrivateSetXkbLayoutFunction::Run() {
std::unique_ptr<SetXkbLayout::Params> params(
SetXkbLayout::Params::Create(args()));
EXTENSION_FUNCTION_VALIDATE(params.get());
auto* manager = ash::input_method::InputMethodManager::Get();
ash::input_method::ImeKeyboard* keyboard = manager->GetImeKeyboard();
keyboard->SetCurrentKeyboardLayoutByName(params->xkb_name);
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
InputMethodPrivateShowInputViewFunction::Run() {
auto* keyboard_client = ChromeKeyboardControllerClient::Get();
if (!keyboard_client->is_keyboard_enabled()) {
return RespondNow(Error(kErrorFailToShowInputView));
}
keyboard_client->ShowKeyboard();
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
InputMethodPrivateHideInputViewFunction::Run() {
auto* keyboard_client = ChromeKeyboardControllerClient::Get();
if (!keyboard_client->is_keyboard_enabled()) {
return RespondNow(Error(kErrorFailToHideInputView));
}
keyboard_client->HideKeyboard(ash::HideReason::kUser);
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
InputMethodPrivateOpenOptionsPageFunction::Run() {
std::unique_ptr<OpenOptionsPage::Params> params(
OpenOptionsPage::Params::Create(args()));
EXTENSION_FUNCTION_VALIDATE(params.get());
scoped_refptr<ash::input_method::InputMethodManager::State> ime_state =
ash::input_method::InputMethodManager::Get()->GetActiveIMEState();
const ash::input_method::InputMethodDescriptor* ime =
ime_state->GetInputMethodFromId(params->input_method_id);
if (!ime)
return RespondNow(Error(InformativeError(
base::StringPrintf("%s Input Method: %s", kErrorInvalidInputMethod,
params->input_method_id.c_str()),
static_function_name())));
content::WebContents* web_contents = GetSenderWebContents();
if (web_contents) {
const GURL& options_page_url = ime->options_page_url();
if (!options_page_url.is_empty()) {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
content::OpenURLParams url_params(options_page_url, content::Referrer(),
WindowOpenDisposition::SINGLETON_TAB,
ui::PAGE_TRANSITION_LINK, false);
browser->OpenURL(url_params);
}
}
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
InputMethodPrivateGetSurroundingTextFunction::Run() {
ui::IMEInputContextHandlerInterface* input_context =
ui::IMEBridge::Get()->GetInputContextHandler();
if (!input_context)
return RespondNow(Error(InformativeError(
kErrorInputContextHandlerNotAvailable, static_function_name())));
std::unique_ptr<GetSurroundingText::Params> params(
GetSurroundingText::Params::Create(args()));
if (params->before_length < 0 || params->after_length < 0)
return RespondNow(Error(InformativeError(
base::StringPrintf("%s before_length = %d, after_length = %d.",
kErrorInvalidParametersForGetSurroundingText,
params->before_length, params->after_length),
static_function_name())));
uint32_t param_before_length = (uint32_t)params->before_length;
uint32_t param_after_length = (uint32_t)params->after_length;
ui::SurroundingTextInfo info = input_context->GetSurroundingTextInfo();
if (!info.selection_range.IsValid())
return RespondNow(OneArgument(base::Value()));
auto ret = std::make_unique<base::DictionaryValue>();
uint32_t selection_start = info.selection_range.start();
uint32_t selection_end = info.selection_range.end();
// Makes sure |selection_start| is less or equals to |selection_end|.
if (selection_start > selection_end)
std::swap(selection_start, selection_end);
uint32_t text_before_end = selection_start;
uint32_t text_before_start = text_before_end > param_before_length
? text_before_end - param_before_length
: 0;
uint32_t text_after_start = selection_end;
uint32_t text_after_end =
text_after_start + param_after_length < info.surrounding_text.length()
? text_after_start + param_after_length
: info.surrounding_text.length();
ret->SetString("before",
info.surrounding_text.substr(
text_before_start, text_before_end - text_before_start));
ret->SetString("selected",
info.surrounding_text.substr(
text_before_end, text_after_start - text_before_end));
ret->SetString(
"after", info.surrounding_text.substr(text_after_start,
text_after_end - text_after_start));
return RespondNow(
OneArgument(base::Value::FromUniquePtrValue(std::move(ret))));
}
ExtensionFunction::ResponseAction InputMethodPrivateGetSettingsFunction::Run() {
const auto params = GetSettings::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params.get());
const base::Value* input_methods =
Profile::FromBrowserContext(browser_context())
->GetPrefs()
->GetDictionary(prefs::kLanguageInputMethodSpecificSettings);
const base::Value* engine_result = input_methods->FindPath(params->engine_id);
base::Value result;
if (engine_result)
result = engine_result->Clone();
return RespondNow(OneArgument(std::move(result)));
}
ExtensionFunction::ResponseAction InputMethodPrivateSetSettingsFunction::Run() {
const auto params = SetSettings::Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params.get());
DictionaryPrefUpdateDeprecated update(
Profile::FromBrowserContext(browser_context())->GetPrefs(),
prefs::kLanguageInputMethodSpecificSettings);
update->SetPath(params->engine_id, params->settings.ToValue()->Clone());
// The router will only send the event to extensions that are listening.
extensions::EventRouter* router =
extensions::EventRouter::Get(browser_context());
if (router->HasEventListener(OnInputMethodOptionsChanged::kEventName)) {
auto event = std::make_unique<extensions::Event>(
extensions::events::INPUT_IME_ON_INPUT_METHOD_OPTIONS_CHANGED,
OnInputMethodOptionsChanged::kEventName,
OnInputMethodOptionsChanged::Create(params->engine_id),
browser_context());
router->BroadcastEvent(std::move(event));
}
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
InputMethodPrivateSetCompositionRangeFunction::Run() {
std::string error;
InputMethodEngine* engine =
GetEngineIfActive(browser_context(), extension_id(), &error);
if (!engine)
return RespondNow(Error(InformativeError(error, static_function_name())));
const auto parent_params = SetCompositionRange::Params::Create(args());
const auto& params = parent_params->parameters;
std::vector<InputMethodEngine::SegmentInfo> segments;
if (params.segments) {
for (const auto& segments_arg : *params.segments) {
InputMethodEngine::SegmentInfo segment_info;
segment_info.start = segments_arg.start;
segment_info.end = segments_arg.end;
switch (segments_arg.style) {
case input_method_private::UNDERLINE_STYLE_UNDERLINE:
segment_info.style = InputMethodEngine::SEGMENT_STYLE_UNDERLINE;
break;
case input_method_private::UNDERLINE_STYLE_DOUBLEUNDERLINE:
segment_info.style =
InputMethodEngine::SEGMENT_STYLE_DOUBLE_UNDERLINE;
break;
case input_method_private::UNDERLINE_STYLE_NOUNDERLINE:
segment_info.style = InputMethodEngine::SEGMENT_STYLE_NO_UNDERLINE;
break;
case input_method_private::UNDERLINE_STYLE_NONE:
EXTENSION_FUNCTION_VALIDATE(false);
break;
}
segments.push_back(segment_info);
}
}
if (!engine->InputMethodEngine::SetCompositionRange(
params.context_id, params.selection_before, params.selection_after,
segments, &error)) {
return RespondNow(Error(InformativeError(error, static_function_name())));
}
return RespondNow(OneArgument(base::Value(true)));
}
ExtensionFunction::ResponseAction
InputMethodPrivateSetComposingRangeFunction::Run() {
std::string error;
InputMethodEngine* engine =
GetEngineIfActive(browser_context(), extension_id(), &error);
if (!engine)
return RespondNow(Error(InformativeError(error, static_function_name())));
const auto parent_params = SetComposingRange::Params::Create(args());
const auto& params = parent_params->parameters;
std::vector<InputMethodEngine::SegmentInfo> segments;
if (params.segments) {
for (const auto& segments_arg : *params.segments) {
InputMethodEngine::SegmentInfo segment_info;
segment_info.start = segments_arg.start;
segment_info.end = segments_arg.end;
switch (segments_arg.style) {
case input_method_private::UNDERLINE_STYLE_UNDERLINE:
segment_info.style = InputMethodEngine::SEGMENT_STYLE_UNDERLINE;
break;
case input_method_private::UNDERLINE_STYLE_DOUBLEUNDERLINE:
segment_info.style =
InputMethodEngine::SEGMENT_STYLE_DOUBLE_UNDERLINE;
break;
case input_method_private::UNDERLINE_STYLE_NOUNDERLINE:
segment_info.style = InputMethodEngine::SEGMENT_STYLE_NO_UNDERLINE;
break;
case input_method_private::UNDERLINE_STYLE_NONE:
EXTENSION_FUNCTION_VALIDATE(false);
break;
}
segments.push_back(segment_info);
}
}
if (!engine->InputMethodEngine::SetComposingRange(
params.context_id, params.start, params.end, segments, &error)) {
return RespondNow(Error(InformativeError(error, static_function_name())));
}
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
InputMethodPrivateGetAutocorrectRangeFunction::Run() {
std::string error;
InputMethodEngine* engine =
GetEngineIfActive(browser_context(), extension_id(), &error);
if (!engine)
return RespondNow(Error(InformativeError(error, static_function_name())));
const auto parent_params = GetAutocorrectRange::Params::Create(args());
const auto& params = parent_params->parameters;
const gfx::Range range =
engine->InputMethodEngine::GetAutocorrectRange(params.context_id, &error);
auto ret = std::make_unique<base::DictionaryValue>();
ret->SetInteger("start", range.is_empty() ? 0 : range.start());
ret->SetInteger("end", range.is_empty() ? 0 : range.end());
return RespondNow(
OneArgument(base::Value::FromUniquePtrValue(std::move(ret))));
}
ExtensionFunction::ResponseAction
InputMethodPrivateGetAutocorrectCharacterBoundsFunction::Run() {
std::string error;
InputMethodEngine* engine =
GetEngineIfActive(browser_context(), extension_id(), &error);
if (!engine)
return RespondNow(Error(InformativeError(error, static_function_name())));
const auto parent_params =
GetAutocorrectCharacterBounds::Params::Create(args());
const auto& params = parent_params->parameters;
const gfx::Rect rect =
engine->InputMethodEngine::GetAutocorrectCharacterBounds(
params.context_id, &error);
if (rect.IsEmpty()) {
return RespondNow(Error(InformativeError(error, static_function_name())));
}
auto ret = std::make_unique<base::DictionaryValue>();
ret->SetInteger("x", rect.x());
ret->SetInteger("y", rect.y());
ret->SetInteger("width", rect.width());
ret->SetInteger("height", rect.height());
return RespondNow(
OneArgument(base::Value::FromUniquePtrValue(std::move(ret))));
}
ExtensionFunction::ResponseAction
InputMethodPrivateGetTextFieldBoundsFunction::Run() {
std::string error;
InputMethodEngine* engine =
GetEngineIfActive(browser_context(), extension_id(), &error);
if (!engine)
return RespondNow(Error(InformativeError(error, static_function_name())));
const auto parent_params = GetTextFieldBounds::Params::Create(args());
const auto& params = parent_params->parameters;
const gfx::Rect rect =
engine->InputMethodEngine::GetTextFieldBounds(params.context_id, &error);
if (rect.IsEmpty()) {
return RespondNow(Error(InformativeError(error, static_function_name())));
}
auto ret = std::make_unique<base::DictionaryValue>();
ret->SetInteger("x", rect.x());
ret->SetInteger("y", rect.y());
ret->SetInteger("width", rect.width());
ret->SetInteger("height", rect.height());
return RespondNow(
OneArgument(base::Value::FromUniquePtrValue(std::move(ret))));
}
ExtensionFunction::ResponseAction
InputMethodPrivateSetAutocorrectRangeFunction::Run() {
std::string error;
InputMethodEngine* engine =
GetEngineIfActive(browser_context(), extension_id(), &error);
if (!engine)
return RespondNow(Error(InformativeError(error, static_function_name())));
const auto parent_params = SetAutocorrectRange::Params::Create(args());
const auto& params = parent_params->parameters;
if (!engine->InputMethodEngine::SetAutocorrectRange(
params.context_id,
gfx::Range(params.selection_start, params.selection_end), &error)) {
auto results = std::make_unique<base::ListValue>();
results->Append(std::make_unique<base::Value>(false));
return RespondNow(Error(InformativeError(error, static_function_name())));
}
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
InputMethodPrivateSetSelectionRangeFunction::Run() {
std::string error;
InputMethodEngine* engine =
GetEngineIfActive(browser_context(), extension_id(), &error);
if (!engine)
return RespondNow(Error(InformativeError(error, static_function_name())));
std::unique_ptr<SetSelectionRange::Params> parent_params(
SetSelectionRange::Params::Create(args()));
const SetSelectionRange::Params::Parameters& params =
parent_params->parameters;
if (!engine->InputMethodEngine::SetSelectionRange(
params.context_id, *params.selection_start, *params.selection_end,
&error)) {
auto results = std::make_unique<base::ListValue>();
results->Append(std::make_unique<base::Value>(false));
return RespondNow(ErrorWithArguments(
std::move(results), InformativeError(error, static_function_name())));
}
return RespondNow(OneArgument(base::Value(true)));
}
ExtensionFunction::ResponseAction InputMethodPrivateResetFunction::Run() {
std::string error;
InputMethodEngine* engine =
GetEngineIfActive(browser_context(), extension_id(), &error);
if (!engine)
return RespondNow(Error(InformativeError(error, static_function_name())));
engine->Reset();
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
InputMethodPrivateOnAutocorrectFunction::Run() {
std::unique_ptr<OnAutocorrect::Params> parent_params(
OnAutocorrect::Params::Create(args()));
const OnAutocorrect::Params::Parameters& params = parent_params->parameters;
std::string error;
ash::input_method::NativeInputMethodEngine* engine =
static_cast<ash::input_method::NativeInputMethodEngine*>(
GetEngineIfActive(Profile::FromBrowserContext(browser_context()),
extension_id(), &error));
if (!engine)
return RespondNow(Error(InformativeError(error, static_function_name())));
// `typed_word` and `corrected_word` are both originally encoded in UTF-16 by
// JavaScript, but the extensions bindings will convert them to UTF-8 without
// changing `start_index` (which is in UTF-16 code units). Hence, convert the
// two strngs back without changing `start_index`.
engine->OnAutocorrect(base::UTF8ToUTF16(params.typed_word),
base::UTF8ToUTF16(params.corrected_word),
params.start_index);
return RespondNow(NoArguments());
}
InputMethodAPI::InputMethodAPI(content::BrowserContext* context)
: context_(context) {
EventRouter::Get(context_)->RegisterObserver(this, OnChanged::kEventName);
EventRouter::Get(context_)
->RegisterObserver(this, OnDictionaryChanged::kEventName);
EventRouter::Get(context_)
->RegisterObserver(this, OnDictionaryLoaded::kEventName);
EventRouter::Get(context_)
->RegisterObserver(this, OnImeMenuActivationChanged::kEventName);
EventRouter::Get(context_)
->RegisterObserver(this, OnImeMenuListChanged::kEventName);
EventRouter::Get(context_)
->RegisterObserver(this, OnImeMenuItemsChanged::kEventName);
ExtensionFunctionRegistry& registry =
ExtensionFunctionRegistry::GetInstance();
registry.RegisterFunction<InputMethodPrivateGetInputMethodConfigFunction>();
registry.RegisterFunction<InputMethodPrivateGetCurrentInputMethodFunction>();
registry.RegisterFunction<InputMethodPrivateSetCurrentInputMethodFunction>();
registry.RegisterFunction<InputMethodPrivateGetInputMethodsFunction>();
registry
.RegisterFunction<InputMethodPrivateFetchAllDictionaryWordsFunction>();
registry.RegisterFunction<InputMethodPrivateAddWordToDictionaryFunction>();
registry.RegisterFunction<InputMethodPrivateGetEncryptSyncEnabledFunction>();
registry
.RegisterFunction<InputMethodPrivateNotifyImeMenuItemActivatedFunction>();
registry.RegisterFunction<InputMethodPrivateOpenOptionsPageFunction>();
}
InputMethodAPI::~InputMethodAPI() {
}
// static
std::string InputMethodAPI::GetInputMethodForXkb(const std::string& xkb_id) {
std::string xkb_prefix =
ash::extension_ime_util::GetInputMethodIDByEngineID(kXkbPrefix);
size_t prefix_length = xkb_prefix.length();
DCHECK(xkb_id.substr(0, prefix_length) == xkb_prefix);
return xkb_id.substr(prefix_length);
}
void InputMethodAPI::Shutdown() {
EventRouter::Get(context_)->UnregisterObserver(this);
}
void InputMethodAPI::OnListenerAdded(
const extensions::EventListenerInfo& details) {
if (details.event_name == OnChanged::kEventName &&
!input_method_event_router_.get()) {
input_method_event_router_ =
std::make_unique<chromeos::ExtensionInputMethodEventRouter>(context_);
} else if (details.event_name == OnDictionaryChanged::kEventName ||
details.event_name == OnDictionaryLoaded::kEventName) {
if (!dictionary_event_router_.get()) {
dictionary_event_router_ =
std::make_unique<chromeos::ExtensionDictionaryEventRouter>(context_);
}
if (details.event_name == OnDictionaryLoaded::kEventName) {
dictionary_event_router_->DispatchLoadedEventIfLoaded();
}
} else if ((details.event_name == OnImeMenuActivationChanged::kEventName ||
details.event_name == OnImeMenuListChanged::kEventName ||
details.event_name == OnImeMenuItemsChanged::kEventName) &&
!ime_menu_event_router_.get()) {
ime_menu_event_router_ =
std::make_unique<chromeos::ExtensionImeMenuEventRouter>(context_);
}
}
static base::LazyInstance<
BrowserContextKeyedAPIFactory<InputMethodAPI>>::DestructorAtExit g_factory =
LAZY_INSTANCE_INITIALIZER;
// static
BrowserContextKeyedAPIFactory<InputMethodAPI>*
InputMethodAPI::GetFactoryInstance() {
return g_factory.Pointer();
}
} // namespace extensions