summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/automation/automation_extension_function.cc115
-rw-r--r--chrome/browser/automation/automation_extension_function.h42
-rw-r--r--chrome/browser/automation/automation_provider.cc8
-rw-r--r--chrome/browser/automation/automation_provider.h1
-rw-r--r--chrome/browser/automation/automation_provider_win.cc15
-rw-r--r--chrome/browser/extensions/extension_uitest.cc39
-rw-r--r--chrome/browser/external_tab_container.cc24
-rw-r--r--chrome/browser/external_tab_container.h9
-rw-r--r--chrome/test/automation/automation_messages_internal.h20
-rw-r--r--chrome/test/automation/automation_proxy.cc6
-rw-r--r--chrome/test/automation/automation_proxy.h17
-rw-r--r--chrome/test/automation/tab_proxy.cc9
-rw-r--r--chrome/test/automation/tab_proxy.h24
-rw-r--r--chrome/test/data/extensions/uitest/roundtrip_api_call/background.html11
-rw-r--r--chrome/test/data/extensions/uitest/roundtrip_api_call/manifest.json3
-rw-r--r--chrome/test/data/extensions/uitest/roundtrip_api_call/test.html9
-rw-r--r--chrome_frame/chrome_frame_automation.cc8
-rw-r--r--chrome_frame/chrome_frame_automation.h7
18 files changed, 256 insertions, 111 deletions
diff --git a/chrome/browser/automation/automation_extension_function.cc b/chrome/browser/automation/automation_extension_function.cc
index 98ca3c9..6f3bb73 100644
--- a/chrome/browser/automation/automation_extension_function.cc
+++ b/chrome/browser/automation/automation_extension_function.cc
@@ -12,26 +12,33 @@
#include "chrome/browser/extensions/extension_function_dispatcher.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_view_host_delegate.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
-bool AutomationExtensionFunction::enabled_ = false;
+TabContents* AutomationExtensionFunction::api_handler_tab_ = NULL;
+AutomationExtensionFunction::PendingFunctionsMap
+ AutomationExtensionFunction::pending_functions_;
void AutomationExtensionFunction::SetArgs(const Value* args) {
+ // Need to JSON-encode for sending over the wire to the automation user.
base::JSONWriter::Write(args, false, &args_);
}
const std::string AutomationExtensionFunction::GetResult() {
- // Our API result passing is done through InterceptMessageFromExternalHost
- return "";
+ // Already JSON-encoded, so override the base class's implementation.
+ return json_result_;
}
-const std::string AutomationExtensionFunction::GetError() {
- // Our API result passing is done through InterceptMessageFromExternalHost
- return "";
-}
-
-void AutomationExtensionFunction::Run() {
+bool AutomationExtensionFunction::RunImpl() {
namespace keys = extension_automation_constants;
+ DCHECK(api_handler_tab_) <<
+ "Why is this function still enabled if no target tab?";
+ if (!api_handler_tab_) {
+ error_ = "No longer automating functions.";
+ return false;
+ }
+
// We are being driven through automation, so we send the extension API
// request over to the automation host. We do this before decoding the
// 'args' JSON, otherwise we'd be decoding it only to encode it again.
@@ -43,39 +50,61 @@ void AutomationExtensionFunction::Run() {
std::string message;
base::JSONWriter::Write(&message_to_host, false, &message);
- dispatcher()->render_view_host_->delegate()->ProcessExternalHostMessage(
- message, keys::kAutomationOrigin, keys::kAutomationRequestTarget);
+ if (api_handler_tab_->delegate()) {
+ api_handler_tab_->delegate()->ForwardMessageToExternalHost(
+ message, keys::kAutomationOrigin, keys::kAutomationRequestTarget);
+ } else {
+ NOTREACHED() << "ExternalTabContainer is supposed to correctly manage "
+ "lifetime of api_handler_tab_.";
+ }
+
+ // Automation APIs are asynchronous so we need to stick around until
+ // our response comes back. Add ourselves to a static hash map keyed
+ // by request ID. The hash map keeps a reference count on us.
+ DCHECK(pending_functions_.find(request_id_) == pending_functions_.end());
+ pending_functions_[request_id_] = this;
+
+ return true;
}
ExtensionFunction* AutomationExtensionFunction::Factory() {
return new AutomationExtensionFunction();
}
-void AutomationExtensionFunction::SetEnabled(
+void AutomationExtensionFunction::Enable(
+ TabContents* api_handler_tab,
const std::vector<std::string>& functions_enabled) {
- if (functions_enabled.size() > 0) {
- std::vector<std::string> function_names;
- if (functions_enabled.size() == 1 && functions_enabled[0] == "*") {
- ExtensionFunctionDispatcher::GetAllFunctionNames(&function_names);
- } else {
- function_names = functions_enabled;
- }
+ DCHECK(api_handler_tab);
+ if (api_handler_tab_ && api_handler_tab != api_handler_tab_) {
+ NOTREACHED() << "Don't call with different API handler.";
+ return;
+ }
+ api_handler_tab_ = api_handler_tab;
- for (std::vector<std::string>::iterator it = function_names.begin();
- it != function_names.end(); it++) {
- // TODO(joi) Could make this a per-profile change rather than a global
- // change. Could e.g. have the AutomationExtensionFunction store the
- // profile pointer and dispatch to the original ExtensionFunction when the
- // current profile is not that.
- bool result = ExtensionFunctionDispatcher::OverrideFunction(
- *it, AutomationExtensionFunction::Factory);
- LOG_IF(WARNING, !result) << "Failed to override API function: " << *it;
- }
+ std::vector<std::string> function_names;
+ if (functions_enabled.size() == 1 && functions_enabled[0] == "*") {
+ ExtensionFunctionDispatcher::GetAllFunctionNames(&function_names);
} else {
- ExtensionFunctionDispatcher::ResetFunctions();
+ function_names = functions_enabled;
+ }
+
+ for (std::vector<std::string>::iterator it = function_names.begin();
+ it != function_names.end(); it++) {
+ // TODO(joi) Could make this a per-profile change rather than a global
+ // change. Could e.g. have the AutomationExtensionFunction store the
+ // profile pointer and dispatch to the original ExtensionFunction when the
+ // current profile is not that.
+ bool result = ExtensionFunctionDispatcher::OverrideFunction(
+ *it, AutomationExtensionFunction::Factory);
+ LOG_IF(WARNING, !result) << "Failed to override API function: " << *it;
}
}
+void AutomationExtensionFunction::Disable() {
+ api_handler_tab_ = NULL;
+ ExtensionFunctionDispatcher::ResetFunctions();
+}
+
bool AutomationExtensionFunction::InterceptMessageFromExternalHost(
RenderViewHost* view_host,
const std::string& message,
@@ -83,7 +112,10 @@ bool AutomationExtensionFunction::InterceptMessageFromExternalHost(
const std::string& target) {
namespace keys = extension_automation_constants;
- if (origin == keys::kAutomationOrigin &&
+ // We want only specially-tagged messages passed via the conduit tab.
+ if (api_handler_tab_ &&
+ view_host == api_handler_tab_->render_view_host() &&
+ origin == keys::kAutomationOrigin &&
target == keys::kAutomationResponseTarget) {
// This is an extension API response being sent back via postMessage,
// so redirect it.
@@ -107,10 +139,23 @@ bool AutomationExtensionFunction::InterceptMessageFromExternalHost(
&response);
DCHECK(!success || got_value);
- // TODO(joi) Once ExtensionFunctionDispatcher supports asynchronous
- // functions, we should use that instead.
- view_host->SendExtensionResponse(request_id, success,
- response, error);
+ PendingFunctionsMap::iterator it = pending_functions_.find(request_id);
+ DCHECK(it != pending_functions_.end());
+
+ if (it != pending_functions_.end()) {
+ scoped_refptr<AutomationExtensionFunction> func = it->second;
+ pending_functions_.erase(it);
+
+ // Our local ref should be the last remaining.
+ DCHECK(func && func->HasOneRef());
+
+ if (func) {
+ func->json_result_ = response;
+ func->error_ = error;
+
+ func->SendResponse(success);
+ }
+ }
return true;
}
}
diff --git a/chrome/browser/automation/automation_extension_function.h b/chrome/browser/automation/automation_extension_function.h
index ed57ca1..faf125e 100644
--- a/chrome/browser/automation/automation_extension_function.h
+++ b/chrome/browser/automation/automation_extension_function.h
@@ -8,38 +8,49 @@
#define CHROME_BROWSER_AUTOMATION_AUTOMATION_EXTENSION_FUNCTION_H_
#include <string>
+#include <map>
#include "chrome/browser/extensions/extension_function.h"
class RenderViewHost;
+class TabContents;
// An extension function that pipes the extension API call through the
// automation interface, so that extensions can be tested using UITests.
-class AutomationExtensionFunction : public ExtensionFunction {
+class AutomationExtensionFunction : public AsyncExtensionFunction {
public:
AutomationExtensionFunction() { }
// ExtensionFunction implementation.
virtual void SetArgs(const Value* args);
virtual const std::string GetResult();
- virtual const std::string GetError();
- virtual void Run();
+ virtual bool RunImpl();
static ExtensionFunction* Factory();
+ // Enable API automation of selected APIs. Overridden extension API messages
+ // will be routed to the automation client attached to |api_handler_tab|.
+ //
// If the list of enabled functions is non-empty, we enable according to the
// list ("*" means enable all, otherwise we enable individual named
- // functions). If empty, we restore the initial functions.
+ // functions). An empty list makes this function a no-op.
+ //
+ // Note that all calls to this function are additive. Functions previously
+ // enabled will remain enabled until you call Disable().
//
- // Note that all calls to this function, except a call with the empty list,
- // are additive. Functions previously enabled will remain enabled until
- // you clear all function forwarding by specifying the empty list.
- static void SetEnabled(const std::vector<std::string>& functions_enabled);
+ // Calling this function after enabling one or more functions with a
+ // tab other than the one previously used is an error.
+ static void Enable(TabContents* api_handler_tab,
+ const std::vector<std::string>& functions_enabled);
+
+ // Restore the default API function implementations and reset the stored
+ // API handler.
+ static void Disable();
// Intercepts messages sent from the external host to check if they
// are actually responses to extension API calls. If they are, redirects
- // the message to view_host->SendExtensionResponse and returns true,
- // otherwise returns false to indicate the message was not intercepted.
+ // the message to respond to the pending asynchronous API call and returns
+ // true, otherwise returns false to indicate the message was not intercepted.
static bool InterceptMessageFromExternalHost(RenderViewHost* view_host,
const std::string& message,
const std::string& origin,
@@ -48,8 +59,17 @@ class AutomationExtensionFunction : public ExtensionFunction {
private:
~AutomationExtensionFunction() {}
- static bool enabled_;
+ // Weak reference, lifetime managed by the ExternalTabContainer instance
+ // owning the TabContents in question.
+ static TabContents* api_handler_tab_;
+
+ typedef std::map<int, scoped_refptr<AutomationExtensionFunction> >
+ PendingFunctionsMap;
+ static PendingFunctionsMap pending_functions_;
+
std::string args_;
+ std::string json_result_;
+
DISALLOW_COPY_AND_ASSIGN(AutomationExtensionFunction);
};
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc
index 64b0133..537cc6d 100644
--- a/chrome/browser/automation/automation_provider.cc
+++ b/chrome/browser/automation/automation_provider.cc
@@ -418,8 +418,11 @@ void AutomationProvider::OnMessageReceived(const IPC::Message& message) {
IPC_MESSAGE_HANDLER(AutomationMsg_SavePackageShouldPromptUser,
SavePackageShouldPromptUser)
IPC_MESSAGE_HANDLER(AutomationMsg_WindowTitle, GetWindowTitle)
+#if defined(OS_WIN)
+ // Depends on ExternalTabContainer, so Windows-only
IPC_MESSAGE_HANDLER(AutomationMsg_SetEnableExtensionAutomation,
SetEnableExtensionAutomation)
+#endif
IPC_MESSAGE_HANDLER(AutomationMsg_SetShelfVisibility, SetShelfVisibility)
IPC_MESSAGE_HANDLER(AutomationMsg_BlockedPopupCount, GetBlockedPopupCount)
IPC_MESSAGE_HANDLER(AutomationMsg_SelectAll, SelectAll)
@@ -1930,11 +1933,6 @@ void AutomationProvider::SavePackageShouldPromptUser(bool should_prompt) {
SavePackage::SetShouldPromptUser(should_prompt);
}
-void AutomationProvider::SetEnableExtensionAutomation(
- const std::vector<std::string>& functions_enabled) {
- AutomationExtensionFunction::SetEnabled(functions_enabled);
-}
-
void AutomationProvider::GetWindowTitle(int handle, string16* text) {
gfx::NativeWindow window = window_tracker_->GetResource(handle);
text->assign(platform_util::GetWindowTitle(window));
diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h
index 418c804..82a2d72 100644
--- a/chrome/browser/automation/automation_provider.h
+++ b/chrome/browser/automation/automation_provider.h
@@ -467,6 +467,7 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>,
// Enables extension automation (for e.g. UITests).
void SetEnableExtensionAutomation(
+ int tab_handle,
const std::vector<std::string>& functions_enabled);
void GetWindowTitle(int handle, string16* text);
diff --git a/chrome/browser/automation/automation_provider_win.cc b/chrome/browser/automation/automation_provider_win.cc
index cfadf91..7b750ca 100644
--- a/chrome/browser/automation/automation_provider_win.cc
+++ b/chrome/browser/automation/automation_provider_win.cc
@@ -489,3 +489,18 @@ void AutomationProvider::TerminateSession(int handle, bool* success) {
*success = (::PostMessageW(window, WM_ENDSESSION, 0, 0) == TRUE);
}
}
+
+void AutomationProvider::SetEnableExtensionAutomation(
+ int tab_handle,
+ const std::vector<std::string>& functions_enabled) {
+ ExternalTabContainer* external_tab = GetExternalTabForHandle(tab_handle);
+ if (external_tab) {
+ external_tab->SetEnableExtensionAutomation(functions_enabled);
+ } else {
+ // Tab must exist, and must be an external tab so that its
+ // delegate has an on-empty
+ // implementation of ForwardMessageToExternalHost.
+ DLOG(WARNING) <<
+ "SetEnableExtensionAutomation called with invalid tab handle.";
+ }
+}
diff --git a/chrome/browser/extensions/extension_uitest.cc b/chrome/browser/extensions/extension_uitest.cc
index ac4504c..2cfcd7c 100644
--- a/chrome/browser/extensions/extension_uitest.cc
+++ b/chrome/browser/extensions/extension_uitest.cc
@@ -55,38 +55,46 @@ class ExtensionUITest : public ParentTestType {
void SetUp() {
ParentTestType::SetUp();
- automation()->SetEnableExtensionAutomation(functions_enabled_);
+
+ AutomationProxyForExternalTab* proxy =
+ static_cast<AutomationProxyForExternalTab*>(automation());
+ HWND external_tab_container = NULL;
+ HWND tab_wnd = NULL;
+ tab_ = proxy->CreateTabWithHostWindow(false,
+ GURL(), &external_tab_container, &tab_wnd);
+
+ tab_->SetEnableExtensionAutomation(functions_enabled_);
}
void TearDown() {
- automation()->SetEnableExtensionAutomation(std::vector<std::string>());
+ tab_->SetEnableExtensionAutomation(std::vector<std::string>());
+
+ AutomationProxyForExternalTab* proxy =
+ static_cast<AutomationProxyForExternalTab*>(automation());
+ proxy->DestroyHostWindow();
+ proxy->WaitForTabCleanup(tab_, action_max_timeout_ms());
+ EXPECT_FALSE(tab_->is_valid());
+ tab_.release();
+
ParentTestType::TearDown();
}
void TestWithURL(const GURL& url) {
- AutomationProxyForExternalTab* proxy =
+ EXPECT_TRUE(tab_->is_valid());
+ if (tab_) {
+ AutomationProxyForExternalTab* proxy =
static_cast<AutomationProxyForExternalTab*>(automation());
- HWND external_tab_container = NULL;
- HWND tab_wnd = NULL;
- scoped_refptr<TabProxy> tab(proxy->CreateTabWithHostWindow(false,
- GURL(), &external_tab_container, &tab_wnd));
- EXPECT_TRUE(tab->is_valid());
- if (tab) {
// Enter a message loop to allow the tab to be created
proxy->WaitForNavigation(2000);
- DoAdditionalPreNavigateSetup(tab.get());
+ DoAdditionalPreNavigateSetup(tab_.get());
// We explicitly do not make this a toolstrip in the extension manifest,
// so that the test can control when it gets loaded, and so that we test
// the intended behavior that tabs should be able to show extension pages
// (useful for development etc.)
- tab->NavigateInExternalTab(url, GURL());
+ tab_->NavigateInExternalTab(url, GURL());
EXPECT_TRUE(proxy->WaitForMessage(action_max_timeout_ms()));
-
- proxy->DestroyHostWindow();
- proxy->WaitForTabCleanup(tab, action_max_timeout_ms());
- EXPECT_FALSE(tab->is_valid());
}
}
@@ -97,6 +105,7 @@ class ExtensionUITest : public ParentTestType {
protected:
// Extension API functions that we want to take over. Defaults to all.
std::vector<std::string> functions_enabled_;
+ scoped_refptr<TabProxy> tab_;
private:
DISALLOW_COPY_AND_ASSIGN(ExtensionUITest);
diff --git a/chrome/browser/external_tab_container.cc b/chrome/browser/external_tab_container.cc
index d200891..4440294 100644
--- a/chrome/browser/external_tab_container.cc
+++ b/chrome/browser/external_tab_container.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/win_util.h"
#include "chrome/browser/automation/automation_provider.h"
+#include "chrome/browser/automation/automation_extension_function.h"
#include "chrome/browser/browser_window.h"
#include "chrome/browser/debugger/devtools_manager.h"
#include "chrome/browser/load_notification_details.h"
@@ -43,7 +44,8 @@ ExternalTabContainer::ExternalTabContainer(
automation_resource_message_filter_(filter),
load_requests_via_automation_(false),
handle_top_level_requests_(false),
- external_method_factory_(this) {
+ external_method_factory_(this),
+ enabled_extension_automation_(false) {
}
ExternalTabContainer::~ExternalTabContainer() {
@@ -161,6 +163,10 @@ bool ExternalTabContainer::Init(Profile* profile,
}
void ExternalTabContainer::Uninitialize() {
+ if (enabled_extension_automation_) {
+ AutomationExtensionFunction::Disable();
+ }
+
registrar_.RemoveAll();
if (tab_contents_) {
RenderViewHost* rvh = tab_contents_->render_view_host();
@@ -659,6 +665,22 @@ ExternalTabContainer* ExternalTabContainer::RemovePendingTab(intptr_t cookie) {
return NULL;
}
+void ExternalTabContainer::SetEnableExtensionAutomation(
+ const std::vector<std::string>& functions_enabled) {
+ if (functions_enabled.size() > 0) {
+ if (!tab_contents_) {
+ NOTREACHED() << "Being invoked via tab so should have TabContents";
+ return;
+ }
+
+ AutomationExtensionFunction::Enable(tab_contents_, functions_enabled);
+ enabled_extension_automation_ = true;
+ } else {
+ AutomationExtensionFunction::Disable();
+ enabled_extension_automation_ = false;
+ }
+}
+
void ExternalTabContainer::Navigate(const GURL& url, const GURL& referrer) {
if (!tab_contents_) {
NOTREACHED();
diff --git a/chrome/browser/external_tab_container.h b/chrome/browser/external_tab_container.h
index 3c97233..95ab846 100644
--- a/chrome/browser/external_tab_container.h
+++ b/chrome/browser/external_tab_container.h
@@ -157,6 +157,12 @@ class ExternalTabContainer : public TabContentsDelegate,
// Returns NULL if we fail to find the cookie in the map.
static ExternalTabContainer* RemovePendingTab(intptr_t cookie);
+ // Enables extension automation (for e.g. UITests), with the current tab
+ // used as a conduit for the extension API messages being handled by the
+ // automation client.
+ void SetEnableExtensionAutomation(
+ const std::vector<std::string>& functions_enabled);
+
protected:
// Overridden from views::WidgetWin:
virtual LRESULT OnCreate(LPCREATESTRUCT create_struct);
@@ -216,6 +222,9 @@ class ExternalTabContainer : public TabContentsDelegate,
// Contains ExternalTabContainers that have not been connected to as yet.
static PendingTabs pending_tabs_;
+ // True if this tab is currently the conduit for extension API automation.
+ bool enabled_extension_automation_;
+
// Allows us to run tasks on the ExternalTabContainer instance which are
// bound by its lifetime.
ScopedRunnableMethodFactory<ExternalTabContainer> external_method_factory_;
diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h
index f7ded561..0616aea 100644
--- a/chrome/test/automation/automation_messages_internal.h
+++ b/chrome/test/automation/automation_messages_internal.h
@@ -905,13 +905,19 @@ IPC_BEGIN_MESSAGES(Automation)
// value is the number of windows.
IPC_SYNC_MESSAGE_ROUTED0_1(AutomationMsg_NormalBrowserWindowCount, int)
- // Used to put the browser into "extension automation mode" for the
- // current profile, or turn off the mode.
- IPC_MESSAGE_ROUTED1(AutomationMsg_SetEnableExtensionAutomation,
- std::vector<std::string> /* empty to disable automation,
- non-empty to enable automation
- of the specified API
- functions */)
+ // Used to put the browser into "extension automation mode" for a given
+ // set of Chrome Extensions API functions for the current profile, or turn
+ // off automation mode. The specified tab is used as the conduit for all
+ // automated API functions. It must be an external tab (as in
+ // AutomationMsg_CreateExternalTab).
+ IPC_MESSAGE_ROUTED2(AutomationMsg_SetEnableExtensionAutomation,
+ // Tab handle.
+ int,
+ // Empty to disable automation, non-empty to enable
+ // automation of the specified API functions, single
+ // entry of "*" to enable automation of all API
+ // functions.
+ std::vector<std::string>)
// This message tells the browser to start using the new proxy configuration
// represented by the given JSON string. The parameters used in the JSON
diff --git a/chrome/test/automation/automation_proxy.cc b/chrome/test/automation/automation_proxy.cc
index 698f969..6dd8032 100644
--- a/chrome/test/automation/automation_proxy.cc
+++ b/chrome/test/automation/automation_proxy.cc
@@ -220,12 +220,6 @@ bool AutomationProxy::SavePackageShouldPromptUser(bool should_prompt) {
return Send(new AutomationMsg_SavePackageShouldPromptUser(0, should_prompt));
}
-bool AutomationProxy::SetEnableExtensionAutomation(
- const std::vector<std::string>& functions_enabled) {
- return Send(
- new AutomationMsg_SetEnableExtensionAutomation(0, functions_enabled));
-}
-
bool AutomationProxy::GetBrowserWindowCount(int* num_windows) {
if (!num_windows) {
NOTREACHED();
diff --git a/chrome/test/automation/automation_proxy.h b/chrome/test/automation/automation_proxy.h
index 5c2d8ab..312c4b0 100644
--- a/chrome/test/automation/automation_proxy.h
+++ b/chrome/test/automation/automation_proxy.h
@@ -181,23 +181,6 @@ class AutomationProxy : public IPC::Channel::Listener,
// sent.
bool SavePackageShouldPromptUser(bool should_prompt);
- // Configure extension automation mode. When extension automation
- // mode is turned on, the automation host can overtake extension API calls
- // e.g. to make UI tests for extensions easier to write. Returns true if
- // the message is successfully sent.
- //
- // The parameter can take the following types of values:
- // a) An empty list to turn off extension automation.
- // b) A list with one item, "*", to turn extension automation on for all
- // functions.
- // c) A list with one or more items which are the names of Chrome Extension
- // API functions that should be forwarded over the automation interface.
- // Other functions will continue to be fulfilled as normal. This lets you
- // write tests where some functionality continues to function as normal,
- // and other functionality is mocked out by the test.
- bool SetEnableExtensionAutomation(
- const std::vector<std::string>& functions_enabled);
-
// Returns the ID of the automation IPC channel, so that it can be
// passed to the app as a launch parameter.
const std::string& channel_id() const { return channel_id_; }
diff --git a/chrome/test/automation/tab_proxy.cc b/chrome/test/automation/tab_proxy.cc
index 1852375..43246ea 100644
--- a/chrome/test/automation/tab_proxy.cc
+++ b/chrome/test/automation/tab_proxy.cc
@@ -356,6 +356,15 @@ bool TabProxy::ExecuteAndExtractValue(const std::wstring& frame_xpath,
return *value != NULL;
}
+bool TabProxy::SetEnableExtensionAutomation(
+ const std::vector<std::string>& functions_enabled) {
+ if (!is_valid())
+ return false;
+
+ return sender_->Send(new AutomationMsg_SetEnableExtensionAutomation(
+ 0, handle_, functions_enabled));
+}
+
bool TabProxy::GetConstrainedWindowCount(int* count) const {
if (!is_valid())
return false;
diff --git a/chrome/test/automation/tab_proxy.h b/chrome/test/automation/tab_proxy.h
index 1eaabea..7693e60 100644
--- a/chrome/test/automation/tab_proxy.h
+++ b/chrome/test/automation/tab_proxy.h
@@ -83,6 +83,30 @@ class TabProxy : public AutomationResourceProxy {
const std::wstring& jscript,
Value** value);
+ // Configure extension automation mode. When extension automation
+ // mode is turned on, the automation host can overtake extension API calls
+ // e.g. to make UI tests for extensions easier to write. Returns true if
+ // the message is successfully sent.
+ //
+ // Note that API calls in _any_ extension view will be routed to the current
+ // tab. This is to enable UI testing of e.g. extension background pages.
+ //
+ // Enabling extension automation from more than one tab is an error.
+ //
+ // You must disable extension automation before destroying the tab.
+ //
+ // The parameter can take the following types of values:
+ // a) An empty list to turn off extension automation.
+ // b) A list with one item, "*", to turn extension automation on for all
+ // functions.
+ // c) A list with one or more items which are the names of Chrome Extension
+ // API functions that should be forwarded over the automation interface.
+ // Other functions will continue to be fulfilled as normal. This lets you
+ // write tests where some functionality continues to function as normal,
+ // and other functionality is mocked out by the test.
+ bool SetEnableExtensionAutomation(
+ const std::vector<std::string>& functions_enabled);
+
// Navigates to a url. This method accepts the same kinds of URL input that
// can be passed to Chrome on the command line. This is a synchronous call and
// hence blocks until the navigation completes.
diff --git a/chrome/test/data/extensions/uitest/roundtrip_api_call/background.html b/chrome/test/data/extensions/uitest/roundtrip_api_call/background.html
new file mode 100644
index 0000000..0c4cdb4
--- /dev/null
+++ b/chrome/test/data/extensions/uitest/roundtrip_api_call/background.html
@@ -0,0 +1,11 @@
+YOU SHOULD NOT BE SEEING THIS, IT'S A BACKGROUND PAGE!!!
+
+<script type="text/javascript">
+function doStuff() {
+ function getSelectedCallback(tab) {
+ chrome.tabs.remove(tab.id);
+ }
+
+ chrome.tabs.getSelected(undefined, getSelectedCallback);
+}
+</script>
diff --git a/chrome/test/data/extensions/uitest/roundtrip_api_call/manifest.json b/chrome/test/data/extensions/uitest/roundtrip_api_call/manifest.json
index 67f9e13..ef87bfe 100644
--- a/chrome/test/data/extensions/uitest/roundtrip_api_call/manifest.json
+++ b/chrome/test/data/extensions/uitest/roundtrip_api_call/manifest.json
@@ -3,5 +3,6 @@
"version": "1.0.0.0",
"name": "Roundtrip ApiCall Test Extension",
"description": "An extension for an extension UITest.",
- "permissions": ["tabs"]
+ "permissions": ["tabs"],
+ "background_page": "background.html"
}
diff --git a/chrome/test/data/extensions/uitest/roundtrip_api_call/test.html b/chrome/test/data/extensions/uitest/roundtrip_api_call/test.html
index a81b9a2..247e934 100644
--- a/chrome/test/data/extensions/uitest/roundtrip_api_call/test.html
+++ b/chrome/test/data/extensions/uitest/roundtrip_api_call/test.html
@@ -1,9 +1,8 @@
HOWDIE!!!
<script type="text/javascript">
- function getSelectedCallback(tab) {
- chrome.tabs.remove(tab.id);
- }
-
- chrome.tabs.getSelected(undefined, getSelectedCallback);
+// Do things in the background page to test that API call
+// routing works from extension views not directly
+// attached to the automation client.
+chrome.extension.getBackgroundPage().doStuff();
</script>
diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc
index 6b9acb8..54e114d 100644
--- a/chrome_frame/chrome_frame_automation.cc
+++ b/chrome_frame/chrome_frame_automation.cc
@@ -755,7 +755,13 @@ void ChromeFrameAutomationClient::SetEnableExtensionAutomation(
if (!is_initialized())
return;
- automation_server_->SetEnableExtensionAutomation(functions_enabled);
+ // We are doing initialization, so there is no need to reset extension
+ // automation, only to set it. Also, we want to avoid resetting extension
+ // automation that some other automation client has set up. Therefore only
+ // send the message if we are going to enable automation of some functions.
+ if (functions_enabled.size() > 0) {
+ tab_->SetEnableExtensionAutomation(functions_enabled);
+ }
}
// Invoked in launch background thread.
diff --git a/chrome_frame/chrome_frame_automation.h b/chrome_frame/chrome_frame_automation.h
index 9d948a3..8def0fd 100644
--- a/chrome_frame/chrome_frame_automation.h
+++ b/chrome_frame/chrome_frame_automation.h
@@ -41,8 +41,6 @@ struct DECLSPEC_NOVTABLE ChromeFrameAutomationProxy {
virtual std::string server_version() = 0;
virtual void SendProxyConfig(const std::string&) = 0;
- virtual void SetEnableExtensionAutomation(
- const std::vector<std::string>& functions_enabled) = 0;
protected:
~ChromeFrameAutomationProxy() {}
};
@@ -73,11 +71,6 @@ class ChromeFrameAutomationProxyImpl : public ChromeFrameAutomationProxy,
AutomationProxy::SendProxyConfig(p);
}
- virtual void SetEnableExtensionAutomation(
- const std::vector<std::string>& functions_enabled) {
- AutomationProxy::SetEnableExtensionAutomation(functions_enabled);
- }
-
protected:
explicit ChromeFrameAutomationProxyImpl(int launch_timeout);
~ChromeFrameAutomationProxyImpl();