Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / test / automation / automation_proxy.cc
blobf82f8fd48a5d79e8fe3e9f67973a3272c59856fb
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/file_util.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 "ipc/ipc_descriptors.h"
21 #if defined(OS_WIN)
22 // TODO(port): Enable when dialog_delegate is ported.
23 #include "ui/views/window/dialog_delegate.h"
24 #endif
26 using base::TimeDelta;
27 using base::TimeTicks;
29 namespace {
31 const char kChannelErrorVersionString[] = "***CHANNEL_ERROR***";
33 // This object allows messages received on the background thread to be
34 // properly triaged.
35 class AutomationMessageFilter : public IPC::ChannelProxy::MessageFilter {
36 public:
37 explicit AutomationMessageFilter(AutomationProxy* server) : server_(server) {}
39 // Return true to indicate that the message was handled, or false to let
40 // the message be handled in the default way.
41 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
42 bool handled = true;
43 IPC_BEGIN_MESSAGE_MAP(AutomationMessageFilter, message)
44 IPC_MESSAGE_HANDLER_GENERIC(AutomationMsg_Hello,
45 OnAutomationHello(message))
46 IPC_MESSAGE_HANDLER_GENERIC(
47 AutomationMsg_InitialLoadsComplete, server_->SignalInitialLoads())
48 IPC_MESSAGE_HANDLER(AutomationMsg_InitialNewTabUILoadComplete,
49 NewTabLoaded)
50 IPC_MESSAGE_HANDLER_GENERIC(
51 AutomationMsg_InvalidateHandle, server_->InvalidateHandle(message))
52 IPC_MESSAGE_UNHANDLED(handled = false)
53 IPC_END_MESSAGE_MAP()
55 return handled;
58 virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE {
59 server_->SetChannel(channel);
62 virtual void OnFilterRemoved() OVERRIDE {
63 server_->ResetChannel();
66 virtual void OnChannelError() OVERRIDE {
67 server_->SignalAppLaunch(kChannelErrorVersionString);
68 server_->SignalNewTabUITab(-1);
71 private:
72 void NewTabLoaded(int load_time) {
73 server_->SignalNewTabUITab(load_time);
76 void OnAutomationHello(const IPC::Message& hello_message) {
77 std::string server_version;
78 PickleIterator iter(hello_message);
79 if (!hello_message.ReadString(&iter, &server_version)) {
80 // We got an AutomationMsg_Hello from an old automation provider
81 // that doesn't send version info. Leave server_version as an empty
82 // string to signal a version mismatch.
83 LOG(ERROR) << "Pre-versioning protocol detected in automation provider.";
86 server_->SignalAppLaunch(server_version);
89 AutomationProxy* server_;
91 DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter);
94 } // anonymous namespace
97 AutomationProxy::AutomationProxy(base::TimeDelta action_timeout,
98 bool disconnect_on_failure)
99 : app_launched_(true, false),
100 initial_loads_complete_(true, false),
101 new_tab_ui_load_complete_(true, false),
102 shutdown_event_(new base::WaitableEvent(true, false)),
103 perform_version_check_(false),
104 disconnect_on_failure_(disconnect_on_failure),
105 channel_disconnected_on_failure_(false),
106 action_timeout_(action_timeout),
107 listener_thread_id_(0) {
108 // base::WaitableEvent::TimedWait() will choke if we give it a negative value.
109 // Zero also seems unreasonable, since we need to wait for IPC, but at
110 // least it is legal... ;-)
111 DCHECK_GE(action_timeout.InMilliseconds(), 0);
112 listener_thread_id_ = base::PlatformThread::CurrentId();
113 InitializeHandleTracker();
114 InitializeThread();
117 AutomationProxy::~AutomationProxy() {
118 // Destruction order is important. Thread has to outlive the channel and
119 // tracker has to outlive the thread since we access the tracker inside
120 // AutomationMessageFilter::OnMessageReceived.
121 Disconnect();
122 thread_.reset();
123 tracker_.reset();
126 std::string AutomationProxy::GenerateChannelID() {
127 // The channel counter keeps us out of trouble if we create and destroy
128 // several AutomationProxies sequentially over the course of a test run.
129 // (Creating the channel sometimes failed before when running a lot of
130 // tests in sequence, and our theory is that sometimes the channel ID
131 // wasn't getting freed up in time for the next test.)
132 static int channel_counter = 0;
134 std::ostringstream buf;
135 buf << "ChromeTestingInterface:" << base::GetCurrentProcId() <<
136 "." << ++channel_counter;
137 return buf.str();
140 void AutomationProxy::InitializeThread() {
141 scoped_ptr<base::Thread> thread(
142 new base::Thread("AutomationProxy_BackgroundThread"));
143 base::Thread::Options options;
144 options.message_loop_type = base::MessageLoop::TYPE_IO;
145 bool thread_result = thread->StartWithOptions(options);
146 DCHECK(thread_result);
147 thread_.swap(thread);
150 void AutomationProxy::InitializeChannel(const std::string& channel_id,
151 bool use_named_interface) {
152 DCHECK(shutdown_event_.get() != NULL);
154 // TODO(iyengar)
155 // The shutdown event could be global on the same lines as the automation
156 // provider, where we use the shutdown event provided by the chrome browser
157 // process.
158 channel_.reset(new IPC::SyncChannel(this, // we are the listener
159 thread_->message_loop_proxy().get(),
160 shutdown_event_.get()));
161 channel_->AddFilter(new AutomationMessageFilter(this));
163 // Create the pipe synchronously so that Chrome doesn't try to connect to an
164 // unready server. Note this is done after adding a message filter to
165 // guarantee that it doesn't miss any messages when we are the client.
166 // See crbug.com/102894.
167 channel_->Init(
168 channel_id,
169 use_named_interface ? IPC::Channel::MODE_NAMED_CLIENT
170 : IPC::Channel::MODE_SERVER,
171 true /* create_pipe_now */);
174 void AutomationProxy::InitializeHandleTracker() {
175 tracker_.reset(new AutomationHandleTracker());
178 AutomationLaunchResult AutomationProxy::WaitForAppLaunch() {
179 AutomationLaunchResult result = AUTOMATION_SUCCESS;
180 if (app_launched_.TimedWait(action_timeout_)) {
181 if (server_version_ == kChannelErrorVersionString) {
182 result = AUTOMATION_CHANNEL_ERROR;
183 } else if (perform_version_check_) {
184 // Obtain our own version number and compare it to what the automation
185 // provider sent.
186 chrome::VersionInfo version_info;
187 DCHECK(version_info.is_valid());
189 // Note that we use a simple string comparison since we expect the version
190 // to be a punctuated numeric string. Consider using base/Version if we
191 // ever need something more complicated here.
192 if (server_version_ != version_info.Version()) {
193 result = AUTOMATION_VERSION_MISMATCH;
196 } else {
197 result = AUTOMATION_TIMEOUT;
199 return result;
202 void AutomationProxy::SignalAppLaunch(const std::string& version_string) {
203 server_version_ = version_string;
204 app_launched_.Signal();
207 bool AutomationProxy::WaitForProcessLauncherThreadToGoIdle() {
208 return Send(new AutomationMsg_WaitForProcessLauncherThreadToGoIdle());
211 bool AutomationProxy::WaitForInitialLoads() {
212 return initial_loads_complete_.TimedWait(action_timeout_);
215 bool AutomationProxy::WaitForInitialNewTabUILoad(int* load_time) {
216 if (new_tab_ui_load_complete_.TimedWait(action_timeout_)) {
217 *load_time = new_tab_ui_load_time_;
218 new_tab_ui_load_complete_.Reset();
219 return true;
221 return false;
224 void AutomationProxy::SignalInitialLoads() {
225 initial_loads_complete_.Signal();
228 void AutomationProxy::SignalNewTabUITab(int load_time) {
229 new_tab_ui_load_time_ = load_time;
230 new_tab_ui_load_complete_.Signal();
233 bool AutomationProxy::GetBrowserWindowCount(int* num_windows) {
234 if (!num_windows) {
235 NOTREACHED();
236 return false;
239 return Send(new AutomationMsg_BrowserWindowCount(num_windows));
242 bool AutomationProxy::GetNormalBrowserWindowCount(int* num_windows) {
243 if (!num_windows) {
244 NOTREACHED();
245 return false;
248 return Send(new AutomationMsg_NormalBrowserWindowCount(num_windows));
251 bool AutomationProxy::WaitForWindowCountToBecome(int count) {
252 bool wait_success = false;
253 if (!Send(new AutomationMsg_WaitForBrowserWindowCountToBecome(
254 count, &wait_success))) {
255 return false;
257 return wait_success;
260 bool AutomationProxy::IsURLDisplayed(GURL url) {
261 int window_count;
262 if (!GetBrowserWindowCount(&window_count))
263 return false;
265 for (int i = 0; i < window_count; i++) {
266 scoped_refptr<BrowserProxy> window = GetBrowserWindow(i);
267 if (!window.get())
268 break;
270 int tab_count;
271 if (!window->GetTabCount(&tab_count))
272 continue;
274 for (int j = 0; j < tab_count; j++) {
275 scoped_refptr<TabProxy> tab = window->GetTab(j);
276 if (!tab.get())
277 break;
279 GURL tab_url;
280 if (!tab->GetCurrentURL(&tab_url))
281 continue;
283 if (tab_url == url)
284 return true;
288 return false;
291 bool AutomationProxy::GetMetricEventDuration(const std::string& event_name,
292 int* duration_ms) {
293 return Send(new AutomationMsg_GetMetricEventDuration(event_name,
294 duration_ms));
297 void AutomationProxy::Disconnect() {
298 DCHECK(shutdown_event_.get() != NULL);
299 shutdown_event_->Signal();
300 channel_.reset();
303 bool AutomationProxy::OnMessageReceived(const IPC::Message& msg) {
304 // This won't get called unless AutomationProxy is run from
305 // inside a message loop.
306 NOTREACHED();
307 return false;
310 void AutomationProxy::OnChannelError() {
311 LOG(ERROR) << "Channel error in AutomationProxy.";
312 if (disconnect_on_failure_)
313 Disconnect();
316 scoped_refptr<BrowserProxy> AutomationProxy::GetBrowserWindow(
317 int window_index) {
318 int handle = 0;
319 if (!Send(new AutomationMsg_BrowserWindow(window_index, &handle)))
320 return NULL;
322 return ProxyObjectFromHandle<BrowserProxy>(handle);
325 IPC::SyncChannel* AutomationProxy::channel() {
326 return channel_.get();
329 bool AutomationProxy::Send(IPC::Message* message) {
330 return Send(message,
331 static_cast<int>(action_timeout_.InMilliseconds()));
334 bool AutomationProxy::Send(IPC::Message* message, int timeout_ms) {
335 if (!channel_.get()) {
336 LOG(ERROR) << "Automation channel has been closed; dropping message!";
337 delete message;
338 return false;
341 bool success = channel_->SendWithTimeout(message, timeout_ms);
343 if (!success && disconnect_on_failure_) {
344 // Send failed (possibly due to a timeout). Browser is likely in a weird
345 // state, and further IPC requests are extremely likely to fail (possibly
346 // timeout, which would make tests slower). Disconnect the channel now
347 // to avoid the slowness.
348 channel_disconnected_on_failure_ = true;
349 LOG(ERROR) << "Disconnecting channel after error!";
350 Disconnect();
353 return success;
356 void AutomationProxy::InvalidateHandle(const IPC::Message& message) {
357 PickleIterator iter(message);
358 int handle;
360 if (message.ReadInt(&iter, &handle)) {
361 tracker_->InvalidateHandle(handle);
365 bool AutomationProxy::OpenNewBrowserWindow(Browser::Type type, bool show) {
366 return Send(
367 new AutomationMsg_OpenNewBrowserWindowOfType(static_cast<int>(type),
368 show));
371 template <class T> scoped_refptr<T> AutomationProxy::ProxyObjectFromHandle(
372 int handle) {
373 if (!handle)
374 return NULL;
376 // Get AddRef-ed pointer to the object if handle is already seen.
377 T* p = static_cast<T*>(tracker_->GetResource(handle));
378 if (!p) {
379 p = new T(this, tracker_.get(), handle);
380 p->AddRef();
383 // Since there is no scoped_refptr::attach.
384 scoped_refptr<T> result;
385 result.swap(&p);
386 return result;
389 void AutomationProxy::SetChannel(IPC::Channel* channel) {
390 if (tracker_.get())
391 tracker_->put_channel(channel);
394 void AutomationProxy::ResetChannel() {
395 if (tracker_.get())
396 tracker_->put_channel(NULL);
399 bool AutomationProxy::BeginTracing(const std::string& category_patterns) {
400 bool result = false;
401 bool send_success = Send(new AutomationMsg_BeginTracing(category_patterns,
402 &result));
403 return send_success && result;
406 bool AutomationProxy::EndTracing(std::string* json_trace_output) {
407 bool success = false;
408 base::FilePath path;
409 if (!Send(new AutomationMsg_EndTracing(&path, &success)) || !success)
410 return false;
412 bool ok = base::ReadFileToString(path, json_trace_output);
413 DCHECK(ok);
414 base::DeleteFile(path, false);
415 return true;
418 bool AutomationProxy::SendJSONRequest(const std::string& request,
419 int timeout_ms,
420 std::string* response) {
421 bool result = false;
422 if (!Send(new AutomationMsg_SendJSONRequest(-1, request, response, &result),
423 timeout_ms))
424 return false;
425 return result;