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/profiles/profile_helper.h"
10 #include "chrome/browser/profiles/profile_manager.h"
11 #include "chrome/common/extensions/api/input_ime.h"
12 #include "chrome/common/extensions/api/input_ime/input_components_handler.h"
13 #include "extensions/browser/event_router.h"
14 #include "extensions/browser/extension_function_registry.h"
15 #include "extensions/browser/extension_registry.h"
18 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
21 namespace input_ime = extensions::api::input_ime;
22 namespace KeyEventHandled = extensions::api::input_ime::KeyEventHandled;
23 namespace DeleteSurroundingText =
24 extensions::api::input_ime::DeleteSurroundingText;
25 namespace UpdateMenuItems = extensions::api::input_ime::UpdateMenuItems;
26 namespace SendKeyEvents = extensions::api::input_ime::SendKeyEvents;
27 namespace HideInputView = extensions::api::input_ime::HideInputView;
28 namespace SetMenuItems = extensions::api::input_ime::SetMenuItems;
29 namespace SetCursorPosition = extensions::api::input_ime::SetCursorPosition;
30 namespace SetCandidates = extensions::api::input_ime::SetCandidates;
31 namespace SetCandidateWindowProperties =
32 extensions::api::input_ime::SetCandidateWindowProperties;
33 namespace CommitText = extensions::api::input_ime::CommitText;
34 namespace ClearComposition = extensions::api::input_ime::ClearComposition;
35 namespace SetComposition = extensions::api::input_ime::SetComposition;
36 using chromeos::InputMethodEngineInterface;
40 const char kErrorEngineNotAvailable[] = "Engine is not available";
41 const char kErrorSetMenuItemsFail[] = "Could not create menu Items";
42 const char kErrorUpdateMenuItemsFail[] = "Could not update menu Items";
44 void SetMenuItemToMenu(const input_ime::MenuItem& input,
45 InputMethodEngineInterface::MenuItem* out) {
49 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_LABEL;
50 out->label = *input.label;
53 if (input.style != input_ime::MenuItem::STYLE_NONE) {
54 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_STYLE;
55 out->style = static_cast<InputMethodEngineInterface::MenuItemStyle>(
60 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_VISIBLE;
61 out->visible = input.visible ? *input.visible : true;
64 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_CHECKED;
65 out->checked = input.checked ? *input.checked : false;
68 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_ENABLED;
69 out->enabled = input.enabled ? *input.enabled : true;
72 static void DispatchEventToExtension(Profile* profile,
73 const std::string& extension_id,
74 const std::string& event_name,
75 scoped_ptr<base::ListValue> args) {
76 scoped_ptr<extensions::Event> event(new extensions::Event(
77 event_name, args.Pass()));
78 event->restrict_to_browser_context = profile;
79 extensions::EventRouter::Get(profile)
80 ->DispatchEventToExtension(extension_id, event.Pass());
86 class ImeObserver : public InputMethodEngineInterface::Observer {
88 ImeObserver(Profile* profile, const std::string& extension_id)
89 : profile_(profile), extension_id_(extension_id) {}
91 virtual ~ImeObserver() {}
93 virtual void OnActivate(const std::string& engine_id) OVERRIDE {
94 if (profile_ == NULL || extension_id_.empty())
97 scoped_ptr<base::ListValue> args(input_ime::OnActivate::Create(engine_id));
99 DispatchEventToExtension(profile_, extension_id_,
100 input_ime::OnActivate::kEventName, args.Pass());
103 virtual void OnDeactivated(const std::string& engine_id) OVERRIDE {
104 if (profile_ == NULL || extension_id_.empty())
107 scoped_ptr<base::ListValue> args(
108 input_ime::OnDeactivated::Create(engine_id));
110 DispatchEventToExtension(profile_, extension_id_,
111 input_ime::OnDeactivated::kEventName, args.Pass());
114 virtual void OnFocus(
115 const InputMethodEngineInterface::InputContext& context) OVERRIDE {
116 if (profile_ == NULL || extension_id_.empty())
119 input_ime::InputContext context_value;
120 context_value.context_id = context.id;
121 context_value.type = input_ime::InputContext::ParseType(context.type);
123 scoped_ptr<base::ListValue> args(input_ime::OnFocus::Create(context_value));
125 DispatchEventToExtension(profile_, extension_id_,
126 input_ime::OnFocus::kEventName, args.Pass());
129 virtual void OnBlur(int context_id) OVERRIDE {
130 if (profile_ == NULL || extension_id_.empty())
133 scoped_ptr<base::ListValue> args(input_ime::OnBlur::Create(context_id));
135 DispatchEventToExtension(profile_, extension_id_,
136 input_ime::OnBlur::kEventName, args.Pass());
139 virtual void OnInputContextUpdate(
140 const InputMethodEngineInterface::InputContext& context) OVERRIDE {
141 if (profile_ == NULL || extension_id_.empty())
144 input_ime::InputContext context_value;
145 context_value.context_id = context.id;
146 context_value.type = input_ime::InputContext::ParseType(context.type);
148 scoped_ptr<base::ListValue> args(
149 input_ime::OnInputContextUpdate::Create(context_value));
151 DispatchEventToExtension(profile_,
153 input_ime::OnInputContextUpdate::kEventName,
157 virtual void OnKeyEvent(
158 const std::string& engine_id,
159 const InputMethodEngineInterface::KeyboardEvent& event,
160 chromeos::input_method::KeyEventHandle* key_data) OVERRIDE {
161 if (profile_ == NULL || extension_id_.empty())
164 // If there is no listener for the event, no need to dispatch the event to
165 // extension. Instead, releases the key event for default system behavior.
166 if (!HasKeyEventListener()) {
167 // Continue processing the key event so that the physical keyboard can
169 base::Callback<void(bool consumed)>* callback =
170 reinterpret_cast<base::Callback<void(bool consumed)>*>(key_data);
171 callback->Run(false);
176 extensions::InputImeEventRouter* ime_event_router =
177 extensions::InputImeEventRouter::GetInstance();
179 const std::string request_id =
180 ime_event_router->AddRequest(engine_id, key_data);
182 input_ime::KeyboardEvent key_data_value;
183 key_data_value.type = input_ime::KeyboardEvent::ParseType(event.type);
184 key_data_value.request_id = request_id;
185 if (!event.extension_id.empty())
186 key_data_value.extension_id.reset(new std::string(event.extension_id));
187 key_data_value.key = event.key;
188 key_data_value.code = event.code;
189 key_data_value.alt_key.reset(new bool(event.alt_key));
190 key_data_value.ctrl_key.reset(new bool(event.ctrl_key));
191 key_data_value.shift_key.reset(new bool(event.shift_key));
192 key_data_value.caps_lock.reset(new bool(event.caps_lock));
194 scoped_ptr<base::ListValue> args(
195 input_ime::OnKeyEvent::Create(engine_id, key_data_value));
197 DispatchEventToExtension(profile_, extension_id_,
198 input_ime::OnKeyEvent::kEventName, args.Pass());
201 virtual void OnCandidateClicked(
202 const std::string& engine_id,
204 InputMethodEngineInterface::MouseButtonEvent button) OVERRIDE {
205 if (profile_ == NULL || extension_id_.empty())
208 input_ime::OnCandidateClicked::Button button_enum =
209 input_ime::OnCandidateClicked::BUTTON_NONE;
211 case InputMethodEngineInterface::MOUSE_BUTTON_MIDDLE:
212 button_enum = input_ime::OnCandidateClicked::BUTTON_MIDDLE;
215 case InputMethodEngineInterface::MOUSE_BUTTON_RIGHT:
216 button_enum = input_ime::OnCandidateClicked::BUTTON_RIGHT;
219 case InputMethodEngineInterface::MOUSE_BUTTON_LEFT:
222 button_enum = input_ime::OnCandidateClicked::BUTTON_LEFT;
226 scoped_ptr<base::ListValue> args(
227 input_ime::OnCandidateClicked::Create(engine_id,
231 DispatchEventToExtension(profile_,
233 input_ime::OnCandidateClicked::kEventName,
237 virtual void OnMenuItemActivated(const std::string& engine_id,
238 const std::string& menu_id) OVERRIDE {
239 if (profile_ == NULL || extension_id_.empty())
242 scoped_ptr<base::ListValue> args(
243 input_ime::OnMenuItemActivated::Create(engine_id, menu_id));
245 DispatchEventToExtension(profile_,
247 input_ime::OnMenuItemActivated::kEventName,
251 virtual void OnSurroundingTextChanged(const std::string& engine_id,
252 const std::string& text,
254 int anchor_pos) OVERRIDE {
255 if (profile_ == NULL || extension_id_.empty())
258 input_ime::OnSurroundingTextChanged::SurroundingInfo info;
260 info.focus = cursor_pos;
261 info.anchor = anchor_pos;
262 scoped_ptr<base::ListValue> args(
263 input_ime::OnSurroundingTextChanged::Create(engine_id, info));
265 DispatchEventToExtension(profile_,
267 input_ime::OnSurroundingTextChanged::kEventName,
271 virtual void OnReset(const std::string& engine_id) OVERRIDE {
272 if (profile_ == NULL || extension_id_.empty())
275 scoped_ptr<base::ListValue> args(input_ime::OnReset::Create(engine_id));
277 DispatchEventToExtension(profile_,
279 input_ime::OnReset::kEventName,
284 bool HasKeyEventListener() const {
285 return extensions::EventRouter::Get(profile_)
286 ->ExtensionHasEventListener(extension_id_,
287 input_ime::OnKeyEvent::kEventName);
291 std::string extension_id_;
293 DISALLOW_COPY_AND_ASSIGN(ImeObserver);
296 } // namespace chromeos
298 namespace extensions {
301 InputImeEventRouter::GetInstance() {
302 return Singleton<InputImeEventRouter>::get();
305 bool InputImeEventRouter::RegisterIme(
306 const std::string& extension_id,
307 const extensions::InputComponentInfo& component) {
309 VLOG(1) << "RegisterIme: " << extension_id << " id: " << component.id;
311 std::vector<std::string> layouts;
312 layouts.assign(component.layouts.begin(), component.layouts.end());
314 std::vector<std::string> languages;
315 languages.assign(component.languages.begin(), component.languages.end());
317 // Ideally Observer should be per (extension_id + Profile), and multiple
318 // InputMethodEngine can share one Observer. But it would become tricky
319 // to maintain an internal map for observers which does nearly nothing
320 // but just make sure they can properly deleted.
321 // Making Obesrver per InputMethodEngine can make things cleaner.
322 Profile* profile = ProfileManager::GetActiveUserProfile();
323 scoped_ptr<chromeos::InputMethodEngineInterface::Observer> observer(
324 new chromeos::ImeObserver(profile, extension_id));
325 chromeos::InputMethodEngine* engine = new chromeos::InputMethodEngine();
326 engine->Initialize(observer.Pass(),
327 component.name.c_str(),
328 extension_id.c_str(),
329 component.id.c_str(),
332 component.options_page_url,
333 component.input_view_url);
334 profile_engine_map_[profile][extension_id][component.id] = engine;
338 // TODO(spang): IME support under ozone.
344 void InputImeEventRouter::UnregisterAllImes(const std::string& extension_id) {
345 Profile* profile = ProfileManager::GetActiveUserProfile();
346 ProfileEngineMap::iterator extension_map =
347 profile_engine_map_.find(profile);
348 if (extension_map == profile_engine_map_.end())
350 ExtensionMap::iterator engine_map = extension_map->second.find(extension_id);
351 if (engine_map == extension_map->second.end())
353 STLDeleteContainerPairSecondPointers(engine_map->second.begin(),
354 engine_map->second.end());
355 extension_map->second.erase(extension_id);
356 profile_engine_map_.erase(profile);
359 InputMethodEngineInterface* InputImeEventRouter::GetEngine(
360 const std::string& extension_id, const std::string& engine_id) {
361 // IME can only work on active user profile.
362 Profile* profile = ProfileManager::GetActiveUserProfile();
364 ProfileEngineMap::const_iterator extension_map =
365 profile_engine_map_.find(profile);
366 if (extension_map == profile_engine_map_.end())
368 ExtensionMap::const_iterator engine_map =
369 extension_map->second.find(extension_id);
370 if (engine_map == extension_map->second.end())
372 EngineMap::const_iterator engine = engine_map->second.find(engine_id);
373 if (engine == engine_map->second.end())
375 return engine->second;
378 InputMethodEngineInterface* InputImeEventRouter::GetActiveEngine(
379 const std::string& extension_id) {
380 // IME can only work on active user profile.
381 Profile* profile = ProfileManager::GetActiveUserProfile();
383 ProfileEngineMap::const_iterator extension_map =
384 profile_engine_map_.find(profile);
385 if (extension_map == profile_engine_map_.end())
387 ExtensionMap::const_iterator engine_map =
388 extension_map->second.find(extension_id);
389 if (engine_map == extension_map->second.end())
392 for (EngineMap::const_iterator i = engine_map->second.begin();
393 i != engine_map->second.end();
395 if (i->second->IsActive())
401 void InputImeEventRouter::OnKeyEventHandled(
402 const std::string& extension_id,
403 const std::string& request_id,
405 RequestMap::iterator request = request_map_.find(request_id);
406 if (request == request_map_.end()) {
407 LOG(ERROR) << "Request ID not found: " << request_id;
411 std::string engine_id = request->second.first;
412 chromeos::input_method::KeyEventHandle* key_data = request->second.second;
413 request_map_.erase(request);
415 InputMethodEngineInterface* engine = GetEngine(extension_id, engine_id);
417 LOG(ERROR) << "Engine does not exist: " << engine_id;
421 engine->KeyEventDone(key_data, handled);
424 std::string InputImeEventRouter::AddRequest(
425 const std::string& engine_id,
426 chromeos::input_method::KeyEventHandle* key_data) {
427 std::string request_id = base::IntToString(next_request_id_);
430 request_map_[request_id] = std::make_pair(engine_id, key_data);
435 InputImeEventRouter::InputImeEventRouter()
436 : next_request_id_(1) {
439 InputImeEventRouter::~InputImeEventRouter() {}
441 bool InputImeSetCompositionFunction::RunSync() {
442 InputMethodEngineInterface* engine =
443 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
445 SetResult(new base::FundamentalValue(false));
449 scoped_ptr<SetComposition::Params> parent_params(
450 SetComposition::Params::Create(*args_));
451 const SetComposition::Params::Parameters& params = parent_params->parameters;
452 std::vector<InputMethodEngineInterface::SegmentInfo> segments;
453 if (params.segments) {
454 const std::vector<linked_ptr<
455 SetComposition::Params::Parameters::SegmentsType> >&
456 segments_args = *params.segments;
457 for (size_t i = 0; i < segments_args.size(); ++i) {
458 EXTENSION_FUNCTION_VALIDATE(
459 segments_args[i]->style !=
460 SetComposition::Params::Parameters::SegmentsType::STYLE_NONE);
461 segments.push_back(InputMethodEngineInterface::SegmentInfo());
462 segments.back().start = segments_args[i]->start;
463 segments.back().end = segments_args[i]->end;
464 if (segments_args[i]->style ==
465 SetComposition::Params::Parameters::SegmentsType::STYLE_UNDERLINE) {
466 segments.back().style =
467 InputMethodEngineInterface::SEGMENT_STYLE_UNDERLINE;
469 segments.back().style =
470 InputMethodEngineInterface::SEGMENT_STYLE_DOUBLE_UNDERLINE;
475 int selection_start =
476 params.selection_start ? *params.selection_start : params.cursor;
478 params.selection_end ? *params.selection_end : params.cursor;
480 SetResult(new base::FundamentalValue(
481 engine->SetComposition(params.context_id, params.text.c_str(),
482 selection_start, selection_end, params.cursor,
483 segments, &error_)));
487 bool InputImeClearCompositionFunction::RunSync() {
488 InputMethodEngineInterface* engine =
489 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
491 SetResult(new base::FundamentalValue(false));
495 scoped_ptr<ClearComposition::Params> parent_params(
496 ClearComposition::Params::Create(*args_));
497 const ClearComposition::Params::Parameters& params =
498 parent_params->parameters;
500 SetResult(new base::FundamentalValue(
501 engine->ClearComposition(params.context_id, &error_)));
505 bool InputImeCommitTextFunction::RunSync() {
506 // TODO(zork): Support committing when not active.
507 InputMethodEngineInterface* engine =
508 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
510 SetResult(new base::FundamentalValue(false));
514 scoped_ptr<CommitText::Params> parent_params(
515 CommitText::Params::Create(*args_));
516 const CommitText::Params::Parameters& params =
517 parent_params->parameters;
519 SetResult(new base::FundamentalValue(
520 engine->CommitText(params.context_id, params.text.c_str(), &error_)));
524 bool InputImeHideInputViewFunction::RunAsync() {
525 InputMethodEngineInterface* engine =
526 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
530 engine->HideInputView();
534 bool InputImeSendKeyEventsFunction::RunAsync() {
535 scoped_ptr<SendKeyEvents::Params> parent_params(
536 SendKeyEvents::Params::Create(*args_));
537 const SendKeyEvents::Params::Parameters& params =
538 parent_params->parameters;
539 chromeos::InputMethodEngineInterface* engine =
540 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
542 error_ = kErrorEngineNotAvailable;
546 const std::vector<linked_ptr<input_ime::KeyboardEvent> >& key_data =
548 std::vector<chromeos::InputMethodEngineInterface::KeyboardEvent> key_data_out;
550 for (size_t i = 0; i < key_data.size(); ++i) {
551 chromeos::InputMethodEngineInterface::KeyboardEvent event;
552 event.type = input_ime::KeyboardEvent::ToString(key_data[i]->type);
553 event.key = key_data[i]->key;
554 event.code = key_data[i]->code;
555 event.key_code = key_data[i]->key_code.get() ? *(key_data[i]->key_code) : 0;
556 if (key_data[i]->alt_key)
557 event.alt_key = *(key_data[i]->alt_key);
558 if (key_data[i]->ctrl_key)
559 event.ctrl_key = *(key_data[i]->ctrl_key);
560 if (key_data[i]->shift_key)
561 event.shift_key = *(key_data[i]->shift_key);
562 if (key_data[i]->caps_lock)
563 event.caps_lock = *(key_data[i]->caps_lock);
564 key_data_out.push_back(event);
567 engine->SendKeyEvents(params.context_id, key_data_out);
571 bool InputImeSetCandidateWindowPropertiesFunction::RunSync() {
572 scoped_ptr<SetCandidateWindowProperties::Params> parent_params(
573 SetCandidateWindowProperties::Params::Create(*args_));
574 const SetCandidateWindowProperties::Params::Parameters&
575 params = parent_params->parameters;
577 InputMethodEngineInterface* engine =
578 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
582 SetResult(new base::FundamentalValue(false));
586 const SetCandidateWindowProperties::Params::Parameters::Properties&
587 properties = params.properties;
589 if (properties.visible &&
590 !engine->SetCandidateWindowVisible(*properties.visible, &error_)) {
591 SetResult(new base::FundamentalValue(false));
595 InputMethodEngineInterface::CandidateWindowProperty properties_out =
596 engine->GetCandidateWindowProperty();
597 bool modified = false;
599 if (properties.cursor_visible) {
600 properties_out.is_cursor_visible = *properties.cursor_visible;
604 if (properties.vertical) {
605 properties_out.is_vertical = *properties.vertical;
609 if (properties.page_size) {
610 properties_out.page_size = *properties.page_size;
614 if (properties.window_position ==
615 SetCandidateWindowProperties::Params::Parameters::Properties::
616 WINDOW_POSITION_COMPOSITION) {
617 properties_out.show_window_at_composition = true;
619 } else if (properties.window_position ==
620 SetCandidateWindowProperties::Params::Parameters::Properties::
621 WINDOW_POSITION_CURSOR) {
622 properties_out.show_window_at_composition = false;
626 if (properties.auxiliary_text) {
627 properties_out.auxiliary_text = *properties.auxiliary_text;
631 if (properties.auxiliary_text_visible) {
632 properties_out.is_auxiliary_text_visible =
633 *properties.auxiliary_text_visible;
638 engine->SetCandidateWindowProperty(properties_out);
641 SetResult(new base::FundamentalValue(true));
646 bool InputImeSetCandidatesFunction::RunSync() {
647 InputMethodEngineInterface* engine =
648 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
650 SetResult(new base::FundamentalValue(false));
654 scoped_ptr<SetCandidates::Params> parent_params(
655 SetCandidates::Params::Create(*args_));
656 const SetCandidates::Params::Parameters& params =
657 parent_params->parameters;
659 std::vector<InputMethodEngineInterface::Candidate> candidates_out;
660 const std::vector<linked_ptr<
661 SetCandidates::Params::Parameters::CandidatesType> >& candidates_in =
663 for (size_t i = 0; i < candidates_in.size(); ++i) {
664 candidates_out.push_back(InputMethodEngineInterface::Candidate());
665 candidates_out.back().value = candidates_in[i]->candidate;
666 candidates_out.back().id = candidates_in[i]->id;
667 if (candidates_in[i]->label)
668 candidates_out.back().label = *candidates_in[i]->label;
669 if (candidates_in[i]->annotation)
670 candidates_out.back().annotation = *candidates_in[i]->annotation;
671 if (candidates_in[i]->usage) {
672 candidates_out.back().usage.title = candidates_in[i]->usage->title;
673 candidates_out.back().usage.body = candidates_in[i]->usage->body;
677 SetResult(new base::FundamentalValue(
678 engine->SetCandidates(params.context_id, candidates_out, &error_)));
682 bool InputImeSetCursorPositionFunction::RunSync() {
683 InputMethodEngineInterface* engine =
684 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
686 SetResult(new base::FundamentalValue(false));
690 scoped_ptr<SetCursorPosition::Params> parent_params(
691 SetCursorPosition::Params::Create(*args_));
692 const SetCursorPosition::Params::Parameters& params =
693 parent_params->parameters;
695 SetResult(new base::FundamentalValue(
696 engine->SetCursorPosition(params.context_id, params.candidate_id,
701 bool InputImeSetMenuItemsFunction::RunSync() {
702 scoped_ptr<SetMenuItems::Params> parent_params(
703 SetMenuItems::Params::Create(*args_));
704 const SetMenuItems::Params::Parameters& params =
705 parent_params->parameters;
707 InputMethodEngineInterface* engine =
708 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
711 error_ = kErrorEngineNotAvailable;
715 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
716 std::vector<InputMethodEngineInterface::MenuItem> items_out;
718 for (size_t i = 0; i < items.size(); ++i) {
719 items_out.push_back(InputMethodEngineInterface::MenuItem());
720 SetMenuItemToMenu(*items[i], &items_out.back());
723 if (!engine->SetMenuItems(items_out))
724 error_ = kErrorSetMenuItemsFail;
728 bool InputImeUpdateMenuItemsFunction::RunSync() {
729 scoped_ptr<UpdateMenuItems::Params> parent_params(
730 UpdateMenuItems::Params::Create(*args_));
731 const UpdateMenuItems::Params::Parameters& params =
732 parent_params->parameters;
734 InputMethodEngineInterface* engine =
735 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
738 error_ = kErrorEngineNotAvailable;
742 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
743 std::vector<InputMethodEngineInterface::MenuItem> items_out;
745 for (size_t i = 0; i < items.size(); ++i) {
746 items_out.push_back(InputMethodEngineInterface::MenuItem());
747 SetMenuItemToMenu(*items[i], &items_out.back());
750 if (!engine->UpdateMenuItems(items_out))
751 error_ = kErrorUpdateMenuItemsFail;
755 bool InputImeDeleteSurroundingTextFunction::RunSync() {
756 scoped_ptr<DeleteSurroundingText::Params> parent_params(
757 DeleteSurroundingText::Params::Create(*args_));
758 const DeleteSurroundingText::Params::Parameters& params =
759 parent_params->parameters;
761 InputMethodEngineInterface* engine =
762 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
765 error_ = kErrorEngineNotAvailable;
769 engine->DeleteSurroundingText(params.context_id, params.offset, params.length,
774 bool InputImeKeyEventHandledFunction::RunAsync() {
775 scoped_ptr<KeyEventHandled::Params> params(
776 KeyEventHandled::Params::Create(*args_));
777 InputImeEventRouter::GetInstance()->OnKeyEventHandled(
778 extension_id(), params->request_id, params->response);
782 InputImeAPI::InputImeAPI(content::BrowserContext* context)
783 : browser_context_(context), extension_registry_observer_(this) {
784 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
786 EventRouter* event_router = EventRouter::Get(browser_context_);
787 event_router->RegisterObserver(this, input_ime::OnActivate::kEventName);
788 event_router->RegisterObserver(this, input_ime::OnFocus::kEventName);
791 InputImeAPI::~InputImeAPI() {
792 EventRouter::Get(browser_context_)->UnregisterObserver(this);
795 static base::LazyInstance<BrowserContextKeyedAPIFactory<InputImeAPI> >
796 g_factory = LAZY_INSTANCE_INITIALIZER;
799 BrowserContextKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() {
800 return g_factory.Pointer();
803 void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
804 const Extension* extension) {
805 const std::vector<InputComponentInfo>* input_components =
806 extensions::InputComponents::GetInputComponents(extension);
807 if (!input_components)
809 for (std::vector<extensions::InputComponentInfo>::const_iterator component =
810 input_components->begin();
811 component != input_components->end();
813 if (component->type == extensions::INPUT_COMPONENT_TYPE_IME) {
814 // Don't pass profile_ to register ime, instead always use
815 // GetActiveUserProfile. It is because:
816 // The original profile for login screen is called signin profile.
817 // And the active profile is the incognito profile based on signin
818 // profile. So if |profile_| is signin profile, we need to make sure
819 // the router/observer runs under its incognito profile, because the
820 // component extensions were installed under its incognito profile.
821 input_ime_event_router()->RegisterIme(extension->id(), *component);
826 void InputImeAPI::OnExtensionUnloaded(content::BrowserContext* browser_context,
827 const Extension* extension,
828 UnloadedExtensionInfo::Reason reason) {
829 const std::vector<InputComponentInfo>* input_components =
830 extensions::InputComponents::GetInputComponents(extension);
831 if (!input_components)
833 if (input_components->size() > 0)
834 input_ime_event_router()->UnregisterAllImes(extension->id());
837 void InputImeAPI::OnListenerAdded(const EventListenerInfo& details) {
838 InputMethodEngineInterface* engine =
839 input_ime_event_router()->GetActiveEngine(details.extension_id);
841 engine->NotifyImeReady();
844 InputImeEventRouter* InputImeAPI::input_ime_event_router() {
845 return InputImeEventRouter::GetInstance();
848 } // namespace extensions