• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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