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.
5 #include "chrome/browser/extensions/api/input_ime/input_ime_api.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "base/values.h"
9 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
10 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
11 #include "chrome/browser/chromeos/login/session/user_session_manager.h"
12 #include "chrome/browser/chromeos/login/ui/user_adding_screen.h"
13 #include "chrome/browser/chromeos/profiles/profile_helper.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/profiles/profile_manager.h"
16 #include "chrome/common/extensions/api/input_ime.h"
17 #include "chrome/common/extensions/api/input_ime/input_components_handler.h"
18 #include "chromeos/ime/component_extension_ime_manager.h"
19 #include "chromeos/ime/extension_ime_util.h"
20 #include "chromeos/ime/input_method_manager.h"
21 #include "extensions/browser/event_router.h"
22 #include "extensions/browser/extension_function_registry.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/common/manifest_handlers/background_info.h"
27 namespace input_ime = extensions::api::input_ime;
28 namespace KeyEventHandled = extensions::api::input_ime::KeyEventHandled;
29 namespace DeleteSurroundingText =
30 extensions::api::input_ime::DeleteSurroundingText;
31 namespace UpdateMenuItems = extensions::api::input_ime::UpdateMenuItems;
32 namespace SendKeyEvents = extensions::api::input_ime::SendKeyEvents;
33 namespace HideInputView = extensions::api::input_ime::HideInputView;
34 namespace SetMenuItems = extensions::api::input_ime::SetMenuItems;
35 namespace SetCursorPosition = extensions::api::input_ime::SetCursorPosition;
36 namespace SetCandidates = extensions::api::input_ime::SetCandidates;
37 namespace SetCandidateWindowProperties =
38 extensions::api::input_ime::SetCandidateWindowProperties;
39 namespace CommitText = extensions::api::input_ime::CommitText;
40 namespace ClearComposition = extensions::api::input_ime::ClearComposition;
41 namespace SetComposition = extensions::api::input_ime::SetComposition;
42 using chromeos::InputMethodEngineInterface;
46 const char kErrorEngineNotAvailable[] = "Engine is not available";
47 const char kErrorSetMenuItemsFail[] = "Could not create menu Items";
48 const char kErrorUpdateMenuItemsFail[] = "Could not update menu Items";
49 const char kOnCompositionBoundsChangedEventName[] =
50 "inputMethodPrivate.onCompositionBoundsChanged";
52 void SetMenuItemToMenu(const input_ime::MenuItem& input,
53 InputMethodEngineInterface::MenuItem* out) {
57 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_LABEL;
58 out->label = *input.label;
61 if (input.style != input_ime::MenuItem::STYLE_NONE) {
62 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_STYLE;
63 out->style = static_cast<InputMethodEngineInterface::MenuItemStyle>(
68 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_VISIBLE;
69 out->visible = input.visible ? *input.visible : true;
72 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_CHECKED;
73 out->checked = input.checked ? *input.checked : false;
76 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_ENABLED;
77 out->enabled = input.enabled ? *input.enabled : true;
80 static void DispatchEventToExtension(const std::string& extension_id,
81 const std::string& event_name,
82 scoped_ptr<base::ListValue> args) {
83 Profile* profile = ProfileManager::GetActiveUserProfile();
84 scoped_ptr<extensions::Event> event(new extensions::Event(
85 event_name, args.Pass()));
86 event->restrict_to_browser_context = profile;
87 extensions::EventRouter::Get(profile)
88 ->DispatchEventToExtension(extension_id, event.Pass());
91 void CallbackKeyEventHandle(chromeos::input_method::KeyEventHandle* key_data,
93 base::Callback<void(bool consumed)>* callback =
94 reinterpret_cast<base::Callback<void(bool consumed)>*>(key_data);
95 callback->Run(handled);
102 class ImeObserver : public InputMethodEngineInterface::Observer {
104 explicit ImeObserver(const std::string& extension_id)
105 : extension_id_(extension_id) {}
107 virtual ~ImeObserver() {}
109 virtual void OnActivate(const std::string& component_id) override {
110 if (extension_id_.empty() ||
111 !HasListener(input_ime::OnActivate::kEventName))
114 scoped_ptr<base::ListValue> args(input_ime::OnActivate::Create(
116 input_ime::OnActivate::ParseScreen(GetCurrentScreenType())));
118 DispatchEventToExtension(
119 extension_id_, input_ime::OnActivate::kEventName, args.Pass());
122 virtual void OnDeactivated(const std::string& component_id) override {
123 if (extension_id_.empty() ||
124 !HasListener(input_ime::OnDeactivated::kEventName))
127 scoped_ptr<base::ListValue> args(
128 input_ime::OnDeactivated::Create(component_id));
130 DispatchEventToExtension(
131 extension_id_, input_ime::OnDeactivated::kEventName, args.Pass());
134 virtual void OnFocus(
135 const InputMethodEngineInterface::InputContext& context) override {
136 if (extension_id_.empty() || !HasListener(input_ime::OnFocus::kEventName))
139 input_ime::InputContext context_value;
140 context_value.context_id = context.id;
141 context_value.type = input_ime::InputContext::ParseType(context.type);
142 context_value.auto_correct = context.auto_correct;
143 context_value.auto_complete = context.auto_complete;
144 context_value.spell_check = context.spell_check;
146 scoped_ptr<base::ListValue> args(input_ime::OnFocus::Create(context_value));
148 DispatchEventToExtension(
149 extension_id_, input_ime::OnFocus::kEventName, args.Pass());
152 virtual void OnBlur(int context_id) override {
153 if (extension_id_.empty() || !HasListener(input_ime::OnBlur::kEventName))
156 scoped_ptr<base::ListValue> args(input_ime::OnBlur::Create(context_id));
158 DispatchEventToExtension(
159 extension_id_, input_ime::OnBlur::kEventName, args.Pass());
162 virtual void OnInputContextUpdate(
163 const InputMethodEngineInterface::InputContext& context) override {
164 if (extension_id_.empty() ||
165 !HasListener(input_ime::OnInputContextUpdate::kEventName))
168 input_ime::InputContext context_value;
169 context_value.context_id = context.id;
170 context_value.type = input_ime::InputContext::ParseType(context.type);
172 scoped_ptr<base::ListValue> args(
173 input_ime::OnInputContextUpdate::Create(context_value));
175 DispatchEventToExtension(extension_id_,
176 input_ime::OnInputContextUpdate::kEventName,
180 virtual void OnKeyEvent(
181 const std::string& component_id,
182 const InputMethodEngineInterface::KeyboardEvent& event,
183 chromeos::input_method::KeyEventHandle* key_data) override {
184 if (extension_id_.empty())
187 // If there is no listener for the event, no need to dispatch the event to
188 // extension. Instead, releases the key event for default system behavior.
189 if (!ShouldForwardKeyEvent()) {
190 // Continue processing the key event so that the physical keyboard can
192 CallbackKeyEventHandle(key_data, false);
196 extensions::InputImeEventRouter* ime_event_router =
197 extensions::InputImeEventRouter::GetInstance();
199 const std::string request_id =
200 ime_event_router->AddRequest(component_id, key_data);
202 input_ime::KeyboardEvent key_data_value;
203 key_data_value.type = input_ime::KeyboardEvent::ParseType(event.type);
204 key_data_value.request_id = request_id;
205 if (!event.extension_id.empty())
206 key_data_value.extension_id.reset(new std::string(event.extension_id));
207 key_data_value.key = event.key;
208 key_data_value.code = event.code;
209 key_data_value.alt_key.reset(new bool(event.alt_key));
210 key_data_value.ctrl_key.reset(new bool(event.ctrl_key));
211 key_data_value.shift_key.reset(new bool(event.shift_key));
212 key_data_value.caps_lock.reset(new bool(event.caps_lock));
214 scoped_ptr<base::ListValue> args(
215 input_ime::OnKeyEvent::Create(component_id, key_data_value));
217 DispatchEventToExtension(
218 extension_id_, input_ime::OnKeyEvent::kEventName, args.Pass());
221 virtual void OnCandidateClicked(
222 const std::string& component_id,
224 InputMethodEngineInterface::MouseButtonEvent button) override {
225 if (extension_id_.empty() ||
226 !HasListener(input_ime::OnCandidateClicked::kEventName))
229 input_ime::OnCandidateClicked::Button button_enum =
230 input_ime::OnCandidateClicked::BUTTON_NONE;
232 case InputMethodEngineInterface::MOUSE_BUTTON_MIDDLE:
233 button_enum = input_ime::OnCandidateClicked::BUTTON_MIDDLE;
236 case InputMethodEngineInterface::MOUSE_BUTTON_RIGHT:
237 button_enum = input_ime::OnCandidateClicked::BUTTON_RIGHT;
240 case InputMethodEngineInterface::MOUSE_BUTTON_LEFT:
243 button_enum = input_ime::OnCandidateClicked::BUTTON_LEFT;
247 scoped_ptr<base::ListValue> args(input_ime::OnCandidateClicked::Create(
248 component_id, candidate_id, button_enum));
250 DispatchEventToExtension(
251 extension_id_, input_ime::OnCandidateClicked::kEventName, args.Pass());
254 virtual void OnMenuItemActivated(const std::string& component_id,
255 const std::string& menu_id) override {
256 if (extension_id_.empty() ||
257 !HasListener(input_ime::OnMenuItemActivated::kEventName))
260 scoped_ptr<base::ListValue> args(
261 input_ime::OnMenuItemActivated::Create(component_id, menu_id));
263 DispatchEventToExtension(
264 extension_id_, input_ime::OnMenuItemActivated::kEventName, args.Pass());
267 virtual void OnSurroundingTextChanged(const std::string& component_id,
268 const std::string& text,
270 int anchor_pos) override {
271 if (extension_id_.empty() ||
272 !HasListener(input_ime::OnSurroundingTextChanged::kEventName))
275 input_ime::OnSurroundingTextChanged::SurroundingInfo info;
277 info.focus = cursor_pos;
278 info.anchor = anchor_pos;
279 scoped_ptr<base::ListValue> args(
280 input_ime::OnSurroundingTextChanged::Create(component_id, info));
282 DispatchEventToExtension(extension_id_,
283 input_ime::OnSurroundingTextChanged::kEventName,
287 virtual void OnCompositionBoundsChanged(const gfx::Rect& bounds) override {
288 if (extension_id_.empty() ||
289 !HasListener(kOnCompositionBoundsChangedEventName))
292 // Note: this is a private API event.
293 scoped_ptr<base::ListValue> args(new base::ListValue());
294 base::DictionaryValue* bounds_value = new base::DictionaryValue();
295 bounds_value->SetInteger("x", bounds.x());
296 bounds_value->SetInteger("y", bounds.y());
297 bounds_value->SetInteger("w", bounds.width());
298 bounds_value->SetInteger("h", bounds.height());
299 args->Append(bounds_value);
301 DispatchEventToExtension(extension_id_,
302 kOnCompositionBoundsChangedEventName,
306 virtual void OnReset(const std::string& component_id) override {
307 if (extension_id_.empty() || !HasListener(input_ime::OnReset::kEventName))
310 scoped_ptr<base::ListValue> args(input_ime::OnReset::Create(component_id));
312 DispatchEventToExtension(
313 extension_id_, input_ime::OnReset::kEventName, args.Pass());
317 // Returns true if the extension is ready to accept key event, otherwise
319 bool ShouldForwardKeyEvent() const {
320 // Only forward key events to extension if there are non-lazy listeners
321 // for onKeyEvent. Because if something wrong with the lazy background
322 // page which doesn't register listener for onKeyEvent, it will not handle
323 // the key events, and therefore, all key events will be eaten.
324 // This is for error-tolerance, and it means that onKeyEvent will never wake
325 // up lazy background page.
326 const extensions::EventListenerMap::ListenerList& listener_list =
327 extensions::EventRouter::Get(ProfileManager::GetActiveUserProfile())
328 ->listeners().GetEventListenersByName(
329 input_ime::OnKeyEvent::kEventName);
330 for (extensions::EventListenerMap::ListenerList::const_iterator it =
331 listener_list.begin();
332 it != listener_list.end(); ++it) {
333 if ((*it)->extension_id() == extension_id_ && !(*it)->IsLazy())
339 bool HasListener(const std::string& event_name) const {
340 return extensions::EventRouter::Get(
341 ProfileManager::GetActiveUserProfile())->HasEventListener(event_name);
344 // The component IME extensions need to know the current screen type (e.g.
345 // lock screen, login screen, etc.) so that its on-screen keyboard page
346 // won't open new windows/pages. See crbug.com/395621.
347 std::string GetCurrentScreenType() {
348 switch (chromeos::input_method::InputMethodManager::Get()
349 ->GetUISessionState()) {
350 case chromeos::input_method::InputMethodManager::STATE_LOGIN_SCREEN:
352 case chromeos::input_method::InputMethodManager::STATE_LOCK_SCREEN:
354 case chromeos::input_method::InputMethodManager::STATE_BROWSER_SCREEN:
355 return UserAddingScreen::Get()->IsRunning() ? "secondary-login"
357 case chromeos::input_method::InputMethodManager::STATE_TERMINATING:
360 NOTREACHED() << "New screen type is added. Please add new entry above.";
364 std::string extension_id_;
366 DISALLOW_COPY_AND_ASSIGN(ImeObserver);
369 } // namespace chromeos
371 namespace extensions {
374 InputImeEventRouter::GetInstance() {
375 return Singleton<InputImeEventRouter>::get();
378 bool InputImeEventRouter::RegisterImeExtension(
380 const std::string& extension_id,
381 const std::vector<extensions::InputComponentInfo>& input_components) {
382 VLOG(1) << "RegisterImeExtension: " << extension_id;
384 if (engine_map_[extension_id])
387 chromeos::input_method::InputMethodManager* manager =
388 chromeos::input_method::InputMethodManager::Get();
389 chromeos::ComponentExtensionIMEManager* comp_ext_ime_manager =
390 manager->GetComponentExtensionIMEManager();
392 chromeos::input_method::InputMethodDescriptors descriptors;
393 // Only creates descriptors for 3rd party IME extension, because the
394 // descriptors for component IME extensions are managed by InputMethodUtil.
395 if (!comp_ext_ime_manager->IsWhitelistedExtension(extension_id)) {
396 for (std::vector<extensions::InputComponentInfo>::const_iterator it =
397 input_components.begin();
398 it != input_components.end();
400 const extensions::InputComponentInfo& component = *it;
401 DCHECK(component.type == extensions::INPUT_COMPONENT_TYPE_IME);
403 std::vector<std::string> layouts;
404 layouts.assign(component.layouts.begin(), component.layouts.end());
405 std::vector<std::string> languages;
406 languages.assign(component.languages.begin(), component.languages.end());
408 const std::string& input_method_id =
409 chromeos::extension_ime_util::GetInputMethodID(extension_id,
411 descriptors.push_back(chromeos::input_method::InputMethodDescriptor(
414 std::string(), // TODO(uekawa): Set short name.
417 false, // 3rd party IMEs are always not for login.
418 component.options_page_url,
419 component.input_view_url));
423 scoped_ptr<chromeos::InputMethodEngineInterface::Observer> observer(
424 new chromeos::ImeObserver(extension_id));
425 chromeos::InputMethodEngine* engine = new chromeos::InputMethodEngine();
426 engine->Initialize(observer.Pass(), extension_id.c_str());
427 engine_map_[extension_id] = engine;
428 chromeos::UserSessionManager::GetInstance()
429 ->GetDefaultIMEState(profile)
430 ->AddInputMethodExtension(extension_id, descriptors, engine);
435 void InputImeEventRouter::UnregisterAllImes(const std::string& extension_id) {
436 std::map<std::string, InputMethodEngineInterface*>::iterator it =
437 engine_map_.find(extension_id);
438 if (it != engine_map_.end()) {
439 chromeos::input_method::InputMethodManager::Get()
440 ->GetActiveIMEState()
441 ->RemoveInputMethodExtension(extension_id);
443 engine_map_.erase(it);
447 InputMethodEngineInterface* InputImeEventRouter::GetEngine(
448 const std::string& extension_id,
449 const std::string& component_id) {
450 std::map<std::string, InputMethodEngineInterface*>::iterator it =
451 engine_map_.find(extension_id);
452 if (it != engine_map_.end())
457 InputMethodEngineInterface* InputImeEventRouter::GetActiveEngine(
458 const std::string& extension_id) {
459 std::map<std::string, InputMethodEngineInterface*>::iterator it =
460 engine_map_.find(extension_id);
461 if (it != engine_map_.end() && it->second->IsActive())
466 void InputImeEventRouter::OnKeyEventHandled(
467 const std::string& extension_id,
468 const std::string& request_id,
470 RequestMap::iterator request = request_map_.find(request_id);
471 if (request == request_map_.end()) {
472 LOG(ERROR) << "Request ID not found: " << request_id;
476 std::string component_id = request->second.first;
477 chromeos::input_method::KeyEventHandle* key_data = request->second.second;
478 request_map_.erase(request);
480 CallbackKeyEventHandle(key_data, handled);
483 std::string InputImeEventRouter::AddRequest(
484 const std::string& component_id,
485 chromeos::input_method::KeyEventHandle* key_data) {
486 std::string request_id = base::IntToString(next_request_id_);
489 request_map_[request_id] = std::make_pair(component_id, key_data);
494 InputImeEventRouter::InputImeEventRouter()
495 : next_request_id_(1) {
498 InputImeEventRouter::~InputImeEventRouter() {}
500 bool InputImeSetCompositionFunction::RunSync() {
501 InputMethodEngineInterface* engine =
502 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
504 SetResult(new base::FundamentalValue(false));
508 scoped_ptr<SetComposition::Params> parent_params(
509 SetComposition::Params::Create(*args_));
510 const SetComposition::Params::Parameters& params = parent_params->parameters;
511 std::vector<InputMethodEngineInterface::SegmentInfo> segments;
512 if (params.segments) {
513 const std::vector<linked_ptr<
514 SetComposition::Params::Parameters::SegmentsType> >&
515 segments_args = *params.segments;
516 for (size_t i = 0; i < segments_args.size(); ++i) {
517 EXTENSION_FUNCTION_VALIDATE(
518 segments_args[i]->style !=
519 SetComposition::Params::Parameters::SegmentsType::STYLE_NONE);
520 segments.push_back(InputMethodEngineInterface::SegmentInfo());
521 segments.back().start = segments_args[i]->start;
522 segments.back().end = segments_args[i]->end;
523 if (segments_args[i]->style ==
524 SetComposition::Params::Parameters::SegmentsType::STYLE_UNDERLINE) {
525 segments.back().style =
526 InputMethodEngineInterface::SEGMENT_STYLE_UNDERLINE;
527 } else if (segments_args[i]->style ==
528 SetComposition::Params::Parameters::SegmentsType::
529 STYLE_DOUBLEUNDERLINE) {
530 segments.back().style =
531 InputMethodEngineInterface::SEGMENT_STYLE_DOUBLE_UNDERLINE;
533 segments.back().style =
534 InputMethodEngineInterface::SEGMENT_STYLE_NO_UNDERLINE;
539 int selection_start =
540 params.selection_start ? *params.selection_start : params.cursor;
542 params.selection_end ? *params.selection_end : params.cursor;
544 SetResult(new base::FundamentalValue(
545 engine->SetComposition(params.context_id, params.text.c_str(),
546 selection_start, selection_end, params.cursor,
547 segments, &error_)));
551 bool InputImeClearCompositionFunction::RunSync() {
552 InputMethodEngineInterface* engine =
553 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
555 SetResult(new base::FundamentalValue(false));
559 scoped_ptr<ClearComposition::Params> parent_params(
560 ClearComposition::Params::Create(*args_));
561 const ClearComposition::Params::Parameters& params =
562 parent_params->parameters;
564 SetResult(new base::FundamentalValue(
565 engine->ClearComposition(params.context_id, &error_)));
569 bool InputImeCommitTextFunction::RunSync() {
570 // TODO(zork): Support committing when not active.
571 InputMethodEngineInterface* engine =
572 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
574 SetResult(new base::FundamentalValue(false));
578 scoped_ptr<CommitText::Params> parent_params(
579 CommitText::Params::Create(*args_));
580 const CommitText::Params::Parameters& params =
581 parent_params->parameters;
583 SetResult(new base::FundamentalValue(
584 engine->CommitText(params.context_id, params.text.c_str(), &error_)));
588 bool InputImeHideInputViewFunction::RunAsync() {
589 InputMethodEngineInterface* engine =
590 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
594 engine->HideInputView();
598 bool InputImeSendKeyEventsFunction::RunAsync() {
599 scoped_ptr<SendKeyEvents::Params> parent_params(
600 SendKeyEvents::Params::Create(*args_));
601 const SendKeyEvents::Params::Parameters& params =
602 parent_params->parameters;
603 chromeos::InputMethodEngineInterface* engine =
604 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
606 error_ = kErrorEngineNotAvailable;
610 const std::vector<linked_ptr<input_ime::KeyboardEvent> >& key_data =
612 std::vector<chromeos::InputMethodEngineInterface::KeyboardEvent> key_data_out;
614 for (size_t i = 0; i < key_data.size(); ++i) {
615 chromeos::InputMethodEngineInterface::KeyboardEvent event;
616 event.type = input_ime::KeyboardEvent::ToString(key_data[i]->type);
617 event.key = key_data[i]->key;
618 event.code = key_data[i]->code;
619 event.key_code = key_data[i]->key_code.get() ? *(key_data[i]->key_code) : 0;
620 if (key_data[i]->alt_key)
621 event.alt_key = *(key_data[i]->alt_key);
622 if (key_data[i]->ctrl_key)
623 event.ctrl_key = *(key_data[i]->ctrl_key);
624 if (key_data[i]->shift_key)
625 event.shift_key = *(key_data[i]->shift_key);
626 if (key_data[i]->caps_lock)
627 event.caps_lock = *(key_data[i]->caps_lock);
628 key_data_out.push_back(event);
631 engine->SendKeyEvents(params.context_id, key_data_out);
635 bool InputImeSetCandidateWindowPropertiesFunction::RunSync() {
636 scoped_ptr<SetCandidateWindowProperties::Params> parent_params(
637 SetCandidateWindowProperties::Params::Create(*args_));
638 const SetCandidateWindowProperties::Params::Parameters&
639 params = parent_params->parameters;
641 InputMethodEngineInterface* engine =
642 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
646 SetResult(new base::FundamentalValue(false));
650 const SetCandidateWindowProperties::Params::Parameters::Properties&
651 properties = params.properties;
653 if (properties.visible &&
654 !engine->SetCandidateWindowVisible(*properties.visible, &error_)) {
655 SetResult(new base::FundamentalValue(false));
659 InputMethodEngineInterface::CandidateWindowProperty properties_out =
660 engine->GetCandidateWindowProperty();
661 bool modified = false;
663 if (properties.cursor_visible) {
664 properties_out.is_cursor_visible = *properties.cursor_visible;
668 if (properties.vertical) {
669 properties_out.is_vertical = *properties.vertical;
673 if (properties.page_size) {
674 properties_out.page_size = *properties.page_size;
678 if (properties.window_position ==
679 SetCandidateWindowProperties::Params::Parameters::Properties::
680 WINDOW_POSITION_COMPOSITION) {
681 properties_out.show_window_at_composition = true;
683 } else if (properties.window_position ==
684 SetCandidateWindowProperties::Params::Parameters::Properties::
685 WINDOW_POSITION_CURSOR) {
686 properties_out.show_window_at_composition = false;
690 if (properties.auxiliary_text) {
691 properties_out.auxiliary_text = *properties.auxiliary_text;
695 if (properties.auxiliary_text_visible) {
696 properties_out.is_auxiliary_text_visible =
697 *properties.auxiliary_text_visible;
702 engine->SetCandidateWindowProperty(properties_out);
705 SetResult(new base::FundamentalValue(true));
710 bool InputImeSetCandidatesFunction::RunSync() {
711 InputMethodEngineInterface* engine =
712 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
714 SetResult(new base::FundamentalValue(false));
718 scoped_ptr<SetCandidates::Params> parent_params(
719 SetCandidates::Params::Create(*args_));
720 const SetCandidates::Params::Parameters& params =
721 parent_params->parameters;
723 std::vector<InputMethodEngineInterface::Candidate> candidates_out;
724 const std::vector<linked_ptr<
725 SetCandidates::Params::Parameters::CandidatesType> >& candidates_in =
727 for (size_t i = 0; i < candidates_in.size(); ++i) {
728 candidates_out.push_back(InputMethodEngineInterface::Candidate());
729 candidates_out.back().value = candidates_in[i]->candidate;
730 candidates_out.back().id = candidates_in[i]->id;
731 if (candidates_in[i]->label)
732 candidates_out.back().label = *candidates_in[i]->label;
733 if (candidates_in[i]->annotation)
734 candidates_out.back().annotation = *candidates_in[i]->annotation;
735 if (candidates_in[i]->usage) {
736 candidates_out.back().usage.title = candidates_in[i]->usage->title;
737 candidates_out.back().usage.body = candidates_in[i]->usage->body;
741 SetResult(new base::FundamentalValue(
742 engine->SetCandidates(params.context_id, candidates_out, &error_)));
746 bool InputImeSetCursorPositionFunction::RunSync() {
747 InputMethodEngineInterface* engine =
748 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
750 SetResult(new base::FundamentalValue(false));
754 scoped_ptr<SetCursorPosition::Params> parent_params(
755 SetCursorPosition::Params::Create(*args_));
756 const SetCursorPosition::Params::Parameters& params =
757 parent_params->parameters;
759 SetResult(new base::FundamentalValue(
760 engine->SetCursorPosition(params.context_id, params.candidate_id,
765 bool InputImeSetMenuItemsFunction::RunSync() {
766 scoped_ptr<SetMenuItems::Params> parent_params(
767 SetMenuItems::Params::Create(*args_));
768 const SetMenuItems::Params::Parameters& params =
769 parent_params->parameters;
771 InputMethodEngineInterface* engine =
772 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
775 error_ = kErrorEngineNotAvailable;
779 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
780 std::vector<InputMethodEngineInterface::MenuItem> items_out;
782 for (size_t i = 0; i < items.size(); ++i) {
783 items_out.push_back(InputMethodEngineInterface::MenuItem());
784 SetMenuItemToMenu(*items[i], &items_out.back());
787 if (!engine->SetMenuItems(items_out))
788 error_ = kErrorSetMenuItemsFail;
792 bool InputImeUpdateMenuItemsFunction::RunSync() {
793 scoped_ptr<UpdateMenuItems::Params> parent_params(
794 UpdateMenuItems::Params::Create(*args_));
795 const UpdateMenuItems::Params::Parameters& params =
796 parent_params->parameters;
798 InputMethodEngineInterface* engine =
799 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
802 error_ = kErrorEngineNotAvailable;
806 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
807 std::vector<InputMethodEngineInterface::MenuItem> items_out;
809 for (size_t i = 0; i < items.size(); ++i) {
810 items_out.push_back(InputMethodEngineInterface::MenuItem());
811 SetMenuItemToMenu(*items[i], &items_out.back());
814 if (!engine->UpdateMenuItems(items_out))
815 error_ = kErrorUpdateMenuItemsFail;
819 bool InputImeDeleteSurroundingTextFunction::RunSync() {
820 scoped_ptr<DeleteSurroundingText::Params> parent_params(
821 DeleteSurroundingText::Params::Create(*args_));
822 const DeleteSurroundingText::Params::Parameters& params =
823 parent_params->parameters;
825 InputMethodEngineInterface* engine =
826 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
829 error_ = kErrorEngineNotAvailable;
833 engine->DeleteSurroundingText(params.context_id, params.offset, params.length,
838 bool InputImeKeyEventHandledFunction::RunAsync() {
839 scoped_ptr<KeyEventHandled::Params> params(
840 KeyEventHandled::Params::Create(*args_));
841 InputImeEventRouter::GetInstance()->OnKeyEventHandled(
842 extension_id(), params->request_id, params->response);
846 InputImeAPI::InputImeAPI(content::BrowserContext* context)
847 : browser_context_(context), extension_registry_observer_(this) {
848 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
850 EventRouter* event_router = EventRouter::Get(browser_context_);
851 event_router->RegisterObserver(this, input_ime::OnFocus::kEventName);
854 InputImeAPI::~InputImeAPI() {
855 EventRouter::Get(browser_context_)->UnregisterObserver(this);
858 static base::LazyInstance<BrowserContextKeyedAPIFactory<InputImeAPI> >
859 g_factory = LAZY_INSTANCE_INITIALIZER;
862 BrowserContextKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() {
863 return g_factory.Pointer();
866 void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
867 const Extension* extension) {
868 const std::vector<InputComponentInfo>* input_components =
869 extensions::InputComponents::GetInputComponents(extension);
870 if (input_components)
871 input_ime_event_router()->RegisterImeExtension(
872 Profile::FromBrowserContext(browser_context),
877 void InputImeAPI::OnExtensionUnloaded(content::BrowserContext* browser_context,
878 const Extension* extension,
879 UnloadedExtensionInfo::Reason reason) {
880 const std::vector<InputComponentInfo>* input_components =
881 extensions::InputComponents::GetInputComponents(extension);
882 if (!input_components)
884 if (input_components->size() > 0)
885 input_ime_event_router()->UnregisterAllImes(extension->id());
888 void InputImeAPI::OnListenerAdded(const EventListenerInfo& details) {
889 InputMethodEngineInterface* engine =
890 input_ime_event_router()->GetActiveEngine(details.extension_id);
891 // Notifies the IME extension for IME ready with onActivate/onFocus events.
893 engine->Enable(engine->GetActiveComponentId());
896 InputImeEventRouter* InputImeAPI::input_ime_event_router() {
897 return InputImeEventRouter::GetInstance();
900 } // namespace extensions