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
;
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
) {
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
>(
69 out
->modified
|= InputMethodEngineInterface::MENU_ITEM_MODIFIED_VISIBLE
;
70 out
->visible
= input
.visible
? *input
.visible
: true;
73 out
->modified
|= InputMethodEngineInterface::MENU_ITEM_MODIFIED_CHECKED
;
74 out
->checked
= input
.checked
? *input
.checked
: false;
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
)) {
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
,
115 base::Callback
<void(bool consumed
)>* callback
=
116 reinterpret_cast<base::Callback
<void(bool consumed
)>*>(key_data
);
117 callback
->Run(handled
);
124 class ImeObserver
: public InputMethodEngineInterface::Observer
{
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
))
136 scoped_ptr
<base::ListValue
> args(input_ime::OnActivate::Create(
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
))
149 scoped_ptr
<base::ListValue
> args(
150 input_ime::OnDeactivated::Create(component_id
));
152 DispatchEventToExtension(
153 extension_id_
, input_ime::OnDeactivated::kEventName
, args
.Pass());
157 const InputMethodEngineInterface::InputContext
& context
) override
{
158 if (extension_id_
.empty() || !HasListener(input_ime::OnFocus::kEventName
))
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
))
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
))
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
,
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())
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
213 CallbackKeyEventHandle(key_data
, false);
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
,
245 InputMethodEngineInterface::MouseButtonEvent button
) override
{
246 if (extension_id_
.empty() ||
247 !HasListener(input_ime::OnCandidateClicked::kEventName
))
250 input_ime::MouseButton button_enum
= input_ime::MOUSE_BUTTON_NONE
;
252 case InputMethodEngineInterface::MOUSE_BUTTON_MIDDLE
:
253 button_enum
= input_ime::MOUSE_BUTTON_MIDDLE
;
256 case InputMethodEngineInterface::MOUSE_BUTTON_RIGHT
:
257 button_enum
= input_ime::MOUSE_BUTTON_RIGHT
;
260 case InputMethodEngineInterface::MOUSE_BUTTON_LEFT
:
263 button_enum
= input_ime::MOUSE_BUTTON_LEFT
;
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
))
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
,
290 int anchor_pos
) override
{
291 if (extension_id_
.empty() ||
292 !HasListener(input_ime::OnSurroundingTextChanged::kEventName
))
295 input_ime::OnSurroundingTextChanged::SurroundingInfo info
;
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
,
307 void OnCompositionBoundsChanged(
308 const std::vector
<gfx::Rect
>& bounds
) override
{
309 if (extension_id_
.empty() ||
310 !HasListener(kOnCompositionBoundsChangedEventName
))
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)
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
,
340 void OnReset(const std::string
& component_id
) override
{
341 if (extension_id_
.empty() || !HasListener(input_ime::OnReset::kEventName
))
344 scoped_ptr
<base::ListValue
> args(input_ime::OnReset::Create(component_id
));
346 DispatchEventToExtension(
347 extension_id_
, input_ime::OnReset::kEventName
, args
.Pass());
351 // Returns true if the extension is ready to accept key event, otherwise
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())
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
:
386 case chromeos::input_method::InputMethodManager::STATE_LOCK_SCREEN
:
388 case chromeos::input_method::InputMethodManager::STATE_BROWSER_SCREEN
:
389 return UserAddingScreen::Get()->IsRunning() ? "secondary-login"
391 case chromeos::input_method::InputMethodManager::STATE_TERMINATING
:
394 NOTREACHED() << "New screen type is added. Please add new entry above.";
398 std::string extension_id_
;
400 DISALLOW_COPY_AND_ASSIGN(ImeObserver
);
403 } // namespace chromeos
405 namespace extensions
{
408 InputImeEventRouter::GetInstance() {
409 return Singleton
<InputImeEventRouter
>::get();
412 bool InputImeEventRouter::RegisterImeExtension(
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
])
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();
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
,
445 descriptors
.push_back(chromeos::input_method::InputMethodDescriptor(
448 std::string(), // TODO(uekawa): Set short name.
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
);
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
);
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())
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())
500 void InputImeEventRouter::OnKeyEventHandled(
501 const std::string
& extension_id
,
502 const std::string
& request_id
,
504 RequestMap::iterator request
= request_map_
.find(request_id
);
505 if (request
== request_map_
.end()) {
506 LOG(ERROR
) << "Request ID not found: " << request_id
;
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_
);
523 request_map_
[request_id
] = std::make_pair(component_id
, key_data
);
528 InputImeEventRouter::InputImeEventRouter()
529 : next_request_id_(1) {
532 InputImeEventRouter::~InputImeEventRouter() {}
534 bool InputImeSetCompositionFunction::RunSync() {
535 InputMethodEngineInterface
* engine
=
536 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
538 SetResult(new base::FundamentalValue(false));
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
;
566 segments
.back().style
=
567 InputMethodEngineInterface::SEGMENT_STYLE_NO_UNDERLINE
;
572 int selection_start
=
573 params
.selection_start
? *params
.selection_start
: params
.cursor
;
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_
)));
584 bool InputImeClearCompositionFunction::RunSync() {
585 InputMethodEngineInterface
* engine
=
586 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
588 SetResult(new base::FundamentalValue(false));
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_
)));
602 bool InputImeCommitTextFunction::RunSync() {
603 // TODO(zork): Support committing when not active.
604 InputMethodEngineInterface
* engine
=
605 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
607 SetResult(new base::FundamentalValue(false));
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_
)));
621 bool InputImeHideInputViewFunction::RunAsync() {
622 InputMethodEngineInterface
* engine
=
623 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
627 engine
->HideInputView();
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());
639 error_
= kErrorEngineNotAvailable
;
643 const std::vector
<linked_ptr
<input_ime::KeyboardEvent
> >& 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
);
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(),
679 SetResult(new base::FundamentalValue(false));
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));
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
;
701 if (properties
.vertical
) {
702 properties_out
.is_vertical
= *properties
.vertical
;
706 if (properties
.page_size
) {
707 properties_out
.page_size
= *properties
.page_size
;
711 if (properties
.window_position
== input_ime::WINDOW_POSITION_COMPOSITION
) {
712 properties_out
.show_window_at_composition
= true;
714 } else if (properties
.window_position
== input_ime::WINDOW_POSITION_CURSOR
) {
715 properties_out
.show_window_at_composition
= false;
719 if (properties
.auxiliary_text
) {
720 properties_out
.auxiliary_text
= *properties
.auxiliary_text
;
724 if (properties
.auxiliary_text_visible
) {
725 properties_out
.is_auxiliary_text_visible
=
726 *properties
.auxiliary_text_visible
;
731 engine
->SetCandidateWindowProperty(properties_out
);
734 SetResult(new base::FundamentalValue(true));
739 bool InputImeSetCandidatesFunction::RunSync() {
740 InputMethodEngineInterface
* engine
=
741 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
743 SetResult(new base::FundamentalValue(false));
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
=
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_
)));
775 bool InputImeSetCursorPositionFunction::RunSync() {
776 InputMethodEngineInterface
* engine
=
777 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
779 SetResult(new base::FundamentalValue(false));
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
,
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(),
804 error_
= kErrorEngineNotAvailable
;
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
;
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(),
831 error_
= kErrorEngineNotAvailable
;
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
;
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(),
858 error_
= kErrorEngineNotAvailable
;
862 engine
->DeleteSurroundingText(params
.context_id
, params
.offset
, params
.length
,
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
);
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
;
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
),
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
)
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.
922 engine
->Enable(engine
->GetActiveComponentId());
925 InputImeEventRouter
* InputImeAPI::input_ime_event_router() {
926 return InputImeEventRouter::GetInstance();
929 } // namespace extensions