Home | History | Annotate | Download | only in input_ime
      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 
     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 
     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 
     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:
     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 
    113   virtual ~ImeObserver() {}
    114 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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.
    305   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*
    326 InputImeEventRouter::GetInstance() {
    327   return Singleton<InputImeEventRouter>::get();
    328 }
    329 
    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 
    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 
    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 
    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 
    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 
    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 
    455 InputImeEventRouter::InputImeEventRouter()
    456   : next_request_id_(1) {
    457 }
    458 
    459 InputImeEventRouter::~InputImeEventRouter() {}
    460 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    819 BrowserContextKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() {
    820   return g_factory.Pointer();
    821 }
    822 
    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 
    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 
    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 
    867 InputImeEventRouter* InputImeAPI::input_ime_event_router() {
    868   return InputImeEventRouter::GetInstance();
    869 }
    870 
    871 }  // namespace extensions
    872