input_ime_api.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/json/json_writer.h" 8#include "base/lazy_instance.h" 9#include "base/stl_util.h" 10#include "base/strings/string_number_conversions.h" 11#include "base/values.h" 12#include "chrome/browser/chromeos/input_method/input_method_engine.h" 13#include "chrome/browser/extensions/event_router.h" 14#include "chrome/browser/extensions/extension_function_registry.h" 15#include "chrome/browser/extensions/extension_input_module_constants.h" 16#include "chrome/browser/extensions/extension_system.h" 17#include "chrome/browser/profiles/profile.h" 18#include "chrome/common/chrome_notification_types.h" 19#include "chrome/common/extensions/api/input_ime/input_components_handler.h" 20#include "content/public/browser/notification_details.h" 21#include "content/public/browser/notification_source.h" 22 23namespace keys = extension_input_module_constants; 24 25namespace { 26 27const char kStyleNone[] = "none"; 28const char kStyleCheck[] = "check"; 29const char kStyleRadio[] = "radio"; 30const char kStyleSeparator[] = "separator"; 31const char kWindowPositionComposition[] = "composition"; 32 33const char kErrorEngineNotAvailable[] = "Engine is not available"; 34const char kErrorBadCandidateList[] = "Invalid candidate list provided"; 35const char kErrorSetMenuItemsFail[] = "Could not create menu Items"; 36const char kErrorUpdateMenuItemsFail[] = "Could not update menu Items"; 37 38bool ReadMenuItems( 39 base::ListValue* menu_items, 40 std::vector<chromeos::InputMethodEngine::MenuItem>* output) { 41 for (size_t i = 0; i < menu_items->GetSize(); ++i) { 42 base::DictionaryValue* item_dict; 43 if (!menu_items->GetDictionary(i, &item_dict)) { 44 return false; 45 } 46 47 std::string id; 48 std::string label; 49 chromeos::InputMethodEngine::MenuItemStyle style = 50 chromeos::InputMethodEngine::MENU_ITEM_STYLE_NONE; 51 bool visible = true; 52 bool enabled = true; 53 bool checked = false; 54 std::string icon; 55 chromeos::InputMethodEngine::KeyboardEvent shortcut_key; 56 57 unsigned int modified = 0; 58 59 if (!item_dict->GetString(keys::kIdKey, &id)) { 60 return false; 61 } 62 63 if (item_dict->HasKey(keys::kLabelKey)) { 64 if (!item_dict->GetString(keys::kLabelKey, &label)) { 65 return false; 66 } 67 modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_LABEL; 68 } 69 if (item_dict->HasKey(keys::kStyleKey)) { 70 std::string style_string; 71 if (!item_dict->GetString(keys::kStyleKey, &style_string)) { 72 return false; 73 } 74 75 if (style_string == kStyleNone) { 76 style = chromeos::InputMethodEngine::MENU_ITEM_STYLE_NONE; 77 } else if (style_string == kStyleCheck) { 78 style = chromeos::InputMethodEngine::MENU_ITEM_STYLE_CHECK; 79 } else if (style_string == kStyleRadio) { 80 style = chromeos::InputMethodEngine::MENU_ITEM_STYLE_RADIO; 81 } else if (style_string == kStyleSeparator) { 82 style = chromeos::InputMethodEngine::MENU_ITEM_STYLE_SEPARATOR; 83 } else { 84 return false; 85 } 86 modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_STYLE; 87 } 88 89 if (item_dict->HasKey(keys::kVisibleKey)) { 90 if (!item_dict->GetBoolean(keys::kVisibleKey, &visible)) { 91 return false; 92 } 93 modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_VISIBLE; 94 } 95 96 if (item_dict->HasKey(keys::kCheckedKey)) { 97 if (!item_dict->GetBoolean(keys::kCheckedKey, &checked)) { 98 return false; 99 } 100 modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_CHECKED; 101 } 102 103 if (item_dict->HasKey(keys::kEnabledKey)) { 104 if (!item_dict->GetBoolean(keys::kEnabledKey, &enabled)) { 105 return false; 106 } 107 modified |= chromeos::InputMethodEngine::MENU_ITEM_MODIFIED_ENABLED; 108 } 109 110 output->push_back(chromeos::InputMethodEngine::MenuItem()); 111 output->back().id = id; 112 output->back().label = label; 113 output->back().style = style; 114 output->back().visible = visible; 115 output->back().enabled = enabled; 116 output->back().checked = checked; 117 118 output->back().modified = modified; 119 120 if (item_dict->HasKey(keys::kItemsKey)) { 121 base::ListValue* sub_list; 122 if (!item_dict->GetList(keys::kItemsKey, &sub_list)) { 123 return false; 124 } 125 126 if (!ReadMenuItems(sub_list, &(output->back().children))) { 127 return false; 128 } 129 } 130 } 131 132 return true; 133} 134 135static void DispatchEventToExtension(Profile* profile, 136 const std::string& extension_id, 137 const std::string& event_name, 138 scoped_ptr<base::ListValue> args) { 139 scoped_ptr<extensions::Event> event(new extensions::Event( 140 event_name, args.Pass())); 141 event->restrict_to_profile = profile; 142 extensions::ExtensionSystem::Get(profile)->event_router()-> 143 DispatchEventToExtension(extension_id, event.Pass()); 144} 145 146} // namespace 147 148namespace events { 149 150const char kOnActivate[] = "input.ime.onActivate"; 151const char kOnDeactivated[] = "input.ime.onDeactivated"; 152const char kOnFocus[] = "input.ime.onFocus"; 153const char kOnBlur[] = "input.ime.onBlur"; 154const char kOnInputContextUpdate[] = "input.ime.onInputContextUpdate"; 155const char kOnKeyEvent[] = "input.ime.onKeyEvent"; 156const char kOnCandidateClicked[] = "input.ime.onCandidateClicked"; 157const char kOnMenuItemActivated[] = "input.ime.onMenuItemActivated"; 158const char kOnSurroundingTextChanged[] = "input.ime.onSurroundingTextChanged"; 159const char kOnReset[] = "input.ime.onReset"; 160 161} // namespace events 162 163namespace chromeos { 164class ImeObserver : public chromeos::InputMethodEngine::Observer { 165 public: 166 ImeObserver(Profile* profile, const std::string& extension_id, 167 const std::string& engine_id) : 168 profile_(profile), 169 extension_id_(extension_id), 170 engine_id_(engine_id) { 171 } 172 173 virtual ~ImeObserver() {} 174 175 virtual void OnActivate(const std::string& engine_id) OVERRIDE { 176 if (profile_ == NULL || extension_id_.empty()) 177 return; 178 179 scoped_ptr<base::ListValue> args(new base::ListValue()); 180 args->Append(Value::CreateStringValue(engine_id)); 181 182 DispatchEventToExtension(profile_, extension_id_, 183 events::kOnActivate, args.Pass()); 184 } 185 186 virtual void OnDeactivated(const std::string& engine_id) OVERRIDE { 187 if (profile_ == NULL || extension_id_.empty()) 188 return; 189 190 scoped_ptr<base::ListValue> args(new base::ListValue()); 191 args->Append(Value::CreateStringValue(engine_id)); 192 193 DispatchEventToExtension(profile_, extension_id_, 194 events::kOnDeactivated, args.Pass()); 195 } 196 197 virtual void OnFocus( 198 const InputMethodEngine::InputContext& context) OVERRIDE { 199 if (profile_ == NULL || extension_id_.empty()) 200 return; 201 202 base::DictionaryValue* dict = new base::DictionaryValue(); 203 dict->SetInteger("contextID", context.id); 204 dict->SetString("type", context.type); 205 206 scoped_ptr<base::ListValue> args(new base::ListValue()); 207 args->Append(dict); 208 209 DispatchEventToExtension(profile_, extension_id_, 210 events::kOnFocus, args.Pass()); 211 } 212 213 virtual void OnBlur(int context_id) OVERRIDE { 214 if (profile_ == NULL || extension_id_.empty()) 215 return; 216 217 scoped_ptr<base::ListValue> args(new base::ListValue()); 218 args->Append(Value::CreateIntegerValue(context_id)); 219 220 DispatchEventToExtension(profile_, extension_id_, 221 events::kOnBlur, args.Pass()); 222 } 223 224 virtual void OnInputContextUpdate( 225 const InputMethodEngine::InputContext& context) OVERRIDE { 226 if (profile_ == NULL || extension_id_.empty()) 227 return; 228 229 base::DictionaryValue* dict = new base::DictionaryValue(); 230 dict->SetInteger("contextID", context.id); 231 dict->SetString("type", context.type); 232 233 scoped_ptr<base::ListValue> args(new base::ListValue()); 234 args->Append(dict); 235 236 DispatchEventToExtension(profile_, extension_id_, 237 events::kOnInputContextUpdate, args.Pass()); 238 } 239 240 virtual void OnKeyEvent( 241 const std::string& engine_id, 242 const InputMethodEngine::KeyboardEvent& event, 243 chromeos::input_method::KeyEventHandle* key_data) OVERRIDE { 244 if (profile_ == NULL || extension_id_.empty()) 245 return; 246 247 std::string request_id = 248 extensions::InputImeEventRouter::GetInstance()->AddRequest(engine_id, 249 key_data); 250 251 base::DictionaryValue* dict = new base::DictionaryValue(); 252 dict->SetString("type", event.type); 253 dict->SetString("requestId", request_id); 254 dict->SetString("key", event.key); 255 dict->SetString("code", event.code); 256 dict->SetBoolean("altKey", event.alt_key); 257 dict->SetBoolean("ctrlKey", event.ctrl_key); 258 dict->SetBoolean("shiftKey", event.shift_key); 259 dict->SetBoolean("capsLock", event.caps_lock); 260 261 scoped_ptr<base::ListValue> args(new base::ListValue()); 262 args->Append(Value::CreateStringValue(engine_id)); 263 args->Append(dict); 264 265 DispatchEventToExtension(profile_, extension_id_, 266 events::kOnKeyEvent, args.Pass()); 267 } 268 269 virtual void OnCandidateClicked( 270 const std::string& engine_id, 271 int candidate_id, 272 chromeos::InputMethodEngine::MouseButtonEvent button) OVERRIDE { 273 if (profile_ == NULL || extension_id_.empty()) 274 return; 275 276 scoped_ptr<base::ListValue> args(new base::ListValue()); 277 args->Append(Value::CreateStringValue(engine_id)); 278 args->Append(Value::CreateIntegerValue(candidate_id)); 279 switch (button) { 280 case chromeos::InputMethodEngine::MOUSE_BUTTON_MIDDLE: 281 args->Append(Value::CreateStringValue("middle")); 282 break; 283 284 case chromeos::InputMethodEngine::MOUSE_BUTTON_RIGHT: 285 args->Append(Value::CreateStringValue("right")); 286 break; 287 288 case chromeos::InputMethodEngine::MOUSE_BUTTON_LEFT: 289 // Default to left. 290 default: 291 args->Append(Value::CreateStringValue("left")); 292 break; 293 } 294 295 DispatchEventToExtension(profile_, extension_id_, 296 events::kOnCandidateClicked, args.Pass()); 297 } 298 299 virtual void OnMenuItemActivated(const std::string& engine_id, 300 const std::string& menu_id) OVERRIDE { 301 if (profile_ == NULL || extension_id_.empty()) 302 return; 303 304 scoped_ptr<base::ListValue> args(new base::ListValue()); 305 args->Append(Value::CreateStringValue(engine_id)); 306 args->Append(Value::CreateStringValue(menu_id)); 307 308 DispatchEventToExtension(profile_, extension_id_, 309 events::kOnMenuItemActivated, args.Pass()); 310 } 311 312 virtual void OnSurroundingTextChanged(const std::string& engine_id, 313 const std::string& text, 314 int cursor_pos, 315 int anchor_pos) OVERRIDE { 316 if (profile_ == NULL || extension_id_.empty()) 317 return; 318 base::DictionaryValue* dict = new base::DictionaryValue(); 319 dict->SetString("text", text); 320 dict->SetInteger("focus", cursor_pos); 321 dict->SetInteger("anchor", anchor_pos); 322 323 scoped_ptr<ListValue> args(new base::ListValue); 324 args->Append(Value::CreateStringValue(engine_id)); 325 args->Append(dict); 326 327 DispatchEventToExtension(profile_, extension_id_, 328 events::kOnSurroundingTextChanged, args.Pass()); 329 } 330 331 virtual void OnReset(const std::string& engine_id) OVERRIDE { 332 if (profile_ == NULL || extension_id_.empty()) 333 return; 334 scoped_ptr<base::ListValue> args(new base::ListValue()); 335 args->Append(Value::CreateStringValue(engine_id)); 336 337 DispatchEventToExtension(profile_, extension_id_, 338 events::kOnReset, args.Pass()); 339 } 340 341 private: 342 Profile* profile_; 343 std::string extension_id_; 344 std::string engine_id_; 345 346 DISALLOW_COPY_AND_ASSIGN(ImeObserver); 347}; 348 349} // namespace chromeos 350 351namespace extensions { 352 353InputImeEventRouter* 354InputImeEventRouter::GetInstance() { 355 return Singleton<InputImeEventRouter>::get(); 356} 357 358#if defined(OS_CHROMEOS) 359bool InputImeEventRouter::RegisterIme( 360 Profile* profile, 361 const std::string& extension_id, 362 const extensions::InputComponentInfo& component) { 363 VLOG(1) << "RegisterIme: " << extension_id << " id: " << component.id; 364 365 std::map<std::string, chromeos::InputMethodEngine*>& engine_map = 366 engines_[extension_id]; 367 368 std::map<std::string, chromeos::InputMethodEngine*>::iterator engine_ix = 369 engine_map.find(component.id); 370 if (engine_ix != engine_map.end()) { 371 return false; 372 } 373 374 std::string error; 375 chromeos::ImeObserver* observer = new chromeos::ImeObserver(profile, 376 extension_id, 377 component.id); 378 std::vector<std::string> layouts; 379 layouts.assign(component.layouts.begin(), component.layouts.end()); 380 381 chromeos::InputMethodEngine* engine = 382 chromeos::InputMethodEngine::CreateEngine( 383 observer, component.name.c_str(), extension_id.c_str(), 384 component.id.c_str(), component.description.c_str(), 385 component.language.c_str(), layouts, component.options_page_url, 386 &error); 387 if (!engine) { 388 delete observer; 389 LOG(ERROR) << "RegisterIme: " << error; 390 return false; 391 } 392 393 engine_map[component.id] = engine; 394 395 std::map<std::string, chromeos::ImeObserver*>& observer_list = 396 observers_[extension_id]; 397 398 observer_list[component.id] = observer; 399 400 return true; 401} 402 403void InputImeEventRouter::UnregisterAllImes( 404 Profile* profile, const std::string& extension_id) { 405 std::map<std::string, 406 std::map<std::string, 407 chromeos::InputMethodEngine*> >::iterator engine_map = 408 engines_.find(extension_id); 409 if (engine_map != engines_.end()) { 410 STLDeleteContainerPairSecondPointers(engine_map->second.begin(), 411 engine_map->second.end()); 412 engines_.erase(engine_map); 413 } 414 415 std::map<std::string, 416 std::map<std::string, 417 chromeos::ImeObserver*> >::iterator observer_list = 418 observers_.find(extension_id); 419 if (observer_list != observers_.end()) { 420 STLDeleteContainerPairSecondPointers(observer_list->second.begin(), 421 observer_list->second.end()); 422 observers_.erase(observer_list); 423 } 424} 425#endif 426 427chromeos::InputMethodEngine* InputImeEventRouter::GetEngine( 428 const std::string& extension_id, const std::string& engine_id) { 429 std::map<std::string, 430 std::map<std::string, chromeos::InputMethodEngine*> >::const_iterator 431 engine_list = engines_.find(extension_id); 432 if (engine_list != engines_.end()) { 433 std::map<std::string, chromeos::InputMethodEngine*>::const_iterator 434 engine_ix = engine_list->second.find(engine_id); 435 if (engine_ix != engine_list->second.end()) { 436 return engine_ix->second; 437 } 438 } 439 return NULL; 440} 441 442chromeos::InputMethodEngine* InputImeEventRouter::GetActiveEngine( 443 const std::string& extension_id) { 444 std::map<std::string, 445 std::map<std::string, chromeos::InputMethodEngine*> >::const_iterator 446 engine_list = engines_.find(extension_id); 447 if (engine_list != engines_.end()) { 448 std::map<std::string, chromeos::InputMethodEngine*>::const_iterator 449 engine_ix; 450 for (engine_ix = engine_list->second.begin(); 451 engine_ix != engine_list->second.end(); 452 ++engine_ix) { 453 if (engine_ix->second->IsActive()) { 454 return engine_ix->second; 455 } 456 } 457 } 458 return NULL; 459} 460 461void InputImeEventRouter::OnKeyEventHandled( 462 const std::string& extension_id, 463 const std::string& request_id, 464 bool handled) { 465 RequestMap::iterator request = request_map_.find(request_id); 466 if (request == request_map_.end()) { 467 LOG(ERROR) << "Request ID not found: " << request_id; 468 return; 469 } 470 471 std::string engine_id = request->second.first; 472 chromeos::input_method::KeyEventHandle* key_data = request->second.second; 473 request_map_.erase(request); 474 475 chromeos::InputMethodEngine* engine = GetEngine(extension_id, engine_id); 476 if (!engine) { 477 LOG(ERROR) << "Engine does not exist: " << engine_id; 478 return; 479 } 480 481 engine->KeyEventDone(key_data, handled); 482} 483 484std::string InputImeEventRouter::AddRequest( 485 const std::string& engine_id, 486 chromeos::input_method::KeyEventHandle* key_data) { 487 std::string request_id = base::IntToString(next_request_id_); 488 ++next_request_id_; 489 490 request_map_[request_id] = std::make_pair(engine_id, key_data); 491 492 return request_id; 493} 494 495InputImeEventRouter::InputImeEventRouter() 496 : next_request_id_(1) { 497} 498 499InputImeEventRouter::~InputImeEventRouter() {} 500 501bool SetCompositionFunction::RunImpl() { 502 chromeos::InputMethodEngine* engine = 503 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id()); 504 if (!engine) { 505 SetResult(Value::CreateBooleanValue(false)); 506 return true; 507 } 508 509 base::DictionaryValue* args; 510 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); 511 int context_id; 512 std::string text; 513 int selection_start; 514 int selection_end; 515 int cursor; 516 std::vector<chromeos::InputMethodEngine::SegmentInfo> segments; 517 518 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kContextIdKey, 519 &context_id)); 520 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kTextKey, &text)); 521 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kCursorKey, &cursor)); 522 if (args->HasKey(keys::kSelectionStartKey)) { 523 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kSelectionStartKey, 524 &selection_start)); 525 } else { 526 selection_start = cursor; 527 } 528 if (args->HasKey(keys::kSelectionEndKey)) { 529 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kSelectionEndKey, 530 &selection_end)); 531 } else { 532 selection_end = cursor; 533 } 534 535 if (args->HasKey(keys::kSegmentsKey)) { 536 base::ListValue* segment_list = NULL; 537 EXTENSION_FUNCTION_VALIDATE(args->GetList(keys::kSegmentsKey, 538 &segment_list)); 539 540 for (size_t i = 0; i < segment_list->GetSize(); ++i) { 541 base::DictionaryValue* segment = NULL; 542 if (!segment_list->GetDictionary(i, &segment)) 543 continue; 544 545 int start; 546 int end; 547 std::string style; 548 549 EXTENSION_FUNCTION_VALIDATE(segment->GetInteger(keys::kStartKey, 550 &start)); 551 EXTENSION_FUNCTION_VALIDATE(segment->GetInteger(keys::kEndKey, &end)); 552 EXTENSION_FUNCTION_VALIDATE(segment->GetString(keys::kStyleKey, &style)); 553 554 segments.push_back(chromeos::InputMethodEngine::SegmentInfo()); 555 segments.back().start = start; 556 segments.back().end = end; 557 if (style == keys::kStyleUnderline) { 558 segments.back().style = 559 chromeos::InputMethodEngine::SEGMENT_STYLE_UNDERLINE; 560 } else if (style == keys::kStyleDoubleUnderline) { 561 segments.back().style = 562 chromeos::InputMethodEngine::SEGMENT_STYLE_DOUBLE_UNDERLINE; 563 } 564 } 565 } 566 567 if (engine->SetComposition(context_id, text.c_str(), selection_start, 568 selection_end, cursor, segments, &error_)) { 569 SetResult(Value::CreateBooleanValue(true)); 570 } else { 571 SetResult(Value::CreateBooleanValue(false)); 572 } 573 return true; 574} 575 576bool ClearCompositionFunction::RunImpl() { 577 chromeos::InputMethodEngine* engine = 578 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id()); 579 if (!engine) { 580 SetResult(Value::CreateBooleanValue(false)); 581 return true; 582 } 583 584 base::DictionaryValue* args; 585 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); 586 int context_id; 587 588 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kContextIdKey, 589 &context_id)); 590 591 if (engine->ClearComposition(context_id, &error_)) { 592 SetResult(Value::CreateBooleanValue(true)); 593 } else { 594 SetResult(Value::CreateBooleanValue(false)); 595 } 596 return true; 597} 598 599bool CommitTextFunction::RunImpl() { 600 // TODO(zork): Support committing when not active. 601 chromeos::InputMethodEngine* engine = 602 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id()); 603 if (!engine) { 604 SetResult(Value::CreateBooleanValue(false)); 605 return true; 606 } 607 608 base::DictionaryValue* args; 609 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); 610 int context_id; 611 std::string text; 612 613 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kContextIdKey, 614 &context_id)); 615 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kTextKey, &text)); 616 617 if (engine->CommitText(context_id, text.c_str(), &error_)) { 618 SetResult(Value::CreateBooleanValue(true)); 619 } else { 620 SetResult(Value::CreateBooleanValue(false)); 621 } 622 return true; 623} 624 625bool SetCandidateWindowPropertiesFunction::RunImpl() { 626 base::DictionaryValue* args; 627 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); 628 629 std::string engine_id; 630 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kEngineIdKey, &engine_id)); 631 632 chromeos::InputMethodEngine* engine = 633 InputImeEventRouter::GetInstance()->GetEngine(extension_id(), engine_id); 634 if (!engine) { 635 SetResult(Value::CreateBooleanValue(false)); 636 return true; 637 } 638 639 base::DictionaryValue* properties; 640 EXTENSION_FUNCTION_VALIDATE(args->GetDictionary(keys::kPropertiesKey, 641 &properties)); 642 643 if (properties->HasKey(keys::kVisibleKey)) { 644 bool visible; 645 EXTENSION_FUNCTION_VALIDATE(properties->GetBoolean(keys::kVisibleKey, 646 &visible)); 647 if (!engine->SetCandidateWindowVisible(visible, &error_)) { 648 SetResult(Value::CreateBooleanValue(false)); 649 return true; 650 } 651 } 652 653 if (properties->HasKey(keys::kCursorVisibleKey)) { 654 bool visible; 655 EXTENSION_FUNCTION_VALIDATE(properties->GetBoolean(keys::kCursorVisibleKey, 656 &visible)); 657 engine->SetCandidateWindowCursorVisible(visible); 658 } 659 660 if (properties->HasKey(keys::kVerticalKey)) { 661 bool vertical; 662 EXTENSION_FUNCTION_VALIDATE(properties->GetBoolean(keys::kVerticalKey, 663 &vertical)); 664 engine->SetCandidateWindowVertical(vertical); 665 } 666 667 if (properties->HasKey(keys::kPageSizeKey)) { 668 int page_size; 669 EXTENSION_FUNCTION_VALIDATE(properties->GetInteger(keys::kPageSizeKey, 670 &page_size)); 671 engine->SetCandidateWindowPageSize(page_size); 672 } 673 674 if (properties->HasKey(keys::kAuxiliaryTextKey)) { 675 std::string aux_text; 676 EXTENSION_FUNCTION_VALIDATE(properties->GetString(keys::kAuxiliaryTextKey, 677 &aux_text)); 678 engine->SetCandidateWindowAuxText(aux_text.c_str()); 679 } 680 681 if (properties->HasKey(keys::kAuxiliaryTextVisibleKey)) { 682 bool visible; 683 EXTENSION_FUNCTION_VALIDATE(properties->GetBoolean( 684 keys::kAuxiliaryTextVisibleKey, 685 &visible)); 686 engine->SetCandidateWindowAuxTextVisible(visible); 687 } 688 689 if (properties->HasKey(keys::kWindowPositionKey)) { 690 // TODO(nona): Switch to scheme compiler. (crbug.com/235552) 691 chromeos::InputMethodEngine::CandidateWindowPosition window_position = 692 chromeos::InputMethodEngine::WINDOW_POS_CURSOR; 693 std::string position_in_str; 694 EXTENSION_FUNCTION_VALIDATE(properties->GetString( 695 keys::kWindowPositionKey, 696 &position_in_str)); 697 window_position = (position_in_str == kWindowPositionComposition) ? 698 chromeos::InputMethodEngine::WINDOW_POS_COMPOSITTION : 699 chromeos::InputMethodEngine::WINDOW_POS_CURSOR; 700 engine->SetCandidateWindowPosition(window_position); 701 } 702 703 SetResult(Value::CreateBooleanValue(true)); 704 705 return true; 706} 707 708#if defined(OS_CHROMEOS) 709bool SetCandidatesFunction::ReadCandidates( 710 base::ListValue* candidates, 711 std::vector<chromeos::InputMethodEngine::Candidate>* output) { 712 for (size_t i = 0; i < candidates->GetSize(); ++i) { 713 base::DictionaryValue* candidate_dict; 714 EXTENSION_FUNCTION_VALIDATE(candidates->GetDictionary(i, &candidate_dict)); 715 716 std::string candidate; 717 int id; 718 std::string label; 719 std::string annotation; 720 chromeos::InputMethodEngine::UsageEntry usage_entry; 721 722 EXTENSION_FUNCTION_VALIDATE(candidate_dict->GetString(keys::kCandidateKey, 723 &candidate)); 724 EXTENSION_FUNCTION_VALIDATE(candidate_dict->GetInteger(keys::kIdKey, &id)); 725 726 if (candidate_dict->HasKey(keys::kLabelKey)) { 727 EXTENSION_FUNCTION_VALIDATE(candidate_dict->GetString(keys::kLabelKey, 728 &label)); 729 } 730 if (candidate_dict->HasKey(keys::kAnnotationKey)) { 731 EXTENSION_FUNCTION_VALIDATE(candidate_dict->GetString( 732 keys::kAnnotationKey, 733 &annotation)); 734 } 735 736 if (candidate_dict->HasKey(keys::kUsageKey)) { 737 base::DictionaryValue* usage_dict; 738 EXTENSION_FUNCTION_VALIDATE(candidate_dict->GetDictionary(keys::kUsageKey, 739 &usage_dict)); 740 EXTENSION_FUNCTION_VALIDATE(usage_dict->GetString(keys::kUsageTitleKey, 741 &usage_entry.title)); 742 EXTENSION_FUNCTION_VALIDATE(usage_dict->GetString(keys::kUsageBodyKey, 743 &usage_entry.body)); 744 } 745 746 output->push_back(chromeos::InputMethodEngine::Candidate()); 747 output->back().value = candidate; 748 output->back().id = id; 749 output->back().label = label; 750 output->back().annotation = annotation; 751 output->back().usage = usage_entry; 752 753 if (candidate_dict->HasKey(keys::kCandidatesKey)) { 754 base::ListValue* sub_list; 755 EXTENSION_FUNCTION_VALIDATE(candidate_dict->GetList(keys::kCandidatesKey, 756 &sub_list)); 757 if (!ReadCandidates(sub_list, &(output->back().candidates))) { 758 error_ = kErrorBadCandidateList; 759 return false; 760 } 761 } 762 } 763 764 return true; 765} 766 767bool SetCandidatesFunction::RunImpl() { 768 chromeos::InputMethodEngine* engine = 769 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id()); 770 if (!engine) { 771 SetResult(Value::CreateBooleanValue(false)); 772 return true; 773 } 774 775 base::DictionaryValue* args; 776 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); 777 778 int context_id; 779 std::vector<chromeos::InputMethodEngine::Candidate> candidates; 780 781 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kContextIdKey, 782 &context_id)); 783 784 base::ListValue* candidate_list; 785 EXTENSION_FUNCTION_VALIDATE(args->GetList(keys::kCandidatesKey, 786 &candidate_list)); 787 if (!ReadCandidates(candidate_list, &candidates)) { 788 error_ = kErrorBadCandidateList; 789 return false; 790 } 791 792 std::string error; 793 if (engine->SetCandidates(context_id, candidates, &error_)) { 794 SetResult(Value::CreateBooleanValue(true)); 795 } else { 796 SetResult(Value::CreateBooleanValue(false)); 797 } 798 return true; 799} 800 801bool SetCursorPositionFunction::RunImpl() { 802 chromeos::InputMethodEngine* engine = 803 InputImeEventRouter::GetInstance()->GetActiveEngine(extension_id()); 804 if (!engine) { 805 SetResult(Value::CreateBooleanValue(false)); 806 return true; 807 } 808 809 base::DictionaryValue* args; 810 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); 811 int context_id; 812 int candidate_id; 813 814 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kContextIdKey, 815 &context_id)); 816 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kCandidateIdKey, 817 &candidate_id)); 818 819 if (engine->SetCursorPosition(context_id, candidate_id, &error_)) { 820 SetResult(Value::CreateBooleanValue(true)); 821 } else { 822 SetResult(Value::CreateBooleanValue(false)); 823 } 824 return true; 825} 826 827bool SetMenuItemsFunction::RunImpl() { 828 base::DictionaryValue* args; 829 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); 830 831 std::string engine_id; 832 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kEngineIdKey, &engine_id)); 833 834 chromeos::InputMethodEngine* engine = 835 InputImeEventRouter::GetInstance()->GetEngine(extension_id(), engine_id); 836 if (!engine) { 837 error_ = kErrorEngineNotAvailable; 838 return false; 839 } 840 841 base::ListValue* items; 842 EXTENSION_FUNCTION_VALIDATE(args->GetList(keys::kItemsKey, &items)); 843 844 std::vector<chromeos::InputMethodEngine::MenuItem> menu_items; 845 EXTENSION_FUNCTION_VALIDATE(ReadMenuItems(items, &menu_items)); 846 847 if (!engine->SetMenuItems(menu_items)) { 848 error_ = kErrorSetMenuItemsFail; 849 } 850 return true; 851} 852 853bool UpdateMenuItemsFunction::RunImpl() { 854 base::DictionaryValue* args; 855 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); 856 857 std::string engine_id; 858 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kEngineIdKey, &engine_id)); 859 860 chromeos::InputMethodEngine* engine = 861 InputImeEventRouter::GetInstance()->GetEngine(extension_id(), engine_id); 862 if (!engine) { 863 error_ = kErrorEngineNotAvailable; 864 return false; 865 } 866 867 base::ListValue* items; 868 EXTENSION_FUNCTION_VALIDATE(args->GetList(keys::kItemsKey, &items)); 869 870 std::vector<chromeos::InputMethodEngine::MenuItem> menu_items; 871 EXTENSION_FUNCTION_VALIDATE(ReadMenuItems(items, &menu_items)); 872 873 if (!engine->UpdateMenuItems(menu_items)) { 874 error_ = kErrorUpdateMenuItemsFail; 875 } 876 return true; 877} 878 879bool DeleteSurroundingTextFunction::RunImpl() { 880 base::DictionaryValue* args; 881 EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &args)); 882 883 std::string engine_id; 884 EXTENSION_FUNCTION_VALIDATE(args->GetString(keys::kEngineIdKey, &engine_id)); 885 886 chromeos::InputMethodEngine* engine = 887 InputImeEventRouter::GetInstance()->GetEngine(extension_id(), engine_id); 888 if (!engine) { 889 error_ = kErrorEngineNotAvailable; 890 return false; 891 } 892 893 int context_id = 0; 894 int offset = 0; 895 int length = 0; 896 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kContextIdKey, 897 &context_id)); 898 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kOffsetKey, &offset)); 899 EXTENSION_FUNCTION_VALIDATE(args->GetInteger(keys::kLengthKey, &length)); 900 901 engine->DeleteSurroundingText(context_id, offset, length, &error_); 902 return true; 903} 904 905bool KeyEventHandled::RunImpl() { 906 std::string request_id_str; 907 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &request_id_str)); 908 909 bool handled = false; 910 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &handled)); 911 912 InputImeEventRouter::GetInstance()->OnKeyEventHandled( 913 extension_id(), request_id_str, handled); 914 915 return true; 916} 917#endif 918 919InputImeAPI::InputImeAPI(Profile* profile) 920 : profile_(profile) { 921 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, 922 content::Source<Profile>(profile)); 923 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 924 content::Source<Profile>(profile)); 925 926 ExtensionFunctionRegistry* registry = 927 ExtensionFunctionRegistry::GetInstance(); 928 registry->RegisterFunction<SetCompositionFunction>(); 929 registry->RegisterFunction<ClearCompositionFunction>(); 930 registry->RegisterFunction<CommitTextFunction>(); 931 registry->RegisterFunction<SetCandidateWindowPropertiesFunction>(); 932 registry->RegisterFunction<SetCandidatesFunction>(); 933 registry->RegisterFunction<SetCursorPositionFunction>(); 934 registry->RegisterFunction<SetMenuItemsFunction>(); 935 registry->RegisterFunction<UpdateMenuItemsFunction>(); 936 registry->RegisterFunction<DeleteSurroundingTextFunction>(); 937 registry->RegisterFunction<KeyEventHandled>(); 938} 939 940InputImeAPI::~InputImeAPI() { 941} 942 943static base::LazyInstance<ProfileKeyedAPIFactory<InputImeAPI> > 944g_factory = LAZY_INSTANCE_INITIALIZER; 945 946// static 947ProfileKeyedAPIFactory<InputImeAPI>* InputImeAPI::GetFactoryInstance() { 948 return &g_factory.Get(); 949} 950 951void InputImeAPI::Observe(int type, 952 const content::NotificationSource& source, 953 const content::NotificationDetails& details) { 954 if (type == chrome::NOTIFICATION_EXTENSION_LOADED) { 955 const Extension* extension = 956 content::Details<const Extension>(details).ptr(); 957 const std::vector<InputComponentInfo>* input_components = 958 extensions::InputComponents::GetInputComponents(extension); 959 if (!input_components) 960 return; 961 for (std::vector<extensions::InputComponentInfo>::const_iterator component = 962 input_components->begin(); component != input_components->end(); 963 ++component) { 964 if (component->type == extensions::INPUT_COMPONENT_TYPE_IME) { 965 input_ime_event_router()->RegisterIme( 966 profile_, extension->id(), *component); 967 } 968 } 969 } else if (type == chrome::NOTIFICATION_EXTENSION_UNLOADED) { 970 const Extension* extension = 971 content::Details<const UnloadedExtensionInfo>(details)->extension; 972 const std::vector<InputComponentInfo>* input_components = 973 extensions::InputComponents::GetInputComponents(extension); 974 if (!input_components) 975 return; 976 if (input_components->size() > 0) { 977 input_ime_event_router()->UnregisterAllImes(profile_, extension->id()); 978 } 979 } 980} 981 982InputImeEventRouter* InputImeAPI::input_ime_event_router() { 983 return InputImeEventRouter::GetInstance(); 984} 985 986} // namespace extensions 987