summaryrefslogtreecommitdiffstats
path: root/chrome/test/automation/automation_proxy.cc
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/test/automation/automation_proxy.cc
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test/automation/automation_proxy.cc')
-rw-r--r--chrome/test/automation/automation_proxy.cc567
1 files changed, 567 insertions, 0 deletions
diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc
new file mode 100644
index 0000000..07e5387
--- /dev/null
+++ b/chrome/test/automation/automation_proxy.cc
@@ -0,0 +1,567 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <sstream>
+
+#include "chrome/test/automation/automation_proxy.h"
+
+#include "base/logging.h"
+#include "base/ref_counted.h"
+#include "chrome/common/ipc_message_macros.h"
+#include "chrome/test/automation/autocomplete_edit_proxy.h"
+#include "chrome/test/automation/automation_constants.h"
+#include "chrome/test/automation/automation_messages.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/automation/window_proxy.h"
+
+// This class exists to group together the data and functionality used for
+// synchronous automation requests.
+class AutomationRequest :
+ public base::RefCountedThreadSafe<AutomationRequest> {
+public:
+ AutomationRequest() {
+ static int32 routing_id = 0;
+ routing_id_ = ++routing_id;
+ received_response_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ DCHECK(received_response_);
+ }
+ ~AutomationRequest() {
+ DCHECK(received_response_);
+ ::CloseHandle(received_response_);
+ }
+
+ // This is called on the foreground thread to block while waiting for a
+ // response from the app.
+ // The function returns true if response is received, and returns false
+ // if there is a failure or timeout.
+ bool WaitForResponse(uint32 timeout_ms, bool* is_timeout) {
+ uint32 result = ::WaitForSingleObject(received_response_, timeout_ms);
+ if (is_timeout)
+ *is_timeout = (result == WAIT_TIMEOUT);
+
+ return result != WAIT_FAILED && result != WAIT_TIMEOUT;
+ }
+
+ // This is called on the background thread once the response has been
+ // received and the foreground thread can resume execution.
+ bool SignalResponseReady(const IPC::Message& response) {
+ response_.reset(new IPC::Message(response));
+
+ DCHECK(received_response_);
+ return !!::SetEvent(received_response_);
+ }
+
+ // This can be used to take ownership of the response object that
+ // we've received, reducing the need to copy the message.
+ void GrabResponse(IPC::Message** response) {
+ DCHECK(response);
+ *response = response_.get();
+ response_.release();
+ }
+
+ int32 routing_id() { return routing_id_; }
+
+ const IPC::Message& response() {
+ DCHECK(response_.get());
+ return *(response_.get());
+ }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(AutomationRequest);
+
+ int32 routing_id_;
+ scoped_ptr<IPC::Message> response_;
+ HANDLE received_response_;
+};
+
+namespace {
+
+// This object allows messages received on the background thread to be
+// properly triaged.
+class AutomationMessageFilter : public IPC::ChannelProxy::MessageFilter {
+ public:
+ AutomationMessageFilter(AutomationProxy* server) : server_(server) {}
+
+ // Return true to indicate that the message was handled, or false to let
+ // the message be handled in the default way.
+ virtual bool OnMessageReceived(const IPC::Message& message) {
+ {
+ // Here we're checking to see if it matches the (because there should
+ // be at most one) synchronous request. If the received message is
+ // the response to the synchronous request, we clear the server's
+ // "current_request" pointer and signal to the request object that
+ // the response is ready for processing. We're clearing current_request
+ // here rather than on the foreground thread so that we can assert
+ // that both threads perceive it as cleared at the time that the
+ // foreground thread wakes up.
+ scoped_refptr<AutomationRequest> request = server_->current_request();
+ if (request.get() && (message.routing_id() == request->routing_id())) {
+ server_->clear_current_request();
+ request->SignalResponseReady(message);
+ return true;
+ }
+ if (message.routing_id() > 0) {
+ // The message is either the previous async response or arrived
+ // after timeout.
+ return true;
+ }
+ }
+
+ bool handled = true;
+
+ IPC_BEGIN_MESSAGE_MAP(AutomationMessageFilter, message)
+ IPC_MESSAGE_HANDLER_GENERIC(
+ AutomationMsg_Hello, server_->SignalAppLaunch());
+ IPC_MESSAGE_HANDLER_GENERIC(
+ AutomationMsg_InitialLoadsComplete, server_->SignalInitialLoads());
+ IPC_MESSAGE_HANDLER(AutomationMsg_InitialNewTabUILoadComplete,
+ NewTabLoaded);
+ IPC_MESSAGE_HANDLER_GENERIC(
+ AutomationMsg_InvalidateHandle, server_->InvalidateHandle(message));
+ IPC_MESSAGE_UNHANDLED(handled = false);
+ IPC_END_MESSAGE_MAP()
+
+ return handled;
+ }
+
+ void NewTabLoaded(int load_time) {
+ server_->SignalNewTabUITab(load_time);
+ }
+
+ private:
+ AutomationProxy* server_;
+};
+
+} // anonymous namespace
+
+
+const int AutomationProxy::kMaxCommandExecutionTime = 30000;
+
+AutomationProxy::AutomationProxy() : current_request_(NULL) {
+ InitializeEvents();
+ InitializeChannelID();
+ InitializeThread();
+ InitializeChannel();
+ InitializeHandleTracker();
+}
+
+AutomationProxy::~AutomationProxy() {
+}
+
+void AutomationProxy::InitializeEvents() {
+ app_launched_ =
+ CreateEvent(NULL, // Handle cannot be inherited by child processes.
+ TRUE, // No automatic reset after a waiting thread released.
+ FALSE, // Initially not signalled.
+ NULL); // No name.
+ DCHECK(app_launched_);
+
+ // See the above call to CreateEvent to understand these parameters.
+ initial_loads_complete_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+ DCHECK(initial_loads_complete_);
+
+ // See the above call to CreateEvent to understand these parameters.
+ new_tab_ui_load_complete_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+ DCHECK(new_tab_ui_load_complete_);
+}
+
+void AutomationProxy::InitializeChannelID() {
+ // The channel counter keeps us out of trouble if we create and destroy
+ // several AutomationProxies sequentially over the course of a test run.
+ // (Creating the channel sometimes failed before when running a lot of
+ // tests in sequence, and our theory is that sometimes the channel ID
+ // wasn't getting freed up in time for the next test.)
+ static int channel_counter = 0;
+
+ std::wostringstream buf;
+ buf << L"ChromeTestingInterface:" << GetCurrentProcessId() <<
+ L"." << ++channel_counter;
+ channel_id_ = buf.str();
+}
+
+void AutomationProxy::InitializeThread() {
+ scoped_ptr<Thread> thread(new Thread("AutomationProxy_BackgroundThread"));
+ bool thread_result = thread->Start();
+ DCHECK(thread_result);
+ thread_.swap(thread);
+}
+
+void AutomationProxy::InitializeChannel() {
+ channel_.reset(new IPC::ChannelProxy(
+ channel_id_,
+ IPC::Channel::MODE_SERVER,
+ this, // we are the listener
+ new AutomationMessageFilter(this),
+ thread_->message_loop()));
+}
+
+void AutomationProxy::InitializeHandleTracker() {
+ tracker_.reset(new AutomationHandleTracker(this));
+}
+
+bool AutomationProxy::WaitForAppLaunch() {
+ return ::WaitForSingleObject(app_launched_,
+ kMaxCommandExecutionTime) == WAIT_OBJECT_0;
+}
+
+void AutomationProxy::SignalAppLaunch() {
+ ::SetEvent(app_launched_);
+}
+
+bool AutomationProxy::WaitForInitialLoads() {
+ return ::WaitForSingleObject(initial_loads_complete_,
+ kMaxCommandExecutionTime) == WAIT_OBJECT_0;
+}
+
+bool AutomationProxy::WaitForInitialNewTabUILoad(int* load_time) {
+ if (::WaitForSingleObject(new_tab_ui_load_complete_,
+ kMaxCommandExecutionTime) == WAIT_OBJECT_0) {
+ *load_time = new_tab_ui_load_time_;
+ ::ResetEvent(new_tab_ui_load_complete_);
+ return true;
+ }
+ return false;
+}
+
+void AutomationProxy::SignalInitialLoads() {
+ ::SetEvent(initial_loads_complete_);
+}
+
+void AutomationProxy::SignalNewTabUITab(int load_time) {
+ new_tab_ui_load_time_ = load_time;
+ ::SetEvent(new_tab_ui_load_complete_);
+}
+
+bool AutomationProxy::GetBrowserWindowCount(int* num_windows) {
+ if (!num_windows) {
+ NOTREACHED();
+ return false;
+ }
+
+ IPC::Message* response = NULL;
+ bool is_timeout = true;
+ bool succeeded = SendAndWaitForResponseWithTimeout(
+ new AutomationMsg_BrowserWindowCountRequest(0), &response,
+ AutomationMsg_BrowserWindowCountResponse::ID,
+ kMaxCommandExecutionTime, &is_timeout);
+ if (!succeeded)
+ return false;
+
+ if (is_timeout) {
+ DLOG(ERROR) << "GetWindowCount did not complete in a timely fashion";
+ return false;
+ }
+
+ void* iter = NULL;
+ if (!response->ReadInt(&iter, num_windows)) {
+ succeeded = false;
+ }
+
+ delete response;
+ return succeeded;
+}
+
+bool AutomationProxy::WaitForWindowCountToChange(int count, int* new_count,
+ int wait_timeout) {
+ const TimeTicks start = TimeTicks::Now();
+ const TimeDelta timeout = TimeDelta::FromMilliseconds(wait_timeout);
+ while (TimeTicks::Now() - start < timeout) {
+ bool succeeded = GetBrowserWindowCount(new_count);
+ if (!succeeded) return false;
+ if (count != *new_count) return true;
+ Sleep(automation::kSleepTime);
+ }
+ // Window count never changed.
+ return false;
+}
+
+bool AutomationProxy::WaitForWindowCountToBecome(int count,
+ int wait_timeout) {
+ const TimeTicks start = TimeTicks::Now();
+ const TimeDelta timeout = TimeDelta::FromMilliseconds(wait_timeout);
+ while (TimeTicks::Now() - start < timeout) {
+ int new_count;
+ bool succeeded = GetBrowserWindowCount(&new_count);
+ if (!succeeded) {
+ // Try again next round, but log it.
+ DLOG(ERROR) << "GetBrowserWindowCount returned false";
+ } else if (count == new_count) {
+ return true;
+ }
+ Sleep(automation::kSleepTime);
+ }
+ // Window count never reached the value we sought.
+ return false;
+}
+
+bool AutomationProxy::SetFilteredInet(bool enabled) {
+ return Send(new AutomationMsg_SetFilteredInet(0, enabled));
+}
+
+void AutomationProxy::OnMessageReceived(const IPC::Message& msg) {
+ // This won't get called unless AutomationProxy is run from
+ // inside a message loop.
+ NOTREACHED();
+}
+
+void AutomationProxy::OnChannelError() {
+ DLOG(ERROR) << "Channel error in AutomationProxy.";
+}
+
+WindowProxy* AutomationProxy::GetActiveWindow() {
+ IPC::Message* response = NULL;
+ bool is_timeout = true;
+ bool succeeded = SendAndWaitForResponseWithTimeout(
+ new AutomationMsg_ActiveWindowRequest(0), &response,
+ AutomationMsg_ActiveWindowResponse::ID,
+ kMaxCommandExecutionTime, &is_timeout);
+ if (!succeeded)
+ return NULL;
+
+ scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit.
+ if (is_timeout) {
+ DLOG(ERROR) << "GetActiveWindow did not complete in a timely fashion";
+ return NULL;
+ }
+
+ void* iter = NULL;
+ int handle;
+ if (response->ReadInt(&iter, &handle) && (handle != 0))
+ return new WindowProxy(this, tracker_.get(), handle);
+
+ return NULL;
+}
+
+
+BrowserProxy* AutomationProxy::GetBrowserWindow(int window_index) {
+ IPC::Message* response;
+ bool is_timeout = true;
+ bool succeeded = SendAndWaitForResponseWithTimeout(
+ new AutomationMsg_BrowserWindowRequest(0, window_index), &response,
+ AutomationMsg_BrowserWindowResponse::ID,
+ kMaxCommandExecutionTime, &is_timeout);
+ if (!succeeded)
+ return NULL;
+
+ scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit.
+ if (is_timeout) {
+ DLOG(ERROR) << "GetBrowserWindow did not complete in a timely fashion";
+ return NULL;
+ }
+
+ void* iter = NULL;
+ int handle;
+ if (!response->ReadInt(&iter, &handle) || (handle == 0)) {
+ DLOG(ERROR) << "Bad response from the window getter.";
+ return NULL;
+ }
+ return new BrowserProxy(this, tracker_.get(), handle);
+}
+
+BrowserProxy* AutomationProxy::GetLastActiveBrowserWindow() {
+ IPC::Message* response;
+ bool is_timeout = true;
+ bool succeeded = SendAndWaitForResponseWithTimeout(
+ new AutomationMsg_LastActiveBrowserWindowRequest(0),
+ &response, AutomationMsg_LastActiveBrowserWindowResponse::ID,
+ kMaxCommandExecutionTime, &is_timeout);
+ if (!succeeded)
+ return NULL;
+
+ scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit.
+ if (is_timeout) {
+ DLOG(ERROR) << "GetLastActiveBrowserWindow did not complete in a timely fashion";
+ return NULL;
+ }
+
+ void* iter = NULL;
+ int handle;
+ if (!response->ReadInt(&iter, &handle) || (handle == 0)) {
+ DLOG(ERROR) << "Bad response from the window getter.";
+ return NULL;
+ }
+ return new BrowserProxy(this, tracker_.get(), handle);
+}
+
+BrowserProxy* AutomationProxy::GetBrowserForWindow(WindowProxy* window) {
+ return GetBrowserForWindowWithTimeout(window, INFINITE, NULL);
+}
+
+BrowserProxy* AutomationProxy::GetBrowserForWindowWithTimeout(
+ WindowProxy* window, uint32 timeout_ms, bool* is_timeout) {
+ DCHECK(window);
+ if (!window->is_valid() || !window->handle())
+ return false;
+
+ IPC::Message* response = NULL;
+ bool succeeded = SendAndWaitForResponseWithTimeout(
+ new AutomationMsg_BrowserForWindowRequest(0, window->handle()), &response,
+ AutomationMsg_BrowserForWindowResponse::ID, timeout_ms, is_timeout);
+ if (!succeeded)
+ return NULL;
+
+ scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit.
+ int browser_handle = 0;
+ void* iter = NULL;
+ bool handle_ok;
+ succeeded = response->ReadBool(&iter, &handle_ok);
+ if (succeeded)
+ succeeded = response->ReadInt(&iter, &browser_handle);
+
+ if (succeeded) {
+ return new BrowserProxy(this, tracker_.get(), browser_handle);
+ } else {
+ return NULL;
+ }
+}
+
+WindowProxy* AutomationProxy::GetWindowForBrowser(BrowserProxy* browser) {
+ if (!browser->is_valid() || !browser->handle())
+ return false;
+
+ IPC::Message* response = NULL;
+ bool succeeded = SendAndWaitForResponse(
+ new AutomationMsg_WindowForBrowserRequest(0, browser->handle()), &response,
+ AutomationMsg_WindowForBrowserResponse::ID);
+ if (!succeeded)
+ return NULL;
+
+ scoped_ptr<IPC::Message> response_deleter(response); // Delete on exit.
+ int window_handle;
+ void* iter = NULL;
+ bool handle_ok;
+ succeeded = response->ReadBool(&iter, &handle_ok);
+ if (succeeded)
+ succeeded = response->ReadInt(&iter, &window_handle);
+
+ if (succeeded) {
+ return new WindowProxy(this, tracker_.get(), window_handle);
+ } else {
+ return NULL;
+ }
+}
+
+AutocompleteEditProxy* AutomationProxy::GetAutocompleteEditForBrowser(
+ BrowserProxy* browser) {
+ if (!browser->is_valid() || !browser->handle())
+ return NULL;
+
+ IPC::Message* response = NULL;
+ if (!SendAndWaitForResponse(
+ new AutomationMsg_AutocompleteEditForBrowserRequest(0, browser->handle()),
+ &response, AutomationMsg_AutocompleteEditForBrowserResponse::ID))
+ return NULL;
+ scoped_ptr<IPC::Message> response_deleter(response);
+
+ int autocomplete_edit_handle;
+ void* iter = NULL;
+ bool handle_ok;
+ if (!response->ReadBool(&iter, &handle_ok) ||
+ !response->ReadInt(&iter, &autocomplete_edit_handle))
+ return NULL;
+
+ return new AutocompleteEditProxy(this, tracker_.get(),
+ autocomplete_edit_handle);
+}
+
+bool AutomationProxy::SendAndWaitForResponse(IPC::Message* request,
+ IPC::Message** response,
+ int response_type) {
+ return SendAndWaitForResponseWithTimeout(request, response, response_type,
+ INFINITE, NULL);
+}
+
+bool AutomationProxy::SendAndWaitForResponseWithTimeout(
+ IPC::Message* request,
+ IPC::Message** response,
+ int response_type,
+ uint32 timeout_ms,
+ bool* is_timeout) {
+
+ DCHECK(request);
+ DCHECK(response);
+ DCHECK(!current_request_) <<
+ "Only one synchronous request should exist at any given time.";
+
+ scoped_refptr<AutomationRequest> req = new AutomationRequest;
+ current_request_ = req;
+
+ // Rewrite the message's routing ID so that we'll recognize the response
+ // to it when it comes back.
+ request->set_routing_id(req->routing_id());
+ bool result = Send(request) && req->WaitForResponse(timeout_ms, is_timeout);
+ if (!result || req->response().type() != response_type) {
+ // If Send() or WaitForResponse() failed, current_request_ may not have
+ // gotten cleared by the background thread, so we'll clear it here.
+ current_request_ = NULL;
+ return false;
+ }
+ req->GrabResponse(response);
+
+ return true;
+}
+
+void AutomationProxy::InvalidateHandle(const IPC::Message& message) {
+ void* iter = NULL;
+ int handle;
+
+ if (message.ReadInt(&iter, &handle)) {
+ tracker_->InvalidateHandle(handle);
+ }
+}
+
+bool AutomationProxy::OpenNewBrowserWindow(int show_command) {
+ return Send(new AutomationMsg_OpenNewBrowserWindow(0, show_command));
+}
+
+TabProxy* AutomationProxy::CreateExternalTab(HWND* external_tab_container) {
+ IPC::Message* response = NULL;
+ bool succeeded = SendAndWaitForResponse(
+ new AutomationMsg_CreateExternalTab(0), &response,
+ AutomationMsg_CreateExternalTabResponse::ID);
+ if (!succeeded) {
+ return NULL;
+ }
+ void* iter = NULL;
+ int handle = 0;
+ TabProxy* tab_proxy = NULL;
+ if (IPC::ReadParam(response, &iter, external_tab_container) &&
+ IsWindow(*external_tab_container)) {
+ if (response->ReadInt(&iter, &handle) &&
+ (handle >= 0)) {
+ succeeded = true;
+ tab_proxy = new TabProxy(this, tracker_.get(), handle);
+ }
+ } else {
+ succeeded = false;
+ }
+ delete response;
+ return tab_proxy;
+}