| Index: chrome/browser/automation/automation_extension_function.cc
|
| ===================================================================
|
| --- chrome/browser/automation/automation_extension_function.cc (revision 31172)
|
| +++ chrome/browser/automation/automation_extension_function.cc (working copy)
|
| @@ -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 @@
|
|
|
| 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 @@
|
| 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 @@
|
| &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;
|
| }
|
| }
|
|
|