From c6cb199d2d1d67af63d7227eff77e0a4163629fd Mon Sep 17 00:00:00 2001 From: "robertshield@google.com" Date: Mon, 13 Apr 2009 16:45:29 +0000 Subject: Adding versioning support to the chrome automation protocol. Review URL: http://codereview.chromium.org/62061 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13598 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/automation/automation_provider.cc | 8 ++- chrome/test/automation/automation_constants.h | 9 +++ .../test/automation/automation_messages_internal.h | 9 ++- chrome/test/automation/automation_proxy.cc | 70 ++++++++++++++++++---- chrome/test/automation/automation_proxy.h | 37 +++++++++++- 5 files changed, 116 insertions(+), 17 deletions(-) (limited to 'chrome') diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 067302a..9dee930 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -4,6 +4,7 @@ #include "chrome/browser/automation/automation_provider.h" +#include "base/file_version_info.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/string_util.h" @@ -774,7 +775,12 @@ void AutomationProvider::ConnectToChannel(const std::wstring& channel_id) { new IPC::SyncChannel(channel_id, IPC::Channel::MODE_CLIENT, this, NULL, g_browser_process->io_thread()->message_loop(), true, g_browser_process->shutdown_event())); - channel_->Send(new AutomationMsg_Hello(0)); + FileVersionInfo* file_version_info = + FileVersionInfo::CreateFileVersionInfoForCurrentModule(); + std::string version_string(WideToASCII(file_version_info->file_version())); + + // Send a hello message with our current automation protocol version. + channel_->Send(new AutomationMsg_Hello(0, version_string.c_str())); } void AutomationProvider::SetExpectedTabCount(size_t expected_tabs) { diff --git a/chrome/test/automation/automation_constants.h b/chrome/test/automation/automation_constants.h index b31139e..06d13e0 100644 --- a/chrome/test/automation/automation_constants.h +++ b/chrome/test/automation/automation_constants.h @@ -10,6 +10,15 @@ namespace automation { static const int kSleepTime = 250; } +// Used by AutomationProxy, declared here so that other headers don't need +// to include automation_proxy.h. +enum AutomationLaunchResult { + AUTOMATION_SUCCESS, + AUTOMATION_TIMEOUT, + AUTOMATION_VERSION_MISMATCH, + AUTOMATION_CREATE_TAB_FAILED +}; + enum AutomationMsg_NavigationResponseValues { AUTOMATION_MSG_NAVIGATION_ERROR = 0, AUTOMATION_MSG_NAVIGATION_SUCCESS, diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h index b954b3f..f6ed6d5 100644 --- a/chrome/test/automation/automation_messages_internal.h +++ b/chrome/test/automation/automation_messages_internal.h @@ -34,8 +34,13 @@ IPC_BEGIN_MESSAGES(Automation) // This message is fired when the AutomationProvider is up and running - // in the app (the app is not fully up at this point). - IPC_MESSAGE_ROUTED0(AutomationMsg_Hello) + // in the app (the app is not fully up at this point). The parameter to this + // message is the version string of the automation provider. This parameter + // is defined to be the version string as returned by + // FileVersionInfo::file_version(). + // The client can choose to use this version string to decide whether or not + // it can talk to the provider. + IPC_MESSAGE_ROUTED1(AutomationMsg_Hello, std::string) // This message is fired when the initial tab(s) are finished loading. IPC_MESSAGE_ROUTED0(AutomationMsg_InitialLoadsComplete) diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc index 416d0c9..5715aac 100644 --- a/chrome/test/automation/automation_proxy.cc +++ b/chrome/test/automation/automation_proxy.cc @@ -7,6 +7,7 @@ #include "chrome/test/automation/automation_proxy.h" #include "base/basictypes.h" +#include "base/file_version_info.h" #include "base/logging.h" #include "base/platform_thread.h" #include "base/process_util.h" @@ -28,9 +29,9 @@ using base::TimeTicks; // This class exists to group together the data and functionality used for // synchronous automation requests. -class AutomationRequest : - public base::RefCountedThreadSafe { -public: +class AutomationRequest + : public base::RefCountedThreadSafe { + public: AutomationRequest() : received_response_(true, false) { static int32 routing_id = 0; routing_id_ = ++routing_id; @@ -59,12 +60,12 @@ public: return *(response_.get()); } -private: - DISALLOW_EVIL_CONSTRUCTORS(AutomationRequest); - + private: int32 routing_id_; scoped_ptr response_; base::WaitableEvent received_response_; + + DISALLOW_EVIL_CONSTRUCTORS(AutomationRequest); }; namespace { @@ -103,8 +104,8 @@ class AutomationMessageFilter : public IPC::ChannelProxy::MessageFilter { bool handled = true; IPC_BEGIN_MESSAGE_MAP(AutomationMessageFilter, message) - IPC_MESSAGE_HANDLER_GENERIC( - AutomationMsg_Hello, server_->SignalAppLaunch()); + IPC_MESSAGE_HANDLER_GENERIC(AutomationMsg_Hello, + OnAutomationHello(message)); IPC_MESSAGE_HANDLER_GENERIC( AutomationMsg_InitialLoadsComplete, server_->SignalInitialLoads()); IPC_MESSAGE_HANDLER(AutomationMsg_InitialNewTabUILoadComplete, @@ -121,6 +122,19 @@ class AutomationMessageFilter : public IPC::ChannelProxy::MessageFilter { server_->SignalNewTabUITab(load_time); } + void OnAutomationHello(const IPC::Message& hello_message) { + std::string server_version; + void* iter = NULL; + if (!hello_message.ReadString(&iter, &server_version)) { + // We got an AutomationMsg_Hello from an old automation provider + // that doesn't send version info. Leave server_version as an empty + // string to signal a version mismatch. + LOG(ERROR) << "Pre-versioning protocol detected in automation provider."; + } + + server_->SignalAppLaunch(server_version); + } + private: AutomationProxy* server_; }; @@ -134,6 +148,8 @@ AutomationProxy::AutomationProxy(int command_execution_timeout_ms) new_tab_ui_load_complete_(true, false), shutdown_event_(new base::WaitableEvent(true, false)), current_request_(NULL), + app_launch_signaled_(0), + perform_version_check_(false), command_execution_timeout_( TimeDelta::FromMilliseconds(command_execution_timeout_ms)) { InitializeChannelID(); @@ -199,11 +215,43 @@ void AutomationProxy::InitializeHandleTracker() { tracker_.reset(new AutomationHandleTracker(this)); } -bool AutomationProxy::WaitForAppLaunch() { - return app_launched_.TimedWait(command_execution_timeout_); +AutomationLaunchResult AutomationProxy::WaitForAppLaunch() { + AutomationLaunchResult result = AUTOMATION_SUCCESS; + if (app_launched_.TimedWait(command_execution_timeout_)) { + if (perform_version_check_) { + // Obtain our own version number and compare it to what the automation + // provider sent. + FileVersionInfo* file_version_info = + FileVersionInfo::CreateFileVersionInfoForCurrentModule(); + DCHECK(file_version_info); + std::string version_string( + WideToASCII(file_version_info->file_version())); + + // Note that we use a simple string comparison since we expect the version + // to be a punctuated numeric string. Consider using base/Version if we + // ever need something more complicated here. + if (server_version_ != version_string) { + result = AUTOMATION_VERSION_MISMATCH; + } + } + } else { + result = AUTOMATION_TIMEOUT; + } + return result; } -void AutomationProxy::SignalAppLaunch() { +void AutomationProxy::SignalAppLaunch(const std::string& version_string) { + // The synchronization of the reading / writing of server_version_ is a bit + // messy but does work as long as SignalAppLaunch is only called once. + // Review this if we ever want an AutomationProxy instance to launch + // multiple AutomationProviders. + app_launch_signaled_++; + if (app_launch_signaled_ > 1) { + NOTREACHED(); + LOG(ERROR) << "Multiple AutomationMsg_Hello messages received"; + return; + } + server_version_ = version_string; app_launched_.Signal(); } diff --git a/chrome/test/automation/automation_proxy.h b/chrome/test/automation/automation_proxy.h index 46a3b23..e229001 100644 --- a/chrome/test/automation/automation_proxy.h +++ b/chrome/test/automation/automation_proxy.h @@ -66,8 +66,13 @@ class AutomationProxy : public IPC::Channel::Listener, // Waits for the app to launch and the automation provider to say hello // (the app isn't fully done loading by this point). - // Returns true if the launch is successful - bool WaitForAppLaunch(); + // Returns SUCCESS if the launch is successful. + // Returns TIMEOUT if there was no response by command_execution_timeout_ + // Returns VERSION_MISMATCH if the automation protocol version of the + // automation provider does not match and if perform_version_check_ is set + // to true. Note that perform_version_check_ defaults to false, call + // set_perform_version_check() to set it. + AutomationLaunchResult WaitForAppLaunch(); // Waits for any initial page loads to complete. // NOTE: this only fires once for a run of the application. @@ -140,7 +145,7 @@ class AutomationProxy : public IPC::Channel::Listener, // These methods are intended to be called by the background thread // to signal that the given event has occurred, and that any corresponding // Wait... function can return. - void SignalAppLaunch(); + void SignalAppLaunch(const std::string& version_string); void SignalInitialLoads(); // load_time is how long, in ms, the tab contents took to load. void SignalNewTabUITab(int load_time); @@ -187,6 +192,20 @@ class AutomationProxy : public IPC::Channel::Listener, return static_cast(command_execution_timeout_.InMilliseconds()); } + // Returns the server version of the server connected. You may only call this + // method after WaitForAppLaunch() has returned SUCCESS or VERSION_MISMATCH. + // If you call it before this, the return value is undefined. + std::string server_version() const { + return server_version_; + } + + // Call this while passing true to tell the automation proxy to perform + // a version check when WaitForAppLaunch() is called. Note that + // platform_version_check_ defaults to false. + void set_perform_version_check(bool perform_version_check) { + perform_version_check_ = perform_version_check; + } + private: void InitializeChannelID(); void InitializeThread(); @@ -208,6 +227,18 @@ class AutomationProxy : public IPC::Channel::Listener, AutomationRequest* current_request_; + // The version of the automation provider we are communicating with. + std::string server_version_; + + // Used to guard against multiple hello messages being received. + int app_launch_signaled_; + + // Whether to perform a version check between the automation proxy and + // the automation provider at connection time. Defaults to false, you can + // set this to true if building the automation proxy into a module with + // a version resource. + bool perform_version_check_; + // Delay to let the browser execute the command. base::TimeDelta command_execution_timeout_; -- cgit v1.1