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
3// found in the LICENSE file.
4
5#include "chromeos/dbus/ibus/ibus_client.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "chromeos/dbus/ibus/ibus_constants.h"
10#include "chromeos/dbus/ibus/ibus_component.h"
11#include "chromeos/dbus/ibus/ibus_engine_service.h"
12#include "chromeos/ime/ibus_bridge.h"
13#include "dbus/bus.h"
14#include "dbus/message.h"
15#include "dbus/object_path.h"
16#include "dbus/object_proxy.h"
17
18namespace chromeos {
19
20namespace {
21
22// The IBusClient implementation.
23class IBusClientImpl : public IBusClient {
24 public:
25  explicit IBusClientImpl(dbus::Bus* bus)
26      : proxy_(bus->GetObjectProxy(ibus::kServiceName,
27                                   dbus::ObjectPath(ibus::bus::kServicePath))),
28        weak_ptr_factory_(this) {
29  }
30
31  virtual ~IBusClientImpl() {}
32
33  // IBusClient override.
34  virtual void CreateInputContext(
35      const std::string& client_name,
36      const CreateInputContextCallback& callback,
37      const ErrorCallback& error_callback) OVERRIDE {
38    DCHECK(!callback.is_null());
39    DCHECK(!error_callback.is_null());
40    dbus::MethodCall method_call(ibus::bus::kServiceInterface,
41                                 ibus::bus::kCreateInputContextMethod);
42    dbus::MessageWriter writer(&method_call);
43    writer.AppendString(client_name);
44    proxy_->CallMethodWithErrorCallback(
45        &method_call,
46        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
47        base::Bind(&IBusClientImpl::OnCreateInputContext,
48                   weak_ptr_factory_.GetWeakPtr(),
49                   callback,
50                   error_callback),
51        base::Bind(&IBusClientImpl::OnDBusMethodCallFail,
52                   weak_ptr_factory_.GetWeakPtr(),
53                   error_callback));
54  }
55
56  // IBusClient override.
57  virtual void RegisterComponent(
58      const IBusComponent& ibus_component,
59      const RegisterComponentCallback& callback,
60      const ErrorCallback& error_callback) OVERRIDE {
61    DCHECK(!callback.is_null());
62    DCHECK(!error_callback.is_null());
63    dbus::MethodCall method_call(ibus::bus::kServiceInterface,
64                                 ibus::bus::kRegisterComponentMethod);
65    dbus::MessageWriter writer(&method_call);
66    AppendIBusComponent(ibus_component, &writer);
67    proxy_->CallMethodWithErrorCallback(
68        &method_call,
69        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
70        base::Bind(&IBusClientImpl::OnRegisterComponent,
71                   weak_ptr_factory_.GetWeakPtr(),
72                   callback,
73                   error_callback),
74        base::Bind(&IBusClientImpl::OnDBusMethodCallFail,
75                   weak_ptr_factory_.GetWeakPtr(),
76                   error_callback));
77  }
78
79  // IBusClient override.
80  virtual void SetGlobalEngine(const std::string& engine_name,
81                               const ErrorCallback& error_callback) OVERRIDE {
82    DCHECK(!error_callback.is_null());
83    dbus::MethodCall method_call(ibus::bus::kServiceInterface,
84                                 ibus::bus::kSetGlobalEngineMethod);
85    dbus::MessageWriter writer(&method_call);
86    writer.AppendString(engine_name);
87    proxy_->CallMethodWithErrorCallback(
88        &method_call,
89        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
90        base::Bind(&IBusClientImpl::OnSetGlobalEngine,
91                   weak_ptr_factory_.GetWeakPtr(),
92                   error_callback),
93        base::Bind(&IBusClientImpl::OnDBusMethodCallFail,
94                   weak_ptr_factory_.GetWeakPtr(),
95                   error_callback));
96  }
97
98  // IBusClient override.
99  virtual void Exit(ExitOption option,
100                    const ErrorCallback& error_callback) OVERRIDE {
101    DCHECK(!error_callback.is_null());
102    dbus::MethodCall method_call(ibus::bus::kServiceInterface,
103                                 ibus::bus::kExitMethod);
104    dbus::MessageWriter writer(&method_call);
105    writer.AppendBool(option == RESTART_IBUS_DAEMON);
106    proxy_->CallMethodWithErrorCallback(
107        &method_call,
108        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
109        base::Bind(&IBusClientImpl::OnExit,
110                   weak_ptr_factory_.GetWeakPtr(),
111                   error_callback),
112        base::Bind(&IBusClientImpl::OnDBusMethodCallFail,
113                   weak_ptr_factory_.GetWeakPtr(),
114                   error_callback));
115  }
116
117 private:
118  // Handles responses of CreateInputContext method calls.
119  void OnCreateInputContext(const CreateInputContextCallback& callback,
120                            const ErrorCallback& error_callback,
121                            dbus::Response* response) {
122    if (!response) {
123      LOG(ERROR) << "Cannot get input context: response is NULL.";
124      error_callback.Run();
125      return;
126    }
127    dbus::MessageReader reader(response);
128    dbus::ObjectPath object_path;
129    if (!reader.PopObjectPath(&object_path)) {
130      // The IBus message structure may be changed.
131      LOG(ERROR) << "Invalid response: " << response->ToString();
132      error_callback.Run();
133      return;
134    }
135    callback.Run(object_path);
136  }
137
138  // Handles responses of RegisterComponent method calls.
139  void OnRegisterComponent(const RegisterComponentCallback& callback,
140                           const ErrorCallback& error_callback,
141                           dbus::Response* response) {
142    if (!response) {
143      LOG(ERROR) << "Response is NULL.";
144      error_callback.Run();
145      return;
146    }
147    callback.Run();
148  }
149
150  // Handles responses of RegisterComponent method calls.
151  void OnSetGlobalEngine(const ErrorCallback& error_callback,
152                         dbus::Response* response) {
153    if (!response) {
154      LOG(ERROR) << "Response is NULL.";
155      error_callback.Run();
156      return;
157    }
158  }
159
160  // Handles responses of RegisterComponent method calls.
161  void OnExit(const ErrorCallback& error_callback,
162              dbus::Response* response) {
163    if (!response) {
164      LOG(ERROR) << "Response is NULL.";
165      error_callback.Run();
166      return;
167    }
168  }
169
170  // Handles error response of RegisterComponent method call.
171  void OnDBusMethodCallFail(const ErrorCallback& error_callback,
172                            dbus::ErrorResponse* response) {
173    error_callback.Run();
174  }
175
176  dbus::ObjectProxy* proxy_;
177  base::WeakPtrFactory<IBusClientImpl> weak_ptr_factory_;
178
179  DISALLOW_COPY_AND_ASSIGN(IBusClientImpl);
180};
181
182// An implementation of IBusClient without ibus-daemon interaction.
183// Currently this class is used only on linux desktop.
184// TODO(nona): Use this on ChromeOS device once crbug.com/171351 is fixed.
185class IBusClientDaemonlessImpl : public IBusClient {
186 public:
187  IBusClientDaemonlessImpl() {}
188  virtual ~IBusClientDaemonlessImpl() {}
189
190  virtual void CreateInputContext(
191      const std::string& client_name,
192      const CreateInputContextCallback & callback,
193      const ErrorCallback& error_callback) OVERRIDE {
194    // TODO(nona): Remove this function once ibus-daemon is gone.
195    // We don't have to do anything for this function except for calling
196    // |callback| as the success of this function. The original spec of ibus
197    // supports multiple input contexts, but there is only one input context in
198    // Chrome OS. That is all IME events will be came from same input context
199    // and all engine action shuold be forwarded to same input context.
200    dbus::ObjectPath path("dummpy path");
201    callback.Run(path);
202  }
203
204  virtual void RegisterComponent(
205      const IBusComponent& ibus_component,
206      const RegisterComponentCallback& callback,
207      const ErrorCallback& error_callback) OVERRIDE {
208    // TODO(nona): Remove this function once ibus-daemon is gone.
209    // The information about engine is stored in chromeos::InputMethodManager so
210    // IBusBridge doesn't do anything except for calling |callback| as success
211    // of this function.
212    callback.Run();
213  }
214
215  virtual void SetGlobalEngine(const std::string& engine_name,
216                               const ErrorCallback& error_callback) OVERRIDE {
217    IBusEngineHandlerInterface* previous_engine =
218        IBusBridge::Get()->GetEngineHandler();
219    if (previous_engine)
220      previous_engine->Disable();
221    IBusBridge::Get()->CreateEngine(engine_name);
222    IBusEngineHandlerInterface* next_engine =
223        IBusBridge::Get()->GetEngineHandler();
224    if (next_engine)
225      next_engine->Enable();
226  }
227
228  virtual void Exit(ExitOption option,
229                    const ErrorCallback& error_callback) OVERRIDE {
230    // Exit is not supported on daemon-less implementation.
231  }
232
233 private:
234  DISALLOW_COPY_AND_ASSIGN(IBusClientDaemonlessImpl);
235};
236
237}  // namespace
238
239///////////////////////////////////////////////////////////////////////////////
240// IBusClient
241
242IBusClient::IBusClient() {}
243
244IBusClient::~IBusClient() {}
245
246// static
247IBusClient* IBusClient::Create(DBusClientImplementationType type,
248                               dbus::Bus* bus) {
249  if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) {
250    return new IBusClientImpl(bus);
251  }
252  DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
253  return new IBusClientDaemonlessImpl();
254}
255
256}  // namespace chromeos
257