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