1// Copyright (c) 2011 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 <algorithm> 8 9#include <glib.h> 10 11#include "unicode/uloc.h" 12 13#include "base/basictypes.h" 14#include "base/message_loop.h" 15#include "base/process_util.h" 16#include "base/string_split.h" 17#include "base/string_util.h" 18#include "chrome/browser/browser_process.h" 19#include "chrome/browser/chromeos/cros/cros_library.h" 20#include "chrome/browser/chromeos/input_method/candidate_window.h" 21#include "chrome/browser/chromeos/input_method/input_method_util.h" 22#include "chrome/browser/chromeos/input_method/xkeyboard.h" 23#include "chrome/browser/chromeos/language_preferences.h" 24#include "content/browser/browser_thread.h" 25#include "content/common/notification_observer.h" 26#include "content/common/notification_registrar.h" 27#include "content/common/notification_service.h" 28 29namespace { 30 31const char kIBusDaemonPath[] = "/usr/bin/ibus-daemon"; 32 33// Finds a property which has |new_prop.key| from |prop_list|, and replaces the 34// property with |new_prop|. Returns true if such a property is found. 35bool FindAndUpdateProperty(const chromeos::ImeProperty& new_prop, 36 chromeos::ImePropertyList* prop_list) { 37 for (size_t i = 0; i < prop_list->size(); ++i) { 38 chromeos::ImeProperty& prop = prop_list->at(i); 39 if (prop.key == new_prop.key) { 40 const int saved_id = prop.selection_item_id; 41 // Update the list except the radio id. As written in 42 // chromeos_input_method.h, |prop.selection_item_id| is dummy. 43 prop = new_prop; 44 prop.selection_item_id = saved_id; 45 return true; 46 } 47 } 48 return false; 49} 50 51} // namespace 52 53namespace chromeos { 54 55// The production implementation of InputMethodLibrary. 56class InputMethodLibraryImpl : public InputMethodLibrary, 57 public NotificationObserver { 58 public: 59 InputMethodLibraryImpl() 60 : input_method_status_connection_(NULL), 61 previous_input_method_("", "", "", ""), 62 current_input_method_("", "", "", ""), 63 should_launch_ime_(false), 64 ime_connected_(false), 65 defer_ime_startup_(false), 66 enable_auto_ime_shutdown_(true), 67 ibus_daemon_process_handle_(base::kNullProcessHandle), 68 initialized_successfully_(false), 69 candidate_window_controller_(NULL) { 70 // Observe APP_TERMINATING to stop input method daemon gracefully. 71 // We should not use APP_EXITING here since logout might be canceled by 72 // JavaScript after APP_EXITING is sent (crosbug.com/11055). 73 // Note that even if we fail to stop input method daemon from 74 // Chrome in case of a sudden crash, we have a way to do it from an 75 // upstart script. See crosbug.com/6515 and crosbug.com/6995 for 76 // details. 77 notification_registrar_.Add(this, NotificationType::APP_TERMINATING, 78 NotificationService::AllSources()); 79 } 80 81 // Initializes the object. On success, returns true on and sets 82 // initialized_successfully_ to true. 83 // 84 // Note that we start monitoring input method status in here in Init() 85 // to avoid a potential race. If we start the monitoring right after 86 // starting ibus-daemon, there is a higher chance of a race between 87 // Chrome and ibus-daemon to occur. 88 bool Init() { 89 DCHECK(!initialized_successfully_) << "Already initialized"; 90 91 if (!CrosLibrary::Get()->EnsureLoaded()) 92 return false; 93 input_method_status_connection_ = chromeos::MonitorInputMethodStatus( 94 this, 95 &InputMethodChangedHandler, 96 &RegisterPropertiesHandler, 97 &UpdatePropertyHandler, 98 &ConnectionChangeHandler); 99 if (!input_method_status_connection_) 100 return false; 101 102 initialized_successfully_ = true; 103 return true; 104 } 105 106 virtual ~InputMethodLibraryImpl() { 107 } 108 109 virtual void AddObserver(Observer* observer) { 110 if (!observers_.size()) { 111 observer->FirstObserverIsAdded(this); 112 } 113 observers_.AddObserver(observer); 114 } 115 116 virtual void RemoveObserver(Observer* observer) { 117 observers_.RemoveObserver(observer); 118 } 119 120 virtual InputMethodDescriptors* GetActiveInputMethods() { 121 chromeos::InputMethodDescriptors* result = 122 new chromeos::InputMethodDescriptors; 123 // Build the active input method descriptors from the active input 124 // methods cache |active_input_method_ids_|. 125 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) { 126 const std::string& input_method_id = active_input_method_ids_[i]; 127 const InputMethodDescriptor* descriptor = 128 chromeos::input_method::GetInputMethodDescriptorFromId( 129 input_method_id); 130 if (descriptor) { 131 result->push_back(*descriptor); 132 } else { 133 LOG(ERROR) << "Descriptor is not found for: " << input_method_id; 134 } 135 } 136 // Initially active_input_method_ids_ is empty. In this case, just 137 // returns the fallback input method descriptor. 138 if (result->empty()) { 139 LOG(WARNING) << "No active input methods found."; 140 result->push_back(input_method::GetFallbackInputMethodDescriptor()); 141 } 142 return result; 143 } 144 145 virtual size_t GetNumActiveInputMethods() { 146 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods()); 147 return input_methods->size(); 148 } 149 150 virtual InputMethodDescriptors* GetSupportedInputMethods() { 151 if (!initialized_successfully_) { 152 // If initialization was failed, return the fallback input method, 153 // as this function is guaranteed to return at least one descriptor. 154 InputMethodDescriptors* result = new InputMethodDescriptors; 155 result->push_back(input_method::GetFallbackInputMethodDescriptor()); 156 return result; 157 } 158 159 // This never returns NULL. 160 return chromeos::GetSupportedInputMethodDescriptors(); 161 } 162 163 virtual void ChangeInputMethod(const std::string& input_method_id) { 164 // Changing the input method isn't guaranteed to succeed here, but we 165 // should remember the last one regardless. See comments in 166 // FlushImeConfig() for details. 167 tentative_current_input_method_id_ = input_method_id; 168 // If the input method daemon is not running and the specified input 169 // method is a keyboard layout, switch the keyboard directly. 170 if (ibus_daemon_process_handle_ == base::kNullProcessHandle && 171 chromeos::input_method::IsKeyboardLayout(input_method_id)) { 172 // We shouldn't use SetCurrentKeyboardLayoutByName() here. See 173 // comments at ChangeCurrentInputMethod() for details. 174 ChangeCurrentInputMethodFromId(input_method_id); 175 } else { 176 // Otherwise, start the input method daemon, and change the input 177 // method via the daemon. 178 StartInputMethodDaemon(); 179 // ChangeInputMethodViaIBus() fails if the IBus daemon is not 180 // ready yet. In this case, we'll defer the input method change 181 // until the daemon is ready. 182 if (!ChangeInputMethodViaIBus(input_method_id)) { 183 VLOG(1) << "Failed to change the input method to " << input_method_id 184 << " (deferring)"; 185 } 186 } 187 } 188 189 virtual void SetImePropertyActivated(const std::string& key, 190 bool activated) { 191 if (!initialized_successfully_) 192 return; 193 194 DCHECK(!key.empty()); 195 chromeos::SetImePropertyActivated( 196 input_method_status_connection_, key.c_str(), activated); 197 } 198 199 virtual bool InputMethodIsActivated(const std::string& input_method_id) { 200 scoped_ptr<InputMethodDescriptors> active_input_method_descriptors( 201 GetActiveInputMethods()); 202 for (size_t i = 0; i < active_input_method_descriptors->size(); ++i) { 203 if (active_input_method_descriptors->at(i).id == input_method_id) { 204 return true; 205 } 206 } 207 return false; 208 } 209 210 virtual bool SetImeConfig(const std::string& section, 211 const std::string& config_name, 212 const ImeConfigValue& value) { 213 // If the config change is for preload engines, update the active 214 // input methods cache |active_input_method_ids_| here. We need to 215 // update the cache before actually flushing the config. since we need 216 // to return active input methods from GetActiveInputMethods() before 217 // the input method daemon starts. For instance, we need to show the 218 // list of available input methods (keyboard layouts) on the login 219 // screen before the input method starts. 220 if (section == language_prefs::kGeneralSectionName && 221 config_name == language_prefs::kPreloadEnginesConfigName && 222 value.type == ImeConfigValue::kValueTypeStringList) { 223 active_input_method_ids_ = value.string_list_value; 224 } 225 226 // Before calling FlushImeConfig(), start input method process if necessary. 227 MaybeStartInputMethodDaemon(section, config_name, value); 228 229 const ConfigKeyType key = std::make_pair(section, config_name); 230 current_config_values_[key] = value; 231 if (ime_connected_) { 232 pending_config_requests_[key] = value; 233 FlushImeConfig(); 234 } 235 236 // Stop input method process if necessary. 237 MaybeStopInputMethodDaemon(section, config_name, value); 238 // Change the current keyboard layout if necessary. 239 MaybeChangeCurrentKeyboardLayout(section, config_name, value); 240 return pending_config_requests_.empty(); 241 } 242 243 virtual InputMethodDescriptor previous_input_method() const { 244 if (previous_input_method_.id.empty()) { 245 return input_method::GetFallbackInputMethodDescriptor(); 246 } 247 return previous_input_method_; 248 } 249 250 virtual InputMethodDescriptor current_input_method() const { 251 if (current_input_method_.id.empty()) { 252 return input_method::GetFallbackInputMethodDescriptor(); 253 } 254 return current_input_method_; 255 } 256 257 virtual const ImePropertyList& current_ime_properties() const { 258 return current_ime_properties_; 259 } 260 261 virtual std::string GetKeyboardOverlayId(const std::string& input_method_id) { 262 if (!initialized_successfully_) 263 return ""; 264 265 return chromeos::GetKeyboardOverlayId(input_method_id); 266 } 267 268 private: 269 // Returns true if the given input method config value is a single 270 // element string list that contains an input method ID of a keyboard 271 // layout. 272 bool ContainOnlyOneKeyboardLayout( 273 const ImeConfigValue& value) { 274 return (value.type == ImeConfigValue::kValueTypeStringList && 275 value.string_list_value.size() == 1 && 276 chromeos::input_method::IsKeyboardLayout( 277 value.string_list_value[0])); 278 } 279 280 // Starts input method daemon based on the |defer_ime_startup_| flag and 281 // input method configuration being updated. |section| is a section name of 282 // the input method configuration (e.g. "general", "general/hotkey"). 283 // |config_name| is a name of the configuration (e.g. "preload_engines", 284 // "previous_engine"). |value| is the configuration value to be set. 285 void MaybeStartInputMethodDaemon(const std::string& section, 286 const std::string& config_name, 287 const ImeConfigValue& value) { 288 if (section == language_prefs::kGeneralSectionName && 289 config_name == language_prefs::kPreloadEnginesConfigName && 290 value.type == ImeConfigValue::kValueTypeStringList && 291 !value.string_list_value.empty()) { 292 // If there is only one input method which is a keyboard layout, 293 // we don't start the input method processes. When 294 // |defer_ime_startup_| is true, we don't start it either. 295 if (ContainOnlyOneKeyboardLayout(value) || defer_ime_startup_) { 296 // Do not start the input method daemon. 297 return; 298 } 299 300 // Otherwise, start the input method daemon. 301 const bool just_started = StartInputMethodDaemon(); 302 if (!just_started) { 303 // The daemon is already running. 304 // Do not |update tentative_current_input_method_id_|. 305 return; 306 } 307 308 // The daemon has just been started. To select the initial input method 309 // engine correctly, update |tentative_current_input_method_id_|. 310 if (tentative_current_input_method_id_.empty()) { 311 tentative_current_input_method_id_ = current_input_method_.id; 312 } 313 if (std::find(value.string_list_value.begin(), 314 value.string_list_value.end(), 315 tentative_current_input_method_id_) 316 != value.string_list_value.end()) { 317 // Since the |current_input_method_| is in the preloaded engine list, 318 // switch to the engine. This is necessary ex. for the following case: 319 // 1. "xkb:jp::jpn" is enabled. ibus-daemon is not running. 320 // 2. A user enabled "mozc" via DOMUI as well. ibus-daemon is started 321 // and the preloaded engine list is set to "mozc,xkb:jp::jpn". 322 // 3. ibus-daemon selects "mozc" as its current engine since "mozc" is 323 // on top of the preloaded engine list. 324 // 4. Therefore, we have to change the current engine to "xkb:jp::jpn" 325 // explicitly to avoid unexpected engine switch. 326 } else { 327 // The |current_input_method_| is NOT in the preloaded engine list. In 328 // this case, we should switch to the first one in the list in order to 329 // workaround crosbug.com/12244. 330 // TODO(yusukes): When crosbug.com/13406, which is a feature request to 331 // ibus-daemon, is fixed, probably we should replace the line below to 332 // "tentative_current_input_method_id_.clear();" 333 tentative_current_input_method_id_ = value.string_list_value[0]; 334 } 335 } 336 } 337 338 // Stops input method daemon based on the |enable_auto_ime_shutdown_| flag 339 // and input method configuration being updated. 340 // See also: MaybeStartInputMethodDaemon(). 341 void MaybeStopInputMethodDaemon(const std::string& section, 342 const std::string& config_name, 343 const ImeConfigValue& value) { 344 // If there is only one input method which is a keyboard layout, 345 // and |enable_auto_ime_shutdown_| is true, we'll stop the input 346 // method daemon. 347 if (section == language_prefs::kGeneralSectionName && 348 config_name == language_prefs::kPreloadEnginesConfigName && 349 ContainOnlyOneKeyboardLayout(value) && 350 enable_auto_ime_shutdown_) { 351 StopInputMethodDaemon(); 352 } 353 } 354 355 // Change the keyboard layout per input method configuration being 356 // updated, if necessary. See also: MaybeStartInputMethodDaemon(). 357 void MaybeChangeCurrentKeyboardLayout(const std::string& section, 358 const std::string& config_name, 359 const ImeConfigValue& value) { 360 361 // If there is only one input method which is a keyboard layout, we'll 362 // change the keyboard layout per the only one input method now 363 // available. 364 if (section == language_prefs::kGeneralSectionName && 365 config_name == language_prefs::kPreloadEnginesConfigName && 366 ContainOnlyOneKeyboardLayout(value)) { 367 // We shouldn't use SetCurrentKeyboardLayoutByName() here. See 368 // comments at ChangeCurrentInputMethod() for details. 369 ChangeCurrentInputMethodFromId(value.string_list_value[0]); 370 } 371 } 372 373 // Changes the current input method to |input_method_id| via IBus 374 // daemon. If the id is not in the preload_engine list, this function 375 // changes the current method to the first preloaded engine. Returns 376 // true if the current engine is switched to |input_method_id| or the 377 // first one. 378 bool ChangeInputMethodViaIBus(const std::string& input_method_id) { 379 if (!initialized_successfully_) 380 return false; 381 382 std::string input_method_id_to_switch = input_method_id; 383 384 if (!InputMethodIsActivated(input_method_id)) { 385 // This path might be taken if prefs::kLanguageCurrentInputMethod (NOT 386 // synced with cloud) and kLanguagePreloadEngines (synced with cloud) are 387 // mismatched. e.g. the former is 'xkb:us::eng' and the latter (on the 388 // sync server) is 'xkb:jp::jpn,mozc'. 389 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods()); 390 DCHECK(!input_methods->empty()); 391 if (!input_methods->empty()) { 392 input_method_id_to_switch = input_methods->at(0).id; 393 LOG(INFO) << "Can't change the current input method to " 394 << input_method_id << " since the engine is not preloaded. " 395 << "Switch to " << input_method_id_to_switch << " instead."; 396 } 397 } 398 399 if (chromeos::ChangeInputMethod(input_method_status_connection_, 400 input_method_id_to_switch.c_str())) { 401 return true; 402 } 403 404 // ChangeInputMethod() fails if the IBus daemon is not yet ready. 405 LOG(ERROR) << "Can't switch input method to " << input_method_id_to_switch; 406 return false; 407 } 408 409 // Flushes the input method config data. The config data is queued up in 410 // |pending_config_requests_| until the config backend (ibus-memconf) 411 // starts. 412 void FlushImeConfig() { 413 if (!initialized_successfully_) 414 return; 415 416 bool active_input_methods_are_changed = false; 417 InputMethodConfigRequests::iterator iter = 418 pending_config_requests_.begin(); 419 while (iter != pending_config_requests_.end()) { 420 const std::string& section = iter->first.first; 421 const std::string& config_name = iter->first.second; 422 const ImeConfigValue& value = iter->second; 423 if (chromeos::SetImeConfig(input_method_status_connection_, 424 section.c_str(), 425 config_name.c_str(), 426 value)) { 427 // Check if it's a change in active input methods. 428 if (config_name == language_prefs::kPreloadEnginesConfigName) { 429 active_input_methods_are_changed = true; 430 VLOG(1) << "Updated preload_engines: " << value.ToString(); 431 } 432 // Successfully sent. Remove the command and proceed to the next one. 433 pending_config_requests_.erase(iter++); 434 } else { 435 // If SetImeConfig() fails, subsequent calls will likely fail. 436 break; 437 } 438 } 439 if (pending_config_requests_.empty()) { 440 // We should change the current input method to the one we have last 441 // remembered in ChangeInputMethod(), for the following reasons: 442 // 443 // 1) Calls to ChangeInputMethod() will fail if the input method has not 444 // yet been added to preload_engines. As such, the call is deferred 445 // until after all config values have been sent to the IME process. 446 // 447 // 2) We might have already changed the current input method to one 448 // of XKB layouts without going through the IBus daemon (we can do 449 // it without the IBus daemon started). 450 if (ime_connected_ && !tentative_current_input_method_id_.empty()) { 451 ChangeInputMethodViaIBus(tentative_current_input_method_id_); 452 tentative_current_input_method_id_.clear(); 453 active_input_methods_are_changed = true; 454 } 455 } 456 457 // Notify the current input method and the number of active input methods to 458 // the UI so that the UI could determine e.g. if it should show/hide the 459 // input method indicator, etc. We have to call FOR_EACH_OBSERVER here since 460 // updating "preload_engine" does not necessarily trigger a DBus signal such 461 // as "global-engine-changed". For example, 462 // 1) If we change the preload_engine from "xkb:us:intl:eng" (i.e. the 463 // indicator is hidden) to "xkb:us:intl:eng,mozc", we have to update UI 464 // so it shows the indicator, but no signal is sent from ibus-daemon 465 // because the current input method is not changed. 466 // 2) If we change the preload_engine from "xkb:us::eng,mozc" (i.e. the 467 // indicator is shown and ibus-daemon is started) to "xkb:us::eng", we 468 // have to update UI so it hides the indicator, but we should not expect 469 // that ibus-daemon could send a DBus signal since the daemon is killed 470 // right after this FlushImeConfig() call. 471 if (active_input_methods_are_changed) { 472 // The |current_input_method_| member might be stale here as 473 // SetImeConfig("preload_engine") call above might change the 474 // current input method in ibus-daemon (ex. this occurs when the 475 // input method currently in use is removed from the options 476 // page). However, it should be safe to use the member here, 477 // for the following reasons: 478 // 1. If ibus-daemon is to be killed, we'll switch to the only one 479 // keyboard layout, and observers are notified. See 480 // MaybeStopInputMethodDaemon() for details. 481 // 2. Otherwise, "global-engine-changed" signal is delivered from 482 // ibus-daemon, and observers are notified. See 483 // InputMethodChangedHandler() for details. 484 const size_t num_active_input_methods = GetNumActiveInputMethods(); 485 FOR_EACH_OBSERVER(Observer, observers_, 486 ActiveInputMethodsChanged(this, 487 current_input_method_, 488 num_active_input_methods)); 489 } 490 } 491 492 // Called when the input method is changed in the IBus daemon 493 // (ex. "global-engine-changed" is delivered from the IBus daemon). 494 static void InputMethodChangedHandler( 495 void* object, 496 const chromeos::InputMethodDescriptor& current_input_method) { 497 // The handler is called when the input method method change is 498 // notified via a DBus connection. Since the DBus notificatiosn are 499 // handled in the UI thread, we can assume that this function always 500 // runs on the UI thread, but just in case. 501 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 502 LOG(ERROR) << "Not on UI thread"; 503 return; 504 } 505 506 InputMethodLibraryImpl* input_method_library = 507 static_cast<InputMethodLibraryImpl*>(object); 508 input_method_library->ChangeCurrentInputMethod(current_input_method); 509 } 510 511 // Called when properties are registered in the IBus daemon. 512 static void RegisterPropertiesHandler( 513 void* object, const ImePropertyList& prop_list) { 514 // See comments in InputMethodChangedHandler. 515 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 516 LOG(ERROR) << "Not on UI thread"; 517 return; 518 } 519 520 InputMethodLibraryImpl* input_method_library = 521 static_cast<InputMethodLibraryImpl*>(object); 522 input_method_library->RegisterProperties(prop_list); 523 } 524 525 // Called when properties are updated in the IBus daemon. 526 static void UpdatePropertyHandler( 527 void* object, const ImePropertyList& prop_list) { 528 // See comments in InputMethodChangedHandler. 529 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 530 LOG(ERROR) << "Not on UI thread"; 531 return; 532 } 533 534 InputMethodLibraryImpl* input_method_library = 535 static_cast<InputMethodLibraryImpl*>(object); 536 input_method_library->UpdateProperty(prop_list); 537 } 538 539 // Called when 1) connection to ibus-daemon and ibus-memconf are established 540 // or 2) connection to ibus-daemon is terminated. 541 static void ConnectionChangeHandler(void* object, bool connected) { 542 // See comments in InputMethodChangedHandler. 543 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 544 LOG(ERROR) << "Not on UI thread"; 545 return; 546 } 547 548 InputMethodLibraryImpl* input_method_library = 549 static_cast<InputMethodLibraryImpl*>(object); 550 input_method_library->ime_connected_ = connected; 551 if (connected) { 552 input_method_library->pending_config_requests_.clear(); 553 input_method_library->pending_config_requests_.insert( 554 input_method_library->current_config_values_.begin(), 555 input_method_library->current_config_values_.end()); 556 input_method_library->FlushImeConfig(); 557 } 558 } 559 560 // Changes the current input method from the given input method 561 // descriptor. This function updates states like current_input_method_ 562 // and notifies observers about the change (that will update the 563 // preferences), hence this function should always be used even if you 564 // just need to change the current keyboard layout. 565 void ChangeCurrentInputMethod(const InputMethodDescriptor& new_input_method) { 566 if (current_input_method_.id != new_input_method.id) { 567 previous_input_method_ = current_input_method_; 568 current_input_method_ = new_input_method; 569 570 // Change the keyboard layout to a preferred layout for the input method. 571 if (!input_method::SetCurrentKeyboardLayoutByName( 572 current_input_method_.keyboard_layout)) { 573 LOG(ERROR) << "Failed to change keyboard layout to " 574 << current_input_method_.keyboard_layout; 575 } 576 577 // Ask the first observer to update preferences. We should not ask every 578 // observer to do so. Otherwise, we'll end up updating preferences many 579 // times when many observers are attached (ex. many windows are opened), 580 // which is unnecessary and expensive. 581 ObserverListBase<Observer>::Iterator it(observers_); 582 Observer* first_observer = it.GetNext(); 583 if (first_observer) { 584 first_observer->PreferenceUpdateNeeded(this, 585 previous_input_method_, 586 current_input_method_); 587 } 588 } 589 590 // Update input method indicators (e.g. "US", "DV") in Chrome windows. 591 // For now, we have to do this every time to keep indicators updated. See 592 // comments near the FOR_EACH_OBSERVER call in FlushImeConfig() for details. 593 const size_t num_active_input_methods = GetNumActiveInputMethods(); 594 FOR_EACH_OBSERVER(Observer, observers_, 595 InputMethodChanged(this, 596 current_input_method_, 597 num_active_input_methods)); 598 } 599 600 // Changes the current input method from the given input method ID. 601 // This function is just a wrapper of ChangeCurrentInputMethod(). 602 void ChangeCurrentInputMethodFromId(const std::string& input_method_id) { 603 const chromeos::InputMethodDescriptor* descriptor = 604 chromeos::input_method::GetInputMethodDescriptorFromId( 605 input_method_id); 606 if (descriptor) { 607 ChangeCurrentInputMethod(*descriptor); 608 } else { 609 LOG(ERROR) << "Descriptor is not found for: " << input_method_id; 610 } 611 } 612 613 // Registers the properties used by the current input method. 614 void RegisterProperties(const ImePropertyList& prop_list) { 615 // |prop_list| might be empty. This means "clear all properties." 616 current_ime_properties_ = prop_list; 617 618 // Update input method menu 619 FOR_EACH_OBSERVER(Observer, observers_, 620 PropertyListChanged(this, 621 current_ime_properties_)); 622 } 623 624 // Starts the input method daemon. Unlike MaybeStopInputMethodDaemon(), 625 // this function always starts the daemon. Returns true if the daemon is 626 // started. Otherwise, e.g. the daemon is already started, returns false. 627 bool StartInputMethodDaemon() { 628 should_launch_ime_ = true; 629 return MaybeLaunchInputMethodDaemon(); 630 } 631 632 // Updates the properties used by the current input method. 633 void UpdateProperty(const ImePropertyList& prop_list) { 634 for (size_t i = 0; i < prop_list.size(); ++i) { 635 FindAndUpdateProperty(prop_list[i], ¤t_ime_properties_); 636 } 637 638 // Update input method menu 639 FOR_EACH_OBSERVER(Observer, observers_, 640 PropertyListChanged(this, 641 current_ime_properties_)); 642 } 643 644 // Launches an input method procsess specified by the given command 645 // line. On success, returns true and stores the process handle in 646 // |process_handle|. Otherwise, returns false, and the contents of 647 // |process_handle| is untouched. OnImeShutdown will be called when the 648 // process terminates. 649 bool LaunchInputMethodProcess(const std::string& command_line, 650 base::ProcessHandle* process_handle) { 651 std::vector<std::string> argv; 652 base::file_handle_mapping_vector fds_to_remap; 653 base::ProcessHandle handle = base::kNullProcessHandle; 654 655 // TODO(zork): export "LD_PRELOAD=/usr/lib/libcrash.so" 656 base::SplitString(command_line, ' ', &argv); 657 const bool result = base::LaunchApp(argv, 658 fds_to_remap, // no remapping 659 false, // wait 660 &handle); 661 if (!result) { 662 LOG(ERROR) << "Could not launch: " << command_line; 663 return false; 664 } 665 666 // g_child_watch_add is necessary to prevent the process from becoming a 667 // zombie. 668 // TODO(yusukes): port g_child_watch_add to base/process_utils_posix.cc. 669 const base::ProcessId pid = base::GetProcId(handle); 670 g_child_watch_add(pid, 671 reinterpret_cast<GChildWatchFunc>(OnImeShutdown), 672 this); 673 674 *process_handle = handle; 675 VLOG(1) << command_line << " (PID=" << pid << ") is started"; 676 return true; 677 } 678 679 // Launches input method daemon if these are not yet running. Returns true if 680 // the daemon is started. Otherwise, e.g. the daemon is already started, 681 // returns false. 682 bool MaybeLaunchInputMethodDaemon() { 683 // CandidateWindowController requires libcros to be loaded. Besides, 684 // launching ibus-daemon without libcros loaded doesn't make sense. 685 if (!initialized_successfully_) 686 return false; 687 688 if (!should_launch_ime_) { 689 return false; 690 } 691 692 if (!candidate_window_controller_.get()) { 693 candidate_window_controller_.reset(new CandidateWindowController); 694 if (!candidate_window_controller_->Init()) { 695 LOG(WARNING) << "Failed to initialize the candidate window controller"; 696 } 697 } 698 699 if (ibus_daemon_process_handle_ != base::kNullProcessHandle) { 700 return false; // ibus-daemon is already running. 701 } 702 703 // TODO(zork): Send output to /var/log/ibus.log 704 const std::string ibus_daemon_command_line = 705 StringPrintf("%s --panel=disable --cache=none --restart --replace", 706 kIBusDaemonPath); 707 if (!LaunchInputMethodProcess( 708 ibus_daemon_command_line, &ibus_daemon_process_handle_)) { 709 LOG(ERROR) << "Failed to launch " << ibus_daemon_command_line; 710 return false; 711 } 712 return true; 713 } 714 715 // Called when the input method process is shut down. 716 static void OnImeShutdown(GPid pid, 717 gint status, 718 InputMethodLibraryImpl* library) { 719 if (library->ibus_daemon_process_handle_ != base::kNullProcessHandle && 720 base::GetProcId(library->ibus_daemon_process_handle_) == pid) { 721 library->ibus_daemon_process_handle_ = base::kNullProcessHandle; 722 } 723 724 // Restart input method daemon if needed. 725 library->MaybeLaunchInputMethodDaemon(); 726 } 727 728 // Stops the backend input method daemon. This function should also be 729 // called from MaybeStopInputMethodDaemon(), except one case where we 730 // stop the input method daemon at Chrome shutdown in Observe(). 731 void StopInputMethodDaemon() { 732 if (!initialized_successfully_) 733 return; 734 735 should_launch_ime_ = false; 736 if (ibus_daemon_process_handle_ != base::kNullProcessHandle) { 737 const base::ProcessId pid = base::GetProcId(ibus_daemon_process_handle_); 738 if (!chromeos::StopInputMethodProcess(input_method_status_connection_)) { 739 LOG(ERROR) << "StopInputMethodProcess IPC failed. Sending SIGTERM to " 740 << "PID " << pid; 741 base::KillProcess(ibus_daemon_process_handle_, -1, false /* wait */); 742 } 743 VLOG(1) << "ibus-daemon (PID=" << pid << ") is terminated"; 744 ibus_daemon_process_handle_ = base::kNullProcessHandle; 745 } 746 } 747 748 void SetDeferImeStartup(bool defer) { 749 VLOG(1) << "Setting DeferImeStartup to " << defer; 750 defer_ime_startup_ = defer; 751 } 752 753 void SetEnableAutoImeShutdown(bool enable) { 754 enable_auto_ime_shutdown_ = enable; 755 } 756 757 // NotificationObserver implementation: 758 void Observe(NotificationType type, 759 const NotificationSource& source, 760 const NotificationDetails& details) { 761 // Stop the input method daemon on browser shutdown. 762 if (type.value == NotificationType::APP_TERMINATING) { 763 notification_registrar_.RemoveAll(); 764 StopInputMethodDaemon(); 765 candidate_window_controller_.reset(NULL); 766 } 767 } 768 769 // A reference to the language api, to allow callbacks when the input method 770 // status changes. 771 InputMethodStatusConnection* input_method_status_connection_; 772 ObserverList<Observer> observers_; 773 774 // The input method which was/is selected. 775 InputMethodDescriptor previous_input_method_; 776 InputMethodDescriptor current_input_method_; 777 778 // The input method properties which the current input method uses. The list 779 // might be empty when no input method is used. 780 ImePropertyList current_ime_properties_; 781 782 typedef std::pair<std::string, std::string> ConfigKeyType; 783 typedef std::map<ConfigKeyType, ImeConfigValue> InputMethodConfigRequests; 784 // SetImeConfig requests that are not yet completed. 785 // Use a map to queue config requests, so we only send the last request for 786 // the same config key (i.e. we'll discard ealier requests for the same 787 // config key). As we discard old requests for the same config key, the order 788 // of requests doesn't matter, so it's safe to use a map. 789 InputMethodConfigRequests pending_config_requests_; 790 791 // Values that have been set via SetImeConfig(). We keep a copy available to 792 // resend if the ime restarts and loses its state. 793 InputMethodConfigRequests current_config_values_; 794 795 // This is used to register this object to APP_EXITING notification. 796 NotificationRegistrar notification_registrar_; 797 798 // True if we should launch the input method daemon. 799 bool should_launch_ime_; 800 // True if the connection to the IBus daemon is alive. 801 bool ime_connected_; 802 // If true, we'll defer the startup until a non-default method is 803 // activated. 804 bool defer_ime_startup_; 805 // True if we should stop input method daemon when there are no input 806 // methods other than one for the hardware keyboard. 807 bool enable_auto_ime_shutdown_; 808 // The ID of the tentative current input method (ex. "mozc"). This value 809 // can be different from the actual current input method, if 810 // ChangeInputMethod() fails. 811 // TODO(yusukes): clear this variable when a user logs in. 812 std::string tentative_current_input_method_id_; 813 814 // The process handle of the IBus daemon. kNullProcessHandle if it's not 815 // running. 816 base::ProcessHandle ibus_daemon_process_handle_; 817 818 // True if initialization is successfully done, meaning that libcros is 819 // loaded and input method status monitoring is started. This value 820 // should be checked where we call libcros functions. 821 bool initialized_successfully_; 822 823 // The candidate window. This will be deleted when the APP_TERMINATING 824 // message is sent. 825 scoped_ptr<CandidateWindowController> candidate_window_controller_; 826 827 // The active input method ids cache. 828 std::vector<std::string> active_input_method_ids_; 829 830 DISALLOW_COPY_AND_ASSIGN(InputMethodLibraryImpl); 831}; 832 833InputMethodLibraryImpl::Observer::~Observer() {} 834 835// The stub implementation of InputMethodLibrary. Used for testing. 836class InputMethodLibraryStubImpl : public InputMethodLibrary { 837 public: 838 InputMethodLibraryStubImpl() 839 : previous_input_method_("", "", "", ""), 840 current_input_method_("", "", "", ""), 841 keyboard_overlay_map_( 842 GetKeyboardOverlayMapForTesting()) { 843 current_input_method_ = input_method::GetFallbackInputMethodDescriptor(); 844 } 845 846 virtual ~InputMethodLibraryStubImpl() {} 847 virtual void AddObserver(Observer* observer) {} 848 virtual void RemoveObserver(Observer* observer) {} 849 850 virtual InputMethodDescriptors* GetActiveInputMethods() { 851 return GetInputMethodDescriptorsForTesting(); 852 } 853 854 855 virtual size_t GetNumActiveInputMethods() { 856 scoped_ptr<InputMethodDescriptors> descriptors(GetActiveInputMethods()); 857 return descriptors->size(); 858 } 859 860 virtual InputMethodDescriptors* GetSupportedInputMethods() { 861 return GetInputMethodDescriptorsForTesting(); 862 } 863 864 virtual void ChangeInputMethod(const std::string& input_method_id) {} 865 virtual void SetImePropertyActivated(const std::string& key, 866 bool activated) {} 867 868 virtual bool InputMethodIsActivated(const std::string& input_method_id) { 869 return true; 870 } 871 872 virtual bool SetImeConfig(const std::string& section, 873 const std::string& config_name, 874 const ImeConfigValue& value) { 875 return false; 876 } 877 878 virtual InputMethodDescriptor previous_input_method() const { 879 return previous_input_method_; 880 } 881 882 virtual InputMethodDescriptor current_input_method() const { 883 return current_input_method_; 884 } 885 886 virtual const ImePropertyList& current_ime_properties() const { 887 return current_ime_properties_; 888 } 889 890 virtual bool StartInputMethodDaemon() { 891 return true; 892 } 893 virtual void StopInputMethodDaemon() {} 894 virtual void SetDeferImeStartup(bool defer) {} 895 virtual void SetEnableAutoImeShutdown(bool enable) {} 896 897 virtual std::string GetKeyboardOverlayId(const std::string& input_method_id) { 898 KeyboardOverlayMap::const_iterator iter = 899 keyboard_overlay_map_->find(input_method_id); 900 return (iter != keyboard_overlay_map_->end()) ? 901 iter->second : ""; 902 } 903 904 private: 905 typedef std::map<std::string, std::string> KeyboardOverlayMap; 906 907 // Gets input method descriptors for testing. Shouldn't be used for 908 // production. 909 InputMethodDescriptors* GetInputMethodDescriptorsForTesting() { 910 InputMethodDescriptors* descriptions = new InputMethodDescriptors; 911 // The list is created from output of gen_engines.py in libcros. 912 // % SHARE=/build/x86-generic/usr/share python gen_engines.py 913 // $SHARE/chromeos-assets/input_methods/whitelist.txt 914 // $SHARE/ibus/component/{chewing,hangul,m17n,mozc,pinyin,xkb-layouts}.xml 915 descriptions->push_back(InputMethodDescriptor( 916 "xkb:nl::nld", "Netherlands", "nl", "nld")); 917 descriptions->push_back(InputMethodDescriptor( 918 "xkb:be::nld", "Belgium", "be", "nld")); 919 descriptions->push_back(InputMethodDescriptor( 920 "xkb:fr::fra", "France", "fr", "fra")); 921 descriptions->push_back(InputMethodDescriptor( 922 "xkb:be::fra", "Belgium", "be", "fra")); 923 descriptions->push_back(InputMethodDescriptor( 924 "xkb:ca::fra", "Canada", "ca", "fra")); 925 descriptions->push_back(InputMethodDescriptor( 926 "xkb:ch:fr:fra", "Switzerland - French", "ch(fr)", "fra")); 927 descriptions->push_back(InputMethodDescriptor( 928 "xkb:de::ger", "Germany", "de", "ger")); 929 descriptions->push_back(InputMethodDescriptor( 930 "xkb:de:neo:ger", "Germany - Neo 2", "de(neo)", "ger")); 931 descriptions->push_back(InputMethodDescriptor( 932 "xkb:be::ger", "Belgium", "be", "ger")); 933 descriptions->push_back(InputMethodDescriptor( 934 "xkb:ch::ger", "Switzerland", "ch", "ger")); 935 descriptions->push_back(InputMethodDescriptor( 936 "mozc", "Mozc (US keyboard layout)", "us", "ja")); 937 descriptions->push_back(InputMethodDescriptor( 938 "mozc-jp", "Mozc (Japanese keyboard layout)", "jp", "ja")); 939 descriptions->push_back(InputMethodDescriptor( 940 "mozc-dv", "Mozc (US Dvorak keyboard layout)", "us(dvorak)", "ja")); 941 descriptions->push_back(InputMethodDescriptor( 942 "xkb:jp::jpn", "Japan", "jp", "jpn")); 943 descriptions->push_back(InputMethodDescriptor( 944 "xkb:ru::rus", "Russia", "ru", "rus")); 945 descriptions->push_back(InputMethodDescriptor( 946 "xkb:ru:phonetic:rus", "Russia - Phonetic", "ru(phonetic)", "rus")); 947 descriptions->push_back(InputMethodDescriptor( 948 "m17n:th:kesmanee", "kesmanee (m17n)", "us", "th")); 949 descriptions->push_back(InputMethodDescriptor( 950 "m17n:th:pattachote", "pattachote (m17n)", "us", "th")); 951 descriptions->push_back(InputMethodDescriptor( 952 "m17n:th:tis820", "tis820 (m17n)", "us", "th")); 953 descriptions->push_back(InputMethodDescriptor( 954 "mozc-chewing", "Mozc Chewing (Chewing)", "us", "zh_TW")); 955 descriptions->push_back(InputMethodDescriptor( 956 "m17n:zh:cangjie", "cangjie (m17n)", "us", "zh")); 957 descriptions->push_back(InputMethodDescriptor( 958 "m17n:zh:quick", "quick (m17n)", "us", "zh")); 959 descriptions->push_back(InputMethodDescriptor( 960 "m17n:vi:tcvn", "tcvn (m17n)", "us", "vi")); 961 descriptions->push_back(InputMethodDescriptor( 962 "m17n:vi:telex", "telex (m17n)", "us", "vi")); 963 descriptions->push_back(InputMethodDescriptor( 964 "m17n:vi:viqr", "viqr (m17n)", "us", "vi")); 965 descriptions->push_back(InputMethodDescriptor( 966 "m17n:vi:vni", "vni (m17n)", "us", "vi")); 967 descriptions->push_back(InputMethodDescriptor( 968 "xkb:us::eng", "USA", "us", "eng")); 969 descriptions->push_back(InputMethodDescriptor( 970 "xkb:us:intl:eng", 971 "USA - International (with dead keys)", "us(intl)", "eng")); 972 descriptions->push_back(InputMethodDescriptor( 973 "xkb:us:altgr-intl:eng", 974 "USA - International (AltGr dead keys)", "us(altgr-intl)", "eng")); 975 descriptions->push_back(InputMethodDescriptor( 976 "xkb:us:dvorak:eng", "USA - Dvorak", "us(dvorak)", "eng")); 977 descriptions->push_back(InputMethodDescriptor( 978 "xkb:us:colemak:eng", "USA - Colemak", "us(colemak)", "eng")); 979 descriptions->push_back(InputMethodDescriptor( 980 "hangul", "Korean", "kr(kr104)", "ko")); 981 descriptions->push_back(InputMethodDescriptor( 982 "pinyin", "Pinyin", "us", "zh")); 983 descriptions->push_back(InputMethodDescriptor( 984 "m17n:ar:kbd", "kbd (m17n)", "us", "ar")); 985 descriptions->push_back(InputMethodDescriptor( 986 "m17n:hi:itrans", "itrans (m17n)", "us", "hi")); 987 descriptions->push_back(InputMethodDescriptor( 988 "m17n:fa:isiri", "isiri (m17n)", "us", "fa")); 989 descriptions->push_back(InputMethodDescriptor( 990 "xkb:br::por", "Brazil", "br", "por")); 991 descriptions->push_back(InputMethodDescriptor( 992 "xkb:bg::bul", "Bulgaria", "bg", "bul")); 993 descriptions->push_back(InputMethodDescriptor( 994 "xkb:bg:phonetic:bul", 995 "Bulgaria - Traditional phonetic", "bg(phonetic)", "bul")); 996 descriptions->push_back(InputMethodDescriptor( 997 "xkb:ca:eng:eng", "Canada - English", "ca(eng)", "eng")); 998 descriptions->push_back(InputMethodDescriptor( 999 "xkb:cz::cze", "Czechia", "cz", "cze")); 1000 descriptions->push_back(InputMethodDescriptor( 1001 "xkb:ee::est", "Estonia", "ee", "est")); 1002 descriptions->push_back(InputMethodDescriptor( 1003 "xkb:es::spa", "Spain", "es", "spa")); 1004 descriptions->push_back(InputMethodDescriptor( 1005 "xkb:es:cat:cat", 1006 "Spain - Catalan variant with middle-dot L", "es(cat)", "cat")); 1007 descriptions->push_back(InputMethodDescriptor( 1008 "xkb:dk::dan", "Denmark", "dk", "dan")); 1009 descriptions->push_back(InputMethodDescriptor( 1010 "xkb:gr::gre", "Greece", "gr", "gre")); 1011 descriptions->push_back(InputMethodDescriptor( 1012 "xkb:il::heb", "Israel", "il", "heb")); 1013 descriptions->push_back(InputMethodDescriptor( 1014 "xkb:kr:kr104:kor", 1015 "Korea, Republic of - 101/104 key Compatible", "kr(kr104)", "kor")); 1016 descriptions->push_back(InputMethodDescriptor( 1017 "xkb:latam::spa", "Latin American", "latam", "spa")); 1018 descriptions->push_back(InputMethodDescriptor( 1019 "xkb:lt::lit", "Lithuania", "lt", "lit")); 1020 descriptions->push_back(InputMethodDescriptor( 1021 "xkb:lv:apostrophe:lav", 1022 "Latvia - Apostrophe (') variant", "lv(apostrophe)", "lav")); 1023 descriptions->push_back(InputMethodDescriptor( 1024 "xkb:hr::scr", "Croatia", "hr", "scr")); 1025 descriptions->push_back(InputMethodDescriptor( 1026 "xkb:gb:extd:eng", 1027 "United Kingdom - Extended - Winkeys", "gb(extd)", "eng")); 1028 descriptions->push_back(InputMethodDescriptor( 1029 "xkb:gb:dvorak:eng", 1030 "United Kingdom - Dvorak", "gb(dvorak)", "eng")); 1031 descriptions->push_back(InputMethodDescriptor( 1032 "xkb:fi::fin", "Finland", "fi", "fin")); 1033 descriptions->push_back(InputMethodDescriptor( 1034 "xkb:hu::hun", "Hungary", "hu", "hun")); 1035 descriptions->push_back(InputMethodDescriptor( 1036 "xkb:it::ita", "Italy", "it", "ita")); 1037 descriptions->push_back(InputMethodDescriptor( 1038 "xkb:no::nob", "Norway", "no", "nob")); 1039 descriptions->push_back(InputMethodDescriptor( 1040 "xkb:pl::pol", "Poland", "pl", "pol")); 1041 descriptions->push_back(InputMethodDescriptor( 1042 "xkb:pt::por", "Portugal", "pt", "por")); 1043 descriptions->push_back(InputMethodDescriptor( 1044 "xkb:ro::rum", "Romania", "ro", "rum")); 1045 descriptions->push_back(InputMethodDescriptor( 1046 "xkb:se::swe", "Sweden", "se", "swe")); 1047 descriptions->push_back(InputMethodDescriptor( 1048 "xkb:sk::slo", "Slovakia", "sk", "slo")); 1049 descriptions->push_back(InputMethodDescriptor( 1050 "xkb:si::slv", "Slovenia", "si", "slv")); 1051 descriptions->push_back(InputMethodDescriptor( 1052 "xkb:rs::srp", "Serbia", "rs", "srp")); 1053 descriptions->push_back(InputMethodDescriptor( 1054 "xkb:tr::tur", "Turkey", "tr", "tur")); 1055 descriptions->push_back(InputMethodDescriptor( 1056 "xkb:ua::ukr", "Ukraine", "ua", "ukr")); 1057 return descriptions; 1058 } 1059 1060 // Gets keyboard overlay map for testing. Shouldn't be used for 1061 // production. 1062 std::map<std::string, std::string>* GetKeyboardOverlayMapForTesting() { 1063 KeyboardOverlayMap* keyboard_overlay_map = 1064 new KeyboardOverlayMap; 1065 (*keyboard_overlay_map)["xkb:nl::nld"] = "nl"; 1066 (*keyboard_overlay_map)["xkb:be::nld"] = "nl"; 1067 (*keyboard_overlay_map)["xkb:fr::fra"] = "fr"; 1068 (*keyboard_overlay_map)["xkb:be::fra"] = "fr"; 1069 (*keyboard_overlay_map)["xkb:ca::fra"] = "fr_CA"; 1070 (*keyboard_overlay_map)["xkb:ch:fr:fra"] = "fr"; 1071 (*keyboard_overlay_map)["xkb:de::ger"] = "de"; 1072 (*keyboard_overlay_map)["xkb:be::ger"] = "de"; 1073 (*keyboard_overlay_map)["xkb:ch::ger"] = "de"; 1074 (*keyboard_overlay_map)["mozc"] = "en_US"; 1075 (*keyboard_overlay_map)["mozc-jp"] = "ja"; 1076 (*keyboard_overlay_map)["mozc-dv"] = "en_US_dvorak"; 1077 (*keyboard_overlay_map)["xkb:jp::jpn"] = "ja"; 1078 (*keyboard_overlay_map)["xkb:ru::rus"] = "ru"; 1079 (*keyboard_overlay_map)["xkb:ru:phonetic:rus"] = "ru"; 1080 (*keyboard_overlay_map)["m17n:th:kesmanee"] = "th"; 1081 (*keyboard_overlay_map)["m17n:th:pattachote"] = "th"; 1082 (*keyboard_overlay_map)["m17n:th:tis820"] = "th"; 1083 (*keyboard_overlay_map)["mozc-chewing"] = "zh_TW"; 1084 (*keyboard_overlay_map)["m17n:zh:cangjie"] = "zh_TW"; 1085 (*keyboard_overlay_map)["m17n:zh:quick"] = "zh_TW"; 1086 (*keyboard_overlay_map)["m17n:vi:tcvn"] = "vi"; 1087 (*keyboard_overlay_map)["m17n:vi:telex"] = "vi"; 1088 (*keyboard_overlay_map)["m17n:vi:viqr"] = "vi"; 1089 (*keyboard_overlay_map)["m17n:vi:vni"] = "vi"; 1090 (*keyboard_overlay_map)["xkb:us::eng"] = "en_US"; 1091 (*keyboard_overlay_map)["xkb:us:intl:eng"] = "en_US"; 1092 (*keyboard_overlay_map)["xkb:us:altgr-intl:eng"] = "en_US"; 1093 (*keyboard_overlay_map)["xkb:us:dvorak:eng"] = 1094 "en_US_dvorak"; 1095 (*keyboard_overlay_map)["xkb:us:colemak:eng"] = 1096 "en_US"; 1097 (*keyboard_overlay_map)["hangul"] = "ko"; 1098 (*keyboard_overlay_map)["pinyin"] = "zh_CN"; 1099 (*keyboard_overlay_map)["m17n:ar:kbd"] = "ar"; 1100 (*keyboard_overlay_map)["m17n:hi:itrans"] = "hi"; 1101 (*keyboard_overlay_map)["m17n:fa:isiri"] = "ar"; 1102 (*keyboard_overlay_map)["xkb:br::por"] = "pt_BR"; 1103 (*keyboard_overlay_map)["xkb:bg::bul"] = "bg"; 1104 (*keyboard_overlay_map)["xkb:bg:phonetic:bul"] = "bg"; 1105 (*keyboard_overlay_map)["xkb:ca:eng:eng"] = "ca"; 1106 (*keyboard_overlay_map)["xkb:cz::cze"] = "cs"; 1107 (*keyboard_overlay_map)["xkb:ee::est"] = "et"; 1108 (*keyboard_overlay_map)["xkb:es::spa"] = "es"; 1109 (*keyboard_overlay_map)["xkb:es:cat:cat"] = "ca"; 1110 (*keyboard_overlay_map)["xkb:dk::dan"] = "da"; 1111 (*keyboard_overlay_map)["xkb:gr::gre"] = "el"; 1112 (*keyboard_overlay_map)["xkb:il::heb"] = "iw"; 1113 (*keyboard_overlay_map)["xkb:kr:kr104:kor"] = "ko"; 1114 (*keyboard_overlay_map)["xkb:latam::spa"] = "es_419"; 1115 (*keyboard_overlay_map)["xkb:lt::lit"] = "lt"; 1116 (*keyboard_overlay_map)["xkb:lv:apostrophe:lav"] = "lv"; 1117 (*keyboard_overlay_map)["xkb:hr::scr"] = "hr"; 1118 (*keyboard_overlay_map)["xkb:gb:extd:eng"] = "en_GB"; 1119 (*keyboard_overlay_map)["xkb:gb:dvorak:eng"] = "en_GB_dvorak"; 1120 (*keyboard_overlay_map)["xkb:fi::fin"] = "fi"; 1121 (*keyboard_overlay_map)["xkb:hu::hun"] = "hu"; 1122 (*keyboard_overlay_map)["xkb:it::ita"] = "it"; 1123 (*keyboard_overlay_map)["xkb:no::nob"] = "no"; 1124 (*keyboard_overlay_map)["xkb:pl::pol"] = "pl"; 1125 (*keyboard_overlay_map)["xkb:pt::por"] = "pt_PT"; 1126 (*keyboard_overlay_map)["xkb:ro::rum"] = "ro"; 1127 (*keyboard_overlay_map)["xkb:se::swe"] = "sv"; 1128 (*keyboard_overlay_map)["xkb:sk::slo"] = "sk"; 1129 (*keyboard_overlay_map)["xkb:si::slv"] = "sl"; 1130 (*keyboard_overlay_map)["xkb:rs::srp"] = "sr"; 1131 (*keyboard_overlay_map)["xkb:tr::tur"] = "tr"; 1132 (*keyboard_overlay_map)["xkb:ua::ukr"] = "uk"; 1133 return keyboard_overlay_map; 1134 } 1135 1136 InputMethodDescriptor previous_input_method_; 1137 InputMethodDescriptor current_input_method_; 1138 ImePropertyList current_ime_properties_; 1139 scoped_ptr<KeyboardOverlayMap> keyboard_overlay_map_; 1140 1141 DISALLOW_COPY_AND_ASSIGN(InputMethodLibraryStubImpl); 1142}; 1143 1144// static 1145InputMethodLibrary* InputMethodLibrary::GetImpl(bool stub) { 1146 if (stub) { 1147 return new InputMethodLibraryStubImpl(); 1148 } else { 1149 InputMethodLibraryImpl* impl = new InputMethodLibraryImpl(); 1150 if (!impl->Init()) { 1151 LOG(ERROR) << "Failed to initialize InputMethodLibraryImpl"; 1152 } 1153 return impl; 1154 } 1155} 1156 1157} // namespace chromeos 1158 1159// Allows InvokeLater without adding refcounting. This class is a Singleton and 1160// won't be deleted until it's last InvokeLater is run. 1161DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::InputMethodLibraryImpl); 1162