- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / test / automation / automation_proxy.cc
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 "chrome/test/automation/automation_proxy.h"
6
7 #include <sstream>
8
9 #include "base/basictypes.h"
10 #include "base/debug/trace_event.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/platform_thread.h"
15 #include "chrome/common/automation_constants.h"
16 #include "chrome/common/automation_messages.h"
17 #include "chrome/common/chrome_version_info.h"
18 #include "chrome/test/automation/browser_proxy.h"
19 #include "chrome/test/automation/tab_proxy.h"
20 #include "chrome/test/automation/window_proxy.h"
21 #include "ipc/ipc_descriptors.h"
22 #if defined(OS_WIN)
23 // TODO(port): Enable when dialog_delegate is ported.
24 #include "ui/views/window/dialog_delegate.h"
25 #endif
26
27 using base::TimeDelta;
28 using base::TimeTicks;
29
30 namespace {
31
32 const char kChannelErrorVersionString[] = "***CHANNEL_ERROR***";
33
34 // This object allows messages received on the background thread to be
35 // properly triaged.
36 class AutomationMessageFilter : public IPC::ChannelProxy::MessageFilter {
37  public:
38   explicit AutomationMessageFilter(AutomationProxy* server) : server_(server) {}
39
40   // Return true to indicate that the message was handled, or false to let
41   // the message be handled in the default way.
42   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
43     bool handled = true;
44     IPC_BEGIN_MESSAGE_MAP(AutomationMessageFilter, message)
45       IPC_MESSAGE_HANDLER_GENERIC(AutomationMsg_Hello,
46                                   OnAutomationHello(message))
47       IPC_MESSAGE_HANDLER_GENERIC(
48         AutomationMsg_InitialLoadsComplete, server_->SignalInitialLoads())
49       IPC_MESSAGE_HANDLER(AutomationMsg_InitialNewTabUILoadComplete,
50                           NewTabLoaded)
51       IPC_MESSAGE_HANDLER_GENERIC(
52         AutomationMsg_InvalidateHandle, server_->InvalidateHandle(message))
53       IPC_MESSAGE_UNHANDLED(handled = false)
54     IPC_END_MESSAGE_MAP()
55
56     return handled;
57   }
58
59   virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE {
60     server_->SetChannel(channel);
61   }
62
63   virtual void OnFilterRemoved() OVERRIDE {
64     server_->ResetChannel();
65   }
66
67   virtual void OnChannelError() OVERRIDE {
68     server_->SignalAppLaunch(kChannelErrorVersionString);
69     server_->SignalNewTabUITab(-1);
70   }
71
72  private:
73   void NewTabLoaded(int load_time) {
74     server_->SignalNewTabUITab(load_time);
75   }
76
77   void OnAutomationHello(const IPC::Message& hello_message) {
78     std::string server_version;
79     PickleIterator iter(hello_message);
80     if (!hello_message.ReadString(&iter, &server_version)) {
81       // We got an AutomationMsg_Hello from an old automation provider
82       // that doesn't send version info. Leave server_version as an empty
83       // string to signal a version mismatch.
84       LOG(ERROR) << "Pre-versioning protocol detected in automation provider.";
85     }
86
87     server_->SignalAppLaunch(server_version);
88   }
89
90   AutomationProxy* server_;
91
92   DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter);
93 };
94
95 }  // anonymous namespace
96
97
98 AutomationProxy::AutomationProxy(base::TimeDelta action_timeout,
99                                  bool disconnect_on_failure)
100     : app_launched_(true, false),
101       initial_loads_complete_(true, false),
102       new_tab_ui_load_complete_(true, false),
103       shutdown_event_(new base::WaitableEvent(true, false)),
104       perform_version_check_(false),
105       disconnect_on_failure_(disconnect_on_failure),
106       channel_disconnected_on_failure_(false),
107       action_timeout_(action_timeout),
108       listener_thread_id_(0) {
109   // base::WaitableEvent::TimedWait() will choke if we give it a negative value.
110   // Zero also seems unreasonable, since we need to wait for IPC, but at
111   // least it is legal... ;-)
112   DCHECK_GE(action_timeout.InMilliseconds(), 0);
113   listener_thread_id_ = base::PlatformThread::CurrentId();
114   InitializeHandleTracker();
115   InitializeThread();
116 }
117
118 AutomationProxy::~AutomationProxy() {
119   // Destruction order is important. Thread has to outlive the channel and
120   // tracker has to outlive the thread since we access the tracker inside
121   // AutomationMessageFilter::OnMessageReceived.
122   Disconnect();
123   thread_.reset();
124   tracker_.reset();
125 }
126
127 std::string AutomationProxy::GenerateChannelID() {
128   // The channel counter keeps us out of trouble if we create and destroy
129   // several AutomationProxies sequentially over the course of a test run.
130   // (Creating the channel sometimes failed before when running a lot of
131   // tests in sequence, and our theory is that sometimes the channel ID
132   // wasn't getting freed up in time for the next test.)
133   static int channel_counter = 0;
134
135   std::ostringstream buf;
136   buf << "ChromeTestingInterface:" << base::GetCurrentProcId() <<
137          "." << ++channel_counter;
138   return buf.str();
139 }
140
141 void AutomationProxy::InitializeThread() {
142   scoped_ptr<base::Thread> thread(
143       new base::Thread("AutomationProxy_BackgroundThread"));
144   base::Thread::Options options;
145   options.message_loop_type = base::MessageLoop::TYPE_IO;
146   bool thread_result = thread->StartWithOptions(options);
147   DCHECK(thread_result);
148   thread_.swap(thread);
149 }
150
151 void AutomationProxy::InitializeChannel(const std::string& channel_id,
152                                         bool use_named_interface) {
153   DCHECK(shutdown_event_.get() != NULL);
154
155   // TODO(iyengar)
156   // The shutdown event could be global on the same lines as the automation
157   // provider, where we use the shutdown event provided by the chrome browser
158   // process.
159   channel_.reset(new IPC::SyncChannel(this,  // we are the listener
160                                       thread_->message_loop_proxy().get(),
161                                       shutdown_event_.get()));
162   channel_->AddFilter(new AutomationMessageFilter(this));
163
164   // Create the pipe synchronously so that Chrome doesn't try to connect to an
165   // unready server. Note this is done after adding a message filter to
166   // guarantee that it doesn't miss any messages when we are the client.
167   // See crbug.com/102894.
168   channel_->Init(
169       channel_id,
170       use_named_interface ? IPC::Channel::MODE_NAMED_CLIENT
171                           : IPC::Channel::MODE_SERVER,
172       true /* create_pipe_now */);
173 }
174
175 void AutomationProxy::InitializeHandleTracker() {
176   tracker_.reset(new AutomationHandleTracker());
177 }
178
179 AutomationLaunchResult AutomationProxy::WaitForAppLaunch() {
180   AutomationLaunchResult result = AUTOMATION_SUCCESS;
181   if (app_launched_.TimedWait(action_timeout_)) {
182     if (server_version_ == kChannelErrorVersionString) {
183       result = AUTOMATION_CHANNEL_ERROR;
184     } else if (perform_version_check_) {
185       // Obtain our own version number and compare it to what the automation
186       // provider sent.
187       chrome::VersionInfo version_info;
188       DCHECK(version_info.is_valid());
189
190       // Note that we use a simple string comparison since we expect the version
191       // to be a punctuated numeric string. Consider using base/Version if we
192       // ever need something more complicated here.
193       if (server_version_ != version_info.Version()) {
194         result = AUTOMATION_VERSION_MISMATCH;
195       }
196     }
197   } else {
198     result = AUTOMATION_TIMEOUT;
199   }
200   return result;
201 }
202
203 void AutomationProxy::SignalAppLaunch(const std::string& version_string) {
204   server_version_ = version_string;
205   app_launched_.Signal();
206 }
207
208 bool AutomationProxy::WaitForProcessLauncherThreadToGoIdle() {
209   return Send(new AutomationMsg_WaitForProcessLauncherThreadToGoIdle());
210 }
211
212 bool AutomationProxy::WaitForInitialLoads() {
213   return initial_loads_complete_.TimedWait(action_timeout_);
214 }
215
216 bool AutomationProxy::WaitForInitialNewTabUILoad(int* load_time) {
217   if (new_tab_ui_load_complete_.TimedWait(action_timeout_)) {
218     *load_time = new_tab_ui_load_time_;
219     new_tab_ui_load_complete_.Reset();
220     return true;
221   }
222   return false;
223 }
224
225 void AutomationProxy::SignalInitialLoads() {
226   initial_loads_complete_.Signal();
227 }
228
229 void AutomationProxy::SignalNewTabUITab(int load_time) {
230   new_tab_ui_load_time_ = load_time;
231   new_tab_ui_load_complete_.Signal();
232 }
233
234 bool AutomationProxy::GetBrowserWindowCount(int* num_windows) {
235   if (!num_windows) {
236     NOTREACHED();
237     return false;
238   }
239
240   return Send(new AutomationMsg_BrowserWindowCount(num_windows));
241 }
242
243 bool AutomationProxy::GetNormalBrowserWindowCount(int* num_windows) {
244   if (!num_windows) {
245     NOTREACHED();
246     return false;
247   }
248
249   return Send(new AutomationMsg_NormalBrowserWindowCount(num_windows));
250 }
251
252 bool AutomationProxy::WaitForWindowCountToBecome(int count) {
253   bool wait_success = false;
254   if (!Send(new AutomationMsg_WaitForBrowserWindowCountToBecome(
255                 count, &wait_success))) {
256     return false;
257   }
258   return wait_success;
259 }
260
261 bool AutomationProxy::IsURLDisplayed(GURL url) {
262   int window_count;
263   if (!GetBrowserWindowCount(&window_count))
264     return false;
265
266   for (int i = 0; i < window_count; i++) {
267     scoped_refptr<BrowserProxy> window = GetBrowserWindow(i);
268     if (!window.get())
269       break;
270
271     int tab_count;
272     if (!window->GetTabCount(&tab_count))
273       continue;
274
275     for (int j = 0; j < tab_count; j++) {
276       scoped_refptr<TabProxy> tab = window->GetTab(j);
277       if (!tab.get())
278         break;
279
280       GURL tab_url;
281       if (!tab->GetCurrentURL(&tab_url))
282         continue;
283
284       if (tab_url == url)
285         return true;
286     }
287   }
288
289   return false;
290 }
291
292 bool AutomationProxy::GetMetricEventDuration(const std::string& event_name,
293                                              int* duration_ms) {
294   return Send(new AutomationMsg_GetMetricEventDuration(event_name,
295                                                        duration_ms));
296 }
297
298 bool AutomationProxy::SendProxyConfig(const std::string& new_proxy_config) {
299   return Send(new AutomationMsg_SetProxyConfig(new_proxy_config));
300 }
301
302 void AutomationProxy::Disconnect() {
303   DCHECK(shutdown_event_.get() != NULL);
304   shutdown_event_->Signal();
305   channel_.reset();
306 }
307
308 bool AutomationProxy::OnMessageReceived(const IPC::Message& msg) {
309   // This won't get called unless AutomationProxy is run from
310   // inside a message loop.
311   NOTREACHED();
312   return false;
313 }
314
315 void AutomationProxy::OnChannelError() {
316   LOG(ERROR) << "Channel error in AutomationProxy.";
317   if (disconnect_on_failure_)
318     Disconnect();
319 }
320
321 scoped_refptr<BrowserProxy> AutomationProxy::GetBrowserWindow(
322     int window_index) {
323   int handle = 0;
324   if (!Send(new AutomationMsg_BrowserWindow(window_index, &handle)))
325     return NULL;
326
327   return ProxyObjectFromHandle<BrowserProxy>(handle);
328 }
329
330 IPC::SyncChannel* AutomationProxy::channel() {
331   return channel_.get();
332 }
333
334 bool AutomationProxy::Send(IPC::Message* message) {
335   return Send(message,
336     static_cast<int>(action_timeout_.InMilliseconds()));
337 }
338
339 bool AutomationProxy::Send(IPC::Message* message, int timeout_ms) {
340   if (!channel_.get()) {
341     LOG(ERROR) << "Automation channel has been closed; dropping message!";
342     delete message;
343     return false;
344   }
345
346   bool success = channel_->SendWithTimeout(message, timeout_ms);
347
348   if (!success && disconnect_on_failure_) {
349     // Send failed (possibly due to a timeout). Browser is likely in a weird
350     // state, and further IPC requests are extremely likely to fail (possibly
351     // timeout, which would make tests slower). Disconnect the channel now
352     // to avoid the slowness.
353     channel_disconnected_on_failure_ = true;
354     LOG(ERROR) << "Disconnecting channel after error!";
355     Disconnect();
356   }
357
358   return success;
359 }
360
361 void AutomationProxy::InvalidateHandle(const IPC::Message& message) {
362   PickleIterator iter(message);
363   int handle;
364
365   if (message.ReadInt(&iter, &handle)) {
366     tracker_->InvalidateHandle(handle);
367   }
368 }
369
370 bool AutomationProxy::OpenNewBrowserWindow(Browser::Type type, bool show) {
371   return Send(
372       new AutomationMsg_OpenNewBrowserWindowOfType(static_cast<int>(type),
373                                                    show));
374 }
375
376 template <class T> scoped_refptr<T> AutomationProxy::ProxyObjectFromHandle(
377     int handle) {
378   if (!handle)
379     return NULL;
380
381   // Get AddRef-ed pointer to the object if handle is already seen.
382   T* p = static_cast<T*>(tracker_->GetResource(handle));
383   if (!p) {
384     p = new T(this, tracker_.get(), handle);
385     p->AddRef();
386   }
387
388   // Since there is no scoped_refptr::attach.
389   scoped_refptr<T> result;
390   result.swap(&p);
391   return result;
392 }
393
394 void AutomationProxy::SetChannel(IPC::Channel* channel) {
395   if (tracker_.get())
396     tracker_->put_channel(channel);
397 }
398
399 void AutomationProxy::ResetChannel() {
400   if (tracker_.get())
401     tracker_->put_channel(NULL);
402 }
403
404 bool AutomationProxy::BeginTracing(const std::string& category_patterns) {
405   bool result = false;
406   bool send_success = Send(new AutomationMsg_BeginTracing(category_patterns,
407                                                           &result));
408   return send_success && result;
409 }
410
411 bool AutomationProxy::EndTracing(std::string* json_trace_output) {
412   bool success = false;
413   size_t num_trace_chunks = 0;
414   if (!Send(new AutomationMsg_EndTracing(&num_trace_chunks, &success)) ||
415       !success)
416     return false;
417
418   std::string chunk;
419   base::debug::TraceResultBuffer buffer;
420   base::debug::TraceResultBuffer::SimpleOutput output;
421   buffer.SetOutputCallback(output.GetCallback());
422
423   // TODO(jbates): See bug 100255, IPC send fails if message is too big. This
424   // code can be simplified if that limitation is fixed.
425   // Workaround IPC payload size limitation by getting chunks.
426   buffer.Start();
427   for (size_t i = 0; i < num_trace_chunks; ++i) {
428     // The broswer side AutomationProvider resets state at BeginTracing,
429     // so it can recover even after this fails mid-way.
430     if (!Send(new AutomationMsg_GetTracingOutput(&chunk, &success)) ||
431         !success)
432       return false;
433     buffer.AddFragment(chunk);
434   }
435   buffer.Finish();
436
437   *json_trace_output = output.json_output;
438   return true;
439 }
440
441 bool AutomationProxy::SendJSONRequest(const std::string& request,
442                                       int timeout_ms,
443                                       std::string* response) {
444   bool result = false;
445   if (!Send(new AutomationMsg_SendJSONRequest(-1, request, response, &result),
446             timeout_ms))
447     return false;
448   return result;
449 }