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.
4
5 #include "chrome/browser/extensions/api/input_ime/input_ime_api.h"
6
7 #include "base/strings/string_number_conversions.h"
8 #include "base/values.h"
9 #include "chrome/browser/chromeos/profiles/profile_helper.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/profiles/profile_manager.h"
12 #include "chrome/common/extensions/api/input_ime.h"
13 #include "chrome/common/extensions/api/input_ime/input_components_handler.h"
14 #include "extensions/browser/event_router.h"
15 #include "extensions/browser/extension_function_registry.h"
16 #include "extensions/browser/extension_registry.h"
17 #include "extensions/browser/extension_system.h"
18 #include "extensions/common/manifest_handlers/background_info.h"
19
20 #if defined(USE_X11)
21 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
22 #endif
23
24 namespace input_ime = extensions::api::input_ime;
25 namespace KeyEventHandled = extensions::api::input_ime::KeyEventHandled;
26 namespace DeleteSurroundingText =
27 extensions::api::input_ime::DeleteSurroundingText;
28 namespace UpdateMenuItems = extensions::api::input_ime::UpdateMenuItems;
29 namespace SendKeyEvents = extensions::api::input_ime::SendKeyEvents;
30 namespace HideInputView = extensions::api::input_ime::HideInputView;
31 namespace SetMenuItems = extensions::api::input_ime::SetMenuItems;
32 namespace SetCursorPosition = extensions::api::input_ime::SetCursorPosition;
33 namespace SetCandidates = extensions::api::input_ime::SetCandidates;
34 namespace SetCandidateWindowProperties =
35 extensions::api::input_ime::SetCandidateWindowProperties;
36 namespace CommitText = extensions::api::input_ime::CommitText;
37 namespace ClearComposition = extensions::api::input_ime::ClearComposition;
38 namespace SetComposition = extensions::api::input_ime::SetComposition;
39 using chromeos::InputMethodEngineInterface;
40
41 namespace {
42
43 const char kErrorEngineNotAvailable[] = "Engine is not available";
44 const char kErrorSetMenuItemsFail[] = "Could not create menu Items";
45 const char kErrorUpdateMenuItemsFail[] = "Could not update menu Items";
46
SetMenuItemToMenu(const input_ime::MenuItem & input,InputMethodEngineInterface::MenuItem * out)47 void SetMenuItemToMenu(const input_ime::MenuItem& input,
48 InputMethodEngineInterface::MenuItem* out) {
49 out->modified = 0;
50 out->id = input.id;
51 if (input.label) {
52 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_LABEL;
53 out->label = *input.label;
54 }
55
56 if (input.style != input_ime::MenuItem::STYLE_NONE) {
57 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_STYLE;
58 out->style = static_cast<InputMethodEngineInterface::MenuItemStyle>(
59 input.style);
60 }
61
62 if (input.visible)
63 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_VISIBLE;
64 out->visible = input.visible ? *input.visible : true;
65
66 if (input.checked)
67 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_CHECKED;
68 out->checked = input.checked ? *input.checked : false;
69
70 if (input.enabled)
71 out->modified |= InputMethodEngineInterface::MENU_ITEM_MODIFIED_ENABLED;
72 out->enabled = input.enabled ? *input.enabled : true;
73 }
74
DispatchEventToExtension(Profile * profile,const std::string & extension_id,const std::string & event_name,scoped_ptr<base::ListValue> args)75 static void DispatchEventToExtension(Profile* profile,
76 const std::string& extension_id,
77 const std::string& event_name,
78 scoped_ptr<base::ListValue> args) {
79 scoped_ptr<extensions::Event> event(new extensions::Event(
80 event_name, args.Pass()));
81 event->restrict_to_browser_context = profile;
82 extensions::EventRouter::Get(profile)
83 ->DispatchEventToExtension(extension_id, event.Pass());
84 }
85
CallbackKeyEventHandle(chromeos::input_method::KeyEventHandle * key_data,bool handled)86 void CallbackKeyEventHandle(chromeos::input_method::KeyEventHandle* key_data,
87 bool handled) {
88 base::Callback<void(bool consumed)>* callback =
89 reinterpret_cast<base::Callback<void(bool consumed)>*>(key_data);
90 callback->Run(handled);
91 delete callback;
92 }
93
94 } // namespace
95
96 namespace chromeos {
97 class ImeObserver : public InputMethodEngineInterface::Observer {
98 public:
ImeObserver(Profile * profile,const std::string & extension_id)99 ImeObserver(Profile* profile, const std::string& extension_id)
100 : profile_(profile), extension_id_(extension_id), has_background_(false) {
101 extensions::ExtensionSystem* extension_system =
102 extensions::ExtensionSystem::Get(profile_);
103 ExtensionService* extension_service = extension_system->extension_service();
104 const extensions::Extension* extension =
105 extension_service->GetExtensionById(extension_id, false);
106 DCHECK(extension);
107 extensions::BackgroundInfo* info = static_cast<extensions::BackgroundInfo*>(
108 extension->GetManifestData("background"));
109 if (info)
110 has_background_ = info->has_background_page();
111 }
112
~ImeObserver()113 virtual ~ImeObserver() {}
114
OnActivate(const std::string & engine_id)115 virtual void OnActivate(const std::string& engine_id) OVERRIDE {
116 if (profile_ == NULL || extension_id_.empty())
117 return;
118
119 scoped_ptr<base::ListValue> args(input_ime::OnActivate::Create(engine_id));
120
121 DispatchEventToExtension(profile_, extension_id_,
122 input_ime::OnActivate::kEventName, args.Pass());
123 }
124
OnDeactivated(const std::string & engine_id)125 virtual void OnDeactivated(const std::string& engine_id) OVERRIDE {
126 if (profile_ == NULL || extension_id_.empty())
127 return;
128
129 scoped_ptr<base::ListValue> args(
130 input_ime::OnDeactivated::Create(engine_id));
131
132 DispatchEventToExtension(profile_, extension_id_,
133 input_ime::OnDeactivated::kEventName, args.Pass());
134 }
135
OnFocus(const InputMethodEngineInterface::InputContext & context)136 virtual void OnFocus(
137 const InputMethodEngineInterface::InputContext& context) OVERRIDE {
138 if (profile_ == NULL || extension_id_.empty())
139 return;
140
141 input_ime::InputContext context_value;
142 context_value.context_id = context.id;
143 context_value.type = input_ime::InputContext::ParseType(context.type);
144
145 scoped_ptr<base::ListValue> args(input_ime::OnFocus::Create(context_value));
146
147 DispatchEventToExtension(profile_, extension_id_,
148 input_ime::OnFocus::kEventName, args.Pass());
149 }
150
OnBlur(int context_id)151 virtual void OnBlur(int context_id) OVERRIDE {
152 if (profile_ == NULL || extension_id_.empty())
153 return;
154
155 scoped_ptr<base::ListValue> args(input_ime::OnBlur::Create(context_id));
156
157 DispatchEventToExtension(profile_, extension_id_,
158 input_ime::OnBlur::kEventName, args.Pass());
159 }
160
OnInputContextUpdate(const InputMethodEngineInterface::InputContext & context)161 virtual void OnInputContextUpdate(
162 const InputMethodEngineInterface::InputContext& context) OVERRIDE {
163 if (profile_ == NULL || extension_id_.empty())
164 return;
165
166 input_ime::InputContext context_value;
167 context_value.context_id = context.id;
168 context_value.type = input_ime::InputContext::ParseType(context.type);
169
170 scoped_ptr<base::ListValue> args(
171 input_ime::OnInputContextUpdate::Create(context_value));
172
173 DispatchEventToExtension(profile_,
174 extension_id_,
175 input_ime::OnInputContextUpdate::kEventName,
176 args.Pass());
177 }
178
OnKeyEvent(const std::string & engine_id,const InputMethodEngineInterface::KeyboardEvent & event,chromeos::input_method::KeyEventHandle * key_data)179 virtual void OnKeyEvent(
180 const std::string& engine_id,
181 const InputMethodEngineInterface::KeyboardEvent& event,
182 chromeos::input_method::KeyEventHandle* key_data) OVERRIDE {
183 if (profile_ == NULL || extension_id_.empty())
184 return;
185
186 // If there is no listener for the event, no need to dispatch the event to
187 // extension. Instead, releases the key event for default system behavior.
188 if (!ShouldForwardKeyEvent()) {
189 // Continue processing the key event so that the physical keyboard can
190 // still work.
191 CallbackKeyEventHandle(key_data, false);
192 return;
193 }
194
195 extensions::InputImeEventRouter* ime_event_router =
196 extensions::InputImeEventRouter::GetInstance();
197
198 const std::string request_id =
199 ime_event_router->AddRequest(engine_id, key_data);
200
201 input_ime::KeyboardEvent key_data_value;
202 key_data_value.type = input_ime::KeyboardEvent::ParseType(event.type);
203 key_data_value.request_id = request_id;
204 if (!event.extension_id.empty())
205 key_data_value.extension_id.reset(new std::string(event.extension_id));
206 key_data_value.key = event.key;
207 key_data_value.code = event.code;
208 key_data_value.alt_key.reset(new bool(event.alt_key));
209 key_data_value.ctrl_key.reset(new bool(event.ctrl_key));
210 key_data_value.shift_key.reset(new bool(event.shift_key));
211 key_data_value.caps_lock.reset(new bool(event.caps_lock));
212
213 scoped_ptr<base::ListValue> args(
214 input_ime::OnKeyEvent::Create(engine_id, key_data_value));
215
216 DispatchEventToExtension(profile_, extension_id_,
217 input_ime::OnKeyEvent::kEventName, args.Pass());
218 }
219
OnCandidateClicked(const std::string & engine_id,int candidate_id,InputMethodEngineInterface::MouseButtonEvent button)220 virtual void OnCandidateClicked(
221 const std::string& engine_id,
222 int candidate_id,
223 InputMethodEngineInterface::MouseButtonEvent button) OVERRIDE {
224 if (profile_ == NULL || extension_id_.empty())
225 return;
226
227 input_ime::OnCandidateClicked::Button button_enum =
228 input_ime::OnCandidateClicked::BUTTON_NONE;
229 switch (button) {
230 case InputMethodEngineInterface::MOUSE_BUTTON_MIDDLE:
231 button_enum = input_ime::OnCandidateClicked::BUTTON_MIDDLE;
232 break;
233
234 case InputMethodEngineInterface::MOUSE_BUTTON_RIGHT:
235 button_enum = input_ime::OnCandidateClicked::BUTTON_RIGHT;
236 break;
237
238 case InputMethodEngineInterface::MOUSE_BUTTON_LEFT:
239 // Default to left.
240 default:
241 button_enum = input_ime::OnCandidateClicked::BUTTON_LEFT;
242 break;
243 }
244
245 scoped_ptr<base::ListValue> args(
246 input_ime::OnCandidateClicked::Create(engine_id,
247 candidate_id,
248 button_enum));
249
250 DispatchEventToExtension(profile_,
251 extension_id_,
252 input_ime::OnCandidateClicked::kEventName,
253 args.Pass());
254 }
255
OnMenuItemActivated(const std::string & engine_id,const std::string & menu_id)256 virtual void OnMenuItemActivated(const std::string& engine_id,
257 const std::string& menu_id) OVERRIDE {
258 if (profile_ == NULL || extension_id_.empty())
259 return;
260
261 scoped_ptr<base::ListValue> args(
262 input_ime::OnMenuItemActivated::Create(engine_id, menu_id));
263
264 DispatchEventToExtension(profile_,
265 extension_id_,
266 input_ime::OnMenuItemActivated::kEventName,
267 args.Pass());
268 }
269
OnSurroundingTextChanged(const std::string & engine_id,const std::string & text,int cursor_pos,int anchor_pos)270 virtual void OnSurroundingTextChanged(const std::string& engine_id,
271 const std::string& text,
272 int cursor_pos,
273 int anchor_pos) OVERRIDE {
274 if (profile_ == NULL || extension_id_.empty())
275 return;
276
277 input_ime::OnSurroundingTextChanged::SurroundingInfo info;
278 info.text = text;
279 info.focus = cursor_pos;
280 info.anchor = anchor_pos;
281 scoped_ptr<base::ListValue> args(
282 input_ime::OnSurroundingTextChanged::Create(engine_id, info));
283
284 DispatchEventToExtension(profile_,
285 extension_id_,
286 input_ime::OnSurroundingTextChanged::kEventName,
287 args.Pass());
288 }
289
OnReset(const std::string & engine_id)290 virtual void OnReset(const std::string& engine_id) OVERRIDE {
291 if (profile_ == NULL || extension_id_.empty())
292 return;
293
294 scoped_ptr<base::ListValue> args(input_ime::OnReset::Create(engine_id));
295
296 DispatchEventToExtension(profile_,
297 extension_id_,
298 input_ime::OnReset::kEventName,
299 args.Pass());
300 }
301
302 private:
303 // Returns true if the extension is ready to accept key event, otherwise
304 // returns false.
ShouldForwardKeyEvent() const305 bool ShouldForwardKeyEvent() const {
306 // Need to check the background page first since the
307 // ExtensionHasEventListner returns true if the extension does not have a
308 // background page. See crbug.com/394682.
309 return has_background_ && extensions::EventRouter::Get(profile_)
310 ->ExtensionHasEventListener(extension_id_,
311 input_ime::OnKeyEvent::kEventName);
312 }
313
314 Profile* profile_;
315 std::string extension_id_;
316 bool has_background_;
317
318 DISALLOW_COPY_AND_ASSIGN(ImeObserver);
319 };
320
321 } // namespace chromeos
322
323 namespace extensions {
324
325 InputImeEventRouter*
GetInstance()326 InputImeEventRouter::GetInstance() {
327 return Singleton<InputImeEventRouter>::get();
328 }
329
RegisterIme(Profile * profile,const std::string & extension_id,const extensions::InputComponentInfo & component)330 bool InputImeEventRouter::RegisterIme(
331 Profile* profile,
332 const std::string& extension_id,
333 const extensions::InputComponentInfo& component) {
334 #if defined(USE_X11)
335 VLOG(1) << "RegisterIme: " << extension_id << " id: " << component.id;
336
337 std::vector<std::string> layouts;
338 layouts.assign(component.layouts.begin(), component.layouts.end());
339
340 std::vector<std::string> languages;
341 languages.assign(component.languages.begin(), component.languages.end());
342
343 // Ideally Observer should be per (extension_id + Profile), and multiple
344 // InputMethodEngine can share one Observer. But it would become tricky
345 // to maintain an internal map for observers which does nearly nothing
346 // but just make sure they can properly deleted.
347 // Making Obesrver per InputMethodEngine can make things cleaner.
348 scoped_ptr<chromeos::InputMethodEngineInterface::Observer> observer(
349 new chromeos::ImeObserver(profile, extension_id));
350 chromeos::InputMethodEngine* engine = new chromeos::InputMethodEngine();
351 engine->Initialize(profile,
352 observer.Pass(),
353 component.name.c_str(),
354 extension_id.c_str(),
355 component.id.c_str(),
356 languages,
357 layouts,
358 component.options_page_url,
359 component.input_view_url);
360 profile_engine_map_[profile][extension_id][component.id] = engine;
361
362 return true;
363 #else
364 // TODO(spang): IME support under ozone.
365 NOTIMPLEMENTED();
366 return false;
367 #endif
368 }
369
UnregisterAllImes(const std::string & extension_id)370 void InputImeEventRouter::UnregisterAllImes(const std::string& extension_id) {
371 Profile* profile = ProfileManager::GetActiveUserProfile();
372 ProfileEngineMap::iterator extension_map =
373 profile_engine_map_.find(profile);
374 if (extension_map == profile_engine_map_.end())
375 return;
376 ExtensionMap::iterator engine_map = extension_map->second.find(extension_id);
377 if (engine_map == extension_map->second.end())
378 return;
379 STLDeleteContainerPairSecondPointers(engine_map->second.begin(),
380 engine_map->second.end());
381 extension_map->second.erase(extension_id);
382 profile_engine_map_.erase(profile);
383 }
384
GetEngine(const std::string & extension_id,const std::string & engine_id)385 InputMethodEngineInterface* InputImeEventRouter::GetEngine(
386 const std::string& extension_id, const std::string& engine_id) {
387 // IME can only work on active user profile.
388 Profile* profile = ProfileManager::GetActiveUserProfile();
389
390 ProfileEngineMap::const_iterator extension_map =
391 profile_engine_map_.find(profile);
392 if (extension_map == profile_engine_map_.end())
393 return NULL;
394 ExtensionMap::const_iterator engine_map =
395 extension_map->second.find(extension_id);
396 if (engine_map == extension_map->second.end())
397 return NULL;
398 EngineMap::const_iterator engine = engine_map->second.find(engine_id);
399 if (engine == engine_map->second.end())
400 return NULL;
401 return engine->second;
402 }
403
GetActiveEngine(const std::string & extension_id)404 InputMethodEngineInterface* InputImeEventRouter::GetActiveEngine(
405 const std::string& extension_id) {
406 // IME can only work on active user profile.
407 Profile* profile = ProfileManager::GetActiveUserProfile();
408
409 ProfileEngineMap::const_iterator extension_map =
410 profile_engine_map_.find(profile);
411 if (extension_map == profile_engine_map_.end())
412 return NULL;
413 ExtensionMap::const_iterator engine_map =
414 extension_map->second.find(extension_id);
415 if (engine_map == extension_map->second.end())
416 return NULL;
417
418 for (EngineMap::const_iterator i = engine_map->second.begin();
419 i != engine_map->second.end();
420 ++i) {
421 if (i->second->IsActive())
422 return i->second;
423 }
424 return NULL;
425 }
426
OnKeyEventHandled(const std::string & extension_id,const std::string & request_id,bool handled)427 void InputImeEventRouter::OnKeyEventHandled(
428 const std::string& extension_id,
429 const std::string& request_id,
430 bool handled) {
431 RequestMap::iterator request = request_map_.find(request_id);
432 if (request == request_map_.end()) {
433 LOG(ERROR) << "Request ID not found: " << request_id;
434 return;
435 }
436
437 std::string engine_id = request->second.first;
438 chromeos::input_method::KeyEventHandle* key_data = request->second.second;
439 request_map_.erase(request);
440
441 CallbackKeyEventHandle(key_data, handled);
442 }
443
AddRequest(const std::string & engine_id,chromeos::input_method::KeyEventHandle * key_data)444 std::string InputImeEventRouter::AddRequest(
445 const std::string& engine_id,
446 chromeos::input_method::KeyEventHandle* key_data) {
447 std::string request_id = base::IntToString(next_request_id_);
448 ++next_request_id_;
449
450 request_map_[request_id] = std::make_pair(engine_id, key_data);
451
452 return request_id;
453 }
454
InputImeEventRouter()455 InputImeEventRouter::InputImeEventRouter()
456 : next_request_id_(1) {
457 }
458
~InputImeEventRouter()459 InputImeEventRouter::~InputImeEventRouter() {}
460
RunSync()461 bool InputImeSetCompositionFunction::RunSync() {
462 InputMethodEngineInterface* engine =
463 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
464 if (!engine) {
465 SetResult(new base::FundamentalValue(false));
466 return true;
467 }
468
469 scoped_ptr<SetComposition::Params> parent_params(
470 SetComposition::Params::Create(*args_));
471 const SetComposition::Params::Parameters& params = parent_params->parameters;
472 std::vector<InputMethodEngineInterface::SegmentInfo> segments;
473 if (params.segments) {
474 const std::vector<linked_ptr<
475 SetComposition::Params::Parameters::SegmentsType> >&
476 segments_args = *params.segments;
477 for (size_t i = 0; i < segments_args.size(); ++i) {
478 EXTENSION_FUNCTION_VALIDATE(
479 segments_args[i]->style !=
480 SetComposition::Params::Parameters::SegmentsType::STYLE_NONE);
481 segments.push_back(InputMethodEngineInterface::SegmentInfo());
482 segments.back().start = segments_args[i]->start;
483 segments.back().end = segments_args[i]->end;
484 if (segments_args[i]->style ==
485 SetComposition::Params::Parameters::SegmentsType::STYLE_UNDERLINE) {
486 segments.back().style =
487 InputMethodEngineInterface::SEGMENT_STYLE_UNDERLINE;
488 } else {
489 segments.back().style =
490 InputMethodEngineInterface::SEGMENT_STYLE_DOUBLE_UNDERLINE;
491 }
492 }
493 }
494
495 int selection_start =
496 params.selection_start ? *params.selection_start : params.cursor;
497 int selection_end =
498 params.selection_end ? *params.selection_end : params.cursor;
499
500 SetResult(new base::FundamentalValue(
501 engine->SetComposition(params.context_id, params.text.c_str(),
502 selection_start, selection_end, params.cursor,
503 segments, &error_)));
504 return true;
505 }
506
RunSync()507 bool InputImeClearCompositionFunction::RunSync() {
508 InputMethodEngineInterface* engine =
509 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
510 if (!engine) {
511 SetResult(new base::FundamentalValue(false));
512 return true;
513 }
514
515 scoped_ptr<ClearComposition::Params> parent_params(
516 ClearComposition::Params::Create(*args_));
517 const ClearComposition::Params::Parameters& params =
518 parent_params->parameters;
519
520 SetResult(new base::FundamentalValue(
521 engine->ClearComposition(params.context_id, &error_)));
522 return true;
523 }
524
RunSync()525 bool InputImeCommitTextFunction::RunSync() {
526 // TODO(zork): Support committing when not active.
527 InputMethodEngineInterface* engine =
528 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
529 if (!engine) {
530 SetResult(new base::FundamentalValue(false));
531 return true;
532 }
533
534 scoped_ptr<CommitText::Params> parent_params(
535 CommitText::Params::Create(*args_));
536 const CommitText::Params::Parameters& params =
537 parent_params->parameters;
538
539 SetResult(new base::FundamentalValue(
540 engine->CommitText(params.context_id, params.text.c_str(), &error_)));
541 return true;
542 }
543
RunAsync()544 bool InputImeHideInputViewFunction::RunAsync() {
545 InputMethodEngineInterface* engine =
546 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
547 if (!engine) {
548 return true;
549 }
550 engine->HideInputView();
551 return true;
552 }
553
RunAsync()554 bool InputImeSendKeyEventsFunction::RunAsync() {
555 scoped_ptr<SendKeyEvents::Params> parent_params(
556 SendKeyEvents::Params::Create(*args_));
557 const SendKeyEvents::Params::Parameters& params =
558 parent_params->parameters;
559 chromeos::InputMethodEngineInterface* engine =
560 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
561 if (!engine) {
562 error_ = kErrorEngineNotAvailable;
563 return false;
564 }
565
566 const std::vector<linked_ptr<input_ime::KeyboardEvent> >& key_data =
567 params.key_data;
568 std::vector<chromeos::InputMethodEngineInterface::KeyboardEvent> key_data_out;
569
570 for (size_t i = 0; i < key_data.size(); ++i) {
571 chromeos::InputMethodEngineInterface::KeyboardEvent event;
572 event.type = input_ime::KeyboardEvent::ToString(key_data[i]->type);
573 event.key = key_data[i]->key;
574 event.code = key_data[i]->code;
575 event.key_code = key_data[i]->key_code.get() ? *(key_data[i]->key_code) : 0;
576 if (key_data[i]->alt_key)
577 event.alt_key = *(key_data[i]->alt_key);
578 if (key_data[i]->ctrl_key)
579 event.ctrl_key = *(key_data[i]->ctrl_key);
580 if (key_data[i]->shift_key)
581 event.shift_key = *(key_data[i]->shift_key);
582 if (key_data[i]->caps_lock)
583 event.caps_lock = *(key_data[i]->caps_lock);
584 key_data_out.push_back(event);
585 }
586
587 engine->SendKeyEvents(params.context_id, key_data_out);
588 return true;
589 }
590
RunSync()591 bool InputImeSetCandidateWindowPropertiesFunction::RunSync() {
592 scoped_ptr<SetCandidateWindowProperties::Params> parent_params(
593 SetCandidateWindowProperties::Params::Create(*args_));
594 const SetCandidateWindowProperties::Params::Parameters&
595 params = parent_params->parameters;
596
597 InputMethodEngineInterface* engine =
598 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
599 params.engine_id);
600
601 if (!engine) {
602 SetResult(new base::FundamentalValue(false));
603 return true;
604 }
605
606 const SetCandidateWindowProperties::Params::Parameters::Properties&
607 properties = params.properties;
608
609 if (properties.visible &&
610 !engine->SetCandidateWindowVisible(*properties.visible, &error_)) {
611 SetResult(new base::FundamentalValue(false));
612 return true;
613 }
614
615 InputMethodEngineInterface::CandidateWindowProperty properties_out =
616 engine->GetCandidateWindowProperty();
617 bool modified = false;
618
619 if (properties.cursor_visible) {
620 properties_out.is_cursor_visible = *properties.cursor_visible;
621 modified = true;
622 }
623
624 if (properties.vertical) {
625 properties_out.is_vertical = *properties.vertical;
626 modified = true;
627 }
628
629 if (properties.page_size) {
630 properties_out.page_size = *properties.page_size;
631 modified = true;
632 }
633
634 if (properties.window_position ==
635 SetCandidateWindowProperties::Params::Parameters::Properties::
636 WINDOW_POSITION_COMPOSITION) {
637 properties_out.show_window_at_composition = true;
638 modified = true;
639 } else if (properties.window_position ==
640 SetCandidateWindowProperties::Params::Parameters::Properties::
641 WINDOW_POSITION_CURSOR) {
642 properties_out.show_window_at_composition = false;
643 modified = true;
644 }
645
646 if (properties.auxiliary_text) {
647 properties_out.auxiliary_text = *properties.auxiliary_text;
648 modified = true;
649 }
650
651 if (properties.auxiliary_text_visible) {
652 properties_out.is_auxiliary_text_visible =
653 *properties.auxiliary_text_visible;
654 modified = true;
655 }
656
657 if (modified) {
658 engine->SetCandidateWindowProperty(properties_out);
659 }
660
661 SetResult(new base::FundamentalValue(true));
662
663 return true;
664 }
665
RunSync()666 bool InputImeSetCandidatesFunction::RunSync() {
667 InputMethodEngineInterface* engine =
668 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
669 if (!engine) {
670 SetResult(new base::FundamentalValue(false));
671 return true;
672 }
673
674 scoped_ptr<SetCandidates::Params> parent_params(
675 SetCandidates::Params::Create(*args_));
676 const SetCandidates::Params::Parameters& params =
677 parent_params->parameters;
678
679 std::vector<InputMethodEngineInterface::Candidate> candidates_out;
680 const std::vector<linked_ptr<
681 SetCandidates::Params::Parameters::CandidatesType> >& candidates_in =
682 params.candidates;
683 for (size_t i = 0; i < candidates_in.size(); ++i) {
684 candidates_out.push_back(InputMethodEngineInterface::Candidate());
685 candidates_out.back().value = candidates_in[i]->candidate;
686 candidates_out.back().id = candidates_in[i]->id;
687 if (candidates_in[i]->label)
688 candidates_out.back().label = *candidates_in[i]->label;
689 if (candidates_in[i]->annotation)
690 candidates_out.back().annotation = *candidates_in[i]->annotation;
691 if (candidates_in[i]->usage) {
692 candidates_out.back().usage.title = candidates_in[i]->usage->title;
693 candidates_out.back().usage.body = candidates_in[i]->usage->body;
694 }
695 }
696
697 SetResult(new base::FundamentalValue(
698 engine->SetCandidates(params.context_id, candidates_out, &error_)));
699 return true;
700 }
701
RunSync()702 bool InputImeSetCursorPositionFunction::RunSync() {
703 InputMethodEngineInterface* engine =
704 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
705 if (!engine) {
706 SetResult(new base::FundamentalValue(false));
707 return true;
708 }
709
710 scoped_ptr<SetCursorPosition::Params> parent_params(
711 SetCursorPosition::Params::Create(*args_));
712 const SetCursorPosition::Params::Parameters& params =
713 parent_params->parameters;
714
715 SetResult(new base::FundamentalValue(
716 engine->SetCursorPosition(params.context_id, params.candidate_id,
717 &error_)));
718 return true;
719 }
720
RunSync()721 bool InputImeSetMenuItemsFunction::RunSync() {
722 scoped_ptr<SetMenuItems::Params> parent_params(
723 SetMenuItems::Params::Create(*args_));
724 const SetMenuItems::Params::Parameters& params =
725 parent_params->parameters;
726
727 InputMethodEngineInterface* engine =
728 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
729 params.engine_id);
730 if (!engine) {
731 error_ = kErrorEngineNotAvailable;
732 return false;
733 }
734
735 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
736 std::vector<InputMethodEngineInterface::MenuItem> items_out;
737
738 for (size_t i = 0; i < items.size(); ++i) {
739 items_out.push_back(InputMethodEngineInterface::MenuItem());
740 SetMenuItemToMenu(*items[i], &items_out.back());
741 }
742
743 if (!engine->SetMenuItems(items_out))
744 error_ = kErrorSetMenuItemsFail;
745 return true;
746 }
747
RunSync()748 bool InputImeUpdateMenuItemsFunction::RunSync() {
749 scoped_ptr<UpdateMenuItems::Params> parent_params(
750 UpdateMenuItems::Params::Create(*args_));
751 const UpdateMenuItems::Params::Parameters& params =
752 parent_params->parameters;
753
754 InputMethodEngineInterface* engine =
755 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
756 params.engine_id);
757 if (!engine) {
758 error_ = kErrorEngineNotAvailable;
759 return false;
760 }
761
762 const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
763 std::vector<InputMethodEngineInterface::MenuItem> items_out;
764
765 for (size_t i = 0; i < items.size(); ++i) {
766 items_out.push_back(InputMethodEngineInterface::MenuItem());
767 SetMenuItemToMenu(*items[i], &items_out.back());
768 }
769
770 if (!engine->UpdateMenuItems(items_out))
771 error_ = kErrorUpdateMenuItemsFail;
772 return true;
773 }
774
RunSync()775 bool InputImeDeleteSurroundingTextFunction::RunSync() {
776 scoped_ptr<DeleteSurroundingText::Params> parent_params(
777 DeleteSurroundingText::Params::Create(*args_));
778 const DeleteSurroundingText::Params::Parameters& params =
779 parent_params->parameters;
780
781 InputMethodEngineInterface* engine =
782 InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
783 params.engine_id);
784 if (!engine) {
785 error_ = kErrorEngineNotAvailable;
786 return false;
787 }
788
789 engine->DeleteSurroundingText(params.context_id, params.offset, params.length,
790 &error_);
791 return true;
792 }
793
RunAsync()794 bool InputImeKeyEventHandledFunction::RunAsync() {
795 scoped_ptr<KeyEventHandled::Params> params(
796 KeyEventHandled::Params::Create(*args_));
797 InputImeEventRouter::GetInstance()->OnKeyEventHandled(
798 extension_id(), params->request_id, params->response);
799 return true;
800 }
801
InputImeAPI(content::BrowserContext * context)802 InputImeAPI::InputImeAPI(content::BrowserContext* context)
803 : browser_context_(context), extension_registry_observer_(this) {
804 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
805
806 EventRouter* event_router = EventRouter::Get(browser_context_);
807 event_router->RegisterObserver(this, input_ime::OnActivate::kEventName);
808 event_router->RegisterObserver(this, input_ime::OnFocus::kEventName);
809 }
810
~InputImeAPI()811 InputImeAPI::~InputImeAPI() {
812 EventRouter::Get(browser_context_)->UnregisterObserver(this);
813 }
814
815 static base::LazyInstance<BrowserContextKeyedAPIFactory<InputImeAPI> >
816 g_factory = LAZY_INSTANCE_INITIALIZER;
817
818 // static
GetFactoryInstance()819 BrowserContextKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() {
820 return g_factory.Pointer();
821 }
822
OnExtensionLoaded(content::BrowserContext * browser_context,const Extension * extension)823 void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context,
824 const Extension* extension) {
825 const std::vector<InputComponentInfo>* input_components =
826 extensions::InputComponents::GetInputComponents(extension);
827 if (!input_components)
828 return;
829 for (std::vector<extensions::InputComponentInfo>::const_iterator component =
830 input_components->begin();
831 component != input_components->end();
832 ++component) {
833 if (component->type == extensions::INPUT_COMPONENT_TYPE_IME) {
834 // If |browser_context| looks like signin profile, use the real signin
835 // profile. This is because IME extensions for signin profile are run
836 // in Off-The-Record profile, based on given static defaults.
837 // So if |profile_| is signin profile, we need to make sure
838 // the router/observer runs under its incognito profile, because the
839 // component extensions were installed under its incognito profile.
840 Profile* profile = Profile::FromBrowserContext(browser_context);
841 if (chromeos::ProfileHelper::IsSigninProfile(profile))
842 profile = chromeos::ProfileHelper::GetSigninProfile();
843 input_ime_event_router()->RegisterIme(
844 profile, extension->id(), *component);
845 }
846 }
847 }
848
OnExtensionUnloaded(content::BrowserContext * browser_context,const Extension * extension,UnloadedExtensionInfo::Reason reason)849 void InputImeAPI::OnExtensionUnloaded(content::BrowserContext* browser_context,
850 const Extension* extension,
851 UnloadedExtensionInfo::Reason reason) {
852 const std::vector<InputComponentInfo>* input_components =
853 extensions::InputComponents::GetInputComponents(extension);
854 if (!input_components)
855 return;
856 if (input_components->size() > 0)
857 input_ime_event_router()->UnregisterAllImes(extension->id());
858 }
859
OnListenerAdded(const EventListenerInfo & details)860 void InputImeAPI::OnListenerAdded(const EventListenerInfo& details) {
861 InputMethodEngineInterface* engine =
862 input_ime_event_router()->GetActiveEngine(details.extension_id);
863 if (engine)
864 engine->NotifyImeReady();
865 }
866
input_ime_event_router()867 InputImeEventRouter* InputImeAPI::input_ime_event_router() {
868 return InputImeEventRouter::GetInstance();
869 }
870
871 } // namespace extensions
872