OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #include "chrome/browser/extensions/api/declarative_content/content_action.h" | 5 #include "chrome/browser/extensions/api/declarative_content/content_action.h" |
6 | 6 |
7 #include <map> | 7 #include <map> |
8 | 8 |
9 #include "base/lazy_instance.h" | 9 #include "base/lazy_instance.h" |
10 #include "base/strings/stringprintf.h" | 10 #include "base/strings/stringprintf.h" |
11 #include "base/values.h" | 11 #include "base/values.h" |
12 #include "chrome/browser/extensions/api/declarative_content/content_constants.h" | 12 #include "chrome/browser/extensions/api/declarative_content/content_constants.h" |
13 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" | 13 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h" |
14 #include "chrome/browser/extensions/declarative_user_script_master.h" | |
15 #include "chrome/browser/extensions/extension_action.h" | 14 #include "chrome/browser/extensions/extension_action.h" |
16 #include "chrome/browser/extensions/extension_action_manager.h" | 15 #include "chrome/browser/extensions/extension_action_manager.h" |
17 #include "chrome/browser/extensions/extension_tab_util.h" | 16 #include "chrome/browser/extensions/extension_tab_util.h" |
18 #include "chrome/browser/profiles/profile.h" | 17 #include "chrome/browser/profiles/profile.h" |
| 18 #include "chrome/browser/sessions/session_tab_helper.h" |
19 #include "content/public/browser/invalidate_type.h" | 19 #include "content/public/browser/invalidate_type.h" |
| 20 #include "content/public/browser/render_view_host.h" |
20 #include "content/public/browser/web_contents.h" | 21 #include "content/public/browser/web_contents.h" |
21 #include "extensions/browser/extension_registry.h" | 22 #include "extensions/browser/extension_registry.h" |
22 #include "extensions/browser/extension_system.h" | 23 #include "extensions/browser/extension_system.h" |
23 #include "extensions/common/extension.h" | 24 #include "extensions/common/extension.h" |
| 25 #include "extensions/common/extension_messages.h" |
24 | 26 |
25 namespace extensions { | 27 namespace extensions { |
26 | 28 |
27 namespace keys = declarative_content_constants; | 29 namespace keys = declarative_content_constants; |
28 | 30 |
29 namespace { | 31 namespace { |
30 // Error messages. | 32 // Error messages. |
31 const char kInvalidInstanceTypeError[] = | 33 const char kInvalidInstanceTypeError[] = |
32 "An action has an invalid instanceType: %s"; | 34 "An action has an invalid instanceType: %s"; |
33 const char kNoPageAction[] = | 35 const char kNoPageAction[] = |
34 "Can't use declarativeContent.ShowPageAction without a page action"; | 36 "Can't use declarativeContent.ShowPageAction without a page action"; |
35 const char kMissingParameter[] = "Missing parameter is required: %s"; | 37 const char kMissingParameter[] = "Missing parameter is required: %s"; |
36 | 38 |
37 #define INPUT_FORMAT_VALIDATE(test) do { \ | 39 #define INPUT_FORMAT_VALIDATE(test) do { \ |
38 if (!(test)) { \ | 40 if (!(test)) { \ |
39 *bad_message = true; \ | 41 *bad_message = true; \ |
40 return scoped_refptr<ContentAction>(NULL); \ | 42 return false; \ |
41 } \ | 43 } \ |
42 } while (0) | 44 } while (0) |
43 | 45 |
44 // | 46 // |
45 // The following are concrete actions. | 47 // The following are concrete actions. |
46 // | 48 // |
47 | 49 |
48 // Action that instructs to show an extension's page action. | 50 // Action that instructs to show an extension's page action. |
49 class ShowPageAction : public ContentAction { | 51 class ShowPageAction : public ContentAction { |
50 public: | 52 public: |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 ->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); | 99 ->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING); |
98 if (!extension) | 100 if (!extension) |
99 return NULL; | 101 return NULL; |
100 return ExtensionActionManager::Get(profile)->GetPageAction(*extension); | 102 return ExtensionActionManager::Get(profile)->GetPageAction(*extension); |
101 } | 103 } |
102 virtual ~ShowPageAction() {} | 104 virtual ~ShowPageAction() {} |
103 | 105 |
104 DISALLOW_COPY_AND_ASSIGN(ShowPageAction); | 106 DISALLOW_COPY_AND_ASSIGN(ShowPageAction); |
105 }; | 107 }; |
106 | 108 |
107 // Action that injects a content script. | |
108 class RequestContentScript : public ContentAction { | |
109 public: | |
110 RequestContentScript(content::BrowserContext* browser_context, | |
111 const Extension* extension, | |
112 const std::vector<std::string>& css_file_names, | |
113 const std::vector<std::string>& js_file_names, | |
114 bool all_frames, | |
115 bool match_about_blank); | |
116 | |
117 static scoped_refptr<ContentAction> Create( | |
118 content::BrowserContext* browser_context, | |
119 const Extension* extension, | |
120 const base::DictionaryValue* dict, | |
121 std::string* error, | |
122 bool* bad_message); | |
123 | |
124 // Implementation of ContentAction: | |
125 virtual Type GetType() const OVERRIDE { | |
126 return ACTION_REQUEST_CONTENT_SCRIPT; | |
127 } | |
128 | |
129 virtual void Apply(const std::string& extension_id, | |
130 const base::Time& extension_install_time, | |
131 ApplyInfo* apply_info) const OVERRIDE { | |
132 InstructRenderProcessToInject(apply_info->tab, extension_id); | |
133 } | |
134 | |
135 virtual void Reapply(const std::string& extension_id, | |
136 const base::Time& extension_install_time, | |
137 ApplyInfo* apply_info) const OVERRIDE { | |
138 InstructRenderProcessToInject(apply_info->tab, extension_id); | |
139 } | |
140 | |
141 virtual void Revert(const std::string& extension_id, | |
142 const base::Time& extension_install_time, | |
143 ApplyInfo* apply_info) const OVERRIDE { | |
144 } | |
145 | |
146 private: | |
147 virtual ~RequestContentScript() { | |
148 DCHECK(master_); | |
149 master_->RemoveScript(script_); | |
150 } | |
151 | |
152 void InstructRenderProcessToInject(content::WebContents* contents, | |
153 const std::string& extension_id) const; | |
154 | |
155 UserScript script_; | |
156 DeclarativeUserScriptMaster* master_; | |
157 | |
158 DISALLOW_COPY_AND_ASSIGN(RequestContentScript); | |
159 }; | |
160 | |
161 // Helper for getting JS collections into C++. | 109 // Helper for getting JS collections into C++. |
162 static bool AppendJSStringsToCPPStrings(const base::ListValue& append_strings, | 110 static bool AppendJSStringsToCPPStrings(const base::ListValue& append_strings, |
163 std::vector<std::string>* append_to) { | 111 std::vector<std::string>* append_to) { |
164 for (base::ListValue::const_iterator it = append_strings.begin(); | 112 for (base::ListValue::const_iterator it = append_strings.begin(); |
165 it != append_strings.end(); | 113 it != append_strings.end(); |
166 ++it) { | 114 ++it) { |
167 std::string value; | 115 std::string value; |
168 if ((*it)->GetAsString(&value)) { | 116 if ((*it)->GetAsString(&value)) { |
169 append_to->push_back(value); | 117 append_to->push_back(value); |
170 } else { | 118 } else { |
171 return false; | 119 return false; |
172 } | 120 } |
173 } | 121 } |
174 | 122 |
175 return true; | 123 return true; |
176 } | 124 } |
177 | 125 |
| 126 struct ContentActionFactory { |
| 127 // Factory methods for ContentAction instances. |extension| is the extension |
| 128 // for which the action is being created. |dict| contains the json dictionary |
| 129 // that describes the action. |error| is used to return error messages in case |
| 130 // the extension passed an action that was syntactically correct but |
| 131 // semantically incorrect. |bad_message| is set to true in case |dict| does |
| 132 // not confirm to the validated JSON specification. |
| 133 typedef scoped_refptr<ContentAction>(*FactoryMethod)( |
| 134 content::BrowserContext* /* browser_context */, |
| 135 const Extension* /* extension */, |
| 136 const base::DictionaryValue* /* dict */, |
| 137 std::string* /* error */, |
| 138 bool* /* bad_message */); |
| 139 // Maps the name of a declarativeContent action type to the factory |
| 140 // function creating it. |
| 141 std::map<std::string, FactoryMethod> factory_methods; |
| 142 |
| 143 ContentActionFactory() { |
| 144 factory_methods[keys::kShowPageAction] = |
| 145 &ShowPageAction::Create; |
| 146 factory_methods[keys::kRequestContentScript] = |
| 147 &RequestContentScript::Create; |
| 148 } |
| 149 }; |
| 150 |
| 151 base::LazyInstance<ContentActionFactory>::Leaky |
| 152 g_content_action_factory = LAZY_INSTANCE_INITIALIZER; |
| 153 |
| 154 } // namespace |
| 155 |
| 156 // |
| 157 // RequestContentScript |
| 158 // |
| 159 |
| 160 RequestContentScript::ScriptData::ScriptData() {} |
| 161 RequestContentScript::ScriptData::~ScriptData() {} |
| 162 |
178 // static | 163 // static |
179 scoped_refptr<ContentAction> RequestContentScript::Create( | 164 scoped_refptr<ContentAction> RequestContentScript::Create( |
180 content::BrowserContext* browser_context, | 165 content::BrowserContext* browser_context, |
181 const Extension* extension, | 166 const Extension* extension, |
182 const base::DictionaryValue* dict, | 167 const base::DictionaryValue* dict, |
183 std::string* error, | 168 std::string* error, |
184 bool* bad_message) { | 169 bool* bad_message) { |
185 std::vector<std::string> css_file_names; | 170 ScriptData script_data; |
186 std::vector<std::string> js_file_names; | 171 if (!InitScriptData(dict, error, bad_message, &script_data)) |
187 bool all_frames = false; | 172 return scoped_refptr<ContentAction>(); |
188 bool match_about_blank = false; | 173 |
189 const base::ListValue* list_value; | 174 return scoped_refptr<ContentAction>(new RequestContentScript( |
| 175 browser_context, |
| 176 extension, |
| 177 script_data)); |
| 178 } |
| 179 |
| 180 // static |
| 181 scoped_refptr<ContentAction> RequestContentScript::CreateForTest( |
| 182 DeclarativeUserScriptMaster* master, |
| 183 const Extension* extension, |
| 184 const base::Value& json_action, |
| 185 std::string* error, |
| 186 bool* bad_message) { |
| 187 // Simulate ContentAction-level initialization. Check that instance type is |
| 188 // RequestContentScript. |
| 189 ContentAction::ResetErrorData(error, bad_message); |
| 190 const base::DictionaryValue* action_dict = NULL; |
| 191 std::string instance_type; |
| 192 if (!ContentAction::Validate( |
| 193 json_action, |
| 194 error, |
| 195 bad_message, |
| 196 &action_dict, |
| 197 &instance_type) || |
| 198 instance_type != std::string(keys::kRequestContentScript)) |
| 199 return scoped_refptr<ContentAction>(); |
| 200 |
| 201 // Normal RequestContentScript data initialization. |
| 202 ScriptData script_data; |
| 203 if (!InitScriptData(action_dict, error, bad_message, &script_data)) |
| 204 return scoped_refptr<ContentAction>(); |
| 205 |
| 206 // Inject provided DeclarativeUserScriptMaster, rather than looking it up |
| 207 // using a BrowserContext. |
| 208 return scoped_refptr<ContentAction>(new RequestContentScript( |
| 209 master, |
| 210 extension, |
| 211 script_data)); |
| 212 } |
| 213 |
| 214 // static |
| 215 bool RequestContentScript::InitScriptData(const base::DictionaryValue* dict, |
| 216 std::string* error, |
| 217 bool* bad_message, |
| 218 ScriptData* script_data) { |
| 219 const base::ListValue* list_value = NULL; |
190 | 220 |
191 if (!dict->HasKey(keys::kCss) && !dict->HasKey(keys::kJs)) { | 221 if (!dict->HasKey(keys::kCss) && !dict->HasKey(keys::kJs)) { |
192 *error = base::StringPrintf(kMissingParameter, "css or js"); | 222 *error = base::StringPrintf(kMissingParameter, "css or js"); |
193 return scoped_refptr<ContentAction>(); | 223 return false; |
194 } | 224 } |
195 if (dict->HasKey(keys::kCss)) { | 225 if (dict->HasKey(keys::kCss)) { |
196 INPUT_FORMAT_VALIDATE(dict->GetList(keys::kCss, &list_value)); | 226 INPUT_FORMAT_VALIDATE(dict->GetList(keys::kCss, &list_value)); |
197 INPUT_FORMAT_VALIDATE( | 227 INPUT_FORMAT_VALIDATE( |
198 AppendJSStringsToCPPStrings(*list_value, &css_file_names)); | 228 AppendJSStringsToCPPStrings(*list_value, &script_data->css_file_names)); |
199 } | 229 } |
200 if (dict->HasKey(keys::kJs)) { | 230 if (dict->HasKey(keys::kJs)) { |
201 INPUT_FORMAT_VALIDATE(dict->GetList(keys::kJs, &list_value)); | 231 INPUT_FORMAT_VALIDATE(dict->GetList(keys::kJs, &list_value)); |
202 INPUT_FORMAT_VALIDATE( | 232 INPUT_FORMAT_VALIDATE( |
203 AppendJSStringsToCPPStrings(*list_value, &js_file_names)); | 233 AppendJSStringsToCPPStrings(*list_value, &script_data->js_file_names)); |
204 } | 234 } |
205 if (dict->HasKey(keys::kAllFrames)) { | 235 if (dict->HasKey(keys::kAllFrames)) { |
206 INPUT_FORMAT_VALIDATE(dict->GetBoolean(keys::kAllFrames, &all_frames)); | 236 INPUT_FORMAT_VALIDATE( |
| 237 dict->GetBoolean(keys::kAllFrames, &script_data->all_frames)); |
207 } | 238 } |
208 if (dict->HasKey(keys::kMatchAboutBlank)) { | 239 if (dict->HasKey(keys::kMatchAboutBlank)) { |
209 INPUT_FORMAT_VALIDATE( | 240 INPUT_FORMAT_VALIDATE( |
210 dict->GetBoolean(keys::kMatchAboutBlank, &match_about_blank)); | 241 dict->GetBoolean( |
| 242 keys::kMatchAboutBlank, |
| 243 &script_data->match_about_blank)); |
211 } | 244 } |
212 | 245 |
213 return scoped_refptr<ContentAction>(new RequestContentScript( | 246 return true; |
214 browser_context, | |
215 extension, | |
216 css_file_names, | |
217 js_file_names, | |
218 all_frames, | |
219 match_about_blank)); | |
220 } | 247 } |
221 | 248 |
222 RequestContentScript::RequestContentScript( | 249 RequestContentScript::RequestContentScript( |
223 content::BrowserContext* browser_context, | 250 content::BrowserContext* browser_context, |
224 const Extension* extension, | 251 const Extension* extension, |
225 const std::vector<std::string>& css_file_names, | 252 const ScriptData& script_data) { |
226 const std::vector<std::string>& js_file_names, | 253 InitScript(extension, script_data); |
227 bool all_frames, | 254 |
228 bool match_about_blank) { | 255 master_ = |
| 256 ExtensionSystem::Get(browser_context)-> |
| 257 GetDeclarativeUserScriptMasterByExtension(extension->id()); |
| 258 AddScript(); |
| 259 } |
| 260 |
| 261 RequestContentScript::RequestContentScript( |
| 262 DeclarativeUserScriptMaster* master, |
| 263 const Extension* extension, |
| 264 const ScriptData& script_data) { |
| 265 InitScript(extension, script_data); |
| 266 |
| 267 master_ = master; |
| 268 AddScript(); |
| 269 } |
| 270 |
| 271 RequestContentScript::~RequestContentScript() { |
| 272 DCHECK(master_); |
| 273 master_->RemoveScript(script_); |
| 274 } |
| 275 |
| 276 void RequestContentScript::InitScript(const Extension* extension, |
| 277 const ScriptData& script_data) { |
229 script_.set_id(UserScript::GenerateUserScriptID()); | 278 script_.set_id(UserScript::GenerateUserScriptID()); |
230 script_.set_extension_id(extension->id()); | 279 script_.set_extension_id(extension->id()); |
231 script_.set_run_location(UserScript::BROWSER_DRIVEN); | 280 script_.set_run_location(UserScript::BROWSER_DRIVEN); |
232 script_.set_match_all_frames(all_frames); | 281 script_.set_match_all_frames(script_data.all_frames); |
233 script_.set_match_about_blank(match_about_blank); | 282 script_.set_match_about_blank(script_data.match_about_blank); |
234 for (std::vector<std::string>::const_iterator it = css_file_names.begin(); | 283 for (std::vector<std::string>::const_iterator it = |
235 it != css_file_names.end(); ++it) { | 284 script_data.css_file_names.begin(); |
| 285 it != script_data.css_file_names.end(); ++it) { |
236 GURL url = extension->GetResourceURL(*it); | 286 GURL url = extension->GetResourceURL(*it); |
237 ExtensionResource resource = extension->GetResource(*it); | 287 ExtensionResource resource = extension->GetResource(*it); |
238 script_.css_scripts().push_back(UserScript::File( | 288 script_.css_scripts().push_back(UserScript::File( |
239 resource.extension_root(), resource.relative_path(), url)); | 289 resource.extension_root(), resource.relative_path(), url)); |
240 } | 290 } |
241 for (std::vector<std::string>::const_iterator it = js_file_names.begin(); | 291 for (std::vector<std::string>::const_iterator it = |
242 it != js_file_names.end(); ++it) { | 292 script_data.js_file_names.begin(); |
| 293 it != script_data.js_file_names.end(); ++it) { |
243 GURL url = extension->GetResourceURL(*it); | 294 GURL url = extension->GetResourceURL(*it); |
244 ExtensionResource resource = extension->GetResource(*it); | 295 ExtensionResource resource = extension->GetResource(*it); |
245 script_.js_scripts().push_back(UserScript::File( | 296 script_.js_scripts().push_back(UserScript::File( |
246 resource.extension_root(), resource.relative_path(), url)); | 297 resource.extension_root(), resource.relative_path(), url)); |
247 } | 298 } |
| 299 } |
248 | 300 |
249 master_ = | 301 ContentAction::Type RequestContentScript::GetType() const { |
250 ExtensionSystem::Get(browser_context)-> | 302 return ACTION_REQUEST_CONTENT_SCRIPT; |
251 GetDeclarativeUserScriptMasterByExtension(extension->id()); | |
252 DCHECK(master_); | |
253 master_->AddScript(script_); | |
254 } | 303 } |
255 | 304 |
| 305 void RequestContentScript::Apply(const std::string& extension_id, |
| 306 const base::Time& extension_install_time, |
| 307 ApplyInfo* apply_info) const { |
| 308 InstructRenderProcessToInject(apply_info->tab, extension_id); |
| 309 } |
| 310 |
| 311 void RequestContentScript::Reapply(const std::string& extension_id, |
| 312 const base::Time& extension_install_time, |
| 313 ApplyInfo* apply_info) const { |
| 314 InstructRenderProcessToInject(apply_info->tab, extension_id); |
| 315 } |
| 316 |
| 317 void RequestContentScript::Revert(const std::string& extension_id, |
| 318 const base::Time& extension_install_time, |
| 319 ApplyInfo* apply_info) const {} |
| 320 |
256 void RequestContentScript::InstructRenderProcessToInject( | 321 void RequestContentScript::InstructRenderProcessToInject( |
257 content::WebContents* contents, | 322 content::WebContents* contents, |
258 const std::string& extension_id) const { | 323 const std::string& extension_id) const { |
259 // TODO(markdittmer): Send ExtensionMsg to renderer. | 324 content::RenderViewHost* render_view_host = contents->GetRenderViewHost(); |
| 325 render_view_host->Send(new ExtensionMsg_ExecuteDeclarativeScript( |
| 326 render_view_host->GetRoutingID(), |
| 327 SessionTabHelper::IdForTab(contents), |
| 328 extension_id, |
| 329 script_.id(), |
| 330 contents->GetLastCommittedURL())); |
260 } | 331 } |
261 | 332 |
262 struct ContentActionFactory { | |
263 // Factory methods for ContentAction instances. |extension| is the extension | |
264 // for which the action is being created. |dict| contains the json dictionary | |
265 // that describes the action. |error| is used to return error messages in case | |
266 // the extension passed an action that was syntactically correct but | |
267 // semantically incorrect. |bad_message| is set to true in case |dict| does | |
268 // not confirm to the validated JSON specification. | |
269 typedef scoped_refptr<ContentAction>(*FactoryMethod)( | |
270 content::BrowserContext* /* browser_context */, | |
271 const Extension* /* extension */, | |
272 const base::DictionaryValue* /* dict */, | |
273 std::string* /* error */, | |
274 bool* /* bad_message */); | |
275 // Maps the name of a declarativeContent action type to the factory | |
276 // function creating it. | |
277 std::map<std::string, FactoryMethod> factory_methods; | |
278 | |
279 ContentActionFactory() { | |
280 factory_methods[keys::kShowPageAction] = | |
281 &ShowPageAction::Create; | |
282 factory_methods[keys::kRequestContentScript] = | |
283 &RequestContentScript::Create; | |
284 } | |
285 }; | |
286 | |
287 base::LazyInstance<ContentActionFactory>::Leaky | |
288 g_content_action_factory = LAZY_INSTANCE_INITIALIZER; | |
289 | |
290 } // namespace | |
291 | |
292 // | 333 // |
293 // ContentAction | 334 // ContentAction |
294 // | 335 // |
295 | 336 |
296 ContentAction::ContentAction() {} | 337 ContentAction::ContentAction() {} |
297 | 338 |
298 ContentAction::~ContentAction() {} | 339 ContentAction::~ContentAction() {} |
299 | 340 |
300 // static | 341 // static |
301 scoped_refptr<ContentAction> ContentAction::Create( | 342 scoped_refptr<ContentAction> ContentAction::Create( |
302 content::BrowserContext* browser_context, | 343 content::BrowserContext* browser_context, |
303 const Extension* extension, | 344 const Extension* extension, |
304 const base::Value& json_action, | 345 const base::Value& json_action, |
305 std::string* error, | 346 std::string* error, |
306 bool* bad_message) { | 347 bool* bad_message) { |
307 *error = ""; | 348 ResetErrorData(error, bad_message); |
308 *bad_message = false; | |
309 | |
310 const base::DictionaryValue* action_dict = NULL; | 349 const base::DictionaryValue* action_dict = NULL; |
311 INPUT_FORMAT_VALIDATE(json_action.GetAsDictionary(&action_dict)); | |
312 | |
313 std::string instance_type; | 350 std::string instance_type; |
314 INPUT_FORMAT_VALIDATE( | 351 if (!Validate(json_action, error, bad_message, &action_dict, &instance_type)) |
315 action_dict->GetString(keys::kInstanceType, &instance_type)); | 352 return scoped_refptr<ContentAction>(); |
316 | 353 |
317 ContentActionFactory& factory = g_content_action_factory.Get(); | 354 ContentActionFactory& factory = g_content_action_factory.Get(); |
318 std::map<std::string, ContentActionFactory::FactoryMethod>::iterator | 355 std::map<std::string, ContentActionFactory::FactoryMethod>::iterator |
319 factory_method_iter = factory.factory_methods.find(instance_type); | 356 factory_method_iter = factory.factory_methods.find(instance_type); |
320 if (factory_method_iter != factory.factory_methods.end()) | 357 if (factory_method_iter != factory.factory_methods.end()) |
321 return (*factory_method_iter->second)( | 358 return (*factory_method_iter->second)( |
322 browser_context, extension, action_dict, error, bad_message); | 359 browser_context, extension, action_dict, error, bad_message); |
323 | 360 |
324 *error = base::StringPrintf(kInvalidInstanceTypeError, instance_type.c_str()); | 361 *error = base::StringPrintf(kInvalidInstanceTypeError, instance_type.c_str()); |
325 return scoped_refptr<ContentAction>(); | 362 return scoped_refptr<ContentAction>(); |
326 } | 363 } |
327 | 364 |
| 365 bool ContentAction::Validate(const base::Value& json_action, |
| 366 std::string* error, |
| 367 bool* bad_message, |
| 368 const base::DictionaryValue** action_dict, |
| 369 std::string* instance_type) { |
| 370 INPUT_FORMAT_VALIDATE(json_action.GetAsDictionary(action_dict)); |
| 371 INPUT_FORMAT_VALIDATE( |
| 372 (*action_dict)->GetString(keys::kInstanceType, instance_type)); |
| 373 return true; |
| 374 } |
| 375 |
328 } // namespace extensions | 376 } // namespace extensions |
OLD | NEW |