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.
FindAndUpdateProperty(const chromeos::ImeProperty & new_prop,chromeos::ImePropertyList * prop_list)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:
InputMethodLibraryImpl()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.
Init()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
~InputMethodLibraryImpl()106 virtual ~InputMethodLibraryImpl() {
107 }
108
AddObserver(Observer * observer)109 virtual void AddObserver(Observer* observer) {
110 if (!observers_.size()) {
111 observer->FirstObserverIsAdded(this);
112 }
113 observers_.AddObserver(observer);
114 }
115
RemoveObserver(Observer * observer)116 virtual void RemoveObserver(Observer* observer) {
117 observers_.RemoveObserver(observer);
118 }
119
GetActiveInputMethods()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
GetNumActiveInputMethods()145 virtual size_t GetNumActiveInputMethods() {
146 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
147 return input_methods->size();
148 }
149
GetSupportedInputMethods()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
ChangeInputMethod(const std::string & input_method_id)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
SetImePropertyActivated(const std::string & key,bool activated)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
InputMethodIsActivated(const std::string & input_method_id)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
SetImeConfig(const std::string & section,const std::string & config_name,const ImeConfigValue & value)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
previous_input_method() const243 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
current_input_method() const250 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
current_ime_properties() const257 virtual const ImePropertyList& current_ime_properties() const {
258 return current_ime_properties_;
259 }
260
GetKeyboardOverlayId(const std::string & input_method_id)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.
ContainOnlyOneKeyboardLayout(const ImeConfigValue & value)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.
MaybeStartInputMethodDaemon(const std::string & section,const std::string & config_name,const ImeConfigValue & value)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().
MaybeStopInputMethodDaemon(const std::string & section,const std::string & config_name,const ImeConfigValue & value)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().
MaybeChangeCurrentKeyboardLayout(const std::string & section,const std::string & config_name,const ImeConfigValue & value)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.
ChangeInputMethodViaIBus(const std::string & input_method_id)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.
FlushImeConfig()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).
InputMethodChangedHandler(void * object,const chromeos::InputMethodDescriptor & current_input_method)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.
RegisterPropertiesHandler(void * object,const ImePropertyList & prop_list)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.
UpdatePropertyHandler(void * object,const ImePropertyList & prop_list)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.
ConnectionChangeHandler(void * object,bool connected)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.
ChangeCurrentInputMethod(const InputMethodDescriptor & new_input_method)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().
ChangeCurrentInputMethodFromId(const std::string & input_method_id)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.
RegisterProperties(const ImePropertyList & prop_list)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.
StartInputMethodDaemon()627 bool StartInputMethodDaemon() {
628 should_launch_ime_ = true;
629 return MaybeLaunchInputMethodDaemon();
630 }
631
632 // Updates the properties used by the current input method.
UpdateProperty(const ImePropertyList & prop_list)633 void UpdateProperty(const ImePropertyList& prop_list) {
634 for (size_t i = 0; i < prop_list.size(); ++i) {
635 FindAndUpdateProperty(prop_list[i], ¤t_ime_properties_);
636 }
637
638 // Update input method menu
639 FOR_EACH_OBSERVER(Observer, observers_,
640 PropertyListChanged(this,
641 current_ime_properties_));
642 }
643
644 // Launches an input method procsess specified by the given command
645 // line. On success, returns true and stores the process handle in
646 // |process_handle|. Otherwise, returns false, and the contents of
647 // |process_handle| is untouched. OnImeShutdown will be called when the
648 // process terminates.
LaunchInputMethodProcess(const std::string & command_line,base::ProcessHandle * process_handle)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.
MaybeLaunchInputMethodDaemon()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.
OnImeShutdown(GPid pid,gint status,InputMethodLibraryImpl * library)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().
StopInputMethodDaemon()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
SetDeferImeStartup(bool defer)748 void SetDeferImeStartup(bool defer) {
749 VLOG(1) << "Setting DeferImeStartup to " << defer;
750 defer_ime_startup_ = defer;
751 }
752
SetEnableAutoImeShutdown(bool enable)753 void SetEnableAutoImeShutdown(bool enable) {
754 enable_auto_ime_shutdown_ = enable;
755 }
756
757 // NotificationObserver implementation:
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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
~Observer()833 InputMethodLibraryImpl::Observer::~Observer() {}
834
835 // The stub implementation of InputMethodLibrary. Used for testing.
836 class InputMethodLibraryStubImpl : public InputMethodLibrary {
837 public:
InputMethodLibraryStubImpl()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
~InputMethodLibraryStubImpl()846 virtual ~InputMethodLibraryStubImpl() {}
AddObserver(Observer * observer)847 virtual void AddObserver(Observer* observer) {}
RemoveObserver(Observer * observer)848 virtual void RemoveObserver(Observer* observer) {}
849
GetActiveInputMethods()850 virtual InputMethodDescriptors* GetActiveInputMethods() {
851 return GetInputMethodDescriptorsForTesting();
852 }
853
854
GetNumActiveInputMethods()855 virtual size_t GetNumActiveInputMethods() {
856 scoped_ptr<InputMethodDescriptors> descriptors(GetActiveInputMethods());
857 return descriptors->size();
858 }
859
GetSupportedInputMethods()860 virtual InputMethodDescriptors* GetSupportedInputMethods() {
861 return GetInputMethodDescriptorsForTesting();
862 }
863
ChangeInputMethod(const std::string & input_method_id)864 virtual void ChangeInputMethod(const std::string& input_method_id) {}
SetImePropertyActivated(const std::string & key,bool activated)865 virtual void SetImePropertyActivated(const std::string& key,
866 bool activated) {}
867
InputMethodIsActivated(const std::string & input_method_id)868 virtual bool InputMethodIsActivated(const std::string& input_method_id) {
869 return true;
870 }
871
SetImeConfig(const std::string & section,const std::string & config_name,const ImeConfigValue & value)872 virtual bool SetImeConfig(const std::string& section,
873 const std::string& config_name,
874 const ImeConfigValue& value) {
875 return false;
876 }
877
previous_input_method() const878 virtual InputMethodDescriptor previous_input_method() const {
879 return previous_input_method_;
880 }
881
current_input_method() const882 virtual InputMethodDescriptor current_input_method() const {
883 return current_input_method_;
884 }
885
current_ime_properties() const886 virtual const ImePropertyList& current_ime_properties() const {
887 return current_ime_properties_;
888 }
889
StartInputMethodDaemon()890 virtual bool StartInputMethodDaemon() {
891 return true;
892 }
StopInputMethodDaemon()893 virtual void StopInputMethodDaemon() {}
SetDeferImeStartup(bool defer)894 virtual void SetDeferImeStartup(bool defer) {}
SetEnableAutoImeShutdown(bool enable)895 virtual void SetEnableAutoImeShutdown(bool enable) {}
896
GetKeyboardOverlayId(const std::string & input_method_id)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.
GetInputMethodDescriptorsForTesting()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.
GetKeyboardOverlayMapForTesting()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
GetImpl(bool stub)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