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