Index: chrome/browser/chromeos/input_method/ibus_controller_impl.cc |
diff --git a/chrome/browser/chromeos/input_method/ibus_controller_impl.cc b/chrome/browser/chromeos/input_method/ibus_controller_impl.cc |
index 10a354a9e7c5bb725a9d1c61288aaa4b0ce4fbb1..788ded4a49e2406990efc9277e847b8d946bfc8c 100644 |
--- a/chrome/browser/chromeos/input_method/ibus_controller_impl.cc |
+++ b/chrome/browser/chromeos/input_method/ibus_controller_impl.cc |
@@ -12,12 +12,19 @@ |
#include <stack> |
#include <utility> |
+#include "base/bind.h" |
+#include "base/environment.h" |
+#include "base/files/file_path_watcher.h" |
#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop.h" |
+#include "base/rand_util.h" |
#include "base/stringprintf.h" |
#include "base/string_split.h" |
#include "chrome/browser/chromeos/input_method/input_method_config.h" |
#include "chrome/browser/chromeos/input_method/input_method_property.h" |
#include "chrome/browser/chromeos/input_method/input_method_util.h" |
+#include "chromeos/dbus/dbus_thread_manager.h" |
+#include "content/public/browser/browser_thread.h" |
// TODO(nona): Remove libibus dependency from this file. Then, write unit tests |
// for all functions in this file. crbug.com/26334 |
@@ -357,13 +364,107 @@ std::string PrintPropList(IBusPropList *prop_list, int tree_level) { |
return stream.str(); |
} |
+class IBusAddressWatcher { |
+ public: |
+ class IBusAddressFileWatcherDelegate |
+ : public base::files::FilePathWatcher::Delegate { |
+ public: |
+ IBusAddressFileWatcherDelegate( |
+ const std::string& ibus_address, |
+ IBusControllerImpl* controller, |
+ IBusAddressWatcher* watcher) |
+ : ibus_address_(ibus_address), |
+ controller_(controller), |
+ watcher_(watcher) { |
+ DCHECK(watcher); |
+ DCHECK(!ibus_address.empty()); |
+ } |
+ |
+ virtual ~IBusAddressFileWatcherDelegate() {} |
+ |
+ virtual void OnFilePathChanged(const FilePath& file_path) OVERRIDE { |
+ if (!watcher_->IsWatching()) |
+ return; |
+ bool success = content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind( |
+ &IBusControllerImpl::IBusDaemonInitializationDone, |
+ controller_, |
+ ibus_address_)); |
+ DCHECK(success); |
+ watcher_->StopSoon(); |
+ } |
+ |
+ private: |
+ // The ibus-daemon address. |
+ const std::string ibus_address_; |
+ IBusControllerImpl* controller_; |
+ IBusAddressWatcher* watcher_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(IBusAddressFileWatcherDelegate); |
+ }; |
+ |
+ static void Start(const std::string& ibus_address, |
+ IBusControllerImpl* controller) { |
+ IBusAddressWatcher* instance = IBusAddressWatcher::Get(); |
+ scoped_ptr<base::Environment> env(base::Environment::Create()); |
+ std::string address_file_path; |
+ // TODO(nona): move reading environment variables to UI thread. |
+ env->GetVar("IBUS_ADDRESS_FILE", &address_file_path); |
+ DCHECK(!address_file_path.empty()); |
+ |
+ if (instance->IsWatching()) |
+ instance->StopNow(); |
+ instance->watcher_ = new base::files::FilePathWatcher; |
+ |
+ // The |delegate| is owned by watcher. |
+ IBusAddressFileWatcherDelegate* delegate = |
+ new IBusAddressFileWatcherDelegate(ibus_address, controller, instance); |
+ bool result = instance->watcher_->Watch(FilePath(address_file_path), |
+ delegate); |
+ DCHECK(result); |
+ } |
+ |
+ void StopNow() { |
+ delete watcher_; |
+ watcher_ = NULL; |
+ } |
+ |
+ void StopSoon() { |
+ if (!watcher_) |
+ return; |
+ MessageLoop::current()->DeleteSoon(FROM_HERE, watcher_); |
+ watcher_ = NULL; |
+ } |
+ |
+ bool IsWatching() const { |
+ return watcher_ != NULL; |
+ } |
+ |
+ private: |
+ static IBusAddressWatcher* Get() { |
+ static IBusAddressWatcher* instance = new IBusAddressWatcher; |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
+ return instance; |
+ } |
+ |
+ // Singleton |
+ IBusAddressWatcher() |
+ : watcher_(NULL) {} |
+ base::files::FilePathWatcher* watcher_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(IBusAddressWatcher); |
+}; |
+ |
} // namespace |
IBusControllerImpl::IBusControllerImpl() |
: ibus_(NULL), |
ibus_config_(NULL), |
- should_launch_daemon_(false), |
- process_handle_(base::kNullProcessHandle) { |
+ process_handle_(base::kNullProcessHandle), |
+ ibus_daemon_status_(IBUS_DAEMON_STOP), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { |
} |
IBusControllerImpl::~IBusControllerImpl() { |
@@ -405,10 +506,13 @@ IBusControllerImpl::~IBusControllerImpl() { |
bool IBusControllerImpl::Start() { |
MaybeInitializeIBusBus(); |
- should_launch_daemon_ = true; |
if (IBusConnectionsAreAlive()) |
return true; |
- return MaybeLaunchIBusDaemon(); |
+ if (ibus_daemon_status_ == IBUS_DAEMON_STOP || |
+ ibus_daemon_status_ == IBUS_DAEMON_SHUTTING_DOWN) { |
+ return StartIBusDaemon(); |
+ } |
+ return true; |
} |
void IBusControllerImpl::Reset() { |
@@ -422,6 +526,13 @@ void IBusControllerImpl::Reset() { |
} |
bool IBusControllerImpl::Stop() { |
+ if (ibus_daemon_status_ == IBUS_DAEMON_SHUTTING_DOWN || |
+ ibus_daemon_status_ == IBUS_DAEMON_STOP) { |
+ return true; |
+ } |
+ |
+ ibus_daemon_status_ = IBUS_DAEMON_SHUTTING_DOWN; |
+ // TODO(nona): Shutdown ibus-bus connection. |
if (IBusConnectionsAreAlive()) { |
// Ask IBus to exit *asynchronously*. |
ibus_bus_exit_async(ibus_, |
@@ -436,21 +547,20 @@ bool IBusControllerImpl::Stop() { |
g_object_unref(ibus_config_); |
ibus_config_ = NULL; |
} |
- } else if (process_handle_ != base::kNullProcessHandle) { |
+ } else { |
base::KillProcess(process_handle_, -1, false /* wait */); |
DVLOG(1) << "Killing ibus-daemon. PID=" |
<< base::GetProcId(process_handle_); |
- } else { |
- // The daemon hasn't been started yet. |
} |
- |
process_handle_ = base::kNullProcessHandle; |
- should_launch_daemon_ = false; |
return true; |
} |
bool IBusControllerImpl::ChangeInputMethod(const std::string& id) { |
- DCHECK(should_launch_daemon_); |
+ if (ibus_daemon_status_ == IBUS_DAEMON_SHUTTING_DOWN || |
+ ibus_daemon_status_ == IBUS_DAEMON_STOP) { |
+ return false; |
+ } |
// Sanity checks. |
DCHECK(!InputMethodUtil::IsKeyboardLayout(id)); |
@@ -572,7 +682,7 @@ void IBusControllerImpl::CancelHandwriting(int n_strokes) { |
#endif |
bool IBusControllerImpl::IBusConnectionsAreAlive() { |
- return (process_handle_ != base::kNullProcessHandle) && |
+ return (ibus_daemon_status_ == IBUS_DAEMON_RUNNING) && |
ibus_ && ibus_bus_is_connected(ibus_) && ibus_config_; |
} |
@@ -892,27 +1002,48 @@ void IBusControllerImpl::UpdateProperty(IBusPanelService* panel, |
} |
} |
-bool IBusControllerImpl::MaybeLaunchIBusDaemon() { |
- static const char kIBusDaemonPath[] = "/usr/bin/ibus-daemon"; |
- |
- if (process_handle_ != base::kNullProcessHandle) { |
+bool IBusControllerImpl::StartIBusDaemon() { |
+ if (ibus_daemon_status_ == IBUS_DAEMON_INITIALIZING || |
+ ibus_daemon_status_ == IBUS_DAEMON_RUNNING) { |
DVLOG(1) << "MaybeLaunchIBusDaemon: ibus-daemon is already running."; |
return false; |
} |
- if (!should_launch_daemon_) |
- return false; |
+ ibus_daemon_status_ = IBUS_DAEMON_INITIALIZING; |
+ ibus_daemon_address_ = base::StringPrintf( |
+ "unix:abstract=ibus-%d", |
+ base::RandInt(0, std::numeric_limits<int>::max())); |
+ |
+ // Set up ibus-daemon address file watcher before launching ibus-daemon, |
+ // because if watcher starts after ibus-daemon, we may miss the ibus |
+ // connection initialization. |
+ bool success = content::BrowserThread::PostTaskAndReply( |
+ content::BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind(&IBusAddressWatcher::Start, |
+ ibus_daemon_address_, |
+ base::Unretained(this)), |
+ base::Bind(&IBusControllerImpl::LaunchIBusDaemon, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ ibus_daemon_address_)); |
+ DCHECK(success); |
+ return true; |
+} |
+ |
+void IBusControllerImpl::LaunchIBusDaemon(const std::string& ibus_address) { |
+ DCHECK_EQ(base::kNullProcessHandle, process_handle_); |
+ static const char kIBusDaemonPath[] = "/usr/bin/ibus-daemon"; |
// TODO(zork): Send output to /var/log/ibus.log |
const std::string ibus_daemon_command_line = |
- base::StringPrintf("%s --panel=disable --cache=none --restart --replace", |
- kIBusDaemonPath); |
+ base::StringPrintf("%s --panel=disable --cache=none --restart --replace" |
+ " --address=%s", |
+ kIBusDaemonPath, |
+ ibus_address.c_str()); |
if (!LaunchProcess(ibus_daemon_command_line, |
&process_handle_, |
reinterpret_cast<GChildWatchFunc>(OnIBusDaemonExit))) { |
DVLOG(1) << "Failed to launch " << ibus_daemon_command_line; |
- return false; |
} |
- return true; |
} |
bool IBusControllerImpl::LaunchProcess(const std::string& command_line, |
@@ -940,6 +1071,22 @@ bool IBusControllerImpl::LaunchProcess(const std::string& command_line, |
} |
// static |
+void IBusControllerImpl::IBusDaemonInitializationDone( |
+ IBusControllerImpl* controller, |
+ const std::string& ibus_address) { |
+ if (controller->ibus_daemon_address_ != ibus_address) |
+ return; |
+ |
+ if (controller->ibus_daemon_status_ != IBUS_DAEMON_INITIALIZING) { |
+ // Stop() or OnIBusDaemonExit() has already been called. |
+ return; |
+ } |
+ chromeos::DBusThreadManager::Get()->InitIBusBus(ibus_address); |
+ controller->ibus_daemon_status_ = IBUS_DAEMON_RUNNING; |
+ VLOG(1) << "The ibus-daemon initialization is done."; |
+} |
+ |
+// static |
void IBusControllerImpl::SetInputMethodConfigCallback(GObject* source_object, |
GAsyncResult* res, |
gpointer user_data) { |
@@ -967,12 +1114,31 @@ void IBusControllerImpl::SetInputMethodConfigCallback(GObject* source_object, |
void IBusControllerImpl::OnIBusDaemonExit(GPid pid, |
gint status, |
IBusControllerImpl* controller) { |
- if (controller->process_handle_ != base::kNullProcessHandle && |
- base::GetProcId(controller->process_handle_) == pid) { |
- controller->process_handle_ = base::kNullProcessHandle; |
+ if (controller->process_handle_ != base::kNullProcessHandle) { |
+ if (base::GetProcId(controller->process_handle_) == pid) { |
+ // ibus-daemon crashed. |
+ // TODO(nona): Shutdown ibus-bus connection. |
+ controller->process_handle_ = base::kNullProcessHandle; |
+ } else { |
+ // This condition is as follows. |
+ // 1. Called Stop (process_handle_ becomes null) |
+ // 2. Called LaunchProcess (process_handle_ becomes new instance) |
+ // 3. Callbacked OnIBusDaemonExit for old instance and reach here. |
+ // In this case, we should not reset process_handle_ as null, and do not |
+ // re-launch ibus-daemon. |
+ return; |
+ } |
+ } |
+ |
+ const IBusDaemonStatus on_exit_state = controller->ibus_daemon_status_; |
+ controller->ibus_daemon_status_ = IBUS_DAEMON_STOP; |
+ if (on_exit_state == IBUS_DAEMON_SHUTTING_DOWN) { |
+ // Normal exitting, so do nothing. |
+ return; |
} |
- // Restart the daemon if needed. |
- controller->MaybeLaunchIBusDaemon(); |
+ |
+ LOG(ERROR) << "The ibus-daemon crashed. Re-launching..."; |
+ controller->StartIBusDaemon(); |
} |
#endif // defined(HAVE_IBUS) |