diff options
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(); |