Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / extensions / api / input_ime / input_ime_api.cc
blob77d6fb52c641c375512140237d9ba4e6ce962472
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 static void DispatchEventToExtension(const std::string& extension_id,
82 const std::string& event_name,
83 scoped_ptr<base::ListValue> args) {
84 Profile* profile = ProfileManager::GetActiveUserProfile();
85 if (event_name != input_ime::OnActivate::kEventName) {
86 // For suspended IME extension (e.g. XKB extension), don't awake it by IME
87 // events except onActivate. The IME extension should be awake by other
88 // events (e.g. runtime.onMessage) from its other pages.
89 // This is to save memory for steady state Chrome OS on which the users
90 // don't want any IME features.
91 extensions::ExtensionSystem* extension_system =
92 extensions::ExtensionSystem::Get(profile);
93 if (extension_system) {
94 const extensions::Extension* extension =
95 extension_system->extension_service()->GetExtensionById(
96 extension_id, false /* include_disabled */);
97 extensions::ProcessManager* process_manager =
98 extensions::ProcessManager::Get(profile);
99 if (extensions::BackgroundInfo::HasBackgroundPage(extension) &&
100 !process_manager->GetBackgroundHostForExtension(extension_id)) {
101 return;
106 scoped_ptr<extensions::Event> event(new extensions::Event(
107 event_name, args.Pass()));
108 event->restrict_to_browser_context = profile;
109 extensions::EventRouter::Get(profile)
110 ->DispatchEventToExtension(extension_id, event.Pass());
113 void CallbackKeyEventHandle(chromeos::input_method::KeyEventHandle* key_data,
114 bool handled) {
115 base::Callback<void(bool consumed)>* callback =
116 reinterpret_cast<base::Callback<void(bool consumed)>*>(key_data);
117 callback->Run(handled);
118 delete callback;
121 } // namespace
123 namespace chromeos {
124 class ImeObserver : public InputMethodEngineInterface::Observer {
125 public:
126 explicit ImeObserver(const std::string& extension_id)
127 : extension_id_(extension_id) {}
129 ~ImeObserver() override {}
131 void OnActivate(const std::string& component_id) override {
132 if (extension_id_.empty() ||
133 !HasListener(input_ime::OnActivate::kEventName))
134 return;
136 scoped_ptr<base::ListValue> args(input_ime::OnActivate::Create(
137 component_id,
138 input_ime::ParseScreenType(GetCurrentScreenType())));
140 DispatchEventToExtension(
141 extension_id_, input_ime::OnActivate::kEventName, args.Pass());
144 void OnDeactivated(const std::string& component_id) override {
145 if (extension_id_.empty() ||
146 !HasListener(input_ime::OnDeactivated::kEventName))
147 return;
149 scoped_ptr<base::ListValue> args(
150 input_ime::OnDeactivated::Create(component_id));
152 DispatchEventToExtension(
153 extension_id_, input_ime::OnDeactivated::kEventName, args.Pass());
156 void OnFocus(
157 const InputMethodEngineInterface::InputContext& context) override {
158 if (extension_id_.empty() || !HasListener(input_ime::OnFocus::kEventName))
159 return;
161 input_ime::InputContext context_value;
162 context_value.context_id = context.id;
163 context_value.type = input_ime::ParseInputContextType(context.type);
164 context_value.auto_correct = context.auto_correct;
165 context_value.auto_complete = context.auto_complete;
166 context_value.spell_check = context.spell_check;
168 scoped_ptr<base::ListValue> args(input_ime::OnFocus::Create(context_value));
170 DispatchEventToExtension(
171 extension_id_, input_ime::OnFocus::kEventName, args.Pass());
174 void OnBlur(int context_id) override {
175 if (extension_id_.empty() || !HasListener(input_ime::OnBlur::kEventName))
176 return;
178 scoped_ptr<base::ListValue> args(input_ime::OnBlur::Create(context_id));
180 DispatchEventToExtension(
181 extension_id_, input_ime::OnBlur::kEventName, args.Pass());
184 void OnInputContextUpdate(
185 const InputMethodEngineInterface::InputContext& context) override {
186 if (extension_id_.empty() ||
187 !HasListener(input_ime::OnInputContextUpdate::kEventName))
188 return;
190 input_ime::InputContext context_value;
191 context_value.context_id = context.id;
192 context_value.type = input_ime::ParseInputContextType(context.type);
194 scoped_ptr<base::ListValue> args(
195 input_ime::OnInputContextUpdate::Create(context_value));
197 DispatchEventToExtension(extension_id_,
198 input_ime::OnInputContextUpdate::kEventName,
199 args.Pass());
202 void OnKeyEvent(const std::string& component_id,
203 const InputMethodEngineInterface::KeyboardEvent& event,
204 chromeos::input_method::KeyEventHandle* key_data) override {
205 if (extension_id_.empty())
206 return;
208 // If there is no listener for the event, no need to dispatch the event to
209 // extension. Instead, releases the key event for default system behavior.
210 if (!ShouldForwardKeyEvent()) {
211 // Continue processing the key event so that the physical keyboard can
212 // still work.
213 CallbackKeyEventHandle(key_data, false);
214 return;
217 extensions::InputImeEventRouter* ime_event_router =
218 extensions::InputImeEventRouter::GetInstance();
220 const std::string request_id =
221 ime_event_router->AddRequest(component_id, key_data);
223 input_ime::KeyboardEvent key_data_value;
224 key_data_value.type = input_ime::ParseKeyboardEventType(event.type);
225 key_data_value.request_id = request_id;
226 if (!event.extension_id.empty())
227 key_data_value.extension_id.reset(new std::string(event.extension_id));
228 key_data_value.key = event.key;
229 key_data_value.code = event.code;
230 key_data_value.alt_key.reset(new bool(event.alt_key));
231 key_data_value.ctrl_key.reset(new bool(event.ctrl_key));
232 key_data_value.shift_key.reset(new bool(event.shift_key));
233 key_data_value.caps_lock.reset(new bool(event.caps_lock));
235 scoped_ptr<base::ListValue> args(
236 input_ime::OnKeyEvent::Create(component_id, key_data_value));
238 DispatchEventToExtension(
239 extension_id_, input_ime::OnKeyEvent::kEventName, args.Pass());
242 void OnCandidateClicked(
243 const std::string& component_id,
244 int candidate_id,
245 InputMethodEngineInterface::MouseButtonEvent button) override {
246 if (extension_id_.empty() ||
247 !HasListener(input_ime::OnCandidateClicked::kEventName))
248 return;
250 input_ime::MouseButton button_enum = input_ime::MOUSE_BUTTON_NONE;
251 switch (button) {
252 case InputMethodEngineInterface::MOUSE_BUTTON_MIDDLE:
253 button_enum = input_ime::MOUSE_BUTTON_MIDDLE;
254 break;
256 case InputMethodEngineInterface::MOUSE_BUTTON_RIGHT:
257 button_enum = input_ime::MOUSE_BUTTON_RIGHT;
258 break;
260 case InputMethodEngineInterface::MOUSE_BUTTON_LEFT:
261 // Default to left.
262 default:
263 button_enum = input_ime::MOUSE_BUTTON_LEFT;
264 break;
267 scoped_ptr<base::ListValue> args(input_ime::OnCandidateClicked::Create(
268 component_id, candidate_id, button_enum));
270 DispatchEventToExtension(
271 extension_id_, input_ime::OnCandidateClicked::kEventName, args.Pass());
274 void OnMenuItemActivated(const std::string& component_id,
275 const std::string& menu_id) override {
276 if (extension_id_.empty() ||
277 !HasListener(input_ime::OnMenuItemActivated::kEventName))
278 return;
280 scoped_ptr<base::ListValue> args(
281 input_ime::OnMenuItemActivated::Create(component_id, menu_id));
283 DispatchEventToExtension(
284 extension_id_, input_ime::OnMenuItemActivated::kEventName, args.Pass());
287 void OnSurroundingTextChanged(const std::string& component_id,
288 const std::string& text,
289 int cursor_pos,
290 int anchor_pos) override {
291 if (extension_id_.empty() ||
292 !HasListener(input_ime::OnSurroundingTextChanged::kEventName))
293 return;
295 input_ime::OnSurroundingTextChanged::SurroundingInfo info;
296 info.text = text;
297 info.focus = cursor_pos;
298 info.anchor = anchor_pos;
299 scoped_ptr<base::ListValue> args(
300 input_ime::OnSurroundingTextChanged::Create(component_id, info));
302 DispatchEventToExtension(extension_id_,
303 input_ime::OnSurroundingTextChanged::kEventName,
304 args.Pass());
307 void OnCompositionBoundsChanged(
308 const std::vector<gfx::Rect>& bounds) override {
309 if (extension_id_.empty() ||
310 !HasListener(kOnCompositionBoundsChangedEventName))
311 return;
313 // Note: this is a private API event.
314 base::ListValue* bounds_list = new base::ListValue();
315 for (size_t i = 0; i < bounds.size(); ++i) {
316 base::DictionaryValue* bounds_value = new base::DictionaryValue();
317 bounds_value->SetInteger("x", bounds[i].x());
318 bounds_value->SetInteger("y", bounds[i].y());
319 bounds_value->SetInteger("w", bounds[i].width());
320 bounds_value->SetInteger("h", bounds[i].height());
321 bounds_list->Append(bounds_value);
324 if (bounds_list->GetSize() <= 0)
325 return;
326 scoped_ptr<base::ListValue> args(new base::ListValue());
328 // The old extension code uses the first parameter to get the bounds of the
329 // first composition character, so for backward compatibility, add it here.
330 base::Value* first_value = NULL;
331 if (bounds_list->Get(0, &first_value))
332 args->Append(first_value->DeepCopy());
333 args->Append(bounds_list);
335 DispatchEventToExtension(extension_id_,
336 kOnCompositionBoundsChangedEventName,
337 args.Pass());
340 void OnReset(const std::string& component_id) override {
341 if (extension_id_.empty() || !HasListener(input_ime::OnReset::kEventName))
342 return;
344 scoped_ptr<base::ListValue> args(input_ime::OnReset::Create(component_id));
346 DispatchEventToExtension(
347 extension_id_, input_ime::OnReset::kEventName, args.Pass());
350 private:
351 // Returns true if the extension is ready to accept key event, otherwise
352 // returns false.
353 bool ShouldForwardKeyEvent() const {
354 // Only forward key events to extension if there are non-lazy listeners
355 // for onKeyEvent. Because if something wrong with the lazy background
356 // page which doesn't register listener for onKeyEvent, it will not handle
357 // the key events, and therefore, all key events will be eaten.
358 // This is for error-tolerance, and it means that onKeyEvent will never wake
359 // up lazy background page.
360 const extensions::EventListenerMap::ListenerList& listener_list =
361 extensions::EventRouter::Get(ProfileManager::GetActiveUserProfile())
362 ->listeners().GetEventListenersByName(
363 input_ime::OnKeyEvent::kEventName);
364 for (extensions::EventListenerMap::ListenerList::const_iterator it =
365 listener_list.begin();
366 it != listener_list.end(); ++it) {
367 if ((*it)->extension_id() == extension_id_ && !(*it)->IsLazy())
368 return true;
370 return false;
373 bool HasListener(const std::string& event_name) const {
374 return extensions::EventRouter::Get(
375 ProfileManager::GetActiveUserProfile())->HasEventListener(event_name);
378 // The component IME extensions need to know the current screen type (e.g.
379 // lock screen, login screen, etc.) so that its on-screen keyboard page
380 // won't open new windows/pages. See crbug.com/395621.
381 std::string GetCurrentScreenType() {
382 switch (chromeos::input_method::InputMethodManager::Get()
383 ->GetUISessionState()) {
384 case chromeos::input_method::InputMethodManager::STATE_LOGIN_SCREEN:
385 return "login";
386 case chromeos::input_method::InputMethodManager::STATE_LOCK_SCREEN:
387 return "lock";
388 case chromeos::input_method::InputMethodManager::STATE_BROWSER_SCREEN:
389 return UserAddingScreen::Get()->IsRunning() ? "secondary-login"
390 : "normal";
391 case chromeos::input_method::InputMethodManager::STATE_TERMINATING:
392 return "normal";
394 NOTREACHED() << "New screen type is added. Please add new entry above.";
395 return "normal";
398 std::string extension_id_;
400 DISALLOW_COPY_AND_ASSIGN(ImeObserver);
403 } // namespace chromeos
405 namespace extensions {
407 InputImeEventRouter*
408 InputImeEventRouter::GetInstance() {
409 return Singleton<InputImeEventRouter>::get();
412 bool InputImeEventRouter::RegisterImeExtension(
413 Profile* profile,
414 const std::string& extension_id,
415 const std::vector<extensions::InputComponentInfo>& input_components) {
416 VLOG(1) << "RegisterImeExtension: " << extension_id;
418 if (engine_map_[extension_id])
419 return false;
421 chromeos::input_method::InputMethodManager* manager =
422 chromeos::input_method::InputMethodManager::Get();
423 chromeos::ComponentExtensionIMEManager* comp_ext_ime_manager =
424 manager->GetComponentExtensionIMEManager();
426 chromeos::input_method::InputMethodDescriptors descriptors;
427 // Only creates descriptors for 3rd party IME extension, because the
428 // descriptors for component IME extensions are managed by InputMethodUtil.
429 if (!comp_ext_ime_manager->IsWhitelistedExtension(extension_id)) {
430 for (std::vector<extensions::InputComponentInfo>::const_iterator it =
431 input_components.begin();
432 it != input_components.end();
433 ++it) {
434 const extensions::InputComponentInfo& component = *it;
435 DCHECK(component.type == extensions::INPUT_COMPONENT_TYPE_IME);
437 std::vector<std::string> layouts;
438 layouts.assign(component.layouts.begin(), component.layouts.end());
439 std::vector<std::string> languages;
440 languages.assign(component.languages.begin(), component.languages.end());
442 const std::string& input_method_id =
443 chromeos::extension_ime_util::GetInputMethodID(extension_id,
444 component.id);
445 descriptors.push_back(chromeos::input_method::InputMethodDescriptor(
446 input_method_id,
447 component.name,
448 std::string(), // TODO(uekawa): Set short name.
449 layouts,
450 languages,
451 false, // 3rd party IMEs are always not for login.
452 component.options_page_url,
453 component.input_view_url));
457 scoped_ptr<chromeos::InputMethodEngineInterface::Observer> observer(
458 new chromeos::ImeObserver(extension_id));
459 chromeos::InputMethodEngine* engine = new chromeos::InputMethodEngine();
460 engine->Initialize(observer.Pass(), extension_id.c_str());
461 engine_map_[extension_id] = engine;
462 chromeos::UserSessionManager::GetInstance()
463 ->GetDefaultIMEState(profile)
464 ->AddInputMethodExtension(extension_id, descriptors, engine);
466 return true;
469 void InputImeEventRouter::UnregisterAllImes(const std::string& extension_id) {
470 std::map<std::string, InputMethodEngineInterface*>::iterator it =
471 engine_map_.find(extension_id);
472 if (it != engine_map_.end()) {
473 chromeos::input_method::InputMethodManager::Get()
474 ->GetActiveIMEState()
475 ->RemoveInputMethodExtension(extension_id);
476 delete it->second;
477 engine_map_.erase(it);
481 InputMethodEngineInterface* InputImeEventRouter::GetEngine(
482 const std::string& extension_id,
483 const std::string& component_id) {
484 std::map<std::string, InputMethodEngineInterface*>::iterator it =
485 engine_map_.find(extension_id);
486 if (it != engine_map_.end())
487 return it->second;
488 return NULL;
491 InputMethodEngineInterface* InputImeEventRouter::GetActiveEngine(
492 const std::string& extension_id) {
493 std::map<std::string, InputMethodEngineInterface*>::iterator it =
494 engine_map_.find(extension_id);
495 if (it != engine_map_.end() && it->second->IsActive())
496 return it->second;
497 return NULL;
500 void InputImeEventRouter::OnKeyEventHandled(
501 const std::string& extension_id,
502 const std::string& request_id,
503 bool handled) {
504 RequestMap::iterator request = request_map_.find(request_id);
505 if (request == request_map_.end()) {
506 LOG(ERROR) << "Request ID not found: " << request_id;
507 return;
510 std::string component_id = request->second.first;
511 chromeos::input_method::KeyEventHandle* key_data = request->second.second;
512 request_map_.erase(request);
514 CallbackKeyEventHandle(key_data, handled);
517 std::string InputImeEventRouter::AddRequest(
518 const std::string& component_id,
519 chromeos::input_method::KeyEventHandle* key_data) {
520 std::string request_id = base::IntToString(next_request_id_);
521 ++next_request_id_;
523 request_map_[request_id] = std::make_pair(component_id, key_data);
525 return request_id;
528 InputImeEventRouter::InputImeEventRouter()
529 : next_request_id_(1) {
532 InputImeEventRouter::~InputImeEventRouter() {}
534 bool InputImeSetCompositionFunction::RunSync() {
535 InputMethodEngineInterface* engine =
536 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
537 if (!engine) {
538 SetResult(new base::FundamentalValue(false));
539 return true;
542 scoped_ptr<SetComposition::Params> parent_params(
543 SetComposition::Params::Create(*args_));
544 const SetComposition::Params::Parameters& params = parent_params->parameters;
545 std::vector<InputMethodEngineInterface::SegmentInfo> segments;
546 if (params.segments) {
547 const std::vector<linked_ptr<
548 SetComposition::Params::Parameters::SegmentsType> >&
549 segments_args = *params.segments;
550 for (size_t i = 0; i < segments_args.size(); ++i) {
551 EXTENSION_FUNCTION_VALIDATE(
552 segments_args[i]->style !=
553 input_ime::UNDERLINE_STYLE_NONE);
554 segments.push_back(InputMethodEngineInterface::SegmentInfo());
555 segments.back().start = segments_args[i]->start;
556 segments.back().end = segments_args[i]->end;
557 if (segments_args[i]->style ==
558 input_ime::UNDERLINE_STYLE_UNDERLINE) {
559 segments.back().style =
560 InputMethodEngineInterface::SEGMENT_STYLE_UNDERLINE;
561 } else if (segments_args[i]->style ==
562 input_ime::UNDERLINE_STYLE_DOUBLEUNDERLINE) {
563 segments.back().style =
564 InputMethodEngineInterface::SEGMENT_STYLE_DOUBLE_UNDERLINE;
565 } else {
566 segments.back().style =
567 InputMethodEngineInterface::SEGMENT_STYLE_NO_UNDERLINE;
572 int selection_start =
573 params.selection_start ? *params.selection_start : params.cursor;
574 int selection_end =
575 params.selection_end ? *params.selection_end : params.cursor;
577 SetResult(new base::FundamentalValue(
578 engine->SetComposition(params.context_id, params.text.c_str(),
579 selection_start, selection_end, params.cursor,
580 segments, &error_)));
581 return true;
584 bool InputImeClearCompositionFunction::RunSync() {
585 InputMethodEngineInterface* engine =
586 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
587 if (!engine) {
588 SetResult(new base::FundamentalValue(false));
589 return true;
592 scoped_ptr<ClearComposition::Params> parent_params(
593 ClearComposition::Params::Create(*args_));
594 const ClearComposition::Params::Parameters& params =
595 parent_params->parameters;
597 SetResult(new base::FundamentalValue(
598 engine->ClearComposition(params.context_id, &error_)));
599 return true;
602 bool InputImeCommitTextFunction::RunSync() {
603 // TODO(zork): Support committing when not active.
604 InputMethodEngineInterface* engine =
605 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
606 if (!engine) {
607 SetResult(new base::FundamentalValue(false));
608 return true;
611 scoped_ptr<CommitText::Params> parent_params(
612 CommitText::Params::Create(*args_));
613 const CommitText::Params::Parameters& params =
614 parent_params->parameters;
616 SetResult(new base::FundamentalValue(
617 engine->CommitText(params.context_id, params.text.c_str(), &error_)));
618 return true;
621 bool InputImeHideInputViewFunction::RunAsync() {
622 InputMethodEngineInterface* engine =
623 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
624 if (!engine) {
625 return true;
627 engine->HideInputView();
628 return true;
631 bool InputImeSendKeyEventsFunction::RunAsync() {
632 scoped_ptr<SendKeyEvents::Params> parent_params(
633 SendKeyEvents::Params::Create(*args_));
634 const SendKeyEvents::Params::Parameters& params =
635 parent_params->parameters;
636 chromeos::InputMethodEngineInterface* engine =
637 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
638 if (!engine) {
639 error_ = kErrorEngineNotAvailable;
640 return false;
643 const std::vector<linked_ptr<input_ime::KeyboardEvent> >& key_data =
644 params.key_data;
645 std::vector<chromeos::InputMethodEngineInterface::KeyboardEvent> key_data_out;
647 for (size_t i = 0; i < key_data.size(); ++i) {
648 chromeos::InputMethodEngineInterface::KeyboardEvent event;
649 event.type = input_ime::ToString(key_data[i]->type);
650 event.key = key_data[i]->key;
651 event.code = key_data[i]->code;
652 event.key_code = key_data[i]->key_code.get() ? *(key_data[i]->key_code) : 0;
653 if (key_data[i]->alt_key)
654 event.alt_key = *(key_data[i]->alt_key);
655 if (key_data[i]->ctrl_key)
656 event.ctrl_key = *(key_data[i]->ctrl_key);
657 if (key_data[i]->shift_key)
658 event.shift_key = *(key_data[i]->shift_key);
659 if (key_data[i]->caps_lock)
660 event.caps_lock = *(key_data[i]->caps_lock);
661 key_data_out.push_back(event);
664 engine->SendKeyEvents(params.context_id, key_data_out);
665 return true;
668 bool InputImeSetCandidateWindowPropertiesFunction::RunSync() {
669 scoped_ptr<SetCandidateWindowProperties::Params> parent_params(
670 SetCandidateWindowProperties::Params::Create(*args_));
671 const SetCandidateWindowProperties::Params::Parameters&
672 params = parent_params->parameters;
674 InputMethodEngineInterface* engine =
675 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
676 params.engine_id);
678 if (!engine) {
679 SetResult(new base::FundamentalValue(false));
680 return true;
683 const SetCandidateWindowProperties::Params::Parameters::Properties&
684 properties = params.properties;
686 if (properties.visible &&
687 !engine->SetCandidateWindowVisible(*properties.visible, &error_)) {
688 SetResult(new base::FundamentalValue(false));
689 return true;
692 InputMethodEngineInterface::CandidateWindowProperty properties_out =
693 engine->GetCandidateWindowProperty();
694 bool modified = false;
696 if (properties.cursor_visible) {
697 properties_out.is_cursor_visible = *properties.cursor_visible;
698 modified = true;
701 if (properties.vertical) {
702 properties_out.is_vertical = *properties.vertical;
703 modified = true;
706 if (properties.page_size) {
707 properties_out.page_size = *properties.page_size;
708 modified = true;
711 if (properties.window_position == input_ime::WINDOW_POSITION_COMPOSITION) {
712 properties_out.show_window_at_composition = true;
713 modified = true;
714 } else if (properties.window_position == input_ime::WINDOW_POSITION_CURSOR) {
715 properties_out.show_window_at_composition = false;
716 modified = true;
719 if (properties.auxiliary_text) {
720 properties_out.auxiliary_text = *properties.auxiliary_text;
721 modified = true;
724 if (properties.auxiliary_text_visible) {
725 properties_out.is_auxiliary_text_visible =
726 *properties.auxiliary_text_visible;
727 modified = true;
730 if (modified) {
731 engine->SetCandidateWindowProperty(properties_out);
734 SetResult(new base::FundamentalValue(true));
736 return true;
739 bool InputImeSetCandidatesFunction::RunSync() {
740 InputMethodEngineInterface* engine =
741 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
742 if (!engine) {
743 SetResult(new base::FundamentalValue(false));
744 return true;
747 scoped_ptr<SetCandidates::Params> parent_params(
748 SetCandidates::Params::Create(*args_));
749 const SetCandidates::Params::Parameters& params =
750 parent_params->parameters;
752 std::vector<InputMethodEngineInterface::Candidate> candidates_out;
753 const std::vector<linked_ptr<
754 SetCandidates::Params::Parameters::CandidatesType> >& candidates_in =
755 params.candidates;
756 for (size_t i = 0; i < candidates_in.size(); ++i) {
757 candidates_out.push_back(InputMethodEngineInterface::Candidate());
758 candidates_out.back().value = candidates_in[i]->candidate;
759 candidates_out.back().id = candidates_in[i]->id;
760 if (candidates_in[i]->label)
761 candidates_out.back().label = *candidates_in[i]->label;
762 if (candidates_in[i]->annotation)
763 candidates_out.back().annotation = *candidates_in[i]->annotation;
764 if (candidates_in[i]->usage) {
765 candidates_out.back().usage.title = candidates_in[i]->usage->title;
766 candidates_out.back().usage.body = candidates_in[i]->usage->body;
770 SetResult(new base::FundamentalValue(
771 engine->SetCandidates(params.context_id, candidates_out, &error_)));
772 return true;
775 bool InputImeSetCursorPositionFunction::RunSync() {
776 InputMethodEngineInterface* engine =
777 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
778 if (!engine) {
779 SetResult(new base::FundamentalValue(false));
780 return true;
783 scoped_ptr<SetCursorPosition::Params> parent_params(
784 SetCursorPosition::Params::Create(*args_));
785 const SetCursorPosition::Params::Parameters& params =
786 parent_params->parameters;
788 SetResult(new base::FundamentalValue(
789 engine->SetCursorPosition(params.context_id, params.candidate_id,
790 &error_)));
791 return true;
794 bool InputImeSetMenuItemsFunction::RunSync() {
795 scoped_ptr<SetMenuItems::Params> parent_params(
796 SetMenuItems::Params::Create(*args_));
797 const SetMenuItems::Params::Parameters& params =
798 parent_params->parameters;
800 InputMethodEngineInterface* engine =
801 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
802 params.engine_id);
803 if (!engine) {
804 error_ = kErrorEngineNotAvailable;
805 return false;
808 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
809 std::vector<InputMethodEngineInterface::MenuItem> items_out;
811 for (size_t i = 0; i < items.size(); ++i) {
812 items_out.push_back(InputMethodEngineInterface::MenuItem());
813 SetMenuItemToMenu(*items[i], &items_out.back());
816 if (!engine->SetMenuItems(items_out))
817 error_ = kErrorSetMenuItemsFail;
818 return true;
821 bool InputImeUpdateMenuItemsFunction::RunSync() {
822 scoped_ptr<UpdateMenuItems::Params> parent_params(
823 UpdateMenuItems::Params::Create(*args_));
824 const UpdateMenuItems::Params::Parameters& params =
825 parent_params->parameters;
827 InputMethodEngineInterface* engine =
828 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
829 params.engine_id);
830 if (!engine) {
831 error_ = kErrorEngineNotAvailable;
832 return false;
835 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
836 std::vector<InputMethodEngineInterface::MenuItem> items_out;
838 for (size_t i = 0; i < items.size(); ++i) {
839 items_out.push_back(InputMethodEngineInterface::MenuItem());
840 SetMenuItemToMenu(*items[i], &items_out.back());
843 if (!engine->UpdateMenuItems(items_out))
844 error_ = kErrorUpdateMenuItemsFail;
845 return true;
848 bool InputImeDeleteSurroundingTextFunction::RunSync() {
849 scoped_ptr<DeleteSurroundingText::Params> parent_params(
850 DeleteSurroundingText::Params::Create(*args_));
851 const DeleteSurroundingText::Params::Parameters& params =
852 parent_params->parameters;
854 InputMethodEngineInterface* engine =
855 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
856 params.engine_id);
857 if (!engine) {
858 error_ = kErrorEngineNotAvailable;
859 return false;
862 engine->DeleteSurroundingText(params.context_id, params.offset, params.length,
863 &error_);
864 return true;
867 bool InputImeKeyEventHandledFunction::RunAsync() {
868 scoped_ptr<KeyEventHandled::Params> params(
869 KeyEventHandled::Params::Create(*args_));
870 InputImeEventRouter::GetInstance()->OnKeyEventHandled(
871 extension_id(), params->request_id, params->response);
872 return true;
875 InputImeAPI::InputImeAPI(content::BrowserContext* context)
876 : browser_context_(context), extension_registry_observer_(this) {
877 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
879 EventRouter* event_router = EventRouter::Get(browser_context_);
880 event_router->RegisterObserver(this, input_ime::OnFocus::kEventName);
883 InputImeAPI::~InputImeAPI() {
884 EventRouter::Get(browser_context_)->UnregisterObserver(this);
887 static base::LazyInstance<BrowserContextKeyedAPIFactory<InputImeAPI> >
888 g_factory = LAZY_INSTANCE_INITIALIZER;
890 // static
891 BrowserContextKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() {
892 return g_factory.Pointer();
895 void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
896 const Extension* extension) {
897 const std::vector<InputComponentInfo>* input_components =
898 extensions::InputComponents::GetInputComponents(extension);
899 if (input_components)
900 input_ime_event_router()->RegisterImeExtension(
901 Profile::FromBrowserContext(browser_context),
902 extension->id(),
903 *input_components);
906 void InputImeAPI::OnExtensionUnloaded(content::BrowserContext* browser_context,
907 const Extension* extension,
908 UnloadedExtensionInfo::Reason reason) {
909 const std::vector<InputComponentInfo>* input_components =
910 extensions::InputComponents::GetInputComponents(extension);
911 if (!input_components)
912 return;
913 if (input_components->size() > 0)
914 input_ime_event_router()->UnregisterAllImes(extension->id());
917 void InputImeAPI::OnListenerAdded(const EventListenerInfo& details) {
918 InputMethodEngineInterface* engine =
919 input_ime_event_router()->GetActiveEngine(details.extension_id);
920 // Notifies the IME extension for IME ready with onActivate/onFocus events.
921 if (engine)
922 engine->Enable(engine->GetActiveComponentId());
925 InputImeEventRouter* InputImeAPI::input_ime_event_router() {
926 return InputImeEventRouter::GetInstance();
929 } // namespace extensions