OLD | NEW |
---|---|
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/ibus_controller_impl.h" | 5 #include "chrome/browser/chromeos/input_method/ibus_controller_impl.h" |
6 | 6 |
7 #include <algorithm> // for std::reverse. | 7 #include <algorithm> // for std::reverse. |
8 #include <cstdio> | 8 #include <cstdio> |
9 #include <cstring> // for std::strcmp. | 9 #include <cstring> // for std::strcmp. |
10 #include <set> | 10 #include <set> |
11 #include <sstream> | 11 #include <sstream> |
12 #include <stack> | 12 #include <stack> |
13 #include <utility> | 13 #include <utility> |
14 | 14 |
15 #include "base/bind.h" | |
16 #include "base/environment.h" | |
17 #include "base/files/file_path_watcher.h" | |
15 #include "base/memory/scoped_ptr.h" | 18 #include "base/memory/scoped_ptr.h" |
19 #include "base/message_loop.h" | |
20 #include "base/rand_util.h" | |
16 #include "base/stringprintf.h" | 21 #include "base/stringprintf.h" |
17 #include "base/string_split.h" | 22 #include "base/string_split.h" |
18 #include "chrome/browser/chromeos/input_method/input_method_config.h" | 23 #include "chrome/browser/chromeos/input_method/input_method_config.h" |
19 #include "chrome/browser/chromeos/input_method/input_method_property.h" | 24 #include "chrome/browser/chromeos/input_method/input_method_property.h" |
20 #include "chrome/browser/chromeos/input_method/input_method_util.h" | 25 #include "chrome/browser/chromeos/input_method/input_method_util.h" |
26 #include "chromeos/dbus/dbus_thread_manager.h" | |
27 #include "content/public/browser/browser_thread.h" | |
21 | 28 |
22 // TODO(nona): Remove libibus dependency from this file. Then, write unit tests | 29 // TODO(nona): Remove libibus dependency from this file. Then, write unit tests |
23 // for all functions in this file. crbug.com/26334 | 30 // for all functions in this file. crbug.com/26334 |
24 #if defined(HAVE_IBUS) | 31 #if defined(HAVE_IBUS) |
25 #include <ibus.h> | 32 #include <ibus.h> |
26 #endif | 33 #endif |
27 | 34 |
28 namespace { | 35 namespace { |
29 | 36 |
30 // Finds a property which has |new_prop.key| from |prop_list|, and replaces the | 37 // Finds a property which has |new_prop.key| from |prop_list|, and replaces the |
(...skipping 319 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
350 std::stringstream stream; | 357 std::stringstream stream; |
351 for (int i = 0;; ++i) { | 358 for (int i = 0;; ++i) { |
352 IBusProperty* prop = ibus_prop_list_get(prop_list, i); | 359 IBusProperty* prop = ibus_prop_list_get(prop_list, i); |
353 if (!prop) | 360 if (!prop) |
354 break; | 361 break; |
355 stream << PrintProp(prop, tree_level); | 362 stream << PrintProp(prop, tree_level); |
356 } | 363 } |
357 return stream.str(); | 364 return stream.str(); |
358 } | 365 } |
359 | 366 |
367 // The implementation of FilePathWatcher::Delegate, used in | |
368 // StartAddressFileWatch function. | |
369 class IBusAddressFileWatcherDelegate | |
370 : public base::files::FilePathWatcher::Delegate { | |
371 public: | |
372 IBusAddressFileWatcherDelegate(const std::string& ibus_address, | |
373 base::files::FilePathWatcher* watcher, | |
374 IBusControllerImpl* controller) | |
375 : ibus_address_(ibus_address), | |
376 watcher_(watcher), | |
377 controller_(controller) { | |
378 } | |
379 | |
380 virtual ~IBusAddressFileWatcherDelegate() {} | |
381 | |
382 virtual void OnFilePathChanged(const FilePath& file_path) { | |
383 content::BrowserThread::PostTaskAndReply( | |
Yusuke Sato
2012/04/27 02:12:13
check return value?
Seigo Nonaka
2012/05/01 00:19:54
Done.
| |
384 content::BrowserThread::UI, | |
385 FROM_HERE, | |
386 base::Bind(&chromeos::DBusThreadManager::MaybeResetIBusBus, | |
387 base::Unretained(chromeos::DBusThreadManager::Get()), | |
388 ibus_address_), | |
389 base::Bind(&IBusControllerImpl::IBusBusInitializationDone, | |
Yusuke Sato
2012/04/27 02:12:13
flipping the flag from FILE thread w/o locking loo
Seigo Nonaka
2012/05/01 00:19:54
Done.
| |
390 controller_)); | |
391 MessageLoop::current()->DeleteSoon(FROM_HERE, watcher_); | |
392 } | |
393 | |
394 private: | |
395 // The ibus-daemon address. | |
396 const std::string ibus_address_; | |
397 | |
Yusuke Sato
2012/04/27 02:12:13
nit: add a comment or remove this line.
Seigo Nonaka
2012/05/01 00:19:54
Done.
| |
398 IBusControllerImpl* controller_; | |
399 | |
Yusuke Sato
2012/04/27 02:12:13
nit: the same as above.
Seigo Nonaka
2012/05/01 00:19:54
Done.
| |
400 base::files::FilePathWatcher* watcher_; | |
401 | |
402 DISALLOW_COPY_AND_ASSIGN(IBusAddressFileWatcherDelegate); | |
403 }; | |
404 | |
405 void StartAddressFileWatch(const std::string& ibus_address, | |
406 IBusControllerImpl* controller) { | |
407 scoped_ptr<base::Environment> env(base::Environment::Create()); | |
408 std::string address_file_path; | |
409 env->GetVar("IBUS_ADDRESS_FILE", &address_file_path); | |
Yusuke Sato
2012/04/27 02:12:13
DCHECK(!address_file_path.empty());
Seigo Nonaka
2012/05/01 00:19:54
Done.
| |
410 | |
411 // The |watcher| instance will be released by |delegate|. | |
412 base::files::FilePathWatcher* watcher = new base::files::FilePathWatcher; | |
413 | |
414 // The |delegate| is owned by watcher. | |
415 IBusAddressFileWatcherDelegate* delegate = new IBusAddressFileWatcherDelegate( | |
416 ibus_address, watcher, controller); | |
417 bool result = watcher->Watch(FilePath(address_file_path), delegate); | |
418 DCHECK(result); | |
419 } | |
360 } // namespace | 420 } // namespace |
361 | 421 |
362 IBusControllerImpl::IBusControllerImpl() | 422 IBusControllerImpl::IBusControllerImpl() |
363 : ibus_(NULL), | 423 : ibus_(NULL), |
364 ibus_config_(NULL), | 424 ibus_config_(NULL), |
365 should_launch_daemon_(false), | 425 should_launch_daemon_(false), |
366 process_handle_(base::kNullProcessHandle) { | 426 process_handle_(base::kNullProcessHandle), |
427 ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), | |
428 is_initializing_ibus_daemon_(false) { | |
367 } | 429 } |
368 | 430 |
369 IBusControllerImpl::~IBusControllerImpl() { | 431 IBusControllerImpl::~IBusControllerImpl() { |
370 // Disconnect signals so the handler functions will not be called with | 432 // Disconnect signals so the handler functions will not be called with |
371 // |this| which is already freed. | 433 // |this| which is already freed. |
372 if (ibus_) { | 434 if (ibus_) { |
373 g_signal_handlers_disconnect_by_func( | 435 g_signal_handlers_disconnect_by_func( |
374 ibus_, | 436 ibus_, |
375 reinterpret_cast<gpointer>(G_CALLBACK(BusConnectedThunk)), | 437 reinterpret_cast<gpointer>(G_CALLBACK(BusConnectedThunk)), |
376 this); | 438 this); |
(...skipping 509 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
886 // Notify the change. | 948 // Notify the change. |
887 if (!prop_list.empty()) { | 949 if (!prop_list.empty()) { |
888 for (size_t i = 0; i < prop_list.size(); ++i) { | 950 for (size_t i = 0; i < prop_list.size(); ++i) { |
889 FindAndUpdateProperty(prop_list[i], ¤t_property_list_); | 951 FindAndUpdateProperty(prop_list[i], ¤t_property_list_); |
890 } | 952 } |
891 FOR_EACH_OBSERVER(Observer, observers_, PropertyChanged()); | 953 FOR_EACH_OBSERVER(Observer, observers_, PropertyChanged()); |
892 } | 954 } |
893 } | 955 } |
894 | 956 |
895 bool IBusControllerImpl::MaybeLaunchIBusDaemon() { | 957 bool IBusControllerImpl::MaybeLaunchIBusDaemon() { |
896 static const char kIBusDaemonPath[] = "/usr/bin/ibus-daemon"; | |
897 | |
898 if (process_handle_ != base::kNullProcessHandle) { | 958 if (process_handle_ != base::kNullProcessHandle) { |
899 DVLOG(1) << "MaybeLaunchIBusDaemon: ibus-daemon is already running."; | 959 DVLOG(1) << "MaybeLaunchIBusDaemon: ibus-daemon is already running."; |
900 return false; | 960 return false; |
901 } | 961 } |
902 if (!should_launch_daemon_) | 962 if (!should_launch_daemon_) |
903 return false; | 963 return false; |
904 | 964 |
965 if (is_initializing_ibus_daemon_) | |
966 return false; | |
967 | |
968 is_initializing_ibus_daemon_ = true; | |
969 const std::string ibus_address = base::StringPrintf( | |
970 "unix:abstract=/tmp/ibus-%0lX", base::RandUint64()); | |
971 | |
972 // Set up ibus-daemon address file watcher before launching ibus-daemon, | |
973 // because if watcher start after ibus-daemon, we may miss the ibus connection | |
974 // initialization. | |
975 content::BrowserThread::PostTaskAndReply( | |
Yusuke Sato
2012/04/27 02:12:13
check return value?
Seigo Nonaka
2012/05/01 00:19:54
Done.
| |
976 content::BrowserThread::FILE, | |
977 FROM_HERE, | |
978 base::Bind(&StartAddressFileWatch, ibus_address), | |
979 base::Bind(&IBusControllerImpl::LaunchIBusDaemon, | |
980 weak_ptr_factory_.GetWeakPtr(), | |
981 ibus_address)); | |
982 return true; | |
983 } | |
984 | |
985 void IBusControllerImpl::LaunchIBusDaemon(const std::string& ibus_address) { | |
986 static const char kIBusDaemonPath[] = "/usr/bin/ibus-daemon"; | |
905 // TODO(zork): Send output to /var/log/ibus.log | 987 // TODO(zork): Send output to /var/log/ibus.log |
906 const std::string ibus_daemon_command_line = | 988 const std::string ibus_daemon_command_line = |
907 base::StringPrintf("%s --panel=disable --cache=none --restart --replace", | 989 base::StringPrintf("%s --panel=disable --cache=none --restart --replace" |
908 kIBusDaemonPath); | 990 " --address=%s", |
991 kIBusDaemonPath, | |
992 ibus_address.c_str()); | |
909 if (!LaunchProcess(ibus_daemon_command_line, | 993 if (!LaunchProcess(ibus_daemon_command_line, |
910 &process_handle_, | 994 &process_handle_, |
911 reinterpret_cast<GChildWatchFunc>(OnIBusDaemonExit))) { | 995 reinterpret_cast<GChildWatchFunc>(OnIBusDaemonExit))) { |
912 DVLOG(1) << "Failed to launch " << ibus_daemon_command_line; | 996 DVLOG(1) << "Failed to launch " << ibus_daemon_command_line; |
913 return false; | |
914 } | 997 } |
915 return true; | |
916 } | 998 } |
917 | 999 |
918 bool IBusControllerImpl::LaunchProcess(const std::string& command_line, | 1000 bool IBusControllerImpl::LaunchProcess(const std::string& command_line, |
919 base::ProcessHandle* process_handle, | 1001 base::ProcessHandle* process_handle, |
920 GChildWatchFunc watch_func) { | 1002 GChildWatchFunc watch_func) { |
921 std::vector<std::string> argv; | 1003 std::vector<std::string> argv; |
922 base::ProcessHandle handle = base::kNullProcessHandle; | 1004 base::ProcessHandle handle = base::kNullProcessHandle; |
923 | 1005 |
924 base::SplitString(command_line, ' ', &argv); | 1006 base::SplitString(command_line, ' ', &argv); |
925 | 1007 |
926 if (!base::LaunchProcess(argv, base::LaunchOptions(), &handle)) { | 1008 if (!base::LaunchProcess(argv, base::LaunchOptions(), &handle)) { |
927 DVLOG(1) << "Could not launch: " << command_line; | 1009 DVLOG(1) << "Could not launch: " << command_line; |
928 return false; | 1010 return false; |
929 } | 1011 } |
930 | 1012 |
931 // g_child_watch_add is necessary to prevent the process from becoming a | 1013 // g_child_watch_add is necessary to prevent the process from becoming a |
932 // zombie. | 1014 // zombie. |
933 // TODO(yusukes): port g_child_watch_add to base/process_utils_posix.cc. | 1015 // TODO(yusukes): port g_child_watch_add to base/process_utils_posix.cc. |
934 const base::ProcessId pid = base::GetProcId(handle); | 1016 const base::ProcessId pid = base::GetProcId(handle); |
935 g_child_watch_add(pid, watch_func, this); | 1017 g_child_watch_add(pid, watch_func, this); |
936 | 1018 |
937 *process_handle = handle; | 1019 *process_handle = handle; |
938 DVLOG(1) << command_line << "is started. PID=" << pid; | 1020 DVLOG(1) << command_line << "is started. PID=" << pid; |
939 return true; | 1021 return true; |
940 } | 1022 } |
941 | 1023 |
1024 void IBusControllerImpl::IBusDaemonInitializationDone() { | |
1025 is_initializing_ibus_daemon_ = false; | |
1026 } | |
1027 | |
942 // static | 1028 // static |
943 void IBusControllerImpl::SetInputMethodConfigCallback(GObject* source_object, | 1029 void IBusControllerImpl::SetInputMethodConfigCallback(GObject* source_object, |
944 GAsyncResult* res, | 1030 GAsyncResult* res, |
945 gpointer user_data) { | 1031 gpointer user_data) { |
946 IBusConfig* config = IBUS_CONFIG(user_data); | 1032 IBusConfig* config = IBUS_CONFIG(user_data); |
947 g_return_if_fail(config); | 1033 g_return_if_fail(config); |
948 | 1034 |
949 GError* error = NULL; | 1035 GError* error = NULL; |
950 const gboolean result = | 1036 const gboolean result = |
951 ibus_config_set_value_async_finish(config, res, &error); | 1037 ibus_config_set_value_async_finish(config, res, &error); |
952 | 1038 |
953 if (!result) { | 1039 if (!result) { |
954 std::string message = "(unknown error)"; | 1040 std::string message = "(unknown error)"; |
955 if (error && error->message) { | 1041 if (error && error->message) { |
956 message = error->message; | 1042 message = error->message; |
957 } | 1043 } |
958 DVLOG(1) << "ibus_config_set_value_async failed: " << message; | 1044 DVLOG(1) << "ibus_config_set_value_async failed: " << message; |
959 } | 1045 } |
960 | 1046 |
961 if (error) | 1047 if (error) |
962 g_error_free(error); | 1048 g_error_free(error); |
963 g_object_unref(config); | 1049 g_object_unref(config); |
964 } | 1050 } |
965 | 1051 |
966 // static | 1052 // static |
967 void IBusControllerImpl::OnIBusDaemonExit(GPid pid, | 1053 void IBusControllerImpl::OnIBusDaemonExit(GPid pid, |
Yusuke Sato
2012/04/27 02:12:13
please also make sure that your code works fine ev
Seigo Nonaka
2012/05/01 00:19:54
Done.
| |
968 gint status, | 1054 gint status, |
969 IBusControllerImpl* controller) { | 1055 IBusControllerImpl* controller) { |
970 if (controller->process_handle_ != base::kNullProcessHandle && | 1056 if (controller->process_handle_ != base::kNullProcessHandle && |
971 base::GetProcId(controller->process_handle_) == pid) { | 1057 base::GetProcId(controller->process_handle_) == pid) { |
972 controller->process_handle_ = base::kNullProcessHandle; | 1058 controller->process_handle_ = base::kNullProcessHandle; |
973 } | 1059 } |
974 // Restart the daemon if needed. | 1060 // Restart the daemon if needed. |
975 controller->MaybeLaunchIBusDaemon(); | 1061 controller->MaybeLaunchIBusDaemon(); |
976 } | 1062 } |
977 #endif // defined(HAVE_IBUS) | 1063 #endif // defined(HAVE_IBUS) |
978 | 1064 |
979 // static | 1065 // static |
980 bool IBusControllerImpl::FindAndUpdatePropertyForTesting( | 1066 bool IBusControllerImpl::FindAndUpdatePropertyForTesting( |
981 const chromeos::input_method::InputMethodProperty& new_prop, | 1067 const chromeos::input_method::InputMethodProperty& new_prop, |
982 chromeos::input_method::InputMethodPropertyList* prop_list) { | 1068 chromeos::input_method::InputMethodPropertyList* prop_list) { |
983 return FindAndUpdateProperty(new_prop, prop_list); | 1069 return FindAndUpdateProperty(new_prop, prop_list); |
984 } | 1070 } |
985 | 1071 |
986 } // namespace input_method | 1072 } // namespace input_method |
987 } // namespace chromeos | 1073 } // namespace chromeos |
OLD | NEW |