OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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/cros/input_method_library.h" | 5 #include "chrome/browser/chromeos/cros/input_method_library.h" |
6 | 6 |
7 #include <glib.h> | 7 #include <glib.h> |
8 #include <signal.h> | |
9 | 8 |
10 #include "unicode/uloc.h" | 9 #include "unicode/uloc.h" |
11 | 10 |
12 #include "base/basictypes.h" | 11 #include "base/basictypes.h" |
13 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
13 #include "base/process_util.h" | |
14 #include "base/string_split.h" | |
14 #include "base/string_util.h" | 15 #include "base/string_util.h" |
15 #include "chrome/browser/browser_process.h" | 16 #include "chrome/browser/browser_process.h" |
16 #include "chrome/browser/chromeos/cros/cros_library.h" | 17 #include "chrome/browser/chromeos/cros/cros_library.h" |
17 #include "chrome/browser/chromeos/cros/keyboard_library.h" | 18 #include "chrome/browser/chromeos/cros/keyboard_library.h" |
18 #include "chrome/browser/chromeos/input_method/candidate_window.h" | 19 #include "chrome/browser/chromeos/input_method/candidate_window.h" |
19 #include "chrome/browser/chromeos/input_method/input_method_util.h" | 20 #include "chrome/browser/chromeos/input_method/input_method_util.h" |
20 #include "chrome/browser/chromeos/language_preferences.h" | 21 #include "chrome/browser/chromeos/language_preferences.h" |
21 #include "content/browser/browser_thread.h" | 22 #include "content/browser/browser_thread.h" |
22 #include "content/common/notification_observer.h" | 23 #include "content/common/notification_observer.h" |
23 #include "content/common/notification_registrar.h" | 24 #include "content/common/notification_registrar.h" |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
55 public: | 56 public: |
56 InputMethodLibraryImpl() | 57 InputMethodLibraryImpl() |
57 : input_method_status_connection_(NULL), | 58 : input_method_status_connection_(NULL), |
58 previous_input_method_("", "", "", ""), | 59 previous_input_method_("", "", "", ""), |
59 current_input_method_("", "", "", ""), | 60 current_input_method_("", "", "", ""), |
60 should_launch_ime_(false), | 61 should_launch_ime_(false), |
61 ime_connected_(false), | 62 ime_connected_(false), |
62 defer_ime_startup_(false), | 63 defer_ime_startup_(false), |
63 enable_auto_ime_shutdown_(true), | 64 enable_auto_ime_shutdown_(true), |
64 should_change_input_method_(false), | 65 should_change_input_method_(false), |
65 ibus_daemon_process_id_(0), | 66 ibus_daemon_process_handle_(base::kNullProcessHandle), |
66 initialized_successfully_(false), | 67 initialized_successfully_(false), |
67 candidate_window_controller_(NULL) { | 68 candidate_window_controller_(NULL) { |
68 // Here, we use the fallback input method descriptor but | 69 // Here, we use the fallback input method descriptor but |
69 // |current_input_method_| will be updated as soon as the login screen | 70 // |current_input_method_| will be updated as soon as the login screen |
70 // is shown or the user is logged in, so there is no problem. | 71 // is shown or the user is logged in, so there is no problem. |
71 current_input_method_ = input_method::GetFallbackInputMethodDescriptor(); | 72 current_input_method_ = input_method::GetFallbackInputMethodDescriptor(); |
72 active_input_method_ids_.push_back(current_input_method_.id); | 73 active_input_method_ids_.push_back(current_input_method_.id); |
73 // Observe APP_TERMINATING to stop input method daemon gracefully. | 74 // Observe APP_TERMINATING to stop input method daemon gracefully. |
74 // We should not use APP_EXITING here since logout might be canceled by | 75 // We should not use APP_EXITING here since logout might be canceled by |
75 // JavaScript after APP_EXITING is sent (crosbug.com/11055). | 76 // JavaScript after APP_EXITING is sent (crosbug.com/11055). |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
163 return chromeos::GetSupportedInputMethodDescriptors(); | 164 return chromeos::GetSupportedInputMethodDescriptors(); |
164 } | 165 } |
165 | 166 |
166 virtual void ChangeInputMethod(const std::string& input_method_id) { | 167 virtual void ChangeInputMethod(const std::string& input_method_id) { |
167 // Changing the input method isn't guaranteed to succeed here, but we | 168 // Changing the input method isn't guaranteed to succeed here, but we |
168 // should remember the last one regardless. See comments in | 169 // should remember the last one regardless. See comments in |
169 // FlushImeConfig() for details. | 170 // FlushImeConfig() for details. |
170 tentative_current_input_method_id_ = input_method_id; | 171 tentative_current_input_method_id_ = input_method_id; |
171 // If the input method daemon is not running and the specified input | 172 // If the input method daemon is not running and the specified input |
172 // method is a keyboard layout, switch the keyboard directly. | 173 // method is a keyboard layout, switch the keyboard directly. |
173 if (ibus_daemon_process_id_ == 0 && | 174 if (ibus_daemon_process_handle_ == base::kNullProcessHandle && |
174 chromeos::input_method::IsKeyboardLayout(input_method_id)) { | 175 chromeos::input_method::IsKeyboardLayout(input_method_id)) { |
175 // We shouldn't use SetCurrentKeyboardLayoutByName() here. See | 176 // We shouldn't use SetCurrentKeyboardLayoutByName() here. See |
176 // comments at ChangeCurrentInputMethod() for details. | 177 // comments at ChangeCurrentInputMethod() for details. |
177 ChangeCurrentInputMethodFromId(input_method_id); | 178 ChangeCurrentInputMethodFromId(input_method_id); |
178 } else { | 179 } else { |
179 // Otherwise, start the input method daemon, and change the input | 180 // Otherwise, start the input method daemon, and change the input |
180 // method via the damon. | 181 // method via the damon. |
181 StartInputMethodDaemon(); | 182 StartInputMethodDaemon(); |
182 // ChangeInputMethodViaIBus() fails if the IBus daemon is not | 183 // ChangeInputMethodViaIBus() fails if the IBus daemon is not |
183 // ready yet. In this case, we'll defer the input method change | 184 // ready yet. In this case, we'll defer the input method change |
(...skipping 406 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
590 } | 591 } |
591 | 592 |
592 // Updates the properties used by the current input method. | 593 // Updates the properties used by the current input method. |
593 void UpdateProperty(const ImePropertyList& prop_list) { | 594 void UpdateProperty(const ImePropertyList& prop_list) { |
594 for (size_t i = 0; i < prop_list.size(); ++i) { | 595 for (size_t i = 0; i < prop_list.size(); ++i) { |
595 FindAndUpdateProperty(prop_list[i], ¤t_ime_properties_); | 596 FindAndUpdateProperty(prop_list[i], ¤t_ime_properties_); |
596 } | 597 } |
597 } | 598 } |
598 | 599 |
599 // Launches an input method procsess specified by the given command | 600 // Launches an input method procsess specified by the given command |
600 // line. On success, returns true and stores the process ID in | 601 // line. On success, returns true and stores the process handle in |
601 // |process_id|. Otherwise, returns false, and the contents of | 602 // |process_handle|. Otherwise, returns false, and the contents of |
602 // |process_id| is untouched. OnImeShutdown will be called when the | 603 // |process_handle| is untouched. OnImeShutdown will be called when the |
603 // process terminates. | 604 // process terminates. |
604 bool LaunchInputMethodProcess(const std::string& command_line, | 605 bool LaunchInputMethodProcess(const std::string& command_line, |
605 int* process_id) { | 606 base::ProcessHandle* process_handle) { |
606 GError *error = NULL; | 607 std::vector<std::string> argv; |
607 gchar **argv = NULL; | 608 base::file_handle_mapping_vector fds_to_remap; |
608 gint argc = NULL; | 609 base::ProcessHandle handle = base::kNullProcessHandle; |
610 | |
609 // TODO(zork): export "LD_PRELOAD=/usr/lib/libcrash.so" | 611 // TODO(zork): export "LD_PRELOAD=/usr/lib/libcrash.so" |
610 if (!g_shell_parse_argv(command_line.c_str(), &argc, &argv, &error)) { | 612 base::SplitString(command_line, ' ', &argv); |
611 LOG(ERROR) << "Could not parse command: " << error->message; | 613 const bool result = base::LaunchApp(argv, |
612 g_error_free(error); | 614 fds_to_remap, // no remapping |
615 false, // wait | |
616 &handle); | |
617 if (!result) { | |
618 LOG(ERROR) << "Could not launch: " << command_line; | |
613 return false; | 619 return false; |
614 } | 620 } |
615 | 621 g_child_watch_add(base::GetProcId(handle), |
Zachary Kuznia
2011/03/21 10:43:52
Perhaps we should add a version of this function t
satorux1
2011/03/21 11:52:11
I agree. Let's add a TODO here.
Yusuke Sato
2011/03/21 12:08:17
Sure, added TODO.
On 2011/03/21 10:43:52, Zachary
Yusuke Sato
2011/03/21 12:08:17
Done.
| |
616 int pid = 0; | 622 reinterpret_cast<GChildWatchFunc>(OnImeShutdown), |
617 const GSpawnFlags kFlags = G_SPAWN_DO_NOT_REAP_CHILD; | |
618 const gboolean result = g_spawn_async(NULL, argv, NULL, | |
619 kFlags, NULL, NULL, | |
620 &pid, &error); | |
621 g_strfreev(argv); | |
622 if (!result) { | |
623 LOG(ERROR) << "Could not launch: " << command_line << ": " | |
624 << error->message; | |
625 g_error_free(error); | |
626 return false; | |
627 } | |
628 g_child_watch_add(pid, reinterpret_cast<GChildWatchFunc>(OnImeShutdown), | |
629 this); | 623 this); |
630 | 624 |
631 *process_id = pid; | 625 *process_handle = handle; |
632 VLOG(1) << command_line << " (PID=" << pid << ") is started"; | 626 VLOG(1) << command_line << " (PID=" << base::GetProcId(handle) |
627 << ") is started"; | |
633 return true; | 628 return true; |
634 } | 629 } |
635 | 630 |
636 // Launches input method daemon if these are not yet running. | 631 // Launches input method daemon if these are not yet running. |
637 void MaybeLaunchInputMethodDaemon() { | 632 void MaybeLaunchInputMethodDaemon() { |
638 // CandidateWindowController requires libcros to be loaded. Besides, | 633 // CandidateWindowController requires libcros to be loaded. Besides, |
639 // launching ibus-daemon without libcros loaded doesn't make sense. | 634 // launching ibus-daemon without libcros loaded doesn't make sense. |
640 if (!initialized_successfully_) | 635 if (!initialized_successfully_) |
641 return; | 636 return; |
642 | 637 |
643 if (!should_launch_ime_) { | 638 if (!should_launch_ime_) { |
644 return; | 639 return; |
645 } | 640 } |
646 | 641 |
647 if (!candidate_window_controller_.get()) { | 642 if (!candidate_window_controller_.get()) { |
648 candidate_window_controller_.reset(new CandidateWindowController); | 643 candidate_window_controller_.reset(new CandidateWindowController); |
649 if (!candidate_window_controller_->Init()) { | 644 if (!candidate_window_controller_->Init()) { |
650 LOG(WARNING) << "Failed to initialize the candidate window controller"; | 645 LOG(WARNING) << "Failed to initialize the candidate window controller"; |
651 } | 646 } |
652 } | 647 } |
653 | 648 |
654 if (ibus_daemon_process_id_ == 0) { | 649 if (ibus_daemon_process_handle_ == base::kNullProcessHandle) { |
655 // TODO(zork): Send output to /var/log/ibus.log | 650 // TODO(zork): Send output to /var/log/ibus.log |
656 const std::string ibus_daemon_command_line = | 651 const std::string ibus_daemon_command_line = |
657 StringPrintf("%s --panel=disable --cache=none --restart --replace", | 652 StringPrintf("%s --panel=disable --cache=none --restart --replace", |
658 kIBusDaemonPath); | 653 kIBusDaemonPath); |
659 if (!LaunchInputMethodProcess( | 654 if (!LaunchInputMethodProcess( |
660 ibus_daemon_command_line, &ibus_daemon_process_id_)) { | 655 ibus_daemon_command_line, &ibus_daemon_process_handle_)) { |
661 LOG(ERROR) << "Failed to launch " << ibus_daemon_command_line; | 656 LOG(ERROR) << "Failed to launch " << ibus_daemon_command_line; |
662 } | 657 } |
663 } | 658 } |
664 } | 659 } |
665 | 660 |
666 // Called when the input method process is shut down. | 661 // Called when the input method process is shut down. |
667 static void OnImeShutdown(int pid, | 662 static void OnImeShutdown(int pid, |
668 int status, | 663 int status, |
669 InputMethodLibraryImpl* library) { | 664 InputMethodLibraryImpl* library) { |
670 g_spawn_close_pid(pid); | 665 if (base::GetProcId(library->ibus_daemon_process_handle_) == pid) { |
Zachary Kuznia
2011/03/21 10:43:52
Should we check that we have a handle before calli
Yusuke Sato
2011/03/21 12:08:17
Added the check.
(right now, calling GetProcId(kNu
| |
671 if (library->ibus_daemon_process_id_ == pid) { | 666 base::CloseProcessHandle(library->ibus_daemon_process_handle_); |
672 library->ibus_daemon_process_id_ = 0; | 667 library->ibus_daemon_process_handle_ = base::kNullProcessHandle; |
673 } | 668 } |
674 | 669 |
675 // Restart input method daemon if needed. | 670 // Restart input method daemon if needed. |
676 library->MaybeLaunchInputMethodDaemon(); | 671 library->MaybeLaunchInputMethodDaemon(); |
677 } | 672 } |
678 | 673 |
679 // Stops the backend input method daemon. This function should also be | 674 // Stops the backend input method daemon. This function should also be |
680 // called from MaybeStopInputMethodDaemon(), except one case where we | 675 // called from MaybeStopInputMethodDaemon(), except one case where we |
681 // stop the input method daemon at Chrome shutdown in Observe(). | 676 // stop the input method daemon at Chrome shutdown in Observe(). |
682 void StopInputMethodDaemon() { | 677 void StopInputMethodDaemon() { |
683 if (!initialized_successfully_) | 678 if (!initialized_successfully_) |
684 return; | 679 return; |
685 | 680 |
686 should_launch_ime_ = false; | 681 should_launch_ime_ = false; |
687 if (ibus_daemon_process_id_) { | 682 if (ibus_daemon_process_handle_ != base::kNullProcessHandle) { |
688 if (!chromeos::StopInputMethodProcess(input_method_status_connection_)) { | 683 if (!chromeos::StopInputMethodProcess(input_method_status_connection_)) { |
689 LOG(ERROR) << "StopInputMethodProcess IPC failed. Sending SIGTERM to " | 684 LOG(ERROR) << "StopInputMethodProcess IPC failed. Sending SIGTERM to " |
690 << "PID " << ibus_daemon_process_id_; | 685 << "PID " << base::GetProcId(ibus_daemon_process_handle_); |
691 kill(ibus_daemon_process_id_, SIGTERM); | 686 base::KillProcess(ibus_daemon_process_handle_, -1, false /* wait */); |
692 } | 687 } |
693 VLOG(1) << "ibus-daemon (PID=" << ibus_daemon_process_id_ << ") is " | 688 VLOG(1) << "ibus-daemon (PID=" |
694 << "terminated"; | 689 << base::GetProcId(ibus_daemon_process_handle_) |
695 ibus_daemon_process_id_ = 0; | 690 << ") is terminated"; |
691 base::CloseProcessHandle(ibus_daemon_process_handle_); | |
Zachary Kuznia
2011/03/21 10:43:52
Shouldn't this already be called in OnImeShutdown,
Yusuke Sato
2011/03/21 12:08:17
Let me remove all CloseProcessHandle calls since b
| |
692 ibus_daemon_process_handle_ = base::kNullProcessHandle; | |
696 } | 693 } |
697 } | 694 } |
698 | 695 |
699 void SetDeferImeStartup(bool defer) { | 696 void SetDeferImeStartup(bool defer) { |
700 VLOG(1) << "Setting DeferImeStartup to " << defer; | 697 VLOG(1) << "Setting DeferImeStartup to " << defer; |
701 defer_ime_startup_ = defer; | 698 defer_ime_startup_ = defer; |
702 } | 699 } |
703 | 700 |
704 void SetEnableAutoImeShutdown(bool enable) { | 701 void SetEnableAutoImeShutdown(bool enable) { |
705 enable_auto_ime_shutdown_ = enable; | 702 enable_auto_ime_shutdown_ = enable; |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
758 bool enable_auto_ime_shutdown_; | 755 bool enable_auto_ime_shutdown_; |
759 // The ID of the tentative current input method (ex. "mozc"). This value | 756 // The ID of the tentative current input method (ex. "mozc"). This value |
760 // can be different from the actual current input method, if | 757 // can be different from the actual current input method, if |
761 // ChangeInputMethod() fails. | 758 // ChangeInputMethod() fails. |
762 std::string tentative_current_input_method_id_; | 759 std::string tentative_current_input_method_id_; |
763 // True if we should change the current input method to | 760 // True if we should change the current input method to |
764 // |tentative_current_input_method_id_| once the queue of the pending config | 761 // |tentative_current_input_method_id_| once the queue of the pending config |
765 // requests becomes empty. | 762 // requests becomes empty. |
766 bool should_change_input_method_; | 763 bool should_change_input_method_; |
767 | 764 |
768 // The process id of the IBus daemon. 0 if it's not running. The process | 765 // The process handle of the IBus daemon. kNullProcessHandle if it's not |
769 // ID 0 is not used in Linux, hence it's safe to use 0 for this purpose. | 766 // running. |
770 int ibus_daemon_process_id_; | 767 base::ProcessHandle ibus_daemon_process_handle_; |
771 | 768 |
772 // True if initialization is successfully done, meaning that libcros is | 769 // True if initialization is successfully done, meaning that libcros is |
773 // loaded and input method status monitoring is started. This value | 770 // loaded and input method status monitoring is started. This value |
774 // should be checked where we call libcros functions. | 771 // should be checked where we call libcros functions. |
775 bool initialized_successfully_; | 772 bool initialized_successfully_; |
776 | 773 |
777 // The candidate window. This will be deleted when the APP_TERMINATING | 774 // The candidate window. This will be deleted when the APP_TERMINATING |
778 // message is sent. | 775 // message is sent. |
779 scoped_ptr<CandidateWindowController> candidate_window_controller_; | 776 scoped_ptr<CandidateWindowController> candidate_window_controller_; |
780 | 777 |
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1100 } | 1097 } |
1101 return impl; | 1098 return impl; |
1102 } | 1099 } |
1103 } | 1100 } |
1104 | 1101 |
1105 } // namespace chromeos | 1102 } // namespace chromeos |
1106 | 1103 |
1107 // Allows InvokeLater without adding refcounting. This class is a Singleton and | 1104 // Allows InvokeLater without adding refcounting. This class is a Singleton and |
1108 // won't be deleted until it's last InvokeLater is run. | 1105 // won't be deleted until it's last InvokeLater is run. |
1109 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::InputMethodLibraryImpl); | 1106 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::InputMethodLibraryImpl); |
OLD | NEW |