Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / input_ime / input_ime_api.cc
blob8b0de986f0fc6d58623f49128d5564db2f0a4e1f
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 "extensions/browser/event_router.h"
19 #include "extensions/browser/extension_function_registry.h"
20 #include "extensions/browser/extension_registry.h"
21 #include "extensions/browser/extension_system.h"
22 #include "extensions/browser/process_manager.h"
23 #include "extensions/common/manifest_handlers/background_info.h"
24 #include "ui/base/ime/chromeos/component_extension_ime_manager.h"
25 #include "ui/base/ime/chromeos/extension_ime_util.h"
26 #include "ui/base/ime/chromeos/input_method_manager.h"
28 namespace input_ime = extensions::api::input_ime;
29 namespace KeyEventHandled = extensions::api::input_ime::KeyEventHandled;
30 namespace DeleteSurroundingText =
31 extensions::api::input_ime::DeleteSurroundingText;
32 namespace UpdateMenuItems = extensions::api::input_ime::UpdateMenuItems;
33 namespace SendKeyEvents = extensions::api::input_ime::SendKeyEvents;
34 namespace HideInputView = extensions::api::input_ime::HideInputView;
35 namespace SetMenuItems = extensions::api::input_ime::SetMenuItems;
36 namespace SetCursorPosition = extensions::api::input_ime::SetCursorPosition;
37 namespace SetCandidates = extensions::api::input_ime::SetCandidates;
38 namespace SetCandidateWindowProperties =
39 extensions::api::input_ime::SetCandidateWindowProperties;
40 namespace CommitText = extensions::api::input_ime::CommitText;
41 namespace ClearComposition = extensions::api::input_ime::ClearComposition;
42 namespace SetComposition = extensions::api::input_ime::SetComposition;
43 using chromeos::InputMethodEngineInterface;
45 namespace {
47 const char kErrorEngineNotAvailable[] = "Engine is not available";
48 const char kErrorSetMenuItemsFail[] = "Could not create menu Items";
49 const char kErrorUpdateMenuItemsFail[] = "Could not update menu Items";
50 const char kOnCompositionBoundsChangedEventName[] =
51 "inputMethodPrivate.onCompositionBoundsChanged";
53 void SetMenuItemToMenu(const input_ime::MenuItem& input,
54 InputMethodEngineInterface::MenuItem* out) {
55 out->modified = 0;
56 out->id = input.id;
57 if (input.label) {
58 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_LABEL;
59 out->label = *input.label;
62 if (input.style != input_ime::MENU_ITEM_STYLE_NONE) {
63 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_STYLE;
64 out->style = static_cast<InputMethodEngineInterface::MenuItemStyle>(
65 input.style);
68 if (input.visible)
69 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_VISIBLE;
70 out->visible = input.visible ? *input.visible : true;
72 if (input.checked)
73 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_CHECKED;
74 out->checked = input.checked ? *input.checked : false;
76 if (input.enabled)
77 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_ENABLED;
78 out->enabled = input.enabled ? *input.enabled : true;
81 void CallbackKeyEventHandle(chromeos::input_method::KeyEventHandle* key_data,
82 bool handled) {
83 base::Callback<void(bool consumed)>* callback =
84 reinterpret_cast<base::Callback<void(bool consumed)>*>(key_data);
85 callback->Run(handled);
86 delete callback;
89 extensions::InputImeEventRouter* GetInputImeEventRouter(Profile* profile) {
90 if (profile->HasOffTheRecordProfile())
91 profile = profile->GetOffTheRecordProfile();
92 return extensions::InputImeEventRouterFactory::GetInstance()->GetRouter(
93 profile);
96 } // namespace
98 namespace chromeos {
99 class ImeObserver : public InputMethodEngineInterface::Observer {
100 public:
101 explicit ImeObserver(const std::string& extension_id, Profile* profile)
102 : extension_id_(extension_id), profile_(profile) {}
104 ~ImeObserver() override {}
106 void OnActivate(const std::string& component_id) override {
107 if (extension_id_.empty() ||
108 !HasListener(input_ime::OnActivate::kEventName))
109 return;
111 scoped_ptr<base::ListValue> args(input_ime::OnActivate::Create(
112 component_id,
113 input_ime::ParseScreenType(GetCurrentScreenType())));
115 DispatchEventToExtension(extensions::events::INPUT_IME_ON_ACTIVATE,
116 input_ime::OnActivate::kEventName, args.Pass());
119 void OnDeactivated(const std::string& component_id) override {
120 if (extension_id_.empty() ||
121 !HasListener(input_ime::OnDeactivated::kEventName))
122 return;
124 scoped_ptr<base::ListValue> args(
125 input_ime::OnDeactivated::Create(component_id));
127 DispatchEventToExtension(extensions::events::INPUT_IME_ON_DEACTIVATED,
128 input_ime::OnDeactivated::kEventName, args.Pass());
131 void OnFocus(
132 const InputMethodEngineInterface::InputContext& context) override {
133 if (extension_id_.empty() || !HasListener(input_ime::OnFocus::kEventName))
134 return;
136 input_ime::InputContext context_value;
137 context_value.context_id = context.id;
138 context_value.type = input_ime::ParseInputContextType(context.type);
139 context_value.auto_correct = context.auto_correct;
140 context_value.auto_complete = context.auto_complete;
141 context_value.spell_check = context.spell_check;
143 scoped_ptr<base::ListValue> args(input_ime::OnFocus::Create(context_value));
145 DispatchEventToExtension(extensions::events::INPUT_IME_ON_FOCUS,
146 input_ime::OnFocus::kEventName, args.Pass());
149 void OnBlur(int context_id) override {
150 if (extension_id_.empty() || !HasListener(input_ime::OnBlur::kEventName))
151 return;
153 scoped_ptr<base::ListValue> args(input_ime::OnBlur::Create(context_id));
155 DispatchEventToExtension(extensions::events::INPUT_IME_ON_BLUR,
156 input_ime::OnBlur::kEventName, args.Pass());
159 void OnInputContextUpdate(
160 const InputMethodEngineInterface::InputContext& context) override {
161 if (extension_id_.empty() ||
162 !HasListener(input_ime::OnInputContextUpdate::kEventName))
163 return;
165 input_ime::InputContext context_value;
166 context_value.context_id = context.id;
167 context_value.type = input_ime::ParseInputContextType(context.type);
169 scoped_ptr<base::ListValue> args(
170 input_ime::OnInputContextUpdate::Create(context_value));
172 DispatchEventToExtension(
173 extensions::events::INPUT_IME_ON_INPUT_CONTEXT_UPDATE,
174 input_ime::OnInputContextUpdate::kEventName, args.Pass());
177 bool IsInterestedInKeyEvent() const override {
178 return ShouldForwardKeyEvent();
181 void OnKeyEvent(const std::string& component_id,
182 const InputMethodEngineInterface::KeyboardEvent& event,
183 chromeos::input_method::KeyEventHandle* key_data) override {
184 if (extension_id_.empty())
185 return;
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
191 // still work.
192 CallbackKeyEventHandle(key_data, false);
193 return;
196 const std::string request_id =
197 GetInputImeEventRouter(profile_)->AddRequest(component_id, key_data);
199 input_ime::KeyboardEvent key_data_value;
200 key_data_value.type = input_ime::ParseKeyboardEventType(event.type);
201 key_data_value.request_id = request_id;
202 if (!event.extension_id.empty())
203 key_data_value.extension_id.reset(new std::string(event.extension_id));
204 key_data_value.key = event.key;
205 key_data_value.code = event.code;
206 key_data_value.alt_key.reset(new bool(event.alt_key));
207 key_data_value.ctrl_key.reset(new bool(event.ctrl_key));
208 key_data_value.shift_key.reset(new bool(event.shift_key));
209 key_data_value.caps_lock.reset(new bool(event.caps_lock));
211 scoped_ptr<base::ListValue> args(
212 input_ime::OnKeyEvent::Create(component_id, key_data_value));
214 DispatchEventToExtension(extensions::events::INPUT_IME_ON_KEY_EVENT,
215 input_ime::OnKeyEvent::kEventName, args.Pass());
218 void OnCandidateClicked(
219 const std::string& component_id,
220 int candidate_id,
221 InputMethodEngineInterface::MouseButtonEvent button) override {
222 if (extension_id_.empty() ||
223 !HasListener(input_ime::OnCandidateClicked::kEventName))
224 return;
226 input_ime::MouseButton button_enum = input_ime::MOUSE_BUTTON_NONE;
227 switch (button) {
228 case InputMethodEngineInterface::MOUSE_BUTTON_MIDDLE:
229 button_enum = input_ime::MOUSE_BUTTON_MIDDLE;
230 break;
232 case InputMethodEngineInterface::MOUSE_BUTTON_RIGHT:
233 button_enum = input_ime::MOUSE_BUTTON_RIGHT;
234 break;
236 case InputMethodEngineInterface::MOUSE_BUTTON_LEFT:
237 // Default to left.
238 default:
239 button_enum = input_ime::MOUSE_BUTTON_LEFT;
240 break;
243 scoped_ptr<base::ListValue> args(input_ime::OnCandidateClicked::Create(
244 component_id, candidate_id, button_enum));
246 DispatchEventToExtension(extensions::events::INPUT_IME_ON_CANDIDATE_CLICKED,
247 input_ime::OnCandidateClicked::kEventName,
248 args.Pass());
251 void OnMenuItemActivated(const std::string& component_id,
252 const std::string& menu_id) override {
253 if (extension_id_.empty() ||
254 !HasListener(input_ime::OnMenuItemActivated::kEventName))
255 return;
257 scoped_ptr<base::ListValue> args(
258 input_ime::OnMenuItemActivated::Create(component_id, menu_id));
260 DispatchEventToExtension(
261 extensions::events::INPUT_IME_ON_MENU_ITEM_ACTIVATED,
262 input_ime::OnMenuItemActivated::kEventName, args.Pass());
265 void OnSurroundingTextChanged(const std::string& component_id,
266 const std::string& text,
267 int cursor_pos,
268 int anchor_pos,
269 int offset_pos) override {
270 if (extension_id_.empty() ||
271 !HasListener(input_ime::OnSurroundingTextChanged::kEventName))
272 return;
274 input_ime::OnSurroundingTextChanged::SurroundingInfo info;
275 info.text = text;
276 info.focus = cursor_pos;
277 info.anchor = anchor_pos;
278 info.offset = offset_pos;
279 scoped_ptr<base::ListValue> args(
280 input_ime::OnSurroundingTextChanged::Create(component_id, info));
282 DispatchEventToExtension(
283 extensions::events::INPUT_IME_ON_SURROUNDING_TEXT_CHANGED,
284 input_ime::OnSurroundingTextChanged::kEventName, args.Pass());
287 void OnCompositionBoundsChanged(
288 const std::vector<gfx::Rect>& bounds) override {
289 if (extension_id_.empty() ||
290 !HasListener(kOnCompositionBoundsChangedEventName))
291 return;
293 // Note: this is a private API event.
294 base::ListValue* bounds_list = new base::ListValue();
295 for (size_t i = 0; i < bounds.size(); ++i) {
296 base::DictionaryValue* bounds_value = new base::DictionaryValue();
297 bounds_value->SetInteger("x", bounds[i].x());
298 bounds_value->SetInteger("y", bounds[i].y());
299 bounds_value->SetInteger("w", bounds[i].width());
300 bounds_value->SetInteger("h", bounds[i].height());
301 bounds_list->Append(bounds_value);
304 if (bounds_list->GetSize() <= 0)
305 return;
306 scoped_ptr<base::ListValue> args(new base::ListValue());
308 // The old extension code uses the first parameter to get the bounds of the
309 // first composition character, so for backward compatibility, add it here.
310 base::Value* first_value = NULL;
311 if (bounds_list->Get(0, &first_value))
312 args->Append(first_value->DeepCopy());
313 args->Append(bounds_list);
315 DispatchEventToExtension(
316 extensions::events::INPUT_METHOD_PRIVATE_ON_COMPOSITION_BOUNDS_CHANGED,
317 kOnCompositionBoundsChangedEventName, args.Pass());
320 void OnReset(const std::string& component_id) override {
321 if (extension_id_.empty() || !HasListener(input_ime::OnReset::kEventName))
322 return;
324 scoped_ptr<base::ListValue> args(input_ime::OnReset::Create(component_id));
326 DispatchEventToExtension(extensions::events::INPUT_IME_ON_RESET,
327 input_ime::OnReset::kEventName, args.Pass());
330 private:
331 void DispatchEventToExtension(
332 extensions::events::HistogramValue histogram_value,
333 const std::string& event_name,
334 scoped_ptr<base::ListValue> args) {
335 if (event_name != input_ime::OnActivate::kEventName) {
336 // For suspended IME extension (e.g. XKB extension), don't awake it by IME
337 // events except onActivate. The IME extension should be awake by other
338 // events (e.g. runtime.onMessage) from its other pages.
339 // This is to save memory for steady state Chrome OS on which the users
340 // don't want any IME features.
341 extensions::ExtensionSystem* extension_system =
342 extensions::ExtensionSystem::Get(profile_);
343 if (extension_system) {
344 const extensions::Extension* extension =
345 extension_system->extension_service()->GetExtensionById(
346 extension_id_, false /* include_disabled */);
347 if (!extension)
348 return;
349 extensions::ProcessManager* process_manager =
350 extensions::ProcessManager::Get(profile_);
351 if (extensions::BackgroundInfo::HasBackgroundPage(extension) &&
352 !process_manager->GetBackgroundHostForExtension(extension_id_)) {
353 return;
358 scoped_ptr<extensions::Event> event(
359 new extensions::Event(histogram_value, event_name, args.Pass()));
360 event->restrict_to_browser_context = profile_;
361 extensions::EventRouter::Get(profile_)
362 ->DispatchEventToExtension(extension_id_, event.Pass());
365 // Returns true if the extension is ready to accept key event, otherwise
366 // returns false.
367 bool ShouldForwardKeyEvent() const {
368 // Only forward key events to extension if there are non-lazy listeners
369 // for onKeyEvent. Because if something wrong with the lazy background
370 // page which doesn't register listener for onKeyEvent, it will not handle
371 // the key events, and therefore, all key events will be eaten.
372 // This is for error-tolerance, and it means that onKeyEvent will never wake
373 // up lazy background page.
374 const extensions::EventListenerMap::ListenerList& listener_list =
375 extensions::EventRouter::Get(profile_)
376 ->listeners()
377 .GetEventListenersByName(input_ime::OnKeyEvent::kEventName);
378 for (extensions::EventListenerMap::ListenerList::const_iterator it =
379 listener_list.begin();
380 it != listener_list.end(); ++it) {
381 if ((*it)->extension_id() == extension_id_ && !(*it)->IsLazy())
382 return true;
384 return false;
387 bool HasListener(const std::string& event_name) const {
388 return extensions::EventRouter::Get(profile_)->HasEventListener(event_name);
391 // The component IME extensions need to know the current screen type (e.g.
392 // lock screen, login screen, etc.) so that its on-screen keyboard page
393 // won't open new windows/pages. See crbug.com/395621.
394 std::string GetCurrentScreenType() {
395 switch (chromeos::input_method::InputMethodManager::Get()
396 ->GetUISessionState()) {
397 case chromeos::input_method::InputMethodManager::STATE_LOGIN_SCREEN:
398 return "login";
399 case chromeos::input_method::InputMethodManager::STATE_LOCK_SCREEN:
400 return "lock";
401 case chromeos::input_method::InputMethodManager::STATE_BROWSER_SCREEN:
402 return UserAddingScreen::Get()->IsRunning() ? "secondary-login"
403 : "normal";
404 case chromeos::input_method::InputMethodManager::STATE_TERMINATING:
405 return "normal";
407 NOTREACHED() << "New screen type is added. Please add new entry above.";
408 return "normal";
411 std::string extension_id_;
412 Profile* profile_;
414 DISALLOW_COPY_AND_ASSIGN(ImeObserver);
417 } // namespace chromeos
419 namespace extensions {
421 InputImeEventRouterFactory* InputImeEventRouterFactory::GetInstance() {
422 return base::Singleton<InputImeEventRouterFactory>::get();
425 InputImeEventRouterFactory::InputImeEventRouterFactory() {
428 InputImeEventRouterFactory::~InputImeEventRouterFactory() {
431 InputImeEventRouter* InputImeEventRouterFactory::GetRouter(Profile* profile) {
432 InputImeEventRouter* router = router_map_[profile];
433 if (!router) {
434 router = new InputImeEventRouter(profile);
435 router_map_[profile] = router;
437 return router;
440 InputImeEventRouter::InputImeEventRouter(Profile* profile)
441 : next_request_id_(1), profile_(profile) {
444 InputImeEventRouter::~InputImeEventRouter() {
447 bool InputImeEventRouter::RegisterImeExtension(
448 const std::string& extension_id,
449 const std::vector<extensions::InputComponentInfo>& input_components) {
450 VLOG(1) << "RegisterImeExtension: " << extension_id;
452 if (engine_map_[extension_id])
453 return false;
455 chromeos::input_method::InputMethodManager* manager =
456 chromeos::input_method::InputMethodManager::Get();
457 chromeos::ComponentExtensionIMEManager* comp_ext_ime_manager =
458 manager->GetComponentExtensionIMEManager();
460 chromeos::input_method::InputMethodDescriptors descriptors;
461 // Only creates descriptors for 3rd party IME extension, because the
462 // descriptors for component IME extensions are managed by InputMethodUtil.
463 if (!comp_ext_ime_manager->IsWhitelistedExtension(extension_id)) {
464 for (std::vector<extensions::InputComponentInfo>::const_iterator it =
465 input_components.begin();
466 it != input_components.end();
467 ++it) {
468 const extensions::InputComponentInfo& component = *it;
469 DCHECK(component.type == extensions::INPUT_COMPONENT_TYPE_IME);
471 std::vector<std::string> layouts;
472 layouts.assign(component.layouts.begin(), component.layouts.end());
473 std::vector<std::string> languages;
474 languages.assign(component.languages.begin(), component.languages.end());
476 const std::string& input_method_id =
477 chromeos::extension_ime_util::GetInputMethodID(extension_id,
478 component.id);
479 descriptors.push_back(chromeos::input_method::InputMethodDescriptor(
480 input_method_id,
481 component.name,
482 std::string(), // TODO(uekawa): Set short name.
483 layouts,
484 languages,
485 false, // 3rd party IMEs are always not for login.
486 component.options_page_url,
487 component.input_view_url));
491 scoped_ptr<chromeos::InputMethodEngineInterface::Observer> observer(
492 new chromeos::ImeObserver(extension_id, profile_));
493 chromeos::InputMethodEngine* engine = new chromeos::InputMethodEngine();
494 engine->Initialize(observer.Pass(), extension_id.c_str(), profile_);
495 engine_map_[extension_id] = engine;
496 chromeos::UserSessionManager::GetInstance()
497 ->GetDefaultIMEState(profile_)
498 ->AddInputMethodExtension(extension_id, descriptors, engine);
500 return true;
503 void InputImeEventRouter::UnregisterAllImes(const std::string& extension_id) {
504 std::map<std::string, InputMethodEngineInterface*>::iterator it =
505 engine_map_.find(extension_id);
506 if (it != engine_map_.end()) {
507 chromeos::input_method::InputMethodManager::Get()
508 ->GetActiveIMEState()
509 ->RemoveInputMethodExtension(extension_id);
510 delete it->second;
511 engine_map_.erase(it);
515 InputMethodEngineInterface* InputImeEventRouter::GetEngine(
516 const std::string& extension_id,
517 const std::string& component_id) {
518 std::map<std::string, InputMethodEngineInterface*>::iterator it =
519 engine_map_.find(extension_id);
520 if (it != engine_map_.end())
521 return it->second;
522 return NULL;
525 InputMethodEngineInterface* InputImeEventRouter::GetActiveEngine(
526 const std::string& extension_id) {
527 std::map<std::string, InputMethodEngineInterface*>::iterator it =
528 engine_map_.find(extension_id);
529 if (it != engine_map_.end() && it->second->IsActive())
530 return it->second;
531 return NULL;
534 void InputImeEventRouter::OnKeyEventHandled(
535 const std::string& extension_id,
536 const std::string& request_id,
537 bool handled) {
538 RequestMap::iterator request = request_map_.find(request_id);
539 if (request == request_map_.end()) {
540 LOG(ERROR) << "Request ID not found: " << request_id;
541 return;
544 std::string component_id = request->second.first;
545 chromeos::input_method::KeyEventHandle* key_data = request->second.second;
546 request_map_.erase(request);
548 CallbackKeyEventHandle(key_data, handled);
551 std::string InputImeEventRouter::AddRequest(
552 const std::string& component_id,
553 chromeos::input_method::KeyEventHandle* key_data) {
554 std::string request_id = base::IntToString(next_request_id_);
555 ++next_request_id_;
557 request_map_[request_id] = std::make_pair(component_id, key_data);
559 return request_id;
562 bool InputImeSetCompositionFunction::RunSync() {
563 InputMethodEngineInterface* engine =
564 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
565 ->GetActiveEngine(extension_id());
566 if (!engine) {
567 SetResult(new base::FundamentalValue(false));
568 return true;
571 scoped_ptr<SetComposition::Params> parent_params(
572 SetComposition::Params::Create(*args_));
573 const SetComposition::Params::Parameters& params = parent_params->parameters;
574 std::vector<InputMethodEngineInterface::SegmentInfo> segments;
575 if (params.segments) {
576 const std::vector<linked_ptr<
577 SetComposition::Params::Parameters::SegmentsType> >&
578 segments_args = *params.segments;
579 for (size_t i = 0; i < segments_args.size(); ++i) {
580 EXTENSION_FUNCTION_VALIDATE(
581 segments_args[i]->style !=
582 input_ime::UNDERLINE_STYLE_NONE);
583 segments.push_back(InputMethodEngineInterface::SegmentInfo());
584 segments.back().start = segments_args[i]->start;
585 segments.back().end = segments_args[i]->end;
586 if (segments_args[i]->style ==
587 input_ime::UNDERLINE_STYLE_UNDERLINE) {
588 segments.back().style =
589 InputMethodEngineInterface::SEGMENT_STYLE_UNDERLINE;
590 } else if (segments_args[i]->style ==
591 input_ime::UNDERLINE_STYLE_DOUBLEUNDERLINE) {
592 segments.back().style =
593 InputMethodEngineInterface::SEGMENT_STYLE_DOUBLE_UNDERLINE;
594 } else {
595 segments.back().style =
596 InputMethodEngineInterface::SEGMENT_STYLE_NO_UNDERLINE;
601 int selection_start =
602 params.selection_start ? *params.selection_start : params.cursor;
603 int selection_end =
604 params.selection_end ? *params.selection_end : params.cursor;
606 SetResult(new base::FundamentalValue(
607 engine->SetComposition(params.context_id, params.text.c_str(),
608 selection_start, selection_end, params.cursor,
609 segments, &error_)));
610 return true;
613 bool InputImeClearCompositionFunction::RunSync() {
614 InputMethodEngineInterface* engine =
615 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
616 ->GetActiveEngine(extension_id());
617 if (!engine) {
618 SetResult(new base::FundamentalValue(false));
619 return true;
622 scoped_ptr<ClearComposition::Params> parent_params(
623 ClearComposition::Params::Create(*args_));
624 const ClearComposition::Params::Parameters& params =
625 parent_params->parameters;
627 SetResult(new base::FundamentalValue(
628 engine->ClearComposition(params.context_id, &error_)));
629 return true;
632 bool InputImeCommitTextFunction::RunSync() {
633 InputMethodEngineInterface* engine =
634 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
635 ->GetActiveEngine(extension_id());
636 if (!engine) {
637 SetResult(new base::FundamentalValue(false));
638 return true;
641 scoped_ptr<CommitText::Params> parent_params(
642 CommitText::Params::Create(*args_));
643 const CommitText::Params::Parameters& params =
644 parent_params->parameters;
646 SetResult(new base::FundamentalValue(
647 engine->CommitText(params.context_id, params.text.c_str(), &error_)));
648 return true;
651 bool InputImeHideInputViewFunction::RunAsync() {
652 InputMethodEngineInterface* engine =
653 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
654 ->GetActiveEngine(extension_id());
655 if (!engine) {
656 return true;
658 engine->HideInputView();
659 return true;
662 bool InputImeSendKeyEventsFunction::RunAsync() {
663 scoped_ptr<SendKeyEvents::Params> parent_params(
664 SendKeyEvents::Params::Create(*args_));
665 const SendKeyEvents::Params::Parameters& params =
666 parent_params->parameters;
667 InputMethodEngineInterface* engine =
668 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
669 ->GetActiveEngine(extension_id());
670 if (!engine) {
671 error_ = kErrorEngineNotAvailable;
672 return false;
675 const std::vector<linked_ptr<input_ime::KeyboardEvent> >& key_data =
676 params.key_data;
677 std::vector<chromeos::InputMethodEngineInterface::KeyboardEvent> key_data_out;
679 for (size_t i = 0; i < key_data.size(); ++i) {
680 chromeos::InputMethodEngineInterface::KeyboardEvent event;
681 event.type = input_ime::ToString(key_data[i]->type);
682 event.key = key_data[i]->key;
683 event.code = key_data[i]->code;
684 event.key_code = key_data[i]->key_code.get() ? *(key_data[i]->key_code) : 0;
685 if (key_data[i]->alt_key)
686 event.alt_key = *(key_data[i]->alt_key);
687 if (key_data[i]->ctrl_key)
688 event.ctrl_key = *(key_data[i]->ctrl_key);
689 if (key_data[i]->shift_key)
690 event.shift_key = *(key_data[i]->shift_key);
691 if (key_data[i]->caps_lock)
692 event.caps_lock = *(key_data[i]->caps_lock);
693 key_data_out.push_back(event);
696 engine->SendKeyEvents(params.context_id, key_data_out);
697 return true;
700 bool InputImeSetCandidateWindowPropertiesFunction::RunSync() {
701 scoped_ptr<SetCandidateWindowProperties::Params> parent_params(
702 SetCandidateWindowProperties::Params::Create(*args_));
703 const SetCandidateWindowProperties::Params::Parameters&
704 params = parent_params->parameters;
706 InputMethodEngineInterface* engine =
707 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
708 ->GetEngine(extension_id(), params.engine_id);
709 if (!engine) {
710 SetResult(new base::FundamentalValue(false));
711 return true;
714 const SetCandidateWindowProperties::Params::Parameters::Properties&
715 properties = params.properties;
717 if (properties.visible &&
718 !engine->SetCandidateWindowVisible(*properties.visible, &error_)) {
719 SetResult(new base::FundamentalValue(false));
720 return true;
723 InputMethodEngineInterface::CandidateWindowProperty properties_out =
724 engine->GetCandidateWindowProperty();
725 bool modified = false;
727 if (properties.cursor_visible) {
728 properties_out.is_cursor_visible = *properties.cursor_visible;
729 modified = true;
732 if (properties.vertical) {
733 properties_out.is_vertical = *properties.vertical;
734 modified = true;
737 if (properties.page_size) {
738 properties_out.page_size = *properties.page_size;
739 modified = true;
742 if (properties.window_position == input_ime::WINDOW_POSITION_COMPOSITION) {
743 properties_out.show_window_at_composition = true;
744 modified = true;
745 } else if (properties.window_position == input_ime::WINDOW_POSITION_CURSOR) {
746 properties_out.show_window_at_composition = false;
747 modified = true;
750 if (properties.auxiliary_text) {
751 properties_out.auxiliary_text = *properties.auxiliary_text;
752 modified = true;
755 if (properties.auxiliary_text_visible) {
756 properties_out.is_auxiliary_text_visible =
757 *properties.auxiliary_text_visible;
758 modified = true;
761 if (modified) {
762 engine->SetCandidateWindowProperty(properties_out);
765 SetResult(new base::FundamentalValue(true));
767 return true;
770 bool InputImeSetCandidatesFunction::RunSync() {
771 InputMethodEngineInterface* engine =
772 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
773 ->GetActiveEngine(extension_id());
774 if (!engine) {
775 SetResult(new base::FundamentalValue(false));
776 return true;
779 scoped_ptr<SetCandidates::Params> parent_params(
780 SetCandidates::Params::Create(*args_));
781 const SetCandidates::Params::Parameters& params =
782 parent_params->parameters;
784 std::vector<InputMethodEngineInterface::Candidate> candidates_out;
785 const std::vector<linked_ptr<
786 SetCandidates::Params::Parameters::CandidatesType> >& candidates_in =
787 params.candidates;
788 for (size_t i = 0; i < candidates_in.size(); ++i) {
789 candidates_out.push_back(InputMethodEngineInterface::Candidate());
790 candidates_out.back().value = candidates_in[i]->candidate;
791 candidates_out.back().id = candidates_in[i]->id;
792 if (candidates_in[i]->label)
793 candidates_out.back().label = *candidates_in[i]->label;
794 if (candidates_in[i]->annotation)
795 candidates_out.back().annotation = *candidates_in[i]->annotation;
796 if (candidates_in[i]->usage) {
797 candidates_out.back().usage.title = candidates_in[i]->usage->title;
798 candidates_out.back().usage.body = candidates_in[i]->usage->body;
802 SetResult(new base::FundamentalValue(
803 engine->SetCandidates(params.context_id, candidates_out, &error_)));
804 return true;
807 bool InputImeSetCursorPositionFunction::RunSync() {
808 InputMethodEngineInterface* engine =
809 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
810 ->GetActiveEngine(extension_id());
811 if (!engine) {
812 SetResult(new base::FundamentalValue(false));
813 return true;
816 scoped_ptr<SetCursorPosition::Params> parent_params(
817 SetCursorPosition::Params::Create(*args_));
818 const SetCursorPosition::Params::Parameters& params =
819 parent_params->parameters;
821 SetResult(new base::FundamentalValue(
822 engine->SetCursorPosition(params.context_id, params.candidate_id,
823 &error_)));
824 return true;
827 bool InputImeSetMenuItemsFunction::RunSync() {
828 scoped_ptr<SetMenuItems::Params> parent_params(
829 SetMenuItems::Params::Create(*args_));
830 const SetMenuItems::Params::Parameters& params =
831 parent_params->parameters;
833 InputMethodEngineInterface* engine =
834 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
835 ->GetEngine(extension_id(), params.engine_id);
836 if (!engine) {
837 error_ = kErrorEngineNotAvailable;
838 return false;
841 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
842 std::vector<InputMethodEngineInterface::MenuItem> items_out;
844 for (size_t i = 0; i < items.size(); ++i) {
845 items_out.push_back(InputMethodEngineInterface::MenuItem());
846 SetMenuItemToMenu(*items[i], &items_out.back());
849 if (!engine->SetMenuItems(items_out))
850 error_ = kErrorSetMenuItemsFail;
851 return true;
854 bool InputImeUpdateMenuItemsFunction::RunSync() {
855 scoped_ptr<UpdateMenuItems::Params> parent_params(
856 UpdateMenuItems::Params::Create(*args_));
857 const UpdateMenuItems::Params::Parameters& params =
858 parent_params->parameters;
860 InputMethodEngineInterface* engine =
861 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
862 ->GetEngine(extension_id(), params.engine_id);
863 if (!engine) {
864 error_ = kErrorEngineNotAvailable;
865 return false;
868 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
869 std::vector<InputMethodEngineInterface::MenuItem> items_out;
871 for (size_t i = 0; i < items.size(); ++i) {
872 items_out.push_back(InputMethodEngineInterface::MenuItem());
873 SetMenuItemToMenu(*items[i], &items_out.back());
876 if (!engine->UpdateMenuItems(items_out))
877 error_ = kErrorUpdateMenuItemsFail;
878 return true;
881 bool InputImeDeleteSurroundingTextFunction::RunSync() {
882 scoped_ptr<DeleteSurroundingText::Params> parent_params(
883 DeleteSurroundingText::Params::Create(*args_));
884 const DeleteSurroundingText::Params::Parameters& params =
885 parent_params->parameters;
887 InputMethodEngineInterface* engine =
888 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
889 ->GetEngine(extension_id(), params.engine_id);
890 if (!engine) {
891 error_ = kErrorEngineNotAvailable;
892 return false;
895 engine->DeleteSurroundingText(params.context_id, params.offset, params.length,
896 &error_);
897 return true;
900 bool InputImeKeyEventHandledFunction::RunAsync() {
901 scoped_ptr<KeyEventHandled::Params> params(
902 KeyEventHandled::Params::Create(*args_));
903 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
904 ->OnKeyEventHandled(extension_id(), params->request_id, params->response);
905 return true;
908 InputImeAPI::InputImeAPI(content::BrowserContext* context)
909 : browser_context_(context), extension_registry_observer_(this) {
910 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
912 EventRouter* event_router = EventRouter::Get(browser_context_);
913 event_router->RegisterObserver(this, input_ime::OnFocus::kEventName);
916 InputImeAPI::~InputImeAPI() {
917 EventRouter::Get(browser_context_)->UnregisterObserver(this);
920 static base::LazyInstance<BrowserContextKeyedAPIFactory<InputImeAPI> >
921 g_factory = LAZY_INSTANCE_INITIALIZER;
923 // static
924 BrowserContextKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() {
925 return g_factory.Pointer();
928 void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
929 const Extension* extension) {
930 const std::vector<InputComponentInfo>* input_components =
931 extensions::InputComponents::GetInputComponents(extension);
932 if (input_components)
933 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context))
934 ->RegisterImeExtension(extension->id(), *input_components);
937 void InputImeAPI::OnExtensionUnloaded(content::BrowserContext* browser_context,
938 const Extension* extension,
939 UnloadedExtensionInfo::Reason reason) {
940 const std::vector<InputComponentInfo>* input_components =
941 extensions::InputComponents::GetInputComponents(extension);
942 if (!input_components)
943 return;
944 if (input_components->size() > 0) {
945 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context))
946 ->UnregisterAllImes(extension->id());
950 void InputImeAPI::OnListenerAdded(const EventListenerInfo& details) {
951 if (!details.browser_context)
952 return;
953 InputMethodEngineInterface* engine =
954 GetInputImeEventRouter(
955 Profile::FromBrowserContext(details.browser_context))
956 ->GetActiveEngine(details.extension_id);
957 // Notifies the IME extension for IME ready with onActivate/onFocus events.
958 if (engine)
959 engine->Enable(engine->GetActiveComponentId());
962 } // namespace extensions