input_method_library.cc revision 731df977c0511bca2206b5f333555b1205ff1f43
1// Copyright (c) 2010 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/cros/input_method_library.h" 6 7#include <glib.h> 8#include <signal.h> 9 10#include "unicode/uloc.h" 11 12#include "base/basictypes.h" 13#include "base/message_loop.h" 14#include "base/string_util.h" 15#include "chrome/browser/browser_process.h" 16#include "chrome/browser/browser_thread.h" 17#include "chrome/browser/chromeos/cros/cros_library.h" 18#include "chrome/browser/chromeos/cros/keyboard_library.h" 19#include "chrome/browser/chromeos/input_method/input_method_util.h" 20#include "chrome/browser/chromeos/language_preferences.h" 21#include "chrome/common/notification_observer.h" 22#include "chrome/common/notification_registrar.h" 23#include "chrome/common/notification_service.h" 24 25namespace { 26const char kIBusDaemonPath[] = "/usr/bin/ibus-daemon"; 27const char kCandidateWindowPath[] = "/opt/google/chrome/candidate_window"; 28 29// Finds a property which has |new_prop.key| from |prop_list|, and replaces the 30// property with |new_prop|. Returns true if such a property is found. 31bool FindAndUpdateProperty(const chromeos::ImeProperty& new_prop, 32 chromeos::ImePropertyList* prop_list) { 33 for (size_t i = 0; i < prop_list->size(); ++i) { 34 chromeos::ImeProperty& prop = prop_list->at(i); 35 if (prop.key == new_prop.key) { 36 const int saved_id = prop.selection_item_id; 37 // Update the list except the radio id. As written in 38 // chromeos_input_method.h, |prop.selection_item_id| is dummy. 39 prop = new_prop; 40 prop.selection_item_id = saved_id; 41 return true; 42 } 43 } 44 return false; 45} 46 47} // namespace 48 49namespace chromeos { 50 51class InputMethodLibraryImpl : public InputMethodLibrary, 52 public NotificationObserver { 53 public: 54 InputMethodLibraryImpl() 55 : input_method_status_connection_(NULL), 56 previous_input_method_("", "", "", ""), 57 current_input_method_("", "", "", ""), 58 should_launch_ime_(false), 59 ime_connected_(false), 60 defer_ime_startup_(false), 61 should_change_input_method_(false), 62 ibus_daemon_process_id_(0), 63 candidate_window_process_id_(0), 64 failure_count_(0) { 65 scoped_ptr<InputMethodDescriptors> input_method_descriptors( 66 CreateFallbackInputMethodDescriptors()); 67 current_input_method_ = input_method_descriptors->at(0); 68 if (CrosLibrary::Get()->EnsureLoaded()) { 69 current_input_method_id_ = chromeos::GetHardwareKeyboardLayoutName(); 70 } 71 // Observe APP_EXITING to stop input method processes gracefully. 72 // Note that even if we fail to stop input method processes from 73 // Chrome in case of a sudden crash, we have a way to do it from an 74 // upstart script. See crosbug.com/6515 and crosbug.com/6995 for 75 // details. 76 notification_registrar_.Add(this, NotificationType::APP_EXITING, 77 NotificationService::AllSources()); 78 } 79 80 ~InputMethodLibraryImpl() { 81 } 82 83 void AddObserver(Observer* observer) { 84 observers_.AddObserver(observer); 85 } 86 87 void RemoveObserver(Observer* observer) { 88 observers_.RemoveObserver(observer); 89 } 90 91 InputMethodDescriptors* GetActiveInputMethods() { 92 chromeos::InputMethodDescriptors* result = NULL; 93 // The connection does not need to be alive, but it does need to be created. 94 if (EnsureLoadedAndStarted()) { 95 result = chromeos::GetActiveInputMethods(input_method_status_connection_); 96 } 97 if (!result || result->empty()) { 98 result = CreateFallbackInputMethodDescriptors(); 99 } 100 return result; 101 } 102 103 size_t GetNumActiveInputMethods() { 104 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods()); 105 return input_methods->size(); 106 } 107 108 InputMethodDescriptors* GetSupportedInputMethods() { 109 InputMethodDescriptors* result = NULL; 110 // The connection does not need to be alive, but it does need to be created. 111 if (EnsureLoadedAndStarted()) { 112 result = chromeos::GetSupportedInputMethods( 113 input_method_status_connection_); 114 } 115 if (!result || result->empty()) { 116 result = CreateFallbackInputMethodDescriptors(); 117 } 118 return result; 119 } 120 121 void ChangeInputMethod(const std::string& input_method_id) { 122 current_input_method_id_ = input_method_id; 123 if (EnsureLoadedAndStarted()) { 124 if (input_method_id != chromeos::GetHardwareKeyboardLayoutName()) { 125 StartInputMethodProcesses(); 126 } 127 chromeos::ChangeInputMethod( 128 input_method_status_connection_, input_method_id.c_str()); 129 } 130 } 131 132 void SetImePropertyActivated(const std::string& key, bool activated) { 133 DCHECK(!key.empty()); 134 if (EnsureLoadedAndStarted()) { 135 chromeos::SetImePropertyActivated( 136 input_method_status_connection_, key.c_str(), activated); 137 } 138 } 139 140 bool InputMethodIsActivated(const std::string& input_method_id) { 141 scoped_ptr<InputMethodDescriptors> active_input_method_descriptors( 142 CrosLibrary::Get()->GetInputMethodLibrary()->GetActiveInputMethods()); 143 for (size_t i = 0; i < active_input_method_descriptors->size(); ++i) { 144 if (active_input_method_descriptors->at(i).id == input_method_id) { 145 return true; 146 } 147 } 148 return false; 149 } 150 151 bool GetImeConfig(const char* section, const char* config_name, 152 ImeConfigValue* out_value) { 153 bool success = false; 154 if (EnsureLoadedAndStarted()) { 155 success = chromeos::GetImeConfig(input_method_status_connection_, 156 section, config_name, out_value); 157 } 158 return success; 159 } 160 161 bool SetImeConfig(const char* section, const char* config_name, 162 const ImeConfigValue& value) { 163 MaybeStartOrStopInputMethodProcesses(section, config_name, value); 164 165 const ConfigKeyType key = std::make_pair(section, config_name); 166 current_config_values_[key] = value; 167 if (ime_connected_) { 168 pending_config_requests_[key] = value; 169 FlushImeConfig(); 170 } 171 return pending_config_requests_.empty(); 172 } 173 174 virtual const InputMethodDescriptor& previous_input_method() const { 175 return previous_input_method_; 176 } 177 virtual const InputMethodDescriptor& current_input_method() const { 178 return current_input_method_; 179 } 180 181 virtual const ImePropertyList& current_ime_properties() const { 182 return current_ime_properties_; 183 } 184 185 private: 186 // Starts or stops the input method processes based on the current state. 187 void MaybeStartOrStopInputMethodProcesses( 188 const char* section, 189 const char* config_name, 190 const ImeConfigValue& value) { 191 if (!strcmp(language_prefs::kGeneralSectionName, section) && 192 !strcmp(language_prefs::kPreloadEnginesConfigName, config_name)) { 193 if (EnsureLoadedAndStarted()) { 194 // If there are no input methods other than one for the hardware 195 // keyboard, we'll stop the input method processes. 196 if (value.type == ImeConfigValue::kValueTypeStringList && 197 value.string_list_value.size() == 1 && 198 value.string_list_value[0] == 199 chromeos::GetHardwareKeyboardLayoutName()) { 200 StopInputMethodProcesses(); 201 } else if (!defer_ime_startup_) { 202 StartInputMethodProcesses(); 203 } 204 chromeos::SetActiveInputMethods(input_method_status_connection_, value); 205 } 206 } 207 } 208 209 // Flushes the input method config data. The config data is queued up in 210 // |pending_config_requests_| until the config backend (ibus-memconf) 211 // starts. Since there is no good way to get notified when the config 212 // backend starts, we use a timer to periodically attempt to send the 213 // config data to the config backend. 214 void FlushImeConfig() { 215 bool active_input_methods_are_changed = false; 216 bool completed = false; 217 if (EnsureLoadedAndStarted()) { 218 InputMethodConfigRequests::iterator iter = 219 pending_config_requests_.begin(); 220 while (iter != pending_config_requests_.end()) { 221 const std::string& section = iter->first.first; 222 const std::string& config_name = iter->first.second; 223 const ImeConfigValue& value = iter->second; 224 if (chromeos::SetImeConfig(input_method_status_connection_, 225 section.c_str(), 226 config_name.c_str(), 227 value)) { 228 // Check if it's a change in active input methods. 229 if (config_name == language_prefs::kPreloadEnginesConfigName) { 230 active_input_methods_are_changed = true; 231 } 232 // Successfully sent. Remove the command and proceed to the next one. 233 pending_config_requests_.erase(iter++); 234 } else { 235 // If SetImeConfig() fails, subsequent calls will likely fail. 236 break; 237 } 238 } 239 if (pending_config_requests_.empty()) { 240 // Calls to ChangeInputMethod() will fail if the input method has not 241 // yet been added to preload_engines. As such, the call is deferred 242 // until after all config values have been sent to the IME process. 243 if (should_change_input_method_) { 244 if (chromeos::ChangeInputMethod(input_method_status_connection_, 245 current_input_method_id_.c_str())) { 246 should_change_input_method_ = false; 247 completed = true; 248 active_input_methods_are_changed = true; 249 } 250 } else { 251 completed = true; 252 } 253 } 254 } 255 256 if (completed) { 257 timer_.Stop(); // no-op if it's not running. 258 } else { 259 // Flush is not completed. Start a timer if it's not yet running. 260 if (!timer_.IsRunning()) { 261 static const int64 kTimerIntervalInMsec = 100; 262 failure_count_ = 0; 263 timer_.Start(base::TimeDelta::FromMilliseconds(kTimerIntervalInMsec), 264 this, &InputMethodLibraryImpl::FlushImeConfig); 265 } else { 266 // The timer is already running. We'll give up if it reaches the 267 // max retry count. 268 static const int kMaxRetries = 15; 269 ++failure_count_; 270 if (failure_count_ > kMaxRetries) { 271 LOG(ERROR) << "FlushImeConfig: Max retries exceeded. " 272 << "current_input_method_id: " << current_input_method_id_ 273 << " pending_config_requests.size: " 274 << pending_config_requests_.size(); 275 timer_.Stop(); 276 } 277 } 278 } 279 if (active_input_methods_are_changed) { 280 FOR_EACH_OBSERVER(Observer, observers_, ActiveInputMethodsChanged(this)); 281 } 282 } 283 284 static void InputMethodChangedHandler( 285 void* object, 286 const chromeos::InputMethodDescriptor& current_input_method) { 287 // The handler is called when the input method method change is 288 // notified via a DBus connection. Since the DBus notificatiosn are 289 // handled in the UI thread, we can assume that this functionalways 290 // runs on the UI thread, but just in case. 291 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 292 LOG(ERROR) << "Not on UI thread"; 293 return; 294 } 295 296 InputMethodLibraryImpl* input_method_library = 297 static_cast<InputMethodLibraryImpl*>(object); 298 input_method_library->ChangeCurrentInputMethod(current_input_method); 299 } 300 301 static void RegisterPropertiesHandler( 302 void* object, const ImePropertyList& prop_list) { 303 // See comments in InputMethodChangedHandler. 304 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 305 LOG(ERROR) << "Not on UI thread"; 306 return; 307 } 308 309 InputMethodLibraryImpl* input_method_library = 310 static_cast<InputMethodLibraryImpl*>(object); 311 input_method_library->RegisterProperties(prop_list); 312 } 313 314 static void UpdatePropertyHandler( 315 void* object, const ImePropertyList& prop_list) { 316 // See comments in InputMethodChangedHandler. 317 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 318 LOG(ERROR) << "Not on UI thread"; 319 return; 320 } 321 322 InputMethodLibraryImpl* input_method_library = 323 static_cast<InputMethodLibraryImpl*>(object); 324 input_method_library->UpdateProperty(prop_list); 325 } 326 327 static void ConnectionChangeHandler(void* object, bool connected) { 328 // See comments in InputMethodChangedHandler. 329 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 330 LOG(ERROR) << "Not on UI thread"; 331 return; 332 } 333 334 InputMethodLibraryImpl* input_method_library = 335 static_cast<InputMethodLibraryImpl*>(object); 336 input_method_library->ime_connected_ = connected; 337 if (connected) { 338 input_method_library->pending_config_requests_.clear(); 339 input_method_library->pending_config_requests_.insert( 340 input_method_library->current_config_values_.begin(), 341 input_method_library->current_config_values_.end()); 342 input_method_library->should_change_input_method_ = true; 343 input_method_library->FlushImeConfig(); 344 } else { 345 // Stop attempting to resend config data, since it will continue to fail. 346 input_method_library->timer_.Stop(); // no-op if it's not running. 347 } 348 } 349 350 bool EnsureStarted() { 351 if (!input_method_status_connection_) { 352 input_method_status_connection_ = chromeos::MonitorInputMethodStatus( 353 this, 354 &InputMethodChangedHandler, 355 &RegisterPropertiesHandler, 356 &UpdatePropertyHandler, 357 &ConnectionChangeHandler); 358 } 359 return true; 360 } 361 362 bool EnsureLoadedAndStarted() { 363 return CrosLibrary::Get()->EnsureLoaded() && 364 EnsureStarted(); 365 } 366 367 void ChangeCurrentInputMethod(const InputMethodDescriptor& new_input_method) { 368 // Change the keyboard layout to a preferred layout for the input method. 369 CrosLibrary::Get()->GetKeyboardLibrary()->SetCurrentKeyboardLayoutByName( 370 new_input_method.keyboard_layout); 371 372 if (current_input_method_.id != new_input_method.id) { 373 previous_input_method_ = current_input_method_; 374 current_input_method_ = new_input_method; 375 } 376 FOR_EACH_OBSERVER(Observer, observers_, InputMethodChanged(this)); 377 } 378 379 void RegisterProperties(const ImePropertyList& prop_list) { 380 // |prop_list| might be empty. This means "clear all properties." 381 current_ime_properties_ = prop_list; 382 FOR_EACH_OBSERVER(Observer, observers_, ImePropertiesChanged(this)); 383 } 384 385 void StartInputMethodProcesses() { 386 should_launch_ime_ = true; 387 MaybeLaunchInputMethodProcesses(); 388 } 389 390 void UpdateProperty(const ImePropertyList& prop_list) { 391 for (size_t i = 0; i < prop_list.size(); ++i) { 392 FindAndUpdateProperty(prop_list[i], ¤t_ime_properties_); 393 } 394 FOR_EACH_OBSERVER(Observer, observers_, ImePropertiesChanged(this)); 395 } 396 397 // Launches an input method procsess specified by the given command 398 // line. On success, returns true and stores the process ID in 399 // |process_id|. Otherwise, returns false, and the contents of 400 // |process_id| is untouched. OnImeShutdown will be called when the 401 // process terminates. 402 bool LaunchInputMethodProcess(const std::string& command_line, 403 int* process_id) { 404 GError *error = NULL; 405 gchar **argv = NULL; 406 gint argc = NULL; 407 // TODO(zork): export "LD_PRELOAD=/usr/lib/libcrash.so" 408 if (!g_shell_parse_argv(command_line.c_str(), &argc, &argv, &error)) { 409 LOG(ERROR) << "Could not parse command: " << error->message; 410 g_error_free(error); 411 return false; 412 } 413 414 int pid = 0; 415 const GSpawnFlags kFlags = G_SPAWN_DO_NOT_REAP_CHILD; 416 const gboolean result = g_spawn_async(NULL, argv, NULL, 417 kFlags, NULL, NULL, 418 &pid, &error); 419 g_strfreev(argv); 420 if (!result) { 421 LOG(ERROR) << "Could not launch: " << command_line << ": " 422 << error->message; 423 g_error_free(error); 424 return false; 425 } 426 g_child_watch_add(pid, reinterpret_cast<GChildWatchFunc>(OnImeShutdown), 427 this); 428 429 *process_id = pid; 430 return true; 431 } 432 433 // Launches input method processes if these are not yet running. 434 void MaybeLaunchInputMethodProcesses() { 435 if (!should_launch_ime_) { 436 return; 437 } 438 439 if (ibus_daemon_process_id_ == 0) { 440 // TODO(zork): Send output to /var/log/ibus.log 441 const std::string ibus_daemon_command_line = 442 StringPrintf("%s --panel=disable --cache=none --restart --replace", 443 kIBusDaemonPath); 444 if (!LaunchInputMethodProcess(ibus_daemon_command_line, 445 &ibus_daemon_process_id_)) { 446 // On failure, we should not attempt to launch candidate_window. 447 return; 448 } 449 } 450 451 if (candidate_window_process_id_ == 0) { 452 // Pass the UI language info to candidate_window via --lang flag. 453 const std::string candidate_window_command_line = 454 StringPrintf("%s --lang=%s", kCandidateWindowPath, 455 g_browser_process->GetApplicationLocale().c_str()); 456 if (!LaunchInputMethodProcess(candidate_window_command_line, 457 &candidate_window_process_id_)) { 458 // Return here just in case we add more code below. 459 return; 460 } 461 } 462 } 463 464 static void OnImeShutdown(int pid, 465 int status, 466 InputMethodLibraryImpl* library) { 467 g_spawn_close_pid(pid); 468 if (library->ibus_daemon_process_id_ == pid) { 469 library->ibus_daemon_process_id_ = 0; 470 } else if (library->candidate_window_process_id_ == pid) { 471 library->candidate_window_process_id_ = 0; 472 } 473 474 // Restart input method processes if needed. 475 library->MaybeLaunchInputMethodProcesses(); 476 } 477 478 void StopInputMethodProcesses() { 479 should_launch_ime_ = false; 480 if (ibus_daemon_process_id_) { 481 const std::string xkb_engine_name = 482 chromeos::GetHardwareKeyboardLayoutName(); 483 // We should not use chromeos::ChangeInputMethod() here since without the 484 // ibus-daemon process, ChangeCurrentInputMethod() callback function which 485 // actually changes the XKB layout will not be called. 486 CrosLibrary::Get()->GetKeyboardLibrary()->SetCurrentKeyboardLayoutByName( 487 chromeos::input_method::GetKeyboardLayoutName(xkb_engine_name)); 488 kill(ibus_daemon_process_id_, SIGTERM); 489 ibus_daemon_process_id_ = 0; 490 } 491 if (candidate_window_process_id_) { 492 kill(candidate_window_process_id_, SIGTERM); 493 candidate_window_process_id_ = 0; 494 } 495 } 496 497 void SetDeferImeStartup(bool defer) { 498 LOG(INFO) << "Setting DeferImeStartup to " << defer; 499 defer_ime_startup_ = defer; 500 } 501 502 // NotificationObserver implementation: 503 void Observe(NotificationType type, 504 const NotificationSource& source, 505 const NotificationDetails& details) { 506 // Stop the input processes on browser shutdown. 507 if (type.value == NotificationType::APP_EXITING) { 508 StopInputMethodProcesses(); 509 } 510 } 511 512 // A reference to the language api, to allow callbacks when the input method 513 // status changes. 514 InputMethodStatusConnection* input_method_status_connection_; 515 ObserverList<Observer> observers_; 516 517 // The input method which was/is selected. 518 InputMethodDescriptor previous_input_method_; 519 InputMethodDescriptor current_input_method_; 520 521 // The input method properties which the current input method uses. The list 522 // might be empty when no input method is used. 523 ImePropertyList current_ime_properties_; 524 525 typedef std::pair<std::string, std::string> ConfigKeyType; 526 typedef std::map<ConfigKeyType, ImeConfigValue> InputMethodConfigRequests; 527 // SetImeConfig requests that are not yet completed. 528 // Use a map to queue config requests, so we only send the last request for 529 // the same config key (i.e. we'll discard ealier requests for the same 530 // config key). As we discard old requests for the same config key, the order 531 // of requests doesn't matter, so it's safe to use a map. 532 InputMethodConfigRequests pending_config_requests_; 533 534 // Values that have been set via SetImeConfig(). We keep a copy available to 535 // resend if the ime restarts and loses its state. 536 InputMethodConfigRequests current_config_values_; 537 538 // A timer for retrying to send |pendning_config_commands_| to the input 539 // method config daemon. 540 base::OneShotTimer<InputMethodLibraryImpl> timer_; 541 542 // This is used to register this object to APP_EXITING notification. 543 NotificationRegistrar notification_registrar_; 544 545 // True if we should launch the input method processes. 546 bool should_launch_ime_; 547 // True if the connection to the IBus daemon is alive. 548 bool ime_connected_; 549 // If true, we'll defer the startup until a non-default method is 550 // activated. 551 bool defer_ime_startup_; 552 // The ID of the current input method (ex. "mozc"). 553 std::string current_input_method_id_; 554 // True if we should change the input method once the queue of the 555 // pending config requests becomes empty. 556 bool should_change_input_method_; 557 558 // The process id of the IBus daemon. 0 if it's not running. The process 559 // ID 0 is not used in Linux, hence it's safe to use 0 for this purpose. 560 int ibus_daemon_process_id_; 561 // The process id of the candidate window. 0 if it's not running. 562 int candidate_window_process_id_; 563 // The failure count of config flush attempts. 564 int failure_count_; 565 566 DISALLOW_COPY_AND_ASSIGN(InputMethodLibraryImpl); 567}; 568 569InputMethodLibraryImpl::Observer::~Observer() {} 570 571class InputMethodLibraryStubImpl : public InputMethodLibrary { 572 public: 573 InputMethodLibraryStubImpl() 574 : previous_input_method_("", "", "", ""), 575 current_input_method_("", "", "", "") { 576 } 577 578 ~InputMethodLibraryStubImpl() {} 579 void AddObserver(Observer* observer) {} 580 void RemoveObserver(Observer* observer) {} 581 582 InputMethodDescriptors* GetActiveInputMethods() { 583 return CreateRealisticInputMethodDescriptors(); 584 } 585 586 587 size_t GetNumActiveInputMethods() { 588 scoped_ptr<InputMethodDescriptors> descriptors( 589 CreateRealisticInputMethodDescriptors()); 590 return descriptors->size(); 591 } 592 593 InputMethodDescriptors* GetSupportedInputMethods() { 594 return CreateRealisticInputMethodDescriptors(); 595 } 596 597 void ChangeInputMethod(const std::string& input_method_id) {} 598 void SetImePropertyActivated(const std::string& key, bool activated) {} 599 600 bool InputMethodIsActivated(const std::string& input_method_id) { 601 return true; 602 } 603 604 bool GetImeConfig(const char* section, 605 const char* config_name, 606 ImeConfigValue* out_value) { 607 return false; 608 } 609 610 bool SetImeConfig(const char* section, 611 const char* config_name, 612 const ImeConfigValue& value) { 613 return false; 614 } 615 616 virtual const InputMethodDescriptor& previous_input_method() const { 617 return previous_input_method_; 618 } 619 620 virtual const InputMethodDescriptor& current_input_method() const { 621 return current_input_method_; 622 } 623 624 virtual const ImePropertyList& current_ime_properties() const { 625 return current_ime_properties_; 626 } 627 628 virtual void StartInputMethodProcesses() {} 629 virtual void StopInputMethodProcesses() {} 630 virtual void SetDeferImeStartup(bool defer) {} 631 632 private: 633 // Creates realistic input method descriptors that can be used for 634 // testing Chrome OS version of chrome on regular Linux desktops. 635 InputMethodDescriptors* CreateRealisticInputMethodDescriptors() { 636 InputMethodDescriptors* descriptions = new InputMethodDescriptors; 637 // The list is created from output of gen_engines.py in libcros. 638 descriptions->push_back(InputMethodDescriptor( 639 "chewing", "Chewing", "us", "zh_TW")); 640 descriptions->push_back(InputMethodDescriptor( 641 "hangul", "Korean", "us", "ko")); 642 descriptions->push_back(InputMethodDescriptor( 643 "m17n:fa:isiri", "isiri (m17n)", "us", "fa")); 644 descriptions->push_back(InputMethodDescriptor( 645 "m17n:he:kbd", "kbd (m17n)", "us", "he")); 646 descriptions->push_back(InputMethodDescriptor( 647 "m17n:ar:kbd", "kbd (m17n)", "us", "ar")); 648 descriptions->push_back(InputMethodDescriptor( 649 "m17n:hi:itrans", "itrans (m17n)", "us", "hi")); 650 descriptions->push_back(InputMethodDescriptor( 651 "m17n:vi:vni", "vni (m17n)", "us", "vi")); 652 descriptions->push_back(InputMethodDescriptor( 653 "m17n:vi:viqr", "viqr (m17n)", "us", "vi")); 654 descriptions->push_back(InputMethodDescriptor( 655 "m17n:vi:tcvn", "tcvn (m17n)", "us", "vi")); 656 descriptions->push_back(InputMethodDescriptor( 657 "m17n:vi:telex", "telex (m17n)", "us", "vi")); 658 descriptions->push_back(InputMethodDescriptor( 659 "m17n:zh:cangjie", "cangjie (m17n)", "us", "zh")); 660 descriptions->push_back(InputMethodDescriptor( 661 "m17n:zh:quick", "quick (m17n)", "us", "zh")); 662 descriptions->push_back(InputMethodDescriptor( 663 "m17n:th:tis820", "tis820 (m17n)", "us", "th")); 664 descriptions->push_back(InputMethodDescriptor( 665 "m17n:th:kesmanee", "kesmanee (m17n)", "us", "th")); 666 descriptions->push_back(InputMethodDescriptor( 667 "m17n:th:pattachote", "pattachote (m17n)", "us", "th")); 668 descriptions->push_back(InputMethodDescriptor( 669 "mozc-jp", "Mozc (Japanese keyboard layout)", "jp", "ja")); 670 descriptions->push_back(InputMethodDescriptor( 671 "mozc", "Mozc (US keyboard layout)", "us", "ja")); 672 descriptions->push_back(InputMethodDescriptor( 673 "mozc-dv", "Mozc (US Dvorak keyboard layout)", "us(dvorak)", "ja")); 674 descriptions->push_back(InputMethodDescriptor( 675 "pinyin", "Pinyin", "us", "zh")); 676 descriptions->push_back(InputMethodDescriptor( 677 "bopomofo", "Bopomofo", "us", "zh")); 678 descriptions->push_back(InputMethodDescriptor( 679 "xkb:us::eng", "USA", "us", "eng")); 680 descriptions->push_back(InputMethodDescriptor( 681 "xkb:us:dvorak:eng", "USA - Dvorak", "us(dvorak)", "eng")); 682 descriptions->push_back(InputMethodDescriptor( 683 "xkb:be::ger", "Belgium", "be", "ger")); 684 descriptions->push_back(InputMethodDescriptor( 685 "xkb:be::nld", "Belgium", "be", "nld")); 686 descriptions->push_back(InputMethodDescriptor( 687 "xkb:be::fra", "Belgium", "be", "fra")); 688 descriptions->push_back(InputMethodDescriptor( 689 "xkb:br::por", "Brazil", "br", "por")); 690 descriptions->push_back(InputMethodDescriptor( 691 "xkb:bg::bul", "Bulgaria", "bg", "bul")); 692 descriptions->push_back(InputMethodDescriptor( 693 "xkb:ca::fra", "Canada", "ca", "fra")); 694 descriptions->push_back(InputMethodDescriptor( 695 "xkb:ca:eng:eng", "Canada - English", "ca(eng)", "eng")); 696 descriptions->push_back(InputMethodDescriptor( 697 "xkb:hr::scr", "Croatia", "hr", "scr")); 698 descriptions->push_back(InputMethodDescriptor( 699 "xkb:cz::cze", "Czechia", "cz", "cze")); 700 descriptions->push_back(InputMethodDescriptor( 701 "xkb:dk::dan", "Denmark", "dk", "dan")); 702 descriptions->push_back(InputMethodDescriptor( 703 "xkb:nl::nld", "Netherlands", "nl", "nld")); 704 descriptions->push_back(InputMethodDescriptor( 705 "xkb:ee::est", "Estonia", "ee", "est")); 706 descriptions->push_back(InputMethodDescriptor( 707 "xkb:fi::fin", "Finland", "fi", "fin")); 708 descriptions->push_back(InputMethodDescriptor( 709 "xkb:fr::fra", "France", "fr", "fra")); 710 descriptions->push_back(InputMethodDescriptor( 711 "xkb:de::ger", "Germany", "de", "ger")); 712 descriptions->push_back(InputMethodDescriptor( 713 "xkb:gr::gre", "Greece", "gr", "gre")); 714 descriptions->push_back(InputMethodDescriptor( 715 "xkb:hu::hun", "Hungary", "hu", "hun")); 716 descriptions->push_back(InputMethodDescriptor( 717 "xkb:it::ita", "Italy", "it", "ita")); 718 descriptions->push_back(InputMethodDescriptor( 719 "xkb:jp::jpn", "Japan", "jp", "jpn")); 720 descriptions->push_back(InputMethodDescriptor( 721 "xkb:lt::lit", "Lithuania", "lt", "lit")); 722 descriptions->push_back(InputMethodDescriptor( 723 "xkb:lv::lav", "Latvia", "lv", "lav")); 724 descriptions->push_back(InputMethodDescriptor( 725 "xkb:no::nor", "Norway", "no", "nor")); 726 descriptions->push_back(InputMethodDescriptor( 727 "xkb:pl::pol", "Poland", "pl", "pol")); 728 descriptions->push_back(InputMethodDescriptor( 729 "xkb:pt::por", "Portugal", "pt", "por")); 730 descriptions->push_back(InputMethodDescriptor( 731 "xkb:ro::rum", "Romania", "ro", "rum")); 732 descriptions->push_back(InputMethodDescriptor( 733 "xkb:ru::rus", "Russia", "ru", "rus")); 734 descriptions->push_back(InputMethodDescriptor( 735 "xkb:rs::srp", "Serbia", "rs", "srp")); 736 descriptions->push_back(InputMethodDescriptor( 737 "xkb:si::slv", "Slovenia", "si", "slv")); 738 descriptions->push_back(InputMethodDescriptor( 739 "xkb:sk::slo", "Slovakia", "sk", "slo")); 740 descriptions->push_back(InputMethodDescriptor( 741 "xkb:es::spa", "Spain", "es", "spa")); 742 descriptions->push_back(InputMethodDescriptor( 743 "xkb:es:cat:cat", 744 "Spain - Catalan variant with middle-dot L", "es(cat)", "cat")); 745 descriptions->push_back(InputMethodDescriptor( 746 "xkb:se::swe", "Sweden", "se", "swe")); 747 descriptions->push_back(InputMethodDescriptor( 748 "xkb:ch::ger", "Switzerland", "ch", "ger")); 749 descriptions->push_back(InputMethodDescriptor( 750 "xkb:ch:fr:fra", "Switzerland - French", "ch(fr)", "fra")); 751 descriptions->push_back(InputMethodDescriptor( 752 "xkb:tr::tur", "Turkey", "tr", "tur")); 753 descriptions->push_back(InputMethodDescriptor( 754 "xkb:ua::ukr", "Ukraine", "ua", "ukr")); 755 descriptions->push_back(InputMethodDescriptor( 756 "xkb:gb:extd:eng", "United Kingdom - Extended - Winkeys", "gb(extd)", 757 "eng")); 758 return descriptions; 759 } 760 761 InputMethodDescriptor previous_input_method_; 762 InputMethodDescriptor current_input_method_; 763 ImePropertyList current_ime_properties_; 764 765 DISALLOW_COPY_AND_ASSIGN(InputMethodLibraryStubImpl); 766}; 767 768// static 769InputMethodLibrary* InputMethodLibrary::GetImpl(bool stub) { 770 if (stub) 771 return new InputMethodLibraryStubImpl(); 772 else 773 return new InputMethodLibraryImpl(); 774} 775 776} // namespace chromeos 777 778// Allows InvokeLater without adding refcounting. This class is a Singleton and 779// won't be deleted until it's last InvokeLater is run. 780DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::InputMethodLibraryImpl); 781