Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: chrome/browser/chromeos/input_method/input_method_manager.cc

Issue 9999018: chrome/browser/chromeos/input_method/ refactoring [part 6 of 6] (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase, remove |should_hide_properties_| Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/chromeos/input_method/input_method_manager.h" 5 #include "chrome/browser/chromeos/input_method/input_method_manager.h"
6 6
7 #include <algorithm> 7 #include "chrome/browser/chromeos/input_method/input_method_manager_impl.h"
8
9 #include <glib.h>
10
11 #include "base/basictypes.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/singleton.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 "base/stringprintf.h"
19 #include "chrome/browser/browser_process.h"
20 #include "chrome/browser/chromeos/input_method/browser_state_monitor.h"
21 #include "chrome/browser/chromeos/input_method/input_method_util.h"
22 #include "chrome/browser/chromeos/input_method/input_method_whitelist.h"
23 #include "chrome/browser/chromeos/input_method/virtual_keyboard_selector.h"
24 #include "chrome/browser/chromeos/input_method/xkeyboard.h"
25 #include "chrome/browser/chromeos/language_preferences.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/notification_observer.h"
28 #include "content/public/browser/notification_registrar.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/notification_types.h"
31 #include "googleurl/src/gurl.h"
32 #include "ui/base/accelerators/accelerator.h"
33 #include "unicode/uloc.h"
34
35 #if !defined(USE_VIRTUAL_KEYBOARD)
36 #include "chrome/browser/chromeos/input_method/candidate_window.h"
37 #endif
38
39 #if defined(HAVE_IBUS)
40 #include <ibus.h>
41 #endif
42
43 using content::BrowserThread;
44
45 namespace {
46
47 const char kIBusDaemonPath[] = "/usr/bin/ibus-daemon";
48
49 // Finds a property which has |new_prop.key| from |prop_list|, and replaces the
50 // property with |new_prop|. Returns true if such a property is found.
51 bool FindAndUpdateProperty(
52 const chromeos::input_method::InputMethodProperty& new_prop,
53 chromeos::input_method::InputMethodPropertyList* prop_list) {
54 for (size_t i = 0; i < prop_list->size(); ++i) {
55 chromeos::input_method::InputMethodProperty& prop = prop_list->at(i);
56 if (prop.key == new_prop.key) {
57 const int saved_id = prop.selection_item_id;
58 // Update the list except the radio id. As written in
59 // chromeos_input_method.h, |prop.selection_item_id| is dummy.
60 prop = new_prop;
61 prop.selection_item_id = saved_id;
62 return true;
63 }
64 }
65 return false;
66 }
67
68 } // namespace
69 8
70 namespace chromeos { 9 namespace chromeos {
71 namespace input_method { 10 namespace input_method {
72 11
73 // The implementation of InputMethodManager. 12 namespace {
74 class InputMethodManagerImpl : public InputMethodManager, 13 InputMethodManager* g_input_method_manager = NULL;
75 public content::NotificationObserver, 14 } // namespace
76 #if !defined(USE_VIRTUAL_KEYBOARD)
77 public CandidateWindowController::Observer,
78 #endif
79 public IBusController::Observer {
80 public:
81 InputMethodManagerImpl()
82 : ibus_controller_(IBusController::Create()),
83 should_hide_properties_(true),
84 should_launch_ime_(false),
85 ime_connected_(false),
86 enable_auto_ime_shutdown_(false), // workaround for crosbug.com/27051.
87 enable_extension_imes_(true),
88 shutting_down_(false),
89 ibus_daemon_process_handle_(base::kNullProcessHandle),
90 util_(whitelist_.GetSupportedInputMethods()),
91 xkeyboard_(XKeyboard::Create(util_)),
92 ALLOW_THIS_IN_INITIALIZER_LIST(
93 browser_state_monitor_(new BrowserStateMonitor(this))),
94 ignore_hotkeys_(false) {
95 // Observe APP_TERMINATING to stop input method daemon gracefully.
96 // We should not use APP_EXITING here since logout might be canceled by
97 // JavaScript after APP_EXITING is sent (crosbug.com/11055).
98 // Note that even if we fail to stop input method daemon from
99 // Chrome in case of a sudden crash, we have a way to do it from an
100 // upstart script. See crosbug.com/6515 and crosbug.com/6995 for
101 // details.
102 notification_registrar_.Add(this, content::NOTIFICATION_APP_TERMINATING,
103 content::NotificationService::AllSources());
104 15
105 // The observer should be added before Connect() so we can capture the 16 // static
106 // initial connection change. 17 void InputMethodManager::Initialize() {
107 ibus_controller_->AddObserver(this); 18 DCHECK(!g_input_method_manager);
108 ibus_controller_->Connect(); 19 InputMethodManagerImpl* impl = new InputMethodManagerImpl;
20 impl->Init();
21 g_input_method_manager = impl;
22 VLOG(1) << "InputMethodManager initialized";
23 }
109 24
110 EnableHotkeys(); 25 // static
111 } 26 void InputMethodManager::InitializeForTesting(
27 InputMethodManager* mock_manager) {
28 DCHECK(!g_input_method_manager);
29 g_input_method_manager = mock_manager;
30 VLOG(1) << "InputMethodManager for testing initialized";
31 }
112 32
113 virtual ~InputMethodManagerImpl() { 33 // static
114 ibus_controller_->RemoveObserver(this); 34 void InputMethodManager::Shutdown() {
115 #if !defined(USE_VIRTUAL_KEYBOARD) 35 // TODO(yusukes): The NULL check is needed for some unit_tests such as
Zachary Kuznia 2012/04/17 01:26:36 It's supposed to be safe to call delete on a null
Yusuke Sato 2012/04/17 02:23:32 Done.
116 if (candidate_window_controller_.get()) 36 // 'WarmConnectionFieldTrial_WarmestSocket' which seem to destruct a
117 candidate_window_controller_->RemoveObserver(this); 37 // ChromeBrowserMainPartsChromeos instance without calling
118 #endif 38 // ChromeBrowserMainPartsChromeos::PostMainMessageLoopStart(). It's probably
119 } 39 // better to fix the tests and remove the hack here.
120 40 if (!g_input_method_manager)
121 virtual void AddObserver(InputMethodManager::Observer* observer) { 41 LOG(WARNING) << "InputMethodManager:Shutdown() called with NULL manager";
122 observers_.AddObserver(observer); 42 else
123 } 43 delete g_input_method_manager;
124 44 g_input_method_manager = NULL;
125 virtual void RemoveObserver(InputMethodManager::Observer* observer) { 45 VLOG(1) << "InputMethodManager shutdown";
126 observers_.RemoveObserver(observer); 46 }
127 }
128
129 virtual void AddCandidateWindowObserver(
130 InputMethodManager::CandidateWindowObserver* observer) {
131 candidate_window_observers_.AddObserver(observer);
132 }
133
134 virtual void RemoveCandidateWindowObserver(
135 InputMethodManager::CandidateWindowObserver* observer) {
136 candidate_window_observers_.RemoveObserver(observer);
137 }
138
139 virtual void AddVirtualKeyboardObserver(VirtualKeyboardObserver* observer) {
140 virtual_keyboard_observers_.AddObserver(observer);
141 }
142
143 virtual void RemoveVirtualKeyboardObserver(
144 VirtualKeyboardObserver* observer) {
145 virtual_keyboard_observers_.RemoveObserver(observer);
146 }
147
148 virtual InputMethodDescriptors* GetActiveInputMethods() {
149 InputMethodDescriptors* result = new InputMethodDescriptors;
150 // Build the active input method descriptors from the active input
151 // methods cache |active_input_method_ids_|.
152 for (size_t i = 0; i < active_input_method_ids_.size(); ++i) {
153 const std::string& input_method_id = active_input_method_ids_[i];
154 const InputMethodDescriptor* descriptor =
155 util_.GetInputMethodDescriptorFromId(input_method_id);
156 if (descriptor) {
157 result->push_back(*descriptor);
158 } else {
159 std::map<std::string, InputMethodDescriptor>::
160 const_iterator ix = extra_input_method_ids_.find(input_method_id);
161 if (ix != extra_input_method_ids_.end()) {
162 result->push_back(ix->second);
163 } else {
164 LOG(ERROR) << "Descriptor is not found for: " << input_method_id;
165 }
166 }
167 }
168 // Initially active_input_method_ids_ is empty. In this case, just
169 // returns the fallback input method descriptor.
170 if (result->empty()) {
171 // Since browser_tests call neither
172 // SetInputMethodConfig("preload_engines") nor EnableInputMethod(), this
173 // path might be taken.
174 result->push_back(
175 InputMethodDescriptor::GetFallbackInputMethodDescriptor());
176 }
177 return result;
178 }
179
180 virtual size_t GetNumActiveInputMethods() {
181 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
182 return input_methods->size();
183 }
184
185 virtual InputMethodDescriptors* GetSupportedInputMethods() {
186 return whitelist_.GetSupportedInputMethods();
187 }
188
189 virtual void ChangeInputMethod(const std::string& input_method_id) {
190 // Changing the input method isn't guaranteed to succeed here, but we
191 // should remember the last one regardless. See comments in
192 // FlushImeConfig() for details.
193 tentative_current_input_method_id_ = input_method_id;
194
195 if (InputMethodUtil::IsKeyboardLayout(input_method_id)) {
196 // We shouldn't use SetCurrentKeyboardLayoutByName() here. See
197 // comments at ChangeCurrentInputMethod() for details.
198 ChangeCurrentInputMethodFromId(input_method_id);
199 OnRegisterImeProperties(InputMethodPropertyList()); // notify the button.
200 } else {
201 // Otherwise, start the input method daemon, and change the input
202 // method via the daemon.
203 StartInputMethodDaemon();
204 // ChangeInputMethodViaIBus() fails if the IBus daemon is not
205 // ready yet. In this case, we'll defer the input method change
206 // until the daemon is ready.
207 if (!ChangeInputMethodViaIBus(input_method_id)) {
208 VLOG(1) << "Failed to change the input method to " << input_method_id
209 << " (deferring)";
210 }
211 }
212 }
213
214 virtual void EnableLayouts(const std::string& language_code,
215 const std::string& initial_input_method_id) {
216 std::vector<std::string> candidates;
217 // Add input methods associated with the language.
218 util_.GetInputMethodIdsFromLanguageCode(language_code,
219 kKeyboardLayoutsOnly,
220 &candidates);
221 // Add the hardware keyboard as well. We should always add this so users
222 // can use the hardware keyboard on the login screen and the screen locker.
223 candidates.push_back(util_.GetHardwareInputMethodId());
224
225 std::vector<std::string> input_method_ids;
226 // First, add the initial input method ID, if it's requested, to
227 // input_method_ids, so it appears first on the list of active input
228 // methods at the input language status menu.
229 if (!initial_input_method_id.empty()) {
230 input_method_ids.push_back(initial_input_method_id);
231 }
232
233 // Add candidates to input_method_ids, while skipping duplicates.
234 for (size_t i = 0; i < candidates.size(); ++i) {
235 const std::string& candidate = candidates[i];
236 // Not efficient, but should be fine, as the two vectors are very
237 // short (2-5 items).
238 if (std::count(input_method_ids.begin(), input_method_ids.end(),
239 candidate) == 0) {
240 input_method_ids.push_back(candidate);
241 }
242 }
243
244 // Update ibus-daemon setting. Here, we don't save the input method list
245 // in the user's preferences.
246 InputMethodConfigValue value;
247 value.type = InputMethodConfigValue::kValueTypeStringList;
248 value.string_list_value = input_method_ids;
249 SetInputMethodConfig(language_prefs::kGeneralSectionName,
250 language_prefs::kPreloadEnginesConfigName,
251 value);
252
253 // Finaly, change to the initial input method, as needed.
254 if (!initial_input_method_id.empty()) {
255 ChangeInputMethod(initial_input_method_id);
256 }
257 }
258
259 virtual void SetImePropertyActivated(const std::string& key,
260 bool activated) {
261 DCHECK(!key.empty());
262 ibus_controller_->SetImePropertyActivated(key, activated);
263 }
264
265 virtual bool InputMethodIsActivated(const std::string& input_method_id) {
266 scoped_ptr<InputMethodDescriptors> active_input_method_descriptors(
267 GetActiveInputMethods());
268 for (size_t i = 0; i < active_input_method_descriptors->size(); ++i) {
269 if (active_input_method_descriptors->at(i).id() == input_method_id) {
270 return true;
271 }
272 }
273 return false;
274 }
275
276 virtual bool SetInputMethodConfig(const std::string& section,
277 const std::string& config_name,
278 const InputMethodConfigValue& value) {
279 // If the config change is for preload engines, update the active
280 // input methods cache |active_input_method_ids_| here. We need to
281 // update the cache before actually flushing the config. since we need
282 // to return active input methods from GetActiveInputMethods() before
283 // the input method daemon starts. For instance, we need to show the
284 // list of available input methods (keyboard layouts) on the login
285 // screen before the input method starts.
286 if (section == language_prefs::kGeneralSectionName &&
287 config_name == language_prefs::kPreloadEnginesConfigName &&
288 value.type == InputMethodConfigValue::kValueTypeStringList) {
289 active_input_method_ids_ = value.string_list_value;
290
291 if (enable_extension_imes_) {
292 std::map<std::string, InputMethodDescriptor>::const_iterator ix;
293 for (ix = extra_input_method_ids_.begin();
294 ix != extra_input_method_ids_.end(); ++ix) {
295 active_input_method_ids_.push_back(ix->first);
296 }
297 }
298 }
299
300 // Before calling FlushImeConfig(), start input method process if necessary.
301 MaybeStartInputMethodDaemon(section, config_name, value);
302
303 const ConfigKeyType key = std::make_pair(section, config_name);
304 current_config_values_[key] = value;
305 if (ime_connected_) {
306 pending_config_requests_[key] = value;
307 FlushImeConfig();
308 }
309
310 if (section == language_prefs::kGeneralSectionName &&
311 config_name == language_prefs::kPreloadEnginesConfigName) {
312 // Stop input method process if necessary.
313 MaybeStopInputMethodDaemon();
314 }
315 // Change the current keyboard layout if necessary.
316 MaybeChangeCurrentKeyboardLayout(section, config_name, value);
317 return pending_config_requests_.empty();
318 }
319
320 // TODO(yusukes): Support input method specific hotkeys.
321 virtual void AddActiveIme(const std::string& id,
322 const std::string& name,
323 const std::vector<std::string>& layouts,
324 const std::string& language) {
325 std::string virtual_layouts = JoinString(layouts, ',');
326
327 extra_input_method_ids_[id] = ibus_controller_->CreateInputMethodDescriptor(
328 id, name, virtual_layouts, language);
329 active_input_method_ids_.push_back(id);
330
331 // Ensure that the input method daemon is running.
332 StartInputMethodDaemon();
333 }
334
335 virtual void RemoveActiveIme(const std::string& id) {
336 std::vector<std::string>::iterator ix =
337 std::find(active_input_method_ids_.begin(),
338 active_input_method_ids_.end(),
339 id);
340 if (ix != active_input_method_ids_.end()) {
341 active_input_method_ids_.erase(ix);
342 // TODO(yusukes): this is a workaround for crosbug.com/27051. Uncomment
343 // this when the bug is fixed.
344 if (!active_input_method_ids_.empty()) {
345 ChangeInputMethod(active_input_method_ids_[0]);
346 }
347 }
348 extra_input_method_ids_.erase(id);
349
350 // Stop the IME daemon if needed.
351 MaybeStopInputMethodDaemon();
352 }
353
354 virtual bool GetExtraDescriptor(const std::string& id,
355 InputMethodDescriptor* descriptor) {
356 std::map<std::string, InputMethodDescriptor>::const_iterator ix =
357 extra_input_method_ids_.find(id);
358 if (ix != extra_input_method_ids_.end()) {
359 *descriptor = ix->second;
360 return true;
361 } else {
362 return false;
363 }
364 }
365
366 virtual InputMethodDescriptor GetPreviousInputMethod() const {
367 if (previous_input_method_.id().empty()) {
368 return InputMethodDescriptor::GetFallbackInputMethodDescriptor();
369 }
370 return previous_input_method_;
371 }
372
373 virtual InputMethodDescriptor GetCurrentInputMethod() const {
374 if (current_input_method_.id().empty()) {
375 return InputMethodDescriptor::GetFallbackInputMethodDescriptor();
376 }
377 return current_input_method_;
378 }
379
380 virtual InputMethodPropertyList GetCurrentInputMethodProperties() const {
381 if (should_hide_properties_ ||
382 InputMethodUtil::IsKeyboardLayout(GetCurrentInputMethod().id())) {
383 return InputMethodPropertyList();
384 }
385 return current_ime_properties_;
386 }
387
388 virtual void SendHandwritingStroke(const HandwritingStroke& stroke) {
389 ibus_controller_->SendHandwritingStroke(stroke);
390 }
391
392 virtual void CancelHandwritingStrokes(int stroke_count) {
393 // TODO(yusukes): Rename the function to CancelHandwritingStrokes.
394 ibus_controller_->CancelHandwriting(stroke_count);
395 }
396
397 virtual void RegisterVirtualKeyboard(const GURL& launch_url,
398 const std::string& name,
399 const std::set<std::string>& layouts,
400 bool is_system) {
401 virtual_keyboard_selector_.AddVirtualKeyboard(launch_url,
402 name,
403 layouts,
404 is_system);
405 }
406
407 virtual const std::map<GURL, const VirtualKeyboard*>&
408 GetUrlToKeyboardMapping() const {
409 return virtual_keyboard_selector_.url_to_keyboard();
410 }
411
412 virtual const std::multimap<std::string, const VirtualKeyboard*>&
413 GetLayoutNameToKeyboardMapping() const {
414 return virtual_keyboard_selector_.layout_to_keyboard();
415 }
416
417 virtual bool SetVirtualKeyboardPreference(const std::string& input_method_id,
418 const GURL& extention_url) {
419 const bool result = virtual_keyboard_selector_.SetUserPreference(
420 input_method_id, extention_url);
421 UpdateVirtualKeyboardUI();
422 return result;
423 }
424
425 virtual void ClearAllVirtualKeyboardPreferences() {
426 virtual_keyboard_selector_.ClearAllUserPreferences();
427 UpdateVirtualKeyboardUI();
428 }
429
430 virtual InputMethodUtil* GetInputMethodUtil() {
431 return &util_;
432 }
433
434 virtual XKeyboard* GetXKeyboard() {
435 return xkeyboard_.get();
436 }
437
438 virtual void CandidateWindowOpened() {
439 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
440 candidate_window_observers_,
441 CandidateWindowOpened(this));
442 }
443
444 virtual void CandidateWindowClosed() {
445 FOR_EACH_OBSERVER(InputMethodManager::CandidateWindowObserver,
446 candidate_window_observers_,
447 CandidateWindowClosed(this));
448 }
449
450 virtual bool SwitchToNextInputMethod() {
451 if (ignore_hotkeys_) {
452 return false;
453 }
454
455 // Sanity checks.
456 if (active_input_method_ids_.empty()) {
457 LOG(ERROR) << "active input method is empty";
458 return false;
459 }
460 if (current_input_method_.id().empty()) {
461 LOG(ERROR) << "current_input_method_ is unknown";
462 return false;
463 }
464
465 // Find the next input method.
466 std::vector<std::string>::const_iterator iter =
467 std::find(active_input_method_ids_.begin(),
468 active_input_method_ids_.end(),
469 current_input_method_.id());
470 if (iter != active_input_method_ids_.end()) {
471 ++iter;
472 }
473 if (iter == active_input_method_ids_.end()) {
474 iter = active_input_method_ids_.begin();
475 }
476 ChangeInputMethod(*iter);
477 return true;
478 }
479
480 virtual bool SwitchToPreviousInputMethod() {
481 if (ignore_hotkeys_) {
482 return false;
483 }
484
485 // Sanity check.
486 if (active_input_method_ids_.empty()) {
487 LOG(ERROR) << "active input method is empty";
488 return false;
489 }
490
491 if (previous_input_method_.id().empty() ||
492 previous_input_method_.id() == current_input_method_.id()) {
493 return SwitchToNextInputMethod();
494 }
495
496 std::vector<std::string>::const_iterator iter =
497 std::find(active_input_method_ids_.begin(),
498 active_input_method_ids_.end(),
499 previous_input_method_.id());
500 if (iter == active_input_method_ids_.end()) {
501 // previous_input_method_ is not supported.
502 return SwitchToNextInputMethod();
503 }
504 ChangeInputMethod(*iter);
505 return true;
506 }
507
508 virtual bool SwitchInputMethod(const ui::Accelerator& accelerator) {
509 if (ignore_hotkeys_) {
510 return false;
511 }
512
513 // Sanity check.
514 if (active_input_method_ids_.empty()) {
515 LOG(ERROR) << "active input method is empty";
516 return false;
517 }
518
519 // Get the list of input method ids for the |accelerator|. For example, get
520 // { "mozc-hangul", "xkb:kr:kr104:kor" } for ui::VKEY_DBE_SBCSCHAR.
521 std::vector<std::string> input_method_ids_to_switch;
522 switch (accelerator.key_code()) {
523 case ui::VKEY_CONVERT: // Henkan key on JP106 keyboard
524 input_method_ids_to_switch.push_back("mozc-jp");
525 break;
526 case ui::VKEY_NONCONVERT: // Muhenkan key on JP106 keyboard
527 input_method_ids_to_switch.push_back("xkb:jp::jpn");
528 break;
529 case ui::VKEY_DBE_SBCSCHAR: // ZenkakuHankaku key on JP106 keyboard
530 case ui::VKEY_DBE_DBCSCHAR:
531 input_method_ids_to_switch.push_back("mozc-jp");
532 input_method_ids_to_switch.push_back("xkb:jp::jpn");
533 break;
534 case ui::VKEY_HANGUL: // Hangul (or right Alt) key on Korean keyboard
535 case ui::VKEY_SPACE: // Shift+Space
536 input_method_ids_to_switch.push_back("mozc-hangul");
537 input_method_ids_to_switch.push_back("xkb:kr:kr104:kor");
538 break;
539 default:
540 NOTREACHED();
541 break;
542 }
543 if (input_method_ids_to_switch.empty()) {
544 LOG(ERROR) << "Unexpected VKEY: " << accelerator.key_code();
545 return false;
546 }
547
548 // Obtain the intersection of input_method_ids_to_switch and
549 // active_input_method_ids_. The order of IDs in active_input_method_ids_ is
550 // preserved.
551 std::vector<std::string> ids;
552 for (size_t i = 0; i < input_method_ids_to_switch.size(); ++i) {
553 const std::string& id = input_method_ids_to_switch[i];
554 if (std::find(active_input_method_ids_.begin(),
555 active_input_method_ids_.end(),
556 id) != active_input_method_ids_.end()) {
557 ids.push_back(id);
558 }
559 }
560 if (ids.empty()) {
561 // No input method for the accelerator is active. For example, we should
562 // just ignore VKEY_HANGUL when mozc-hangul is not active.
563 return false;
564 }
565
566 // If |current_input_method_| is not in ids, switch to ids[0]. If
567 // |current_input_method_| is ids[N], switch to ids[N+1].
568 std::vector<std::string>::const_iterator iter =
569 std::find(ids.begin(), ids.end(), current_input_method_.id());
570 if (iter != ids.end()) {
571 ++iter;
572 }
573 if (iter == ids.end()) {
574 iter = ids.begin();
575 }
576 ChangeInputMethod(*iter);
577 return true; // consume the accelerator.
578 }
579
580 virtual void EnableHotkeys() {
581 ignore_hotkeys_ = false;
582 }
583
584 virtual void DisableHotkeys() {
585 ignore_hotkeys_ = true;
586 }
587
588 static InputMethodManagerImpl* GetInstance() {
589 return Singleton<InputMethodManagerImpl,
590 DefaultSingletonTraits<InputMethodManagerImpl> >::get();
591 }
592
593 private:
594 friend struct DefaultSingletonTraits<InputMethodManagerImpl>;
595
596 // Returns true if the given input method config value is a string list
597 // that only contains an input method ID of a keyboard layout.
598 bool ContainOnlyKeyboardLayout(const std::vector<std::string>& value) {
599 for (size_t i = 0; i < value.size(); ++i) {
600 if (!InputMethodUtil::IsKeyboardLayout(value[i])) {
601 return false;
602 }
603 }
604 return true;
605 }
606
607 // Starts input method daemon based on the input method configuration being
608 // updated. |section| is a section name of the input method configuration
609 // (e.g. "general", "general/hotkey"). |config_name| is a name of the
610 // configuration (e.g. "preload_engines", "previous_engine"). |value| is the
611 // configuration value to be set.
612 void MaybeStartInputMethodDaemon(const std::string& section,
613 const std::string& config_name,
614 const InputMethodConfigValue& value) {
615 if (section == language_prefs::kGeneralSectionName &&
616 config_name == language_prefs::kPreloadEnginesConfigName &&
617 value.type == InputMethodConfigValue::kValueTypeStringList &&
618 !value.string_list_value.empty()) {
619 // If there is only one input method which is a keyboard layout,
620 // we don't start the input method processes.
621 if (ContainOnlyKeyboardLayout(value.string_list_value)) {
622 // Do not start the input method daemon.
623 return;
624 }
625
626 // Otherwise, start the input method daemon.
627 const bool just_started = StartInputMethodDaemon();
628 if (!just_started) {
629 // The daemon is already running.
630 // Do not |update tentative_current_input_method_id_|.
631 return;
632 }
633
634 // The daemon has just been started. To select the initial input method
635 // engine correctly, update |tentative_current_input_method_id_|.
636 if (tentative_current_input_method_id_.empty()) {
637 // Since the |current_input_method_| is in the preloaded engine list,
638 // switch to the engine. This is necessary ex. for the following case:
639 // 1. "xkb:jp::jpn" is enabled. ibus-daemon is not running.
640 // 2. A user enabled "mozc" via DOMUI as well. ibus-daemon is started
641 // and the preloaded engine list is set to "mozc,xkb:jp::jpn".
642 // 3. ibus-daemon selects "mozc" as its current engine since "mozc" is
643 // on top of the preloaded engine list.
644 // 4. Therefore, we have to change the current engine to "xkb:jp::jpn"
645 // explicitly to avoid unexpected engine switch.
646 tentative_current_input_method_id_ = current_input_method_.id();
647 }
648
649 if (std::find(value.string_list_value.begin(),
650 value.string_list_value.end(),
651 tentative_current_input_method_id_)
652 == value.string_list_value.end()) {
653 // The |current_input_method_| is NOT in the preloaded engine list.
654 // In this case, ibus-daemon will automatically select the first engine
655 // in the list, |value.string_list_value[0]|, and send global engine
656 // changed signal to Chrome. See crosbug.com/13406.
657 tentative_current_input_method_id_.clear();
658 }
659 }
660 }
661
662 // Stops input method daemon based on the |enable_auto_ime_shutdown_| flag
663 // and input method configuration being updated.
664 // See also: MaybeStartInputMethodDaemon().
665 void MaybeStopInputMethodDaemon() {
666 // If there is only one input method which is a keyboard layout,
667 // and |enable_auto_ime_shutdown_| is true, we'll stop the input
668 // method daemon.
669 if (ContainOnlyKeyboardLayout(active_input_method_ids_) &&
670 enable_auto_ime_shutdown_) {
671 if (StopInputMethodDaemon()) {
672 // The process is killed. Change the current keyboard layout.
673 // We shouldn't use SetCurrentKeyboardLayoutByName() here. See
674 // comments at ChangeCurrentInputMethod() for details.
675 ChangeCurrentInputMethodFromId(active_input_method_ids_[0]);
676 }
677 }
678 }
679
680 // Change the keyboard layout per input method configuration being
681 // updated, if necessary. See also: MaybeStartInputMethodDaemon().
682 void MaybeChangeCurrentKeyboardLayout(const std::string& section,
683 const std::string& config_name,
684 const InputMethodConfigValue& value) {
685 if (section == language_prefs::kGeneralSectionName &&
686 config_name == language_prefs::kPreloadEnginesConfigName) {
687 if (value.string_list_value.size() == 1 ||
688 (value.string_list_value.size() != 0 &&
689 std::find(value.string_list_value.begin(),
690 value.string_list_value.end(),
691 current_input_method_.id()) ==
692 value.string_list_value.end())) {
693 // This is necessary to initialize current_input_method_. This is also
694 // necessary when the current layout (e.g. INTL) out of two or more
695 // active ones (e.g. US, DV, and INTL) is disabled.
696 ChangeCurrentInputMethodFromId(value.string_list_value[0]);
697 }
698 DCHECK(!current_input_method_.id().empty());
699
700 // Update the indicator.
701 // TODO(yusukes): Remove ActiveInputMethodsChanged notification in
702 // FlushImeConfig().
703 const size_t num_active_input_methods = GetNumActiveInputMethods();
704 FOR_EACH_OBSERVER(InputMethodManager::Observer, observers_,
705 ActiveInputMethodsChanged(this,
706 GetCurrentInputMethod(),
707 num_active_input_methods));
708 }
709 }
710
711 // Changes the current input method to |input_method_id| via IBus
712 // daemon. If the id is not in the preload_engine list, this function
713 // changes the current method to the first preloaded engine. Returns
714 // true if the current engine is switched to |input_method_id| or the
715 // first one.
716 bool ChangeInputMethodViaIBus(const std::string& input_method_id) {
717 std::string input_method_id_to_switch = input_method_id;
718
719 if (!InputMethodIsActivated(input_method_id)) {
720 // This path might be taken if prefs::kLanguageCurrentInputMethod (NOT
721 // synced with cloud) and kLanguagePreloadEngines (synced with cloud) are
722 // mismatched. e.g. the former is 'xkb:us::eng' and the latter (on the
723 // sync server) is 'xkb:jp::jpn,mozc'.
724 scoped_ptr<InputMethodDescriptors> input_methods(GetActiveInputMethods());
725 DCHECK(!input_methods->empty());
726 if (!input_methods->empty()) {
727 input_method_id_to_switch = input_methods->at(0).id();
728 LOG(INFO) << "Can't change the current input method to "
729 << input_method_id << " since the engine is not preloaded. "
730 << "Switch to " << input_method_id_to_switch << " instead.";
731 }
732 }
733
734 if (ibus_controller_->ChangeInputMethod(input_method_id_to_switch)) {
735 return true;
736 }
737
738 // ChangeInputMethod() fails if the IBus daemon is not yet ready.
739 LOG(ERROR) << "Can't switch input method to " << input_method_id_to_switch;
740 return false;
741 }
742
743 // Flushes the input method config data. The config data is queued up in
744 // |pending_config_requests_| until the config backend (ibus-memconf)
745 // starts.
746 void FlushImeConfig() {
747 bool active_input_methods_are_changed = false;
748 InputMethodConfigRequests::iterator iter = pending_config_requests_.begin();
749 while (iter != pending_config_requests_.end()) {
750 const std::string& section = iter->first.first;
751 const std::string& config_name = iter->first.second;
752 InputMethodConfigValue& value = iter->second;
753
754 if (config_name == language_prefs::kPreloadEnginesConfigName &&
755 !tentative_current_input_method_id_.empty()) {
756 // We should use |tentative_current_input_method_id_| as the initial
757 // active input method for the following reasons:
758 //
759 // 1) Calls to ChangeInputMethod() will fail if the input method has not
760 // yet been added to preload_engines. As such, the call is deferred
761 // until after all config values have been sent to the IME process.
762 //
763 // 2) We might have already changed the current input method to one
764 // of XKB layouts without going through the IBus daemon (we can do
765 // it without the IBus daemon started).
766 std::vector<std::string>::iterator engine_iter = std::find(
767 value.string_list_value.begin(),
768 value.string_list_value.end(),
769 tentative_current_input_method_id_);
770 if (engine_iter != value.string_list_value.end()) {
771 // Use std::rotate to keep the relative order of engines the same e.g.
772 // from "A,B,C" to "C,A,B".
773 // We don't have to |active_input_method_ids_|, which decides the
774 // order of engines in the switcher menu, since the relative order
775 // of |value.string_list_value| is not changed.
776 std::rotate(value.string_list_value.begin(),
777 engine_iter, // this becomes the new first element
778 value.string_list_value.end());
779 #if defined(HAVE_IBUS)
780 #if IBUS_CHECK_VERSION(1, 4, 99)
781 ibus_controller_->ChangeInputMethod(value.string_list_value[0]);
782 #endif
783 #endif
784 } else {
785 LOG(WARNING) << tentative_current_input_method_id_
786 << " is not in preload_engines: " << value.ToString();
787 }
788 tentative_current_input_method_id_.erase();
789 }
790
791 if (ibus_controller_->SetInputMethodConfig(section, config_name, value)) {
792 // Check if it's a change in active input methods.
793 if (config_name == language_prefs::kPreloadEnginesConfigName) {
794 active_input_methods_are_changed = true;
795 VLOG(1) << "Updated preload_engines: " << value.ToString();
796 }
797 // Successfully sent. Remove the command and proceed to the next one.
798 pending_config_requests_.erase(iter++);
799 } else {
800 // If SetInputMethodConfig() fails, subsequent calls will likely fail.
801 break;
802 }
803 }
804
805 // Notify the current input method and the number of active input methods to
806 // the UI so that the UI could determine e.g. if it should show/hide the
807 // input method indicator, etc. We have to call FOR_EACH_OBSERVER here since
808 // updating "preload_engine" does not necessarily trigger a DBus signal such
809 // as "global-engine-changed". For example,
810 // 1) If we change the preload_engine from "xkb:us:intl:eng" (i.e. the
811 // indicator is hidden) to "xkb:us:intl:eng,mozc", we have to update UI
812 // so it shows the indicator, but no signal is sent from ibus-daemon
813 // because the current input method is not changed.
814 // 2) If we change the preload_engine from "xkb:us::eng,mozc" (i.e. the
815 // indicator is shown and ibus-daemon is started) to "xkb:us::eng", we
816 // have to update UI so it hides the indicator, but we should not expect
817 // that ibus-daemon could send a DBus signal since the daemon is killed
818 // right after this FlushImeConfig() call.
819 if (active_input_methods_are_changed) {
820 // The |current_input_method_| member might be stale here as
821 // SetInputMethodConfig("preload_engine") call above might change the
822 // current input method in ibus-daemon (ex. this occurs when the
823 // input method currently in use is removed from the options
824 // page). However, it should be safe to use the member here,
825 // for the following reasons:
826 // 1. If ibus-daemon is to be killed, we'll switch to the only one
827 // keyboard layout, and observers are notified. See
828 // MaybeStopInputMethodDaemon() for details.
829 // 2. Otherwise, "global-engine-changed" signal is delivered from
830 // ibus-daemon, and observers are notified. See
831 // InputMethodChangedHandler() for details.
832 const size_t num_active_input_methods = GetNumActiveInputMethods();
833 FOR_EACH_OBSERVER(InputMethodManager::Observer, observers_,
834 ActiveInputMethodsChanged(this,
835 current_input_method_,
836 num_active_input_methods));
837 }
838 }
839
840 // IBusController override.
841 virtual void OnCurrentInputMethodChanged(
842 const InputMethodDescriptor& current_input_method) {
843 // The handler is called when the input method method change is
844 // notified via a DBus connection. Since the DBus notificatiosn are
845 // handled in the UI thread, we can assume that this function always
846 // runs on the UI thread, but just in case.
847 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
848 LOG(ERROR) << "Not on UI thread";
849 return;
850 }
851
852 ChangeCurrentInputMethod(current_input_method);
853 }
854
855 // IBusController override.
856 virtual void OnRegisterImeProperties(
857 const InputMethodPropertyList& prop_list) {
858 // See comments in InputMethodChangedHandler.
859 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
860 LOG(ERROR) << "Not on UI thread";
861 return;
862 }
863
864 RegisterProperties(prop_list);
865 }
866
867 // IBusController override.
868 virtual void OnUpdateImeProperty(
869 const InputMethodPropertyList& prop_list) {
870 // See comments in InputMethodChangedHandler.
871 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
872 LOG(ERROR) << "Not on UI thread";
873 return;
874 }
875
876 UpdateProperty(prop_list);
877 }
878
879 // IBusController override.
880 virtual void OnConnectionChange(bool connected) {
881 // See comments in InputMethodChangedHandler.
882 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
883 LOG(ERROR) << "Not on UI thread";
884 return;
885 }
886
887 ime_connected_ = connected;
888 if (connected) {
889 pending_config_requests_.clear();
890 pending_config_requests_.insert(current_config_values_.begin(),
891 current_config_values_.end());
892 FlushImeConfig();
893 }
894 }
895
896 // Changes the current input method from the given input method
897 // descriptor. This function updates states like current_input_method_
898 // and notifies observers about the change (that will update the
899 // preferences), hence this function should always be used even if you
900 // just need to change the current keyboard layout.
901 void ChangeCurrentInputMethod(const InputMethodDescriptor& new_input_method) {
902 if (current_input_method_.id() != new_input_method.id()) {
903 previous_input_method_ = current_input_method_;
904 current_input_method_ = new_input_method;
905
906 // Change the keyboard layout to a preferred layout for the input method.
907 if (!xkeyboard_->SetCurrentKeyboardLayoutByName(
908 current_input_method_.keyboard_layout())) {
909 LOG(ERROR) << "Failed to change keyboard layout to "
910 << current_input_method_.keyboard_layout();
911 }
912 }
913
914 // Update input method indicators (e.g. "US", "DV") in Chrome windows.
915 // For now, we have to do this every time to keep indicators updated. See
916 // comments near the FOR_EACH_OBSERVER call in FlushImeConfig() for details.
917 const size_t num_active_input_methods = GetNumActiveInputMethods();
918 FOR_EACH_OBSERVER(InputMethodManager::Observer, observers_,
919 InputMethodChanged(this,
920 current_input_method_,
921 num_active_input_methods));
922
923 UpdateVirtualKeyboardUI();
924 }
925
926 void UpdateVirtualKeyboardUI() {
927 #if defined(USE_VIRTUAL_KEYBOARD)
928 const VirtualKeyboard* virtual_keyboard = NULL;
929 std::string virtual_keyboard_layout = "";
930
931 const std::vector<std::string>& layouts_vector =
932 current_input_method_.virtual_keyboard_layouts();
933 std::vector<std::string>::const_iterator iter;
934 for (iter = layouts_vector.begin(); iter != layouts_vector.end(); ++iter) {
935 virtual_keyboard =
936 virtual_keyboard_selector_.SelectVirtualKeyboard(*iter);
937 if (virtual_keyboard) {
938 virtual_keyboard_layout = *iter;
939 break;
940 }
941 }
942
943 if (!virtual_keyboard) {
944 // The system virtual keyboard does not support some XKB layouts? or
945 // a third-party input method engine uses a wrong virtual keyboard
946 // layout name? Fallback to the default layout.
947 LOG(ERROR) << "Could not find a virtual keyboard for "
948 << current_input_method_.id();
949
950 // If the hardware is for US, show US Qwerty virtual keyboard.
951 // If it's for France, show Azerty one.
952 const std::string fallback_id = GetInputMethodUtil()->
953 GetHardwareInputMethodId();
954 const InputMethodDescriptor* fallback_desc =
955 GetInputMethodUtil()->GetInputMethodDescriptorFromId(fallback_id);
956
957 DCHECK(fallback_desc);
958 const std::vector<std::string>& fallback_layouts_vector =
959 fallback_desc->virtual_keyboard_layouts();
960
961 for (iter = fallback_layouts_vector.begin();
962 iter != fallback_layouts_vector.end();
963 ++iter) {
964 virtual_keyboard =
965 virtual_keyboard_selector_.SelectVirtualKeyboard(*iter);
966 if (virtual_keyboard) {
967 virtual_keyboard_layout = *iter;
968 LOG(ERROR) << "Fall back to '" << (*iter) << "' virtual keyboard";
969 break;
970 }
971 }
972 }
973
974 if (!virtual_keyboard) {
975 // kFallbackVirtualKeyboardLayout should always be supported by one of the
976 // system virtual keyboards.
977 static const char kFallbackVirtualKeyboardLayout[] = "us";
978
979 LOG(ERROR) << "Could not find a FALLBACK virtual keyboard for "
980 << current_input_method_.id()
981 << ". Use '" << kFallbackVirtualKeyboardLayout
982 << "' virtual keyboard";
983 virtual_keyboard = virtual_keyboard_selector_.SelectVirtualKeyboard(
984 kFallbackVirtualKeyboardLayout);
985 virtual_keyboard_layout = kFallbackVirtualKeyboardLayout;
986 }
987
988 if (virtual_keyboard) {
989 FOR_EACH_OBSERVER(VirtualKeyboardObserver, virtual_keyboard_observers_,
990 VirtualKeyboardChanged(this,
991 *virtual_keyboard,
992 virtual_keyboard_layout));
993 }
994 #endif // USE_VIRTUAL_KEYBOARD
995 }
996
997 // Changes the current input method from the given input method ID.
998 // This function is just a wrapper of ChangeCurrentInputMethod().
999 void ChangeCurrentInputMethodFromId(const std::string& input_method_id) {
1000 const InputMethodDescriptor* descriptor =
1001 util_.GetInputMethodDescriptorFromId(input_method_id);
1002 if (descriptor) {
1003 ChangeCurrentInputMethod(*descriptor);
1004 } else {
1005 LOG(ERROR) << "Descriptor is not found for: " << input_method_id;
1006 }
1007 }
1008
1009 // Registers the properties used by the current input method.
1010 void RegisterProperties(const InputMethodPropertyList& prop_list) {
1011 // |prop_list| might be empty. This means "hide properties."
1012 if (prop_list.empty()) {
1013 should_hide_properties_ = true;
1014 } else {
1015 should_hide_properties_ = false;
1016 current_ime_properties_ = prop_list;
1017 }
1018 // Update input method menu
1019 FOR_EACH_OBSERVER(InputMethodManager::Observer, observers_,
1020 PropertyListChanged(this,
1021 GetCurrentInputMethodProperties()));
1022 }
1023
1024 // Starts the input method daemon. Unlike MaybeStopInputMethodDaemon(),
1025 // this function always starts the daemon. Returns true if the daemon is
1026 // started. Otherwise, e.g. the daemon is already started, returns false.
1027 bool StartInputMethodDaemon() {
1028 should_launch_ime_ = true;
1029 return MaybeLaunchInputMethodDaemon();
1030 }
1031
1032 // Updates the properties used by the current input method.
1033 void UpdateProperty(const InputMethodPropertyList& prop_list) {
1034 for (size_t i = 0; i < prop_list.size(); ++i) {
1035 FindAndUpdateProperty(prop_list[i], &current_ime_properties_);
1036 }
1037
1038 // Update input method menu
1039 FOR_EACH_OBSERVER(InputMethodManager::Observer, observers_,
1040 PropertyListChanged(this,
1041 GetCurrentInputMethodProperties()));
1042 }
1043
1044 // Launches an input method procsess specified by the given command
1045 // line. On success, returns true and stores the process handle in
1046 // |process_handle|. Otherwise, returns false, and the contents of
1047 // |process_handle| is untouched. OnImeShutdown will be called when the
1048 // process terminates.
1049 bool LaunchInputMethodProcess(const std::string& command_line,
1050 base::ProcessHandle* process_handle) {
1051 std::vector<std::string> argv;
1052 base::ProcessHandle handle = base::kNullProcessHandle;
1053
1054 // TODO(zork): export "LD_PRELOAD=/usr/lib/libcrash.so"
1055 base::SplitString(command_line, ' ', &argv);
1056
1057 if (!base::LaunchProcess(argv, base::LaunchOptions(), &handle)) {
1058 LOG(ERROR) << "Could not launch: " << command_line;
1059 return false;
1060 }
1061
1062 // g_child_watch_add is necessary to prevent the process from becoming a
1063 // zombie.
1064 // TODO(yusukes): port g_child_watch_add to base/process_utils_posix.cc.
1065 const base::ProcessId pid = base::GetProcId(handle);
1066 g_child_watch_add(pid,
1067 reinterpret_cast<GChildWatchFunc>(OnImeShutdown),
1068 this);
1069
1070 *process_handle = handle;
1071 VLOG(1) << command_line << " (PID=" << pid << ") is started";
1072 return true;
1073 }
1074
1075 // Launches input method daemon if these are not yet running. Returns true if
1076 // the daemon is started. Otherwise, e.g. the daemon is already started,
1077 // returns false.
1078 bool MaybeLaunchInputMethodDaemon() {
1079 if (!should_launch_ime_) {
1080 return false;
1081 }
1082
1083 if (shutting_down_) {
1084 NOTREACHED() << "Trying to launch input method while shutting down";
1085 return false;
1086 }
1087
1088 #if !defined(USE_VIRTUAL_KEYBOARD)
1089 if (!candidate_window_controller_.get()) {
1090 candidate_window_controller_.reset(
1091 CandidateWindowController::CreateCandidateWindowController());
1092 if (candidate_window_controller_->Init()) {
1093 candidate_window_controller_->AddObserver(this);
1094 } else {
1095 LOG(WARNING) << "Failed to initialize the candidate window controller";
1096 }
1097 }
1098 #endif
1099
1100 if (ibus_daemon_process_handle_ != base::kNullProcessHandle) {
1101 return false; // ibus-daemon is already running.
1102 }
1103
1104 // TODO(zork): Send output to /var/log/ibus.log
1105 const std::string ibus_daemon_command_line =
1106 base::StringPrintf(
1107 "%s --panel=disable --cache=none --restart --replace",
1108 kIBusDaemonPath);
1109 if (!LaunchInputMethodProcess(
1110 ibus_daemon_command_line, &ibus_daemon_process_handle_)) {
1111 LOG(ERROR) << "Failed to launch " << ibus_daemon_command_line;
1112 return false;
1113 }
1114 return true;
1115 }
1116
1117 // Called when the input method process is shut down.
1118 static void OnImeShutdown(GPid pid,
1119 gint status,
1120 InputMethodManagerImpl* library) {
1121 if (library->ibus_daemon_process_handle_ != base::kNullProcessHandle &&
1122 base::GetProcId(library->ibus_daemon_process_handle_) == pid) {
1123 library->ibus_daemon_process_handle_ = base::kNullProcessHandle;
1124 }
1125
1126 // Restart input method daemon if needed.
1127 library->MaybeLaunchInputMethodDaemon();
1128 }
1129
1130 // Stops the backend input method daemon. This function should also be
1131 // called from MaybeStopInputMethodDaemon(), except one case where we
1132 // stop the input method daemon at Chrome shutdown in Observe(). Returns true
1133 // if the daemon is stopped. Otherwise, e.g. the daemon is already stopped,
1134 // returns false.
1135 bool StopInputMethodDaemon() {
1136 should_launch_ime_ = false;
1137 if (ibus_daemon_process_handle_ != base::kNullProcessHandle) {
1138 const base::ProcessId pid = base::GetProcId(ibus_daemon_process_handle_);
1139 if (!ibus_controller_->StopInputMethodProcess()) {
1140 LOG(ERROR) << "StopInputMethodProcess IPC failed. Sending SIGTERM to "
1141 << "PID " << pid;
1142 base::KillProcess(ibus_daemon_process_handle_, -1, false /* wait */);
1143 }
1144 VLOG(1) << "ibus-daemon (PID=" << pid << ") is terminated";
1145 ibus_daemon_process_handle_ = base::kNullProcessHandle;
1146 return true;
1147 }
1148 return false;
1149 }
1150
1151 void SetEnableAutoImeShutdown(bool enable) {
1152 // TODO(yusukes): this is a workaround for crosbug.com/27051. Uncommen this
1153 // when the bug is fixed.
1154
1155 // enable_auto_ime_shutdown_ = enable;
1156 }
1157
1158 void SetEnableExtensionIMEs(bool enable) {
1159 enable_extension_imes_ = enable;
1160 }
1161
1162 // content::NotificationObserver implementation:
1163 void Observe(int type,
1164 const content::NotificationSource& source,
1165 const content::NotificationDetails& details) {
1166 // Stop the input method daemon on browser shutdown.
1167 if (type == content::NOTIFICATION_APP_TERMINATING) {
1168 shutting_down_ = true;
1169 notification_registrar_.RemoveAll();
1170 StopInputMethodDaemon();
1171 #if !defined(USE_VIRTUAL_KEYBOARD)
1172 if (candidate_window_controller_.get())
1173 candidate_window_controller_->RemoveObserver(this);
1174 candidate_window_controller_.reset(NULL);
1175 #endif
1176 // |browser_state_monitor_| has to be destructed while the PrefService
1177 // object associated with the monitor is alive. (crbug.com/120183)
1178 browser_state_monitor_.reset();
1179 }
1180 }
1181
1182 // The IBus controller is used to control the input method status and
1183 // allow allow callbacks when the input method status changes.
1184 scoped_ptr<IBusController> ibus_controller_;
1185 ObserverList<InputMethodManager::Observer> observers_;
1186 ObserverList<InputMethodManager::CandidateWindowObserver>
1187 candidate_window_observers_;
1188 ObserverList<VirtualKeyboardObserver> virtual_keyboard_observers_;
1189
1190 // The input method which was/is selected.
1191 InputMethodDescriptor previous_input_method_;
1192 InputMethodDescriptor current_input_method_;
1193
1194 // The input method properties which the current input method uses. The list
1195 // might be empty when no input method is used.
1196 InputMethodPropertyList current_ime_properties_;
1197 bool should_hide_properties_;
1198
1199 typedef std::pair<std::string, std::string> ConfigKeyType;
1200 typedef std::map<
1201 ConfigKeyType, InputMethodConfigValue> InputMethodConfigRequests;
1202 // SetInputMethodConfig requests that are not yet completed.
1203 // Use a map to queue config requests, so we only send the last request for
1204 // the same config key (i.e. we'll discard ealier requests for the same
1205 // config key). As we discard old requests for the same config key, the order
1206 // of requests doesn't matter, so it's safe to use a map.
1207 InputMethodConfigRequests pending_config_requests_;
1208
1209 // Values that have been set via SetInputMethodConfig(). We keep a copy
1210 // available to resend if the ime restarts and loses its state.
1211 InputMethodConfigRequests current_config_values_;
1212
1213 // This is used to register this object to APP_TERMINATING notification.
1214 content::NotificationRegistrar notification_registrar_;
1215
1216 // True if we should launch the input method daemon.
1217 bool should_launch_ime_;
1218 // True if the connection to the IBus daemon is alive.
1219 bool ime_connected_;
1220 // True if we should stop input method daemon when there are no input
1221 // methods other than one for the hardware keyboard.
1222 bool enable_auto_ime_shutdown_;
1223 // True if extension IMEs can be enabled.
1224 bool enable_extension_imes_;
1225 // The ID of the tentative current input method (ex. "mozc"). This value
1226 // can be different from the actual current input method, if
1227 // ChangeInputMethod() fails.
1228 // TODO(yusukes): clear this variable when a user logs in.
1229 std::string tentative_current_input_method_id_;
1230
1231 // The candidate window. This will be deleted when the APP_TERMINATING
1232 // message is sent.
1233 #if !defined(USE_VIRTUAL_KEYBOARD)
1234 scoped_ptr<CandidateWindowController> candidate_window_controller_;
1235 #endif
1236
1237 // True if we've received the APP_TERMINATING notification.
1238 bool shutting_down_;
1239
1240 // The process handle of the IBus daemon. kNullProcessHandle if it's not
1241 // running.
1242 base::ProcessHandle ibus_daemon_process_handle_;
1243
1244 // An object which keeps a list of available virtual keyboards.
1245 VirtualKeyboardSelector virtual_keyboard_selector_;
1246
1247 // The active input method ids cache.
1248 std::vector<std::string> active_input_method_ids_;
1249
1250 // Extra input methods that have been explicitly added to the menu, such as
1251 // those created by extension.
1252 std::map<std::string, InputMethodDescriptor> extra_input_method_ids_;
1253
1254 InputMethodWhitelist whitelist_;
1255
1256 // An object which provides miscellaneous input method utility functions. Note
1257 // that |util_| is required to initialize |xkeyboard_|.
1258 InputMethodUtil util_;
1259
1260 // An object for switching XKB layouts and keyboard status like caps lock and
1261 // auto-repeat interval.
1262 scoped_ptr<XKeyboard> xkeyboard_;
1263
1264 // An object which monitors a notification from the browser to keep track of
1265 // the browser state (not logged in, logged in, etc.).
1266 scoped_ptr<BrowserStateMonitor> browser_state_monitor_;
1267
1268 // true when DisableHotkeys() is called to temporarily disable IME hotkeys.
1269 // EnableHotkeys() resets the flag to the default value, false.
1270 bool ignore_hotkeys_;
1271
1272 DISALLOW_COPY_AND_ASSIGN(InputMethodManagerImpl);
1273 };
1274 47
1275 // static 48 // static
1276 InputMethodManager* InputMethodManager::GetInstance() { 49 InputMethodManager* InputMethodManager::GetInstance() {
1277 return InputMethodManagerImpl::GetInstance(); 50 DCHECK(g_input_method_manager);
51 return g_input_method_manager;
1278 } 52 }
1279 53
1280 } // namespace input_method 54 } // namespace input_method
1281 } // namespace chromeos 55 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698