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/chrome_notification_types.h"
     10 #include "chrome/browser/chromeos/input_method/input_method_engine.h"
     11 #include "chrome/browser/extensions/event_router.h"
     12 #include "chrome/browser/extensions/extension_function_registry.h"
     13 #include "chrome/browser/extensions/extension_system.h"
     14 #include "chrome/browser/profiles/profile.h"
     15 #include "chrome/common/extensions/api/input_ime.h"
     16 #include "chrome/common/extensions/api/input_ime/input_components_handler.h"
     17 #include "content/public/browser/notification_details.h"
     18 #include "content/public/browser/notification_source.h"
     19 
     20 namespace input_ime = extensions::api::input_ime;
     21 namespace KeyEventHandled = extensions::api::input_ime::KeyEventHandled;
     22 namespace DeleteSurroundingText =
     23     extensions::api::input_ime::DeleteSurroundingText;
     24 namespace UpdateMenuItems = extensions::api::input_ime::UpdateMenuItems;
     25 namespace SetMenuItems = extensions::api::input_ime::SetMenuItems;
     26 namespace SetCursorPosition = extensions::api::input_ime::SetCursorPosition;
     27 namespace SetCandidates = extensions::api::input_ime::SetCandidates;
     28 namespace SetCandidateWindowProperties =
     29     extensions::api::input_ime::SetCandidateWindowProperties;
     30 namespace CommitText = extensions::api::input_ime::CommitText;
     31 namespace ClearComposition = extensions::api::input_ime::ClearComposition;
     32 namespace SetComposition = extensions::api::input_ime::SetComposition;
     33 
     34 namespace {
     35 
     36 const char kErrorEngineNotAvailable[] = "Engine is not available";
     37 const char kErrorBadCandidateList[] = "Invalid candidate list provided";
     38 const char kErrorSetMenuItemsFail[] = "Could not create menu Items";
     39 const char kErrorUpdateMenuItemsFail[] = "Could not update menu Items";
     40 
     41 void SetMenuItemToMenu(const input_ime::MenuItem& input,
     42                        chromeos::InputMethodEngine::MenuItem* out) {
     43   out->modified = 0;
     44   out->id = input.id;
     45   if (input.label) {
     46     out->modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_LABEL;
     47     out->label = *input.label;
     48   }
     49 
     50   if (input.style != input_ime::MenuItem::STYLE_NONE) {
     51     out->modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_STYLE;
     52     out->style = static_cast<chromeos::InputMethodEngine::MenuItemStyle>(
     53         input.style);
     54   }
     55 
     56   if (input.visible)
     57     out->modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_VISIBLE;
     58   out->visible = input.visible ? *input.visible : true;
     59 
     60   if (input.checked)
     61     out->modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_CHECKED;
     62   out->checked = input.checked ? *input.checked : false;
     63 
     64   if (input.enabled)
     65     out->modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_ENABLED;
     66   out->enabled = input.enabled ? *input.enabled : true;
     67 }
     68 
     69 static void DispatchEventToExtension(Profile* profile,
     70                                      const std::string& extension_id,
     71                                      const std::string& event_name,
     72                                      scoped_ptr<base::ListValue> args) {
     73   scoped_ptr<extensions::Event> event(new extensions::Event(
     74       event_name, args.Pass()));
     75   event->restrict_to_profile = profile;
     76   extensions::ExtensionSystem::Get(profile)->event_router()->
     77       DispatchEventToExtension(extension_id, event.Pass());
     78 }
     79 
     80 }  // namespace
     81 
     82 namespace events {
     83 
     84 const char kOnActivate[] = "input.ime.onActivate";
     85 const char kOnDeactivated[] = "input.ime.onDeactivated";
     86 const char kOnFocus[] = "input.ime.onFocus";
     87 const char kOnBlur[] = "input.ime.onBlur";
     88 const char kOnInputContextUpdate[] = "input.ime.onInputContextUpdate";
     89 const char kOnKeyEvent[] = "input.ime.onKeyEvent";
     90 const char kOnCandidateClicked[] = "input.ime.onCandidateClicked";
     91 const char kOnMenuItemActivated[] = "input.ime.onMenuItemActivated";
     92 const char kOnSurroundingTextChanged[] = "input.ime.onSurroundingTextChanged";
     93 const char kOnReset[] = "input.ime.onReset";
     94 
     95 }  // namespace events
     96 
     97 namespace chromeos {
     98 class ImeObserver : public chromeos::InputMethodEngine::Observer {
     99  public:
    100   ImeObserver(Profile* profile, const std::string& extension_id,
    101               const std::string& engine_id) :
    102     profile_(profile),
    103     extension_id_(extension_id),
    104     engine_id_(engine_id) {
    105   }
    106 
    107   virtual ~ImeObserver() {}
    108 
    109   virtual void OnActivate(const std::string& engine_id) OVERRIDE {
    110     if (profile_ == NULL || extension_id_.empty())
    111       return;
    112 
    113     scoped_ptr<base::ListValue> args(new base::ListValue());
    114     args->Append(Value::CreateStringValue(engine_id));
    115 
    116     DispatchEventToExtension(profile_, extension_id_,
    117                              events::kOnActivate, args.Pass());
    118   }
    119 
    120   virtual void OnDeactivated(const std::string& engine_id) OVERRIDE {
    121     if (profile_ == NULL || extension_id_.empty())
    122       return;
    123 
    124     scoped_ptr<base::ListValue> args(new base::ListValue());
    125     args->Append(Value::CreateStringValue(engine_id));
    126 
    127     DispatchEventToExtension(profile_, extension_id_,
    128                              events::kOnDeactivated, args.Pass());
    129   }
    130 
    131   virtual void OnFocus(
    132       const InputMethodEngine::InputContext& context) OVERRIDE {
    133     if (profile_ == NULL || extension_id_.empty())
    134       return;
    135 
    136     base::DictionaryValue* dict = new base::DictionaryValue();
    137     dict->SetInteger("contextID", context.id);
    138     dict->SetString("type", context.type);
    139 
    140     scoped_ptr<base::ListValue> args(new base::ListValue());
    141     args->Append(dict);
    142 
    143     DispatchEventToExtension(profile_, extension_id_,
    144                              events::kOnFocus, args.Pass());
    145   }
    146 
    147   virtual void OnBlur(int context_id) OVERRIDE {
    148     if (profile_ == NULL || extension_id_.empty())
    149       return;
    150 
    151     scoped_ptr<base::ListValue> args(new base::ListValue());
    152     args->Append(Value::CreateIntegerValue(context_id));
    153 
    154     DispatchEventToExtension(profile_, extension_id_,
    155                              events::kOnBlur, args.Pass());
    156   }
    157 
    158   virtual void OnInputContextUpdate(
    159       const InputMethodEngine::InputContext& context) OVERRIDE {
    160     if (profile_ == NULL || extension_id_.empty())
    161       return;
    162 
    163     base::DictionaryValue* dict = new base::DictionaryValue();
    164     dict->SetInteger("contextID", context.id);
    165     dict->SetString("type", context.type);
    166 
    167     scoped_ptr<base::ListValue> args(new base::ListValue());
    168     args->Append(dict);
    169 
    170     DispatchEventToExtension(profile_, extension_id_,
    171                              events::kOnInputContextUpdate, args.Pass());
    172   }
    173 
    174   virtual void OnKeyEvent(
    175       const std::string& engine_id,
    176       const InputMethodEngine::KeyboardEvent& event,
    177       chromeos::input_method::KeyEventHandle* key_data) OVERRIDE {
    178     if (profile_ == NULL || extension_id_.empty())
    179       return;
    180 
    181     std::string request_id =
    182         extensions::InputImeEventRouter::GetInstance()->AddRequest(engine_id,
    183                                                                    key_data);
    184 
    185     base::DictionaryValue* dict = new base::DictionaryValue();
    186     dict->SetString("type", event.type);
    187     dict->SetString("requestId", request_id);
    188     dict->SetString("key", event.key);
    189     dict->SetString("code", event.code);
    190     dict->SetBoolean("altKey", event.alt_key);
    191     dict->SetBoolean("ctrlKey", event.ctrl_key);
    192     dict->SetBoolean("shiftKey", event.shift_key);
    193     dict->SetBoolean("capsLock", event.caps_lock);
    194 
    195     scoped_ptr<base::ListValue> args(new base::ListValue());
    196     args->Append(Value::CreateStringValue(engine_id));
    197     args->Append(dict);
    198 
    199     DispatchEventToExtension(profile_, extension_id_,
    200                              events::kOnKeyEvent, args.Pass());
    201   }
    202 
    203   virtual void OnCandidateClicked(
    204       const std::string& engine_id,
    205       int candidate_id,
    206       chromeos::InputMethodEngine::MouseButtonEvent button) OVERRIDE {
    207     if (profile_ == NULL || extension_id_.empty())
    208       return;
    209 
    210     scoped_ptr<base::ListValue> args(new base::ListValue());
    211     args->Append(Value::CreateStringValue(engine_id));
    212     args->Append(Value::CreateIntegerValue(candidate_id));
    213     switch (button) {
    214       case chromeos::InputMethodEngine::MOUSE_BUTTON_MIDDLE:
    215         args->Append(Value::CreateStringValue("middle"));
    216         break;
    217 
    218       case chromeos::InputMethodEngine::MOUSE_BUTTON_RIGHT:
    219         args->Append(Value::CreateStringValue("right"));
    220         break;
    221 
    222       case chromeos::InputMethodEngine::MOUSE_BUTTON_LEFT:
    223       // Default to left.
    224       default:
    225         args->Append(Value::CreateStringValue("left"));
    226         break;
    227     }
    228 
    229     DispatchEventToExtension(profile_, extension_id_,
    230                              events::kOnCandidateClicked, args.Pass());
    231   }
    232 
    233   virtual void OnMenuItemActivated(const std::string& engine_id,
    234                                    const std::string& menu_id) OVERRIDE {
    235     if (profile_ == NULL || extension_id_.empty())
    236       return;
    237 
    238     scoped_ptr<base::ListValue> args(new base::ListValue());
    239     args->Append(Value::CreateStringValue(engine_id));
    240     args->Append(Value::CreateStringValue(menu_id));
    241 
    242     DispatchEventToExtension(profile_, extension_id_,
    243                              events::kOnMenuItemActivated, args.Pass());
    244   }
    245 
    246   virtual void OnSurroundingTextChanged(const std::string& engine_id,
    247                                         const std::string& text,
    248                                         int cursor_pos,
    249                                         int anchor_pos) OVERRIDE {
    250     if (profile_ == NULL || extension_id_.empty())
    251       return;
    252     base::DictionaryValue* dict = new base::DictionaryValue();
    253     dict->SetString("text", text);
    254     dict->SetInteger("focus", cursor_pos);
    255     dict->SetInteger("anchor", anchor_pos);
    256 
    257     scoped_ptr<ListValue> args(new base::ListValue);
    258     args->Append(Value::CreateStringValue(engine_id));
    259     args->Append(dict);
    260 
    261     DispatchEventToExtension(profile_, extension_id_,
    262                              events::kOnSurroundingTextChanged, args.Pass());
    263   }
    264 
    265   virtual void OnReset(const std::string& engine_id) OVERRIDE {
    266     if (profile_ == NULL || extension_id_.empty())
    267       return;
    268     scoped_ptr<base::ListValue> args(new base::ListValue());
    269     args->Append(Value::CreateStringValue(engine_id));
    270 
    271     DispatchEventToExtension(profile_, extension_id_,
    272                              events::kOnReset, args.Pass());
    273   }
    274 
    275  private:
    276   Profile* profile_;
    277   std::string extension_id_;
    278   std::string engine_id_;
    279 
    280   DISALLOW_COPY_AND_ASSIGN(ImeObserver);
    281 };
    282 
    283 }  // namespace chromeos
    284 
    285 namespace extensions {
    286 
    287 InputImeEventRouter*
    288 InputImeEventRouter::GetInstance() {
    289   return Singleton<InputImeEventRouter>::get();
    290 }
    291 
    292 #if defined(OS_CHROMEOS)
    293 bool InputImeEventRouter::RegisterIme(
    294     Profile* profile,
    295     const std::string& extension_id,
    296     const extensions::InputComponentInfo& component) {
    297   VLOG(1) << "RegisterIme: " << extension_id << " id: " << component.id;
    298 
    299   std::map<std::string, chromeos::InputMethodEngine*>& engine_map =
    300       engines_[extension_id];
    301 
    302   std::map<std::string, chromeos::InputMethodEngine*>::iterator engine_ix =
    303       engine_map.find(component.id);
    304   if (engine_ix != engine_map.end())
    305     return false;
    306 
    307   std::string error;
    308   chromeos::ImeObserver* observer = new chromeos::ImeObserver(profile,
    309                                                               extension_id,
    310                                                               component.id);
    311   std::vector<std::string> layouts;
    312   layouts.assign(component.layouts.begin(), component.layouts.end());
    313 
    314   std::vector<std::string> languages;
    315   languages.assign(component.languages.begin(), component.languages.end());
    316 
    317   chromeos::InputMethodEngine* engine =
    318       chromeos::InputMethodEngine::CreateEngine(
    319           observer, component.name.c_str(), extension_id.c_str(),
    320           component.id.c_str(), component.description.c_str(),
    321           languages, layouts, component.options_page_url,
    322           &error);
    323   if (!engine) {
    324     delete observer;
    325     LOG(ERROR) << "RegisterIme: " << error;
    326     return false;
    327   }
    328 
    329   engine_map[component.id] = engine;
    330 
    331   std::map<std::string, chromeos::ImeObserver*>& observer_list =
    332       observers_[extension_id];
    333 
    334   observer_list[component.id] = observer;
    335 
    336   return true;
    337 }
    338 
    339 void InputImeEventRouter::UnregisterAllImes(
    340     Profile* profile, const std::string& extension_id) {
    341   std::map<std::string,
    342            std::map<std::string,
    343                     chromeos::InputMethodEngine*> >::iterator engine_map =
    344       engines_.find(extension_id);
    345   if (engine_map != engines_.end()) {
    346     STLDeleteContainerPairSecondPointers(engine_map->second.begin(),
    347                                          engine_map->second.end());
    348     engines_.erase(engine_map);
    349   }
    350 
    351   std::map<std::string,
    352            std::map<std::string,
    353                     chromeos::ImeObserver*> >::iterator observer_list =
    354       observers_.find(extension_id);
    355   if (observer_list != observers_.end()) {
    356     STLDeleteContainerPairSecondPointers(observer_list->second.begin(),
    357                                          observer_list->second.end());
    358     observers_.erase(observer_list);
    359   }
    360 }
    361 #endif
    362 
    363 chromeos::InputMethodEngine* InputImeEventRouter::GetEngine(
    364     const std::string& extension_id, const std::string& engine_id) {
    365   std::map<std::string,
    366            std::map<std::string, chromeos::InputMethodEngine*> >::const_iterator
    367                engine_list = engines_.find(extension_id);
    368   if (engine_list != engines_.end()) {
    369     std::map<std::string, chromeos::InputMethodEngine*>::const_iterator
    370         engine_ix = engine_list->second.find(engine_id);
    371     if (engine_ix != engine_list->second.end())
    372       return engine_ix->second;
    373   }
    374   return NULL;
    375 }
    376 
    377 chromeos::InputMethodEngine* InputImeEventRouter::GetActiveEngine(
    378     const std::string& extension_id) {
    379   std::map<std::string,
    380            std::map<std::string, chromeos::InputMethodEngine*> >::const_iterator
    381                engine_list = engines_.find(extension_id);
    382   if (engine_list != engines_.end()) {
    383     std::map<std::string, chromeos::InputMethodEngine*>::const_iterator
    384         engine_ix;
    385     for (engine_ix = engine_list->second.begin();
    386          engine_ix != engine_list->second.end();
    387          ++engine_ix) {
    388       if (engine_ix->second->IsActive())
    389         return engine_ix->second;
    390     }
    391   }
    392   return NULL;
    393 }
    394 
    395 void InputImeEventRouter::OnKeyEventHandled(
    396     const std::string& extension_id,
    397     const std::string& request_id,
    398     bool handled) {
    399   RequestMap::iterator request = request_map_.find(request_id);
    400   if (request == request_map_.end()) {
    401     LOG(ERROR) << "Request ID not found: " << request_id;
    402     return;
    403   }
    404 
    405   std::string engine_id = request->second.first;
    406   chromeos::input_method::KeyEventHandle* key_data = request->second.second;
    407   request_map_.erase(request);
    408 
    409   chromeos::InputMethodEngine* engine = GetEngine(extension_id, engine_id);
    410   if (!engine) {
    411     LOG(ERROR) << "Engine does not exist: " << engine_id;
    412     return;
    413   }
    414 
    415   engine->KeyEventDone(key_data, handled);
    416 }
    417 
    418 std::string InputImeEventRouter::AddRequest(
    419     const std::string& engine_id,
    420     chromeos::input_method::KeyEventHandle* key_data) {
    421   std::string request_id = base::IntToString(next_request_id_);
    422   ++next_request_id_;
    423 
    424   request_map_[request_id] = std::make_pair(engine_id, key_data);
    425 
    426   return request_id;
    427 }
    428 
    429 InputImeEventRouter::InputImeEventRouter()
    430   : next_request_id_(1) {
    431 }
    432 
    433 InputImeEventRouter::~InputImeEventRouter() {}
    434 
    435 bool InputImeSetCompositionFunction::RunImpl() {
    436   chromeos::InputMethodEngine* engine =
    437       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
    438   if (!engine) {
    439     SetResult(Value::CreateBooleanValue(false));
    440     return true;
    441   }
    442 
    443   scoped_ptr<SetComposition::Params> parent_params(
    444       SetComposition::Params::Create(*args_));
    445   const SetComposition::Params::Parameters& params = parent_params->parameters;
    446   std::vector<chromeos::InputMethodEngine::SegmentInfo> segments;
    447   if (params.segments) {
    448     const std::vector<linked_ptr<
    449         SetComposition::Params::Parameters::SegmentsType> >&
    450             segments_args = *params.segments;
    451     for (size_t i = 0; i < segments_args.size(); ++i) {
    452       EXTENSION_FUNCTION_VALIDATE(
    453           segments_args[i]->style !=
    454           SetComposition::Params::Parameters::SegmentsType::STYLE_NONE);
    455       segments.push_back(chromeos::InputMethodEngine::SegmentInfo());
    456       segments.back().start = segments_args[i]->start;
    457       segments.back().end = segments_args[i]->end;
    458       if (segments_args[i]->style ==
    459           SetComposition::Params::Parameters::SegmentsType::STYLE_UNDERLINE) {
    460         segments.back().style =
    461             chromeos::InputMethodEngine::SEGMENT_STYLE_UNDERLINE;
    462       } else {
    463         segments.back().style =
    464             chromeos::InputMethodEngine::SEGMENT_STYLE_DOUBLE_UNDERLINE;
    465       }
    466     }
    467   }
    468 
    469   int selection_start =
    470       params.selection_start ? *params.selection_start : params.cursor;
    471   int selection_end =
    472       params.selection_end ? *params.selection_end : params.cursor;
    473 
    474   SetResult(Value::CreateBooleanValue(
    475       engine->SetComposition(params.context_id, params.text.c_str(),
    476                              selection_start, selection_end, params.cursor,
    477                              segments, &error_)));
    478   return true;
    479 }
    480 
    481 bool InputImeClearCompositionFunction::RunImpl() {
    482   chromeos::InputMethodEngine* engine =
    483       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
    484   if (!engine) {
    485     SetResult(Value::CreateBooleanValue(false));
    486     return true;
    487   }
    488 
    489   scoped_ptr<ClearComposition::Params> parent_params(
    490       ClearComposition::Params::Create(*args_));
    491   const ClearComposition::Params::Parameters& params =
    492       parent_params->parameters;
    493 
    494   SetResult(Value::CreateBooleanValue(
    495       engine->ClearComposition(params.context_id, &error_)));
    496   return true;
    497 }
    498 
    499 bool InputImeCommitTextFunction::RunImpl() {
    500   // TODO(zork): Support committing when not active.
    501   chromeos::InputMethodEngine* engine =
    502       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
    503   if (!engine) {
    504     SetResult(Value::CreateBooleanValue(false));
    505     return true;
    506   }
    507 
    508   scoped_ptr<CommitText::Params> parent_params(
    509       CommitText::Params::Create(*args_));
    510   const CommitText::Params::Parameters& params =
    511       parent_params->parameters;
    512 
    513   SetResult(Value::CreateBooleanValue(
    514       engine->CommitText(params.context_id, params.text.c_str(), &error_)));
    515   return true;
    516 }
    517 
    518 bool InputImeSetCandidateWindowPropertiesFunction::RunImpl() {
    519   scoped_ptr<SetCandidateWindowProperties::Params> parent_params(
    520       SetCandidateWindowProperties::Params::Create(*args_));
    521   const SetCandidateWindowProperties::Params::Parameters&
    522       params = parent_params->parameters;
    523 
    524   chromeos::InputMethodEngine* engine =
    525       InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
    526                                                     params.engine_id);
    527 
    528   if (!engine) {
    529     SetResult(Value::CreateBooleanValue(false));
    530     return true;
    531   }
    532 
    533   const SetCandidateWindowProperties::Params::Parameters::Properties&
    534       properties = params.properties;
    535 
    536   if (properties.visible &&
    537       !engine->SetCandidateWindowVisible(*properties.visible, &error_)) {
    538     SetResult(Value::CreateBooleanValue(false));
    539     return true;
    540   }
    541 
    542   if (properties.cursor_visible)
    543     engine->SetCandidateWindowCursorVisible(*properties.cursor_visible);
    544 
    545   if (properties.vertical)
    546     engine->SetCandidateWindowVertical(*properties.vertical);
    547 
    548   if (properties.page_size)
    549     engine->SetCandidateWindowPageSize(*properties.page_size);
    550 
    551   if (properties.auxiliary_text)
    552     engine->SetCandidateWindowAuxText(properties.auxiliary_text->c_str());
    553 
    554   if (properties.auxiliary_text_visible) {
    555     engine->SetCandidateWindowAuxTextVisible(
    556         *properties.auxiliary_text_visible);
    557   }
    558 
    559   if (properties.window_position ==
    560       SetCandidateWindowProperties::Params::Parameters::Properties::
    561           WINDOW_POSITION_COMPOSITION) {
    562     engine->SetCandidateWindowPosition(
    563         chromeos::InputMethodEngine::WINDOW_POS_COMPOSITTION);
    564   } else if (properties.window_position ==
    565              SetCandidateWindowProperties::Params::Parameters::Properties::
    566                  WINDOW_POSITION_CURSOR) {
    567     engine->SetCandidateWindowPosition(
    568         chromeos::InputMethodEngine::WINDOW_POS_CURSOR);
    569   }
    570 
    571   SetResult(Value::CreateBooleanValue(true));
    572 
    573   return true;
    574 }
    575 
    576 #if defined(OS_CHROMEOS)
    577 bool InputImeSetCandidatesFunction::RunImpl() {
    578   chromeos::InputMethodEngine* engine =
    579       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
    580   if (!engine) {
    581     SetResult(Value::CreateBooleanValue(false));
    582     return true;
    583   }
    584 
    585   scoped_ptr<SetCandidates::Params> parent_params(
    586       SetCandidates::Params::Create(*args_));
    587   const SetCandidates::Params::Parameters& params =
    588       parent_params->parameters;
    589 
    590   std::vector<chromeos::InputMethodEngine::Candidate> candidates_out;
    591   const std::vector<linked_ptr<
    592       SetCandidates::Params::Parameters::CandidatesType> >& candidates_in =
    593           params.candidates;
    594   for (size_t i = 0; i < candidates_in.size(); ++i) {
    595     candidates_out.push_back(chromeos::InputMethodEngine::Candidate());
    596     candidates_out.back().value = candidates_in[i]->candidate;
    597     candidates_out.back().id = candidates_in[i]->id;
    598     if (candidates_in[i]->label)
    599       candidates_out.back().label = *candidates_in[i]->label;
    600     if (candidates_in[i]->annotation)
    601       candidates_out.back().annotation = *candidates_in[i]->annotation;
    602     if (candidates_in[i]->usage) {
    603       candidates_out.back().usage.title = candidates_in[i]->usage->title;
    604       candidates_out.back().usage.body = candidates_in[i]->usage->body;
    605     }
    606   }
    607 
    608   SetResult(Value::CreateBooleanValue(
    609       engine->SetCandidates(params.context_id, candidates_out, &error_)));
    610   return true;
    611 }
    612 
    613 bool InputImeSetCursorPositionFunction::RunImpl() {
    614   chromeos::InputMethodEngine* engine =
    615       InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id());
    616   if (!engine) {
    617     SetResult(Value::CreateBooleanValue(false));
    618     return true;
    619   }
    620 
    621   scoped_ptr<SetCursorPosition::Params> parent_params(
    622       SetCursorPosition::Params::Create(*args_));
    623   const SetCursorPosition::Params::Parameters& params =
    624       parent_params->parameters;
    625 
    626   SetResult(Value::CreateBooleanValue(
    627       engine->SetCursorPosition(params.context_id, params.candidate_id,
    628                                 &error_)));
    629   return true;
    630 }
    631 
    632 bool InputImeSetMenuItemsFunction::RunImpl() {
    633   scoped_ptr<SetMenuItems::Params> parent_params(
    634       SetMenuItems::Params::Create(*args_));
    635   const SetMenuItems::Params::Parameters& params =
    636       parent_params->parameters;
    637 
    638   chromeos::InputMethodEngine* engine =
    639       InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
    640                                                     params.engine_id);
    641   if (!engine) {
    642     error_ = kErrorEngineNotAvailable;
    643     return false;
    644   }
    645 
    646   const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
    647   std::vector<chromeos::InputMethodEngine::MenuItem> items_out;
    648 
    649   for (size_t i = 0; i < items.size(); ++i) {
    650     items_out.push_back(chromeos::InputMethodEngine::MenuItem());
    651     SetMenuItemToMenu(*items[i], &items_out.back());
    652   }
    653 
    654   if (!engine->SetMenuItems(items_out))
    655     error_ = kErrorSetMenuItemsFail;
    656   return true;
    657 }
    658 
    659 bool InputImeUpdateMenuItemsFunction::RunImpl() {
    660   scoped_ptr<UpdateMenuItems::Params> parent_params(
    661       UpdateMenuItems::Params::Create(*args_));
    662   const UpdateMenuItems::Params::Parameters& params =
    663       parent_params->parameters;
    664 
    665   chromeos::InputMethodEngine* engine =
    666       InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
    667                                                     params.engine_id);
    668   if (!engine) {
    669     error_ = kErrorEngineNotAvailable;
    670     return false;
    671   }
    672 
    673   const std::vector<linked_ptr<input_ime::MenuItem> >& items = params.items;
    674   std::vector<chromeos::InputMethodEngine::MenuItem> items_out;
    675 
    676   for (size_t i = 0; i < items.size(); ++i) {
    677     items_out.push_back(chromeos::InputMethodEngine::MenuItem());
    678     SetMenuItemToMenu(*items[i], &items_out.back());
    679   }
    680 
    681   if (!engine->UpdateMenuItems(items_out))
    682     error_ = kErrorUpdateMenuItemsFail;
    683   return true;
    684 }
    685 
    686 bool InputImeDeleteSurroundingTextFunction::RunImpl() {
    687   scoped_ptr<DeleteSurroundingText::Params> parent_params(
    688       DeleteSurroundingText::Params::Create(*args_));
    689   const DeleteSurroundingText::Params::Parameters& params =
    690       parent_params->parameters;
    691 
    692   chromeos::InputMethodEngine* engine =
    693       InputImeEventRouter::GetInstance()->GetEngine(extension_id(),
    694                                                     params.engine_id);
    695   if (!engine) {
    696     error_ = kErrorEngineNotAvailable;
    697     return false;
    698   }
    699 
    700   engine->DeleteSurroundingText(params.context_id, params.offset, params.length,
    701                                 &error_);
    702   return true;
    703 }
    704 
    705 bool InputImeKeyEventHandledFunction::RunImpl() {
    706   scoped_ptr<KeyEventHandled::Params> params(
    707       KeyEventHandled::Params::Create(*args_));
    708   InputImeEventRouter::GetInstance()->OnKeyEventHandled(
    709       extension_id(), params->request_id, params->response);
    710   return true;
    711 }
    712 #endif
    713 
    714 InputImeAPI::InputImeAPI(Profile* profile)
    715     : profile_(profile) {
    716   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
    717                  content::Source<Profile>(profile));
    718   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
    719                  content::Source<Profile>(profile));
    720 }
    721 
    722 InputImeAPI::~InputImeAPI() {
    723 }
    724 
    725 static base::LazyInstance<ProfileKeyedAPIFactory<InputImeAPI> >
    726 g_factory = LAZY_INSTANCE_INITIALIZER;
    727 
    728 // static
    729 ProfileKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() {
    730   return &g_factory.Get();
    731 }
    732 
    733 void InputImeAPI::Observe(int type,
    734                           const content::NotificationSource& source,
    735                           const content::NotificationDetails& details) {
    736   if (type == chrome::NOTIFICATION_EXTENSION_LOADED) {
    737     const Extension* extension =
    738         content::Details<const Extension>(details).ptr();
    739     const std::vector<InputComponentInfo>* input_components =
    740         extensions::InputComponents::GetInputComponents(extension);
    741     if (!input_components)
    742       return;
    743     for (std::vector<extensions::InputComponentInfo>::const_iterator component =
    744         input_components->begin(); component != input_components->end();
    745         ++component) {
    746       if (component->type == extensions::INPUT_COMPONENT_TYPE_IME) {
    747         input_ime_event_router()->RegisterIme(
    748             profile_, extension->id(), *component);
    749       }
    750     }
    751   } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) {
    752     const Extension* extension =
    753         content::Details<const UnloadedExtensionInfo>(details)->extension;
    754     const std::vector<InputComponentInfo>* input_components =
    755         extensions::InputComponents::GetInputComponents(extension);
    756     if (!input_components)
    757       return;
    758     if (input_components->size() > 0)
    759       input_ime_event_router()->UnregisterAllImes(profile_, extension->id());
    760   }
    761 }
    762 
    763 InputImeEventRouter* InputImeAPI::input_ime_event_router() {
    764   return InputImeEventRouter::GetInstance();
    765 }
    766 
    767 }  // namespace extensions
    768