| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // Implements AutomationExtensionFunction. | |
| 6 | |
| 7 #include "chrome/browser/automation/automation_extension_function.h" | |
| 8 | |
| 9 #include "base/json/json_reader.h" | |
| 10 #include "base/json/json_writer.h" | |
| 11 #include "base/values.h" | |
| 12 #include "chrome/browser/automation/extension_automation_constants.h" | |
| 13 #include "chrome/browser/extensions/extension_function_dispatcher.h" | |
| 14 #include "chrome/browser/extensions/extension_tabs_module.h" | |
| 15 #include "content/browser/renderer_host/render_view_host.h" | |
| 16 #include "content/browser/renderer_host/render_view_host_delegate.h" | |
| 17 #include "content/browser/tab_contents/tab_contents.h" | |
| 18 #include "content/browser/tab_contents/tab_contents_delegate.h" | |
| 19 | |
| 20 TabContents* AutomationExtensionFunction::api_handler_tab_ = NULL; | |
| 21 AutomationExtensionFunction::PendingFunctionsMap | |
| 22 AutomationExtensionFunction::pending_functions_; | |
| 23 | |
| 24 AutomationExtensionFunction::AutomationExtensionFunction() { | |
| 25 } | |
| 26 | |
| 27 void AutomationExtensionFunction::SetArgs(const ListValue* args) { | |
| 28 // Need to JSON-encode for sending over the wire to the automation user. | |
| 29 base::JSONWriter::Write(args, false, &args_); | |
| 30 } | |
| 31 | |
| 32 const std::string AutomationExtensionFunction::GetResult() { | |
| 33 // Already JSON-encoded, so override the base class's implementation. | |
| 34 return json_result_; | |
| 35 } | |
| 36 | |
| 37 bool AutomationExtensionFunction::RunImpl() { | |
| 38 namespace keys = extension_automation_constants; | |
| 39 | |
| 40 DCHECK(api_handler_tab_) << | |
| 41 "Why is this function still enabled if no target tab?"; | |
| 42 if (!api_handler_tab_) { | |
| 43 error_ = "No longer automating functions."; | |
| 44 return false; | |
| 45 } | |
| 46 | |
| 47 // We are being driven through automation, so we send the extension API | |
| 48 // request over to the automation host. We do this before decoding the | |
| 49 // 'args' JSON, otherwise we'd be decoding it only to encode it again. | |
| 50 DictionaryValue message_to_host; | |
| 51 message_to_host.SetString(keys::kAutomationNameKey, name_); | |
| 52 message_to_host.SetString(keys::kAutomationArgsKey, args_); | |
| 53 message_to_host.SetInteger(keys::kAutomationRequestIdKey, request_id_); | |
| 54 message_to_host.SetBoolean(keys::kAutomationHasCallbackKey, has_callback_); | |
| 55 // Send the API request's associated tab along to the automation client, so | |
| 56 // that it can determine the execution context of the API call. | |
| 57 TabContents* contents = NULL; | |
| 58 ExtensionFunctionDispatcher* function_dispatcher = dispatcher(); | |
| 59 if (function_dispatcher && function_dispatcher->delegate()) { | |
| 60 contents = function_dispatcher->delegate()->associated_tab_contents(); | |
| 61 } else { | |
| 62 NOTREACHED() << "Extension function dispatcher delegate not found."; | |
| 63 } | |
| 64 if (contents) | |
| 65 message_to_host.Set(keys::kAutomationTabJsonKey, | |
| 66 ExtensionTabUtil::CreateTabValue(contents)); | |
| 67 | |
| 68 std::string message; | |
| 69 base::JSONWriter::Write(&message_to_host, false, &message); | |
| 70 if (api_handler_tab_->delegate()) { | |
| 71 api_handler_tab_->delegate()->ForwardMessageToExternalHost( | |
| 72 message, keys::kAutomationOrigin, keys::kAutomationRequestTarget); | |
| 73 } else { | |
| 74 NOTREACHED() << "ExternalTabContainer is supposed to correctly manage " | |
| 75 "lifetime of api_handler_tab_."; | |
| 76 } | |
| 77 | |
| 78 // Automation APIs are asynchronous so we need to stick around until | |
| 79 // our response comes back. Add ourselves to a static hash map keyed | |
| 80 // by request ID. The hash map keeps a reference count on us. | |
| 81 DCHECK(pending_functions_.find(request_id_) == pending_functions_.end()); | |
| 82 pending_functions_[request_id_] = this; | |
| 83 | |
| 84 return true; | |
| 85 } | |
| 86 | |
| 87 ExtensionFunction* AutomationExtensionFunction::Factory() { | |
| 88 return new AutomationExtensionFunction(); | |
| 89 } | |
| 90 | |
| 91 void AutomationExtensionFunction::Enable( | |
| 92 TabContents* api_handler_tab, | |
| 93 const std::vector<std::string>& functions_enabled) { | |
| 94 DCHECK(api_handler_tab); | |
| 95 if (api_handler_tab_ && api_handler_tab != api_handler_tab_) { | |
| 96 NOTREACHED() << "Don't call with different API handler."; | |
| 97 return; | |
| 98 } | |
| 99 api_handler_tab_ = api_handler_tab; | |
| 100 | |
| 101 std::vector<std::string> function_names; | |
| 102 if (functions_enabled.size() == 1 && functions_enabled[0] == "*") { | |
| 103 ExtensionFunctionDispatcher::GetAllFunctionNames(&function_names); | |
| 104 } else { | |
| 105 function_names = functions_enabled; | |
| 106 } | |
| 107 | |
| 108 for (std::vector<std::string>::iterator it = function_names.begin(); | |
| 109 it != function_names.end(); it++) { | |
| 110 // TODO(joi) Could make this a per-profile change rather than a global | |
| 111 // change. Could e.g. have the AutomationExtensionFunction store the | |
| 112 // profile pointer and dispatch to the original ExtensionFunction when the | |
| 113 // current profile is not that. | |
| 114 bool result = ExtensionFunctionDispatcher::OverrideFunction( | |
| 115 *it, AutomationExtensionFunction::Factory); | |
| 116 LOG_IF(WARNING, !result) << "Failed to override API function: " << *it; | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 void AutomationExtensionFunction::Disable() { | |
| 121 api_handler_tab_ = NULL; | |
| 122 ExtensionFunctionDispatcher::ResetFunctions(); | |
| 123 } | |
| 124 | |
| 125 bool AutomationExtensionFunction::InterceptMessageFromExternalHost( | |
| 126 RenderViewHost* view_host, | |
| 127 const std::string& message, | |
| 128 const std::string& origin, | |
| 129 const std::string& target) { | |
| 130 namespace keys = extension_automation_constants; | |
| 131 | |
| 132 // We want only specially-tagged messages passed via the conduit tab. | |
| 133 if (api_handler_tab_ && | |
| 134 view_host == api_handler_tab_->render_view_host() && | |
| 135 origin == keys::kAutomationOrigin && | |
| 136 target == keys::kAutomationResponseTarget) { | |
| 137 // This is an extension API response being sent back via postMessage, | |
| 138 // so redirect it. | |
| 139 scoped_ptr<Value> message_value(base::JSONReader::Read(message, false)); | |
| 140 DCHECK(message_value->IsType(Value::TYPE_DICTIONARY)); | |
| 141 if (message_value->IsType(Value::TYPE_DICTIONARY)) { | |
| 142 DictionaryValue* message_dict = | |
| 143 reinterpret_cast<DictionaryValue*>(message_value.get()); | |
| 144 | |
| 145 int request_id = -1; | |
| 146 bool got_value = message_dict->GetInteger(keys::kAutomationRequestIdKey, | |
| 147 &request_id); | |
| 148 DCHECK(got_value); | |
| 149 if (got_value) { | |
| 150 std::string error; | |
| 151 bool success = !message_dict->GetString(keys::kAutomationErrorKey, | |
| 152 &error); | |
| 153 | |
| 154 std::string response; | |
| 155 got_value = message_dict->GetString(keys::kAutomationResponseKey, | |
| 156 &response); | |
| 157 DCHECK(!success || got_value); | |
| 158 | |
| 159 PendingFunctionsMap::iterator it = pending_functions_.find(request_id); | |
| 160 DCHECK(it != pending_functions_.end()); | |
| 161 | |
| 162 if (it != pending_functions_.end()) { | |
| 163 scoped_refptr<AutomationExtensionFunction> func = it->second; | |
| 164 pending_functions_.erase(it); | |
| 165 | |
| 166 // Our local ref should be the last remaining. | |
| 167 DCHECK(func && func->HasOneRef()); | |
| 168 | |
| 169 if (func) { | |
| 170 func->json_result_ = response; | |
| 171 func->error_ = error; | |
| 172 | |
| 173 func->SendResponse(success); | |
| 174 } | |
| 175 } | |
| 176 return true; | |
| 177 } | |
| 178 } | |
| 179 } | |
| 180 | |
| 181 return false; | |
| 182 } | |
| 183 | |
| 184 AutomationExtensionFunction::~AutomationExtensionFunction() { | |
| 185 } | |
| OLD | NEW |