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/chromeos/input_method/input_method_manager_impl.h" 6 7 #include <algorithm> // std::find 8 9 #include "ash/ime/input_method_menu_item.h" 10 #include "ash/ime/input_method_menu_manager.h" 11 #include "base/basictypes.h" 12 #include "base/bind.h" 13 #include "base/location.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/prefs/pref_service.h" 16 #include "base/strings/string_split.h" 17 #include "base/strings/string_util.h" 18 #include "base/strings/stringprintf.h" 19 #include "base/sys_info.h" 20 #include "chrome/browser/browser_process.h" 21 #include "chrome/browser/chromeos/input_method/candidate_window_controller.h" 22 #include "chrome/browser/chromeos/input_method/component_extension_ime_manager_impl.h" 23 #include "chrome/browser/chromeos/input_method/input_method_engine.h" 24 #include "chrome/browser/chromeos/language_preferences.h" 25 #include "chrome/browser/chromeos/login/users/user_manager.h" 26 #include "chrome/browser/profiles/profile_manager.h" 27 #include "chrome/common/pref_names.h" 28 #include "chromeos/ime/component_extension_ime_manager.h" 29 #include "chromeos/ime/extension_ime_util.h" 30 #include "chromeos/ime/fake_ime_keyboard.h" 31 #include "chromeos/ime/ime_keyboard.h" 32 #include "chromeos/ime/input_method_delegate.h" 33 #include "third_party/icu/source/common/unicode/uloc.h" 34 #include "ui/base/accelerators/accelerator.h" 35 36 namespace chromeos { 37 namespace input_method { 38 39 namespace { 40 41 bool Contains(const std::vector<std::string>& container, 42 const std::string& value) { 43 return std::find(container.begin(), container.end(), value) != 44 container.end(); 45 } 46 47 } // namespace 48 49 bool InputMethodManagerImpl::IsLoginKeyboard( 50 const std::string& layout) const { 51 return util_.IsLoginKeyboard(layout); 52 } 53 54 bool InputMethodManagerImpl::MigrateInputMethods( 55 std::vector<std::string>* input_method_ids) { 56 return util_.MigrateInputMethods(input_method_ids); 57 } 58 59 InputMethodManagerImpl::InputMethodManagerImpl( 60 scoped_ptr<InputMethodDelegate> delegate) 61 : delegate_(delegate.Pass()), 62 state_(STATE_LOGIN_SCREEN), 63 util_(delegate_.get(), whitelist_.GetSupportedInputMethods()), 64 component_extension_ime_manager_(new ComponentExtensionIMEManager()), 65 weak_ptr_factory_(this) { 66 } 67 68 InputMethodManagerImpl::~InputMethodManagerImpl() { 69 if (candidate_window_controller_.get()) 70 candidate_window_controller_->RemoveObserver(this); 71 } 72 73 void InputMethodManagerImpl::AddObserver( 74 InputMethodManager::Observer* observer) { 75 observers_.AddObserver(observer); 76 } 77 78 void InputMethodManagerImpl::AddCandidateWindowObserver( 79 InputMethodManager::CandidateWindowObserver* observer) { 80 candidate_window_observers_.AddObserver(observer); 81 } 82 83 void InputMethodManagerImpl::RemoveObserver( 84 InputMethodManager::Observer* observer) { 85 observers_.RemoveObserver(observer); 86 } 87 88 void InputMethodManagerImpl::RemoveCandidateWindowObserver( 89 InputMethodManager::CandidateWindowObserver* observer) { 90 candidate_window_observers_.RemoveObserver(observer); 91 } 92 93 void InputMethodManagerImpl::SetState(State new_state) { 94 const State old_state = state_; 95 state_ = new_state; 96 switch (state_) { 97 case STATE_LOGIN_SCREEN: 98 break; 99 case STATE_BROWSER_SCREEN: 100 if (old_state == STATE_LOCK_SCREEN) 101 OnScreenUnlocked(); 102 break; 103 case STATE_LOCK_SCREEN: 104 OnScreenLocked(); 105 break; 106 case STATE_TERMINATING: { 107 if (candidate_window_controller_.get()) 108 candidate_window_controller_.reset(); 109 break; 110 } 111 } 112 } 113 114 scoped_ptr<InputMethodDescriptors> 115 InputMethodManagerImpl::GetSupportedInputMethods() const { 116 if (!IsXkbComponentExtensionAvailable()) 117 return whitelist_.GetSupportedInputMethods().Pass(); 118 return scoped_ptr<InputMethodDescriptors>(new InputMethodDescriptors).Pass(); 119 } 120 121 scoped_ptr<InputMethodDescriptors> 122 InputMethodManagerImpl::GetActiveInputMethods() const { 123 scoped_ptr<InputMethodDescriptors> result(new InputMethodDescriptors); 124 // Build the active input method descriptors from the active input 125 // methods cache |active_input_method_ids_|. 126 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) { 127 const std::string& input_method_id = active_input_method_ids_[i]; 128 const InputMethodDescriptor* descriptor = 129 util_.GetInputMethodDescriptorFromId(input_method_id); 130 if (descriptor) { 131 result->push_back(*descriptor); 132 } else { 133 std::map<std::string, InputMethodDescriptor>::const_iterator ix = 134 extra_input_methods_.find(input_method_id); 135 if (ix != extra_input_methods_.end()) 136 result->push_back(ix->second); 137 else 138 DVLOG(1) << "Descriptor is not found for: " << input_method_id; 139 } 140 } 141 if (result->empty()) { 142 // Initially |active_input_method_ids_| is empty. browser_tests might take 143 // this path. 144 result->push_back( 145 InputMethodUtil::GetFallbackInputMethodDescriptor()); 146 } 147 return result.Pass(); 148 } 149 150 const std::vector<std::string>& 151 InputMethodManagerImpl::GetActiveInputMethodIds() const { 152 return active_input_method_ids_; 153 } 154 155 size_t InputMethodManagerImpl::GetNumActiveInputMethods() const { 156 return active_input_method_ids_.size(); 157 } 158 159 const InputMethodDescriptor* InputMethodManagerImpl::GetInputMethodFromId( 160 const std::string& input_method_id) const { 161 const InputMethodDescriptor* ime = util_.GetInputMethodDescriptorFromId( 162 input_method_id); 163 if (!ime) { 164 std::map<std::string, InputMethodDescriptor>::const_iterator ix = 165 extra_input_methods_.find(input_method_id); 166 if (ix != extra_input_methods_.end()) 167 ime = &ix->second; 168 } 169 return ime; 170 } 171 172 void InputMethodManagerImpl::EnableLoginLayouts( 173 const std::string& language_code, 174 const std::vector<std::string>& initial_layouts) { 175 if (state_ == STATE_TERMINATING) 176 return; 177 178 // First, hardware keyboard layout should be shown. 179 std::vector<std::string> candidates = 180 util_.GetHardwareLoginInputMethodIds(); 181 182 // Seocnd, locale based input method should be shown. 183 // Add input methods associated with the language. 184 std::vector<std::string> layouts_from_locale; 185 util_.GetInputMethodIdsFromLanguageCode(language_code, 186 kKeyboardLayoutsOnly, 187 &layouts_from_locale); 188 candidates.insert(candidates.end(), layouts_from_locale.begin(), 189 layouts_from_locale.end()); 190 191 std::vector<std::string> layouts; 192 // First, add the initial input method ID, if it's requested, to 193 // layouts, so it appears first on the list of active input 194 // methods at the input language status menu. 195 for (size_t i = 0; i < initial_layouts.size(); ++i) { 196 if (util_.IsValidInputMethodId(initial_layouts[i])) { 197 if (IsLoginKeyboard(initial_layouts[i])) { 198 layouts.push_back(initial_layouts[i]); 199 } else { 200 DVLOG(1) 201 << "EnableLoginLayouts: ignoring non-login initial keyboard layout:" 202 << initial_layouts[i]; 203 } 204 } else if (!initial_layouts[i].empty()) { 205 DVLOG(1) << "EnableLoginLayouts: ignoring non-keyboard or invalid ID: " 206 << initial_layouts[i]; 207 } 208 } 209 210 // Add candidates to layouts, while skipping duplicates. 211 for (size_t i = 0; i < candidates.size(); ++i) { 212 const std::string& candidate = candidates[i]; 213 // Not efficient, but should be fine, as the two vectors are very 214 // short (2-5 items). 215 if (!Contains(layouts, candidate) && IsLoginKeyboard(candidate)) 216 layouts.push_back(candidate); 217 } 218 219 MigrateInputMethods(&layouts); 220 active_input_method_ids_.swap(layouts); 221 222 // Initialize candidate window controller and widgets such as 223 // candidate window, infolist and mode indicator. Note, mode 224 // indicator is used by only keyboard layout input methods. 225 if (active_input_method_ids_.size() > 1) 226 MaybeInitializeCandidateWindowController(); 227 228 // you can pass empty |initial_layout|. 229 ChangeInputMethod(initial_layouts.empty() ? "" : 230 extension_ime_util::GetInputMethodIDByEngineID(initial_layouts[0])); 231 } 232 233 // Adds new input method to given list. 234 bool InputMethodManagerImpl::EnableInputMethodImpl( 235 const std::string& input_method_id, 236 std::vector<std::string>* new_active_input_method_ids) const { 237 DCHECK(new_active_input_method_ids); 238 if (!util_.IsValidInputMethodId(input_method_id)) { 239 DVLOG(1) << "EnableInputMethod: Invalid ID: " << input_method_id; 240 return false; 241 } 242 243 if (!Contains(*new_active_input_method_ids, input_method_id)) 244 new_active_input_method_ids->push_back(input_method_id); 245 246 return true; 247 } 248 249 // Starts or stops the system input method framework as needed. 250 void InputMethodManagerImpl::ReconfigureIMFramework() { 251 LoadNecessaryComponentExtensions(); 252 253 // Initialize candidate window controller and widgets such as 254 // candidate window, infolist and mode indicator. Note, mode 255 // indicator is used by only keyboard layout input methods. 256 MaybeInitializeCandidateWindowController(); 257 } 258 259 bool InputMethodManagerImpl::EnableInputMethod( 260 const std::string& input_method_id) { 261 if (!EnableInputMethodImpl(input_method_id, &active_input_method_ids_)) 262 return false; 263 264 ReconfigureIMFramework(); 265 return true; 266 } 267 268 bool InputMethodManagerImpl::ReplaceEnabledInputMethods( 269 const std::vector<std::string>& new_active_input_method_ids) { 270 if (state_ == STATE_TERMINATING) 271 return false; 272 273 // Filter unknown or obsolete IDs. 274 std::vector<std::string> new_active_input_method_ids_filtered; 275 276 for (size_t i = 0; i < new_active_input_method_ids.size(); ++i) 277 EnableInputMethodImpl(new_active_input_method_ids[i], 278 &new_active_input_method_ids_filtered); 279 280 if (new_active_input_method_ids_filtered.empty()) { 281 DVLOG(1) << "ReplaceEnabledInputMethods: No valid input method ID"; 282 return false; 283 } 284 285 // Copy extension IDs to |new_active_input_method_ids_filtered|. We have to 286 // keep relative order of the extension input method IDs. 287 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) { 288 const std::string& input_method_id = active_input_method_ids_[i]; 289 if (extension_ime_util::IsExtensionIME(input_method_id)) 290 new_active_input_method_ids_filtered.push_back(input_method_id); 291 } 292 active_input_method_ids_.swap(new_active_input_method_ids_filtered); 293 MigrateInputMethods(&active_input_method_ids_); 294 295 ReconfigureIMFramework(); 296 297 // If |current_input_method| is no longer in |active_input_method_ids_|, 298 // ChangeInputMethod() picks the first one in |active_input_method_ids_|. 299 ChangeInputMethod(current_input_method_.id()); 300 return true; 301 } 302 303 void InputMethodManagerImpl::ChangeInputMethod( 304 const std::string& input_method_id) { 305 ChangeInputMethodInternal(input_method_id, false); 306 } 307 308 bool InputMethodManagerImpl::ChangeInputMethodInternal( 309 const std::string& input_method_id, 310 bool show_message) { 311 if (state_ == STATE_TERMINATING) 312 return false; 313 314 std::string input_method_id_to_switch = input_method_id; 315 316 // Sanity check. 317 if (!InputMethodIsActivated(input_method_id)) { 318 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods()); 319 DCHECK(!input_methods->empty()); 320 input_method_id_to_switch = input_methods->at(0).id(); 321 if (!input_method_id.empty()) { 322 DVLOG(1) << "Can't change the current input method to " 323 << input_method_id << " since the engine is not enabled. " 324 << "Switch to " << input_method_id_to_switch << " instead."; 325 } 326 } 327 328 if (!component_extension_ime_manager_->IsInitialized() && 329 !InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch)) { 330 // We can't change input method before the initialization of 331 // component extension ime manager. ChangeInputMethod will be 332 // called with |pending_input_method_| when the initialization is 333 // done. 334 pending_input_method_ = input_method_id_to_switch; 335 return false; 336 } 337 pending_input_method_.clear(); 338 339 // Hide candidate window and info list. 340 if (candidate_window_controller_.get()) 341 candidate_window_controller_->Hide(); 342 343 // Disable the current engine handler. 344 IMEEngineHandlerInterface* engine = 345 IMEBridge::Get()->GetCurrentEngineHandler(); 346 if (engine) 347 engine->Disable(); 348 349 // Configure the next engine handler. 350 if (InputMethodUtil::IsKeyboardLayout(input_method_id_to_switch) && 351 !extension_ime_util::IsKeyboardLayoutExtension( 352 input_method_id_to_switch)) { 353 IMEBridge::Get()->SetCurrentEngineHandler(NULL); 354 } else { 355 IMEEngineHandlerInterface* next_engine = 356 profile_engine_map_[GetProfile()][input_method_id_to_switch]; 357 if (next_engine) { 358 IMEBridge::Get()->SetCurrentEngineHandler(next_engine); 359 next_engine->Enable(); 360 } 361 } 362 363 // TODO(komatsu): Check if it is necessary to perform the above routine 364 // when the current input method is equal to |input_method_id_to_swich|. 365 if (current_input_method_.id() != input_method_id_to_switch) { 366 // Clear property list. Property list would be updated by 367 // extension IMEs via InputMethodEngine::(Set|Update)MenuItems. 368 // If the current input method is a keyboard layout, empty 369 // properties are sufficient. 370 const ash::ime::InputMethodMenuItemList empty_menu_item_list; 371 ash::ime::InputMethodMenuManager* input_method_menu_manager = 372 ash::ime::InputMethodMenuManager::GetInstance(); 373 input_method_menu_manager->SetCurrentInputMethodMenuItemList( 374 empty_menu_item_list); 375 376 const InputMethodDescriptor* descriptor = NULL; 377 if (extension_ime_util::IsExtensionIME(input_method_id_to_switch)) { 378 DCHECK(extra_input_methods_.find(input_method_id_to_switch) != 379 extra_input_methods_.end()); 380 descriptor = &(extra_input_methods_[input_method_id_to_switch]); 381 } else { 382 descriptor = 383 util_.GetInputMethodDescriptorFromId(input_method_id_to_switch); 384 if (!descriptor) 385 LOG(ERROR) << "Unknown input method id: " << input_method_id_to_switch; 386 } 387 DCHECK(descriptor); 388 389 previous_input_method_ = current_input_method_; 390 current_input_method_ = *descriptor; 391 } 392 393 // Change the keyboard layout to a preferred layout for the input method. 394 if (!keyboard_->SetCurrentKeyboardLayoutByName( 395 current_input_method_.GetPreferredKeyboardLayout())) { 396 LOG(ERROR) << "Failed to change keyboard layout to " 397 << current_input_method_.GetPreferredKeyboardLayout(); 398 } 399 400 // Update input method indicators (e.g. "US", "DV") in Chrome windows. 401 FOR_EACH_OBSERVER(InputMethodManager::Observer, 402 observers_, 403 InputMethodChanged(this, show_message)); 404 return true; 405 } 406 407 bool InputMethodManagerImpl::IsXkbComponentExtensionAvailable() const { 408 if (!component_extension_ime_manager_->IsInitialized()) 409 return false; 410 InputMethodDescriptors imes = 411 component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor(); 412 for (size_t i = 0; i < imes.size(); ++i) { 413 if (StartsWithASCII(extension_ime_util::MaybeGetLegacyXkbId( 414 imes[i].id()), "xkb:", true)) 415 return true; 416 } 417 return false; 418 } 419 420 void InputMethodManagerImpl::OnComponentExtensionInitialized( 421 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) { 422 DCHECK(thread_checker_.CalledOnValidThread()); 423 component_extension_ime_manager_->Initialize(delegate.Pass()); 424 InputMethodDescriptors imes = 425 component_extension_ime_manager_->GetAllIMEAsInputMethodDescriptor(); 426 // In case of XKB extension is not available (e.g. linux_chromeos), don't 427 // reset the input methods in InputMethodUtil, Instead append input methods. 428 if (IsXkbComponentExtensionAvailable()) 429 util_.ResetInputMethods(imes); 430 else 431 util_.AppendInputMethods(imes); 432 433 LoadNecessaryComponentExtensions(); 434 435 if (!pending_input_method_.empty()) 436 ChangeInputMethodInternal(pending_input_method_, false); 437 } 438 439 void InputMethodManagerImpl::LoadNecessaryComponentExtensions() { 440 if (!component_extension_ime_manager_->IsInitialized()) 441 return; 442 // Load component extensions but also update |active_input_method_ids_| as 443 // some component extension IMEs may have been removed from the Chrome OS 444 // image. If specified component extension IME no longer exists, falling back 445 // to an existing IME. 446 std::vector<std::string> unfiltered_input_method_ids; 447 unfiltered_input_method_ids.swap(active_input_method_ids_); 448 for (size_t i = 0; i < unfiltered_input_method_ids.size(); ++i) { 449 if (!extension_ime_util::IsComponentExtensionIME( 450 unfiltered_input_method_ids[i])) { 451 // Legacy IMEs or xkb layouts are alwayes active. 452 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]); 453 } else if (component_extension_ime_manager_->IsWhitelisted( 454 unfiltered_input_method_ids[i])) { 455 component_extension_ime_manager_->LoadComponentExtensionIME( 456 unfiltered_input_method_ids[i]); 457 active_input_method_ids_.push_back(unfiltered_input_method_ids[i]); 458 } 459 } 460 // TODO(shuchen): move this call in ComponentExtensionIMEManager. 461 component_extension_ime_manager_->NotifyInitialized(); 462 } 463 464 void InputMethodManagerImpl::ActivateInputMethodMenuItem( 465 const std::string& key) { 466 DCHECK(!key.empty()); 467 468 if (ash::ime::InputMethodMenuManager::GetInstance()-> 469 HasInputMethodMenuItemForKey(key)) { 470 IMEEngineHandlerInterface* engine = 471 IMEBridge::Get()->GetCurrentEngineHandler(); 472 if (engine) 473 engine->PropertyActivate(key); 474 return; 475 } 476 477 DVLOG(1) << "ActivateInputMethodMenuItem: unknown key: " << key; 478 } 479 480 void InputMethodManagerImpl::AddInputMethodExtension( 481 Profile* profile, 482 const std::string& id, 483 InputMethodEngineInterface* engine) { 484 if (state_ == STATE_TERMINATING) 485 return; 486 487 DCHECK(engine); 488 489 profile_engine_map_[profile][id] = engine; 490 491 if (id == current_input_method_.id()) { 492 IMEBridge::Get()->SetCurrentEngineHandler(engine); 493 engine->Enable(); 494 } 495 496 if (extension_ime_util::IsComponentExtensionIME(id)) 497 return; 498 499 CHECK(extension_ime_util::IsExtensionIME(id)) 500 << id << "is not a valid extension input method ID"; 501 502 const InputMethodDescriptor& descriptor = engine->GetDescriptor(); 503 extra_input_methods_[id] = descriptor; 504 505 if (Contains(enabled_extension_imes_, id)) { 506 if (!Contains(active_input_method_ids_, id)) { 507 active_input_method_ids_.push_back(id); 508 } else { 509 DVLOG(1) << "AddInputMethodExtension: alread added: " 510 << id << ", " << descriptor.name(); 511 } 512 513 // Ensure that the input method daemon is running. 514 MaybeInitializeCandidateWindowController(); 515 } 516 } 517 518 void InputMethodManagerImpl::RemoveInputMethodExtension(Profile* profile, 519 const std::string& id) { 520 if (!extension_ime_util::IsExtensionIME(id)) 521 DVLOG(1) << id << " is not a valid extension input method ID."; 522 523 std::vector<std::string>::iterator i = std::find( 524 active_input_method_ids_.begin(), active_input_method_ids_.end(), id); 525 if (i != active_input_method_ids_.end()) 526 active_input_method_ids_.erase(i); 527 extra_input_methods_.erase(id); 528 529 EngineMap& engine_map = profile_engine_map_[profile]; 530 if (IMEBridge::Get()->GetCurrentEngineHandler() == engine_map[id]) 531 IMEBridge::Get()->SetCurrentEngineHandler(NULL); 532 engine_map.erase(id); 533 534 // No need to switch input method when terminating. 535 if (state_ != STATE_TERMINATING) { 536 // If |current_input_method| is no longer in |active_input_method_ids_|, 537 // switch to the first one in |active_input_method_ids_|. 538 ChangeInputMethod(current_input_method_.id()); 539 } 540 } 541 542 void InputMethodManagerImpl::GetInputMethodExtensions( 543 InputMethodDescriptors* result) { 544 // Build the extension input method descriptors from the extra input 545 // methods cache |extra_input_methods_|. 546 std::map<std::string, InputMethodDescriptor>::iterator iter; 547 for (iter = extra_input_methods_.begin(); iter != extra_input_methods_.end(); 548 ++iter) { 549 if (extension_ime_util::IsExtensionIME(iter->first)) 550 result->push_back(iter->second); 551 } 552 } 553 554 void InputMethodManagerImpl::SetEnabledExtensionImes( 555 std::vector<std::string>* ids) { 556 enabled_extension_imes_.clear(); 557 enabled_extension_imes_.insert(enabled_extension_imes_.end(), 558 ids->begin(), 559 ids->end()); 560 561 bool active_imes_changed = false; 562 563 for (std::map<std::string, InputMethodDescriptor>::iterator extra_iter = 564 extra_input_methods_.begin(); extra_iter != extra_input_methods_.end(); 565 ++extra_iter) { 566 if (extension_ime_util::IsComponentExtensionIME( 567 extra_iter->first)) 568 continue; // Do not filter component extension. 569 std::vector<std::string>::iterator active_iter = std::find( 570 active_input_method_ids_.begin(), active_input_method_ids_.end(), 571 extra_iter->first); 572 573 bool active = active_iter != active_input_method_ids_.end(); 574 bool enabled = Contains(enabled_extension_imes_, extra_iter->first); 575 576 if (active && !enabled) 577 active_input_method_ids_.erase(active_iter); 578 579 if (!active && enabled) 580 active_input_method_ids_.push_back(extra_iter->first); 581 582 if (active == !enabled) 583 active_imes_changed = true; 584 } 585 586 if (active_imes_changed) { 587 MaybeInitializeCandidateWindowController(); 588 589 // If |current_input_method| is no longer in |active_input_method_ids_|, 590 // switch to the first one in |active_input_method_ids_|. 591 ChangeInputMethod(current_input_method_.id()); 592 } 593 } 594 595 void InputMethodManagerImpl::SetInputMethodLoginDefaultFromVPD( 596 const std::string& locale, const std::string& oem_layout) { 597 std::string layout; 598 if (!oem_layout.empty()) { 599 // If the OEM layout information is provided, use it. 600 layout = oem_layout; 601 } else { 602 // Otherwise, determine the hardware keyboard from the locale. 603 std::vector<std::string> input_method_ids; 604 if (util_.GetInputMethodIdsFromLanguageCode( 605 locale, 606 chromeos::input_method::kKeyboardLayoutsOnly, 607 &input_method_ids)) { 608 // The output list |input_method_ids| is sorted by popularity, hence 609 // input_method_ids[0] now contains the most popular keyboard layout 610 // for the given locale. 611 DCHECK_GE(input_method_ids.size(), 1U); 612 layout = input_method_ids[0]; 613 } 614 } 615 616 if (layout.empty()) 617 return; 618 619 std::vector<std::string> layouts; 620 base::SplitString(layout, ',', &layouts); 621 MigrateInputMethods(&layouts); 622 623 PrefService* prefs = g_browser_process->local_state(); 624 prefs->SetString(prefs::kHardwareKeyboardLayout, JoinString(layouts, ",")); 625 626 // This asks the file thread to save the prefs (i.e. doesn't block). 627 // The latest values of Local State reside in memory so we can safely 628 // get the value of kHardwareKeyboardLayout even if the data is not 629 // yet saved to disk. 630 prefs->CommitPendingWrite(); 631 632 util_.UpdateHardwareLayoutCache(); 633 634 EnableLoginLayouts(locale, layouts); 635 } 636 637 void InputMethodManagerImpl::SetInputMethodLoginDefault() { 638 // Set up keyboards. For example, when |locale| is "en-US", enable US qwerty 639 // and US dvorak keyboard layouts. 640 if (g_browser_process && g_browser_process->local_state()) { 641 const std::string locale = g_browser_process->GetApplicationLocale(); 642 // If the preferred keyboard for the login screen has been saved, use it. 643 PrefService* prefs = g_browser_process->local_state(); 644 std::string initial_input_method_id = 645 prefs->GetString(chromeos::language_prefs::kPreferredKeyboardLayout); 646 std::vector<std::string> input_methods_to_be_enabled; 647 if (initial_input_method_id.empty()) { 648 // If kPreferredKeyboardLayout is not specified, use the hardware layout. 649 input_methods_to_be_enabled = util_.GetHardwareLoginInputMethodIds(); 650 } else { 651 input_methods_to_be_enabled.push_back(initial_input_method_id); 652 } 653 EnableLoginLayouts(locale, input_methods_to_be_enabled); 654 } 655 } 656 657 bool InputMethodManagerImpl::SwitchToNextInputMethod() { 658 // Sanity checks. 659 if (active_input_method_ids_.empty()) { 660 DVLOG(1) << "active input method is empty"; 661 return false; 662 } 663 664 if (current_input_method_.id().empty()) { 665 DVLOG(1) << "current_input_method_ is unknown"; 666 return false; 667 } 668 669 // Do not consume key event if there is only one input method is enabled. 670 // Ctrl+Space or Alt+Shift may be used by other application. 671 if (active_input_method_ids_.size() == 1) 672 return false; 673 674 // Find the next input method and switch to it. 675 SwitchToNextInputMethodInternal(active_input_method_ids_, 676 current_input_method_.id()); 677 return true; 678 } 679 680 bool InputMethodManagerImpl::SwitchToPreviousInputMethod( 681 const ui::Accelerator& accelerator) { 682 // Sanity check. 683 if (active_input_method_ids_.empty()) { 684 DVLOG(1) << "active input method is empty"; 685 return false; 686 } 687 688 // Do not consume key event if there is only one input method is enabled. 689 // Ctrl+Space or Alt+Shift may be used by other application. 690 if (active_input_method_ids_.size() == 1) 691 return false; 692 693 if (accelerator.type() == ui::ET_KEY_RELEASED) 694 return true; 695 696 if (previous_input_method_.id().empty() || 697 previous_input_method_.id() == current_input_method_.id()) { 698 return SwitchToNextInputMethod(); 699 } 700 701 std::vector<std::string>::const_iterator iter = 702 std::find(active_input_method_ids_.begin(), 703 active_input_method_ids_.end(), 704 previous_input_method_.id()); 705 if (iter == active_input_method_ids_.end()) { 706 // previous_input_method_ is not supported. 707 return SwitchToNextInputMethod(); 708 } 709 ChangeInputMethodInternal(*iter, true); 710 return true; 711 } 712 713 bool InputMethodManagerImpl::SwitchInputMethod( 714 const ui::Accelerator& accelerator) { 715 // Sanity check. 716 if (active_input_method_ids_.empty()) { 717 DVLOG(1) << "active input method is empty"; 718 return false; 719 } 720 721 // Get the list of input method ids for the |accelerator|. For example, get 722 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR. 723 std::vector<std::string> input_method_ids_to_switch; 724 switch (accelerator.key_code()) { 725 case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard 726 input_method_ids_to_switch.push_back( 727 extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp")); 728 break; 729 case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard 730 input_method_ids_to_switch.push_back( 731 extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn")); 732 break; 733 case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard 734 case ui::VKEY_DBE_DBCSCHAR: 735 input_method_ids_to_switch.push_back( 736 extension_ime_util::GetInputMethodIDByEngineID("nacl_mozc_jp")); 737 input_method_ids_to_switch.push_back( 738 extension_ime_util::GetInputMethodIDByEngineID("xkb:jp::jpn")); 739 break; 740 default: 741 NOTREACHED(); 742 break; 743 } 744 if (input_method_ids_to_switch.empty()) { 745 DVLOG(1) << "Unexpected VKEY: " << accelerator.key_code(); 746 return false; 747 } 748 749 // Obtain the intersection of input_method_ids_to_switch and 750 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is 751 // preserved. 752 std::vector<std::string> ids; 753 for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) { 754 const std::string& id = input_method_ids_to_switch[i]; 755 if (Contains(active_input_method_ids_, id)) 756 ids.push_back(id); 757 } 758 if (ids.empty()) { 759 // No input method for the accelerator is active. For example, we should 760 // just ignore VKEY_HANGUL when mozc-hangul is not active. 761 return false; 762 } 763 764 SwitchToNextInputMethodInternal(ids, current_input_method_.id()); 765 return true; // consume the accelerator. 766 } 767 768 void InputMethodManagerImpl::SwitchToNextInputMethodInternal( 769 const std::vector<std::string>& input_method_ids, 770 const std::string& current_input_method_id) { 771 std::vector<std::string>::const_iterator iter = 772 std::find(input_method_ids.begin(), 773 input_method_ids.end(), 774 current_input_method_id); 775 if (iter != input_method_ids.end()) 776 ++iter; 777 if (iter == input_method_ids.end()) 778 iter = input_method_ids.begin(); 779 ChangeInputMethodInternal(*iter, true); 780 } 781 782 InputMethodDescriptor InputMethodManagerImpl::GetCurrentInputMethod() const { 783 if (current_input_method_.id().empty()) 784 return InputMethodUtil::GetFallbackInputMethodDescriptor(); 785 786 return current_input_method_; 787 } 788 789 bool InputMethodManagerImpl::IsISOLevel5ShiftUsedByCurrentInputMethod() const { 790 return keyboard_->IsISOLevel5ShiftAvailable(); 791 } 792 793 bool InputMethodManagerImpl::IsAltGrUsedByCurrentInputMethod() const { 794 return keyboard_->IsAltGrAvailable(); 795 } 796 797 ImeKeyboard* InputMethodManagerImpl::GetImeKeyboard() { 798 return keyboard_.get(); 799 } 800 801 InputMethodUtil* InputMethodManagerImpl::GetInputMethodUtil() { 802 return &util_; 803 } 804 805 ComponentExtensionIMEManager* 806 InputMethodManagerImpl::GetComponentExtensionIMEManager() { 807 DCHECK(thread_checker_.CalledOnValidThread()); 808 return component_extension_ime_manager_.get(); 809 } 810 811 void InputMethodManagerImpl::InitializeComponentExtension() { 812 ComponentExtensionIMEManagerImpl* impl = 813 new ComponentExtensionIMEManagerImpl(); 814 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate(impl); 815 impl->InitializeAsync(base::Bind( 816 &InputMethodManagerImpl::OnComponentExtensionInitialized, 817 weak_ptr_factory_.GetWeakPtr(), 818 base::Passed(&delegate))); 819 } 820 821 void InputMethodManagerImpl::Init(base::SequencedTaskRunner* ui_task_runner) { 822 DCHECK(thread_checker_.CalledOnValidThread()); 823 824 if (base::SysInfo::IsRunningOnChromeOS()) 825 keyboard_.reset(ImeKeyboard::Create()); 826 else 827 keyboard_.reset(new FakeImeKeyboard()); 828 829 // We can't call impl->Initialize here, because file thread is not available 830 // at this moment. 831 ui_task_runner->PostTask( 832 FROM_HERE, 833 base::Bind(&InputMethodManagerImpl::InitializeComponentExtension, 834 weak_ptr_factory_.GetWeakPtr())); 835 } 836 837 void InputMethodManagerImpl::SetCandidateWindowControllerForTesting( 838 CandidateWindowController* candidate_window_controller) { 839 candidate_window_controller_.reset(candidate_window_controller); 840 candidate_window_controller_->AddObserver(this); 841 } 842 843 void InputMethodManagerImpl::SetImeKeyboardForTesting(ImeKeyboard* keyboard) { 844 keyboard_.reset(keyboard); 845 } 846 847 void InputMethodManagerImpl::InitializeComponentExtensionForTesting( 848 scoped_ptr<ComponentExtensionIMEManagerDelegate> delegate) { 849 OnComponentExtensionInitialized(delegate.Pass()); 850 } 851 852 void InputMethodManagerImpl::CandidateClicked(int index) { 853 IMEEngineHandlerInterface* engine = 854 IMEBridge::Get()->GetCurrentEngineHandler(); 855 if (engine) 856 engine->CandidateClicked(index); 857 } 858 859 void InputMethodManagerImpl::CandidateWindowOpened() { 860 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver, 861 candidate_window_observers_, 862 CandidateWindowOpened(this)); 863 } 864 865 void InputMethodManagerImpl::CandidateWindowClosed() { 866 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver, 867 candidate_window_observers_, 868 CandidateWindowClosed(this)); 869 } 870 871 void InputMethodManagerImpl::OnScreenLocked() { 872 saved_previous_input_method_ = previous_input_method_; 873 saved_current_input_method_ = current_input_method_; 874 saved_active_input_method_ids_ = active_input_method_ids_; 875 876 std::set<std::string> added_ids_; 877 878 const std::vector<std::string>& hardware_keyboard_ids = 879 util_.GetHardwareLoginInputMethodIds(); 880 881 active_input_method_ids_.clear(); 882 for (size_t i = 0; i < saved_active_input_method_ids_.size(); ++i) { 883 const std::string& input_method_id = saved_active_input_method_ids_[i]; 884 // Skip if it's not a keyboard layout. Drop input methods including 885 // extension ones. 886 if (!IsLoginKeyboard(input_method_id) || 887 added_ids_.find(input_method_id) != added_ids_.end()) 888 continue; 889 active_input_method_ids_.push_back(input_method_id); 890 added_ids_.insert(input_method_id); 891 } 892 893 // We'll add the hardware keyboard if it's not included in 894 // |active_input_method_ids_| so that the user can always use the hardware 895 // keyboard on the screen locker. 896 for (size_t i = 0; i < hardware_keyboard_ids.size(); ++i) { 897 if (added_ids_.find(hardware_keyboard_ids[i]) == added_ids_.end()) { 898 active_input_method_ids_.push_back(hardware_keyboard_ids[i]); 899 added_ids_.insert(hardware_keyboard_ids[i]); 900 } 901 } 902 903 ChangeInputMethod(current_input_method_.id()); 904 } 905 906 void InputMethodManagerImpl::OnScreenUnlocked() { 907 previous_input_method_ = saved_previous_input_method_; 908 current_input_method_ = saved_current_input_method_; 909 active_input_method_ids_ = saved_active_input_method_ids_; 910 911 ChangeInputMethod(current_input_method_.id()); 912 } 913 914 bool InputMethodManagerImpl::InputMethodIsActivated( 915 const std::string& input_method_id) { 916 return Contains(active_input_method_ids_, input_method_id); 917 } 918 919 void InputMethodManagerImpl::MaybeInitializeCandidateWindowController() { 920 if (candidate_window_controller_.get()) 921 return; 922 923 candidate_window_controller_.reset( 924 CandidateWindowController::CreateCandidateWindowController()); 925 candidate_window_controller_->AddObserver(this); 926 } 927 928 Profile* InputMethodManagerImpl::GetProfile() const { 929 return ProfileManager::GetActiveUserProfile(); 930 } 931 932 } // namespace input_method 933 } // namespace chromeos 934