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