Home | History | Annotate | Download | only in cros
      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 
     29 namespace {
     30 
     31 const 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.
     35 bool 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 
     53 namespace chromeos {
     54 
     55 // The production implementation of InputMethodLibrary.
     56 class 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], &current_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 
    833 InputMethodLibraryImpl::Observer::~Observer() {}
    834 
    835 // The stub implementation of InputMethodLibrary. Used for testing.
    836 class 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
   1145 InputMethodLibrary* 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.
   1161 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::InputMethodLibraryImpl);
   1162