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 |
616 int pid = 0; | 622 // g_child_watch_add is necessary to prevent the process from becoming a |
617 const GSpawnFlags kFlags = G_SPAWN_DO_NOT_REAP_CHILD; | 623 // zombie. |
618 const gboolean result = g_spawn_async(NULL, argv, NULL, | 624 // TODO(yusukes): port g_child_watch_add to base/process_utils_posix.cc. |
619 kFlags, NULL, NULL, | 625 const base::ProcessId pid = base::GetProcId(handle); |
620 &pid, &error); | 626 g_child_watch_add(pid, |
621 g_strfreev(argv); | 627 reinterpret_cast<GChildWatchFunc>(OnImeShutdown), |
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); | 628 this); |
630 | 629 |
631 *process_id = pid; | 630 *process_handle = handle; |
632 VLOG(1) << command_line << " (PID=" << pid << ") is started"; | 631 VLOG(1) << command_line << " (PID=" << pid << ") is started"; |
633 return true; | 632 return true; |
634 } | 633 } |
635 | 634 |
636 // Launches input method daemon if these are not yet running. | 635 // Launches input method daemon if these are not yet running. |
637 void MaybeLaunchInputMethodDaemon() { | 636 void MaybeLaunchInputMethodDaemon() { |
638 // CandidateWindowController requires libcros to be loaded. Besides, | 637 // CandidateWindowController requires libcros to be loaded. Besides, |
639 // launching ibus-daemon without libcros loaded doesn't make sense. | 638 // launching ibus-daemon without libcros loaded doesn't make sense. |
640 if (!initialized_successfully_) | 639 if (!initialized_successfully_) |
641 return; | 640 return; |
642 | 641 |
643 if (!should_launch_ime_) { | 642 if (!should_launch_ime_) { |
644 return; | 643 return; |
645 } | 644 } |
646 | 645 |
647 if (!candidate_window_controller_.get()) { | 646 if (!candidate_window_controller_.get()) { |
648 candidate_window_controller_.reset(new CandidateWindowController); | 647 candidate_window_controller_.reset(new CandidateWindowController); |
649 if (!candidate_window_controller_->Init()) { | 648 if (!candidate_window_controller_->Init()) { |
650 LOG(WARNING) << "Failed to initialize the candidate window controller"; | 649 LOG(WARNING) << "Failed to initialize the candidate window controller"; |
651 } | 650 } |
652 } | 651 } |
653 | 652 |
654 if (ibus_daemon_process_id_ == 0) { | 653 if (ibus_daemon_process_handle_ == base::kNullProcessHandle) { |
655 // TODO(zork): Send output to /var/log/ibus.log | 654 // TODO(zork): Send output to /var/log/ibus.log |
656 const std::string ibus_daemon_command_line = | 655 const std::string ibus_daemon_command_line = |
657 StringPrintf("%s --panel=disable --cache=none --restart --replace", | 656 StringPrintf("%s --panel=disable --cache=none --restart --replace", |
658 kIBusDaemonPath); | 657 kIBusDaemonPath); |
659 if (!LaunchInputMethodProcess( | 658 if (!LaunchInputMethodProcess( |
660 ibus_daemon_command_line, &ibus_daemon_process_id_)) { | 659 ibus_daemon_command_line, &ibus_daemon_process_handle_)) { |
661 LOG(ERROR) << "Failed to launch " << ibus_daemon_command_line; | 660 LOG(ERROR) << "Failed to launch " << ibus_daemon_command_line; |
662 } | 661 } |
663 } | 662 } |
664 } | 663 } |
665 | 664 |
666 // Called when the input method process is shut down. | 665 // Called when the input method process is shut down. |
667 static void OnImeShutdown(int pid, | 666 static void OnImeShutdown(GPid pid, |
668 int status, | 667 gint status, |
669 InputMethodLibraryImpl* library) { | 668 InputMethodLibraryImpl* library) { |
670 g_spawn_close_pid(pid); | 669 if (library->ibus_daemon_process_handle_ != base::kNullProcessHandle && |
671 if (library->ibus_daemon_process_id_ == pid) { | 670 base::GetProcId(library->ibus_daemon_process_handle_) == pid) { |
672 library->ibus_daemon_process_id_ = 0; | 671 library->ibus_daemon_process_handle_ = base::kNullProcessHandle; |
673 } | 672 } |
674 | 673 |
675 // Restart input method daemon if needed. | 674 // Restart input method daemon if needed. |
676 library->MaybeLaunchInputMethodDaemon(); | 675 library->MaybeLaunchInputMethodDaemon(); |
677 } | 676 } |
678 | 677 |
679 // Stops the backend input method daemon. This function should also be | 678 // Stops the backend input method daemon. This function should also be |
680 // called from MaybeStopInputMethodDaemon(), except one case where we | 679 // called from MaybeStopInputMethodDaemon(), except one case where we |
681 // stop the input method daemon at Chrome shutdown in Observe(). | 680 // stop the input method daemon at Chrome shutdown in Observe(). |
682 void StopInputMethodDaemon() { | 681 void StopInputMethodDaemon() { |
683 if (!initialized_successfully_) | 682 if (!initialized_successfully_) |
684 return; | 683 return; |
685 | 684 |
686 should_launch_ime_ = false; | 685 should_launch_ime_ = false; |
687 if (ibus_daemon_process_id_) { | 686 if (ibus_daemon_process_handle_ != base::kNullProcessHandle) { |
| 687 const base::ProcessId pid = base::GetProcId(ibus_daemon_process_handle_); |
688 if (!chromeos::StopInputMethodProcess(input_method_status_connection_)) { | 688 if (!chromeos::StopInputMethodProcess(input_method_status_connection_)) { |
689 LOG(ERROR) << "StopInputMethodProcess IPC failed. Sending SIGTERM to " | 689 LOG(ERROR) << "StopInputMethodProcess IPC failed. Sending SIGTERM to " |
690 << "PID " << ibus_daemon_process_id_; | 690 << "PID " << pid; |
691 kill(ibus_daemon_process_id_, SIGTERM); | 691 base::KillProcess(ibus_daemon_process_handle_, -1, false /* wait */); |
692 } | 692 } |
693 VLOG(1) << "ibus-daemon (PID=" << ibus_daemon_process_id_ << ") is " | 693 VLOG(1) << "ibus-daemon (PID=" << pid << ") is terminated"; |
694 << "terminated"; | 694 ibus_daemon_process_handle_ = base::kNullProcessHandle; |
695 ibus_daemon_process_id_ = 0; | |
696 } | 695 } |
697 } | 696 } |
698 | 697 |
699 void SetDeferImeStartup(bool defer) { | 698 void SetDeferImeStartup(bool defer) { |
700 VLOG(1) << "Setting DeferImeStartup to " << defer; | 699 VLOG(1) << "Setting DeferImeStartup to " << defer; |
701 defer_ime_startup_ = defer; | 700 defer_ime_startup_ = defer; |
702 } | 701 } |
703 | 702 |
704 void SetEnableAutoImeShutdown(bool enable) { | 703 void SetEnableAutoImeShutdown(bool enable) { |
705 enable_auto_ime_shutdown_ = enable; | 704 enable_auto_ime_shutdown_ = enable; |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
758 bool enable_auto_ime_shutdown_; | 757 bool enable_auto_ime_shutdown_; |
759 // The ID of the tentative current input method (ex. "mozc"). This value | 758 // The ID of the tentative current input method (ex. "mozc"). This value |
760 // can be different from the actual current input method, if | 759 // can be different from the actual current input method, if |
761 // ChangeInputMethod() fails. | 760 // ChangeInputMethod() fails. |
762 std::string tentative_current_input_method_id_; | 761 std::string tentative_current_input_method_id_; |
763 // True if we should change the current input method to | 762 // True if we should change the current input method to |
764 // |tentative_current_input_method_id_| once the queue of the pending config | 763 // |tentative_current_input_method_id_| once the queue of the pending config |
765 // requests becomes empty. | 764 // requests becomes empty. |
766 bool should_change_input_method_; | 765 bool should_change_input_method_; |
767 | 766 |
768 // The process id of the IBus daemon. 0 if it's not running. The process | 767 // 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. | 768 // running. |
770 int ibus_daemon_process_id_; | 769 base::ProcessHandle ibus_daemon_process_handle_; |
771 | 770 |
772 // True if initialization is successfully done, meaning that libcros is | 771 // True if initialization is successfully done, meaning that libcros is |
773 // loaded and input method status monitoring is started. This value | 772 // loaded and input method status monitoring is started. This value |
774 // should be checked where we call libcros functions. | 773 // should be checked where we call libcros functions. |
775 bool initialized_successfully_; | 774 bool initialized_successfully_; |
776 | 775 |
777 // The candidate window. This will be deleted when the APP_TERMINATING | 776 // The candidate window. This will be deleted when the APP_TERMINATING |
778 // message is sent. | 777 // message is sent. |
779 scoped_ptr<CandidateWindowController> candidate_window_controller_; | 778 scoped_ptr<CandidateWindowController> candidate_window_controller_; |
780 | 779 |
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1100 } | 1099 } |
1101 return impl; | 1100 return impl; |
1102 } | 1101 } |
1103 } | 1102 } |
1104 | 1103 |
1105 } // namespace chromeos | 1104 } // namespace chromeos |
1106 | 1105 |
1107 // Allows InvokeLater without adding refcounting. This class is a Singleton and | 1106 // Allows InvokeLater without adding refcounting. This class is a Singleton and |
1108 // won't be deleted until it's last InvokeLater is run. | 1107 // won't be deleted until it's last InvokeLater is run. |
1109 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::InputMethodLibraryImpl); | 1108 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::InputMethodLibraryImpl); |
OLD | NEW |