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], &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
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