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 void CallbackKeyEventHandle(chromeos::input_method::KeyEventHandle
* key_data
,
83 base::Callback
<void(bool consumed
)>* callback
=
84 reinterpret_cast<base::Callback
<void(bool consumed
)>*>(key_data
);
85 callback
->Run(handled
);
89 extensions::InputImeEventRouter
* GetInputImeEventRouter(Profile
* profile
) {
90 if (profile
->HasOffTheRecordProfile())
91 profile
= profile
->GetOffTheRecordProfile();
92 return extensions::InputImeEventRouterFactory::GetInstance()->GetRouter(
99 class ImeObserver
: public InputMethodEngineInterface::Observer
{
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
))
111 scoped_ptr
<base::ListValue
> args(input_ime::OnActivate::Create(
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
))
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());
132 const InputMethodEngineInterface::InputContext
& context
) override
{
133 if (extension_id_
.empty() || !HasListener(input_ime::OnFocus::kEventName
))
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
))
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
))
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())
187 // If there is no listener for the event, no need to dispatch the event to
188 // extension. Instead, releases the key event for default system behavior.
189 if (!ShouldForwardKeyEvent()) {
190 // Continue processing the key event so that the physical keyboard can
192 CallbackKeyEventHandle(key_data
, false);
196 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
,
221 InputMethodEngineInterface::MouseButtonEvent button
) override
{
222 if (extension_id_
.empty() ||
223 !HasListener(input_ime::OnCandidateClicked::kEventName
))
226 input_ime::MouseButton button_enum
= input_ime::MOUSE_BUTTON_NONE
;
228 case InputMethodEngineInterface::MOUSE_BUTTON_MIDDLE
:
229 button_enum
= input_ime::MOUSE_BUTTON_MIDDLE
;
232 case InputMethodEngineInterface::MOUSE_BUTTON_RIGHT
:
233 button_enum
= input_ime::MOUSE_BUTTON_RIGHT
;
236 case InputMethodEngineInterface::MOUSE_BUTTON_LEFT
:
239 button_enum
= input_ime::MOUSE_BUTTON_LEFT
;
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
,
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
))
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
,
269 int offset_pos
) override
{
270 if (extension_id_
.empty() ||
271 !HasListener(input_ime::OnSurroundingTextChanged::kEventName
))
274 input_ime::OnSurroundingTextChanged::SurroundingInfo info
;
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
))
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)
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
))
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());
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 */);
349 extensions::ProcessManager
* process_manager
=
350 extensions::ProcessManager::Get(profile_
);
351 if (extensions::BackgroundInfo::HasBackgroundPage(extension
) &&
352 !process_manager
->GetBackgroundHostForExtension(extension_id_
)) {
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
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_
)
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())
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
:
399 case chromeos::input_method::InputMethodManager::STATE_LOCK_SCREEN
:
401 case chromeos::input_method::InputMethodManager::STATE_BROWSER_SCREEN
:
402 return UserAddingScreen::Get()->IsRunning() ? "secondary-login"
404 case chromeos::input_method::InputMethodManager::STATE_TERMINATING
:
407 NOTREACHED() << "New screen type is added. Please add new entry above.";
411 std::string extension_id_
;
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
];
434 router
= new InputImeEventRouter(profile
);
435 router_map_
[profile
] = 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
])
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();
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
,
479 descriptors
.push_back(chromeos::input_method::InputMethodDescriptor(
482 std::string(), // TODO(uekawa): Set short name.
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
);
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
);
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())
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())
534 void InputImeEventRouter::OnKeyEventHandled(
535 const std::string
& extension_id
,
536 const std::string
& request_id
,
538 RequestMap::iterator request
= request_map_
.find(request_id
);
539 if (request
== request_map_
.end()) {
540 LOG(ERROR
) << "Request ID not found: " << request_id
;
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_
);
557 request_map_
[request_id
] = std::make_pair(component_id
, key_data
);
562 bool InputImeSetCompositionFunction::RunSync() {
563 InputMethodEngineInterface
* engine
=
564 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
565 ->GetActiveEngine(extension_id());
567 SetResult(new base::FundamentalValue(false));
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
;
595 segments
.back().style
=
596 InputMethodEngineInterface::SEGMENT_STYLE_NO_UNDERLINE
;
601 int selection_start
=
602 params
.selection_start
? *params
.selection_start
: params
.cursor
;
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_
)));
613 bool InputImeClearCompositionFunction::RunSync() {
614 InputMethodEngineInterface
* engine
=
615 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
616 ->GetActiveEngine(extension_id());
618 SetResult(new base::FundamentalValue(false));
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_
)));
632 bool InputImeCommitTextFunction::RunSync() {
633 InputMethodEngineInterface
* engine
=
634 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
635 ->GetActiveEngine(extension_id());
637 SetResult(new base::FundamentalValue(false));
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_
)));
651 bool InputImeHideInputViewFunction::RunAsync() {
652 InputMethodEngineInterface
* engine
=
653 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
654 ->GetActiveEngine(extension_id());
658 engine
->HideInputView();
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());
671 error_
= kErrorEngineNotAvailable
;
675 const std::vector
<linked_ptr
<input_ime::KeyboardEvent
> >& 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
);
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
);
710 SetResult(new base::FundamentalValue(false));
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));
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
;
732 if (properties
.vertical
) {
733 properties_out
.is_vertical
= *properties
.vertical
;
737 if (properties
.page_size
) {
738 properties_out
.page_size
= *properties
.page_size
;
742 if (properties
.window_position
== input_ime::WINDOW_POSITION_COMPOSITION
) {
743 properties_out
.show_window_at_composition
= true;
745 } else if (properties
.window_position
== input_ime::WINDOW_POSITION_CURSOR
) {
746 properties_out
.show_window_at_composition
= false;
750 if (properties
.auxiliary_text
) {
751 properties_out
.auxiliary_text
= *properties
.auxiliary_text
;
755 if (properties
.auxiliary_text_visible
) {
756 properties_out
.is_auxiliary_text_visible
=
757 *properties
.auxiliary_text_visible
;
762 engine
->SetCandidateWindowProperty(properties_out
);
765 SetResult(new base::FundamentalValue(true));
770 bool InputImeSetCandidatesFunction::RunSync() {
771 InputMethodEngineInterface
* engine
=
772 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
773 ->GetActiveEngine(extension_id());
775 SetResult(new base::FundamentalValue(false));
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
=
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_
)));
807 bool InputImeSetCursorPositionFunction::RunSync() {
808 InputMethodEngineInterface
* engine
=
809 GetInputImeEventRouter(Profile::FromBrowserContext(browser_context()))
810 ->GetActiveEngine(extension_id());
812 SetResult(new base::FundamentalValue(false));
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
,
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
);
837 error_
= kErrorEngineNotAvailable
;
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
;
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
);
864 error_
= kErrorEngineNotAvailable
;
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
;
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
);
891 error_
= kErrorEngineNotAvailable
;
895 engine
->DeleteSurroundingText(params
.context_id
, params
.offset
, params
.length
,
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
);
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
;
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
)
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
)
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.
959 engine
->Enable(engine
->GetActiveComponentId());
962 } // namespace extensions