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