Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Side by Side Diff: chrome/common/extensions/manifest_handlers/content_scripts_handler.cc

Issue 11724002: Move ContentScripts out of Extension (Closed) Base URL: http://git.chromium.org/chromium/src.git@dc_unref_browser_action
Patch Set: Latest master for CQ Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013 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 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
6
7 #include "base/file_util.h"
8 #include "base/lazy_instance.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/string_number_conversions.h"
11 #include "base/string_util.h"
12 #include "base/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/common/extensions/extension.h"
15 #include "chrome/common/extensions/extension_manifest_constants.h"
16 #include "content/public/common/url_constants.h"
17 #include "extensions/common/error_utils.h"
18 #include "extensions/common/extension_resource.h"
19 #include "extensions/common/url_pattern.h"
20 #include "googleurl/src/gurl.h"
21 #include "grit/generated_resources.h"
22 #include "ui/base/l10n/l10n_util.h"
23
24 namespace extensions {
25
26 namespace keys = extension_manifest_keys;
27 namespace values = extension_manifest_values;
28 namespace errors = extension_manifest_errors;
29
30 namespace {
31
32 // Helper method that loads either the include_globs or exclude_globs list
33 // from an entry in the content_script lists of the manifest.
34 bool LoadGlobsHelper(const DictionaryValue* content_script,
35 int content_script_index,
36 const char* globs_property_name,
37 string16* error,
38 void(UserScript::*add_method)(const std::string& glob),
39 UserScript* instance) {
40 if (!content_script->HasKey(globs_property_name))
41 return true; // they are optional
42
43 const ListValue* list = NULL;
44 if (!content_script->GetList(globs_property_name, &list)) {
45 *error = ErrorUtils::FormatErrorMessageUTF16(
46 errors::kInvalidGlobList,
47 base::IntToString(content_script_index),
48 globs_property_name);
49 return false;
50 }
51
52 for (size_t i = 0; i < list->GetSize(); ++i) {
53 std::string glob;
54 if (!list->GetString(i, &glob)) {
55 *error = ErrorUtils::FormatErrorMessageUTF16(
56 errors::kInvalidGlob,
57 base::IntToString(content_script_index),
58 globs_property_name,
59 base::IntToString(i));
60 return false;
61 }
62
63 (instance->*add_method)(glob);
64 }
65
66 return true;
67 }
68
69 // Helper method that loads a UserScript object from a dictionary in the
70 // content_script list of the manifest.
71 bool LoadUserScriptFromDictionary(const DictionaryValue* content_script,
72 int definition_index,
73 Extension* extension,
74 string16* error,
75 UserScript* result) {
76 // run_at
77 if (content_script->HasKey(keys::kRunAt)) {
78 std::string run_location;
79 if (!content_script->GetString(keys::kRunAt, &run_location)) {
80 *error = ErrorUtils::FormatErrorMessageUTF16(
81 errors::kInvalidRunAt,
82 base::IntToString(definition_index));
83 return false;
84 }
85
86 if (run_location == values::kRunAtDocumentStart) {
87 result->set_run_location(UserScript::DOCUMENT_START);
88 } else if (run_location == values::kRunAtDocumentEnd) {
89 result->set_run_location(UserScript::DOCUMENT_END);
90 } else if (run_location == values::kRunAtDocumentIdle) {
91 result->set_run_location(UserScript::DOCUMENT_IDLE);
92 } else {
93 *error = ErrorUtils::FormatErrorMessageUTF16(
94 errors::kInvalidRunAt,
95 base::IntToString(definition_index));
96 return false;
97 }
98 }
99
100 // all frames
101 if (content_script->HasKey(keys::kAllFrames)) {
102 bool all_frames = false;
103 if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) {
104 *error = ErrorUtils::FormatErrorMessageUTF16(
105 errors::kInvalidAllFrames, base::IntToString(definition_index));
106 return false;
107 }
108 result->set_match_all_frames(all_frames);
109 }
110
111 // matches (required)
112 const ListValue* matches = NULL;
113 if (!content_script->GetList(keys::kMatches, &matches)) {
114 *error = ErrorUtils::FormatErrorMessageUTF16(
115 errors::kInvalidMatches,
116 base::IntToString(definition_index));
117 return false;
118 }
119
120 if (matches->GetSize() == 0) {
121 *error = ErrorUtils::FormatErrorMessageUTF16(
122 errors::kInvalidMatchCount,
123 base::IntToString(definition_index));
124 return false;
125 }
126 for (size_t j = 0; j < matches->GetSize(); ++j) {
127 std::string match_str;
128 if (!matches->GetString(j, &match_str)) {
129 *error = ErrorUtils::FormatErrorMessageUTF16(
130 errors::kInvalidMatch,
131 base::IntToString(definition_index),
132 base::IntToString(j),
133 errors::kExpectString);
134 return false;
135 }
136
137 URLPattern pattern(UserScript::ValidUserScriptSchemes(
138 extension->CanExecuteScriptEverywhere()));
139
140 URLPattern::ParseResult parse_result = pattern.Parse(match_str);
141 if (parse_result != URLPattern::PARSE_SUCCESS) {
142 *error = ErrorUtils::FormatErrorMessageUTF16(
143 errors::kInvalidMatch,
144 base::IntToString(definition_index),
145 base::IntToString(j),
146 URLPattern::GetParseResultString(parse_result));
147 return false;
148 }
149
150 // TODO(aboxhall): check for webstore
151 if (!extension->CanExecuteScriptEverywhere() &&
152 pattern.scheme() != chrome::kChromeUIScheme) {
153 // Exclude SCHEME_CHROMEUI unless it's been explicitly requested.
154 // If the --extensions-on-chrome-urls flag has not been passed, requesting
155 // a chrome:// url will cause a parse failure above, so there's no need to
156 // check the flag here.
157 pattern.SetValidSchemes(
158 pattern.valid_schemes() & ~URLPattern::SCHEME_CHROMEUI);
159 }
160
161 if (pattern.MatchesScheme(chrome::kFileScheme) &&
162 !extension->CanExecuteScriptEverywhere()) {
163 extension->set_wants_file_access(true);
164 if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS)) {
165 pattern.SetValidSchemes(
166 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
167 }
168 }
169
170 result->add_url_pattern(pattern);
171 }
172
173 // exclude_matches
174 if (content_script->HasKey(keys::kExcludeMatches)) { // optional
175 const ListValue* exclude_matches = NULL;
176 if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) {
177 *error = ErrorUtils::FormatErrorMessageUTF16(
178 errors::kInvalidExcludeMatches,
179 base::IntToString(definition_index));
180 return false;
181 }
182
183 for (size_t j = 0; j < exclude_matches->GetSize(); ++j) {
184 std::string match_str;
185 if (!exclude_matches->GetString(j, &match_str)) {
186 *error = ErrorUtils::FormatErrorMessageUTF16(
187 errors::kInvalidExcludeMatch,
188 base::IntToString(definition_index),
189 base::IntToString(j),
190 errors::kExpectString);
191 return false;
192 }
193
194 int valid_schemes = UserScript::ValidUserScriptSchemes(
195 extension->CanExecuteScriptEverywhere());
196 URLPattern pattern(valid_schemes);
197
198 URLPattern::ParseResult parse_result = pattern.Parse(match_str);
199 if (parse_result != URLPattern::PARSE_SUCCESS) {
200 *error = ErrorUtils::FormatErrorMessageUTF16(
201 errors::kInvalidExcludeMatch,
202 base::IntToString(definition_index), base::IntToString(j),
203 URLPattern::GetParseResultString(parse_result));
204 return false;
205 }
206
207 result->add_exclude_url_pattern(pattern);
208 }
209 }
210
211 // include/exclude globs (mostly for Greasemonkey compatibility)
212 if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs,
213 error, &UserScript::add_glob, result)) {
214 return false;
215 }
216
217 if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs,
218 error, &UserScript::add_exclude_glob, result)) {
219 return false;
220 }
221
222 // js and css keys
223 const ListValue* js = NULL;
224 if (content_script->HasKey(keys::kJs) &&
225 !content_script->GetList(keys::kJs, &js)) {
226 *error = ErrorUtils::FormatErrorMessageUTF16(
227 errors::kInvalidJsList,
228 base::IntToString(definition_index));
229 return false;
230 }
231
232 const ListValue* css = NULL;
233 if (content_script->HasKey(keys::kCss) &&
234 !content_script->GetList(keys::kCss, &css)) {
235 *error = ErrorUtils::
236 FormatErrorMessageUTF16(errors::kInvalidCssList,
237 base::IntToString(definition_index));
238 return false;
239 }
240
241 // The manifest needs to have at least one js or css user script definition.
242 if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) {
243 *error = ErrorUtils::FormatErrorMessageUTF16(
244 errors::kMissingFile,
245 base::IntToString(definition_index));
246 return false;
247 }
248
249 if (js) {
250 for (size_t script_index = 0; script_index < js->GetSize();
251 ++script_index) {
252 const Value* value;
253 std::string relative;
254 if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) {
255 *error = ErrorUtils::FormatErrorMessageUTF16(
256 errors::kInvalidJs,
257 base::IntToString(definition_index),
258 base::IntToString(script_index));
259 return false;
260 }
261 GURL url = extension->GetResourceURL(relative);
262 ExtensionResource resource = extension->GetResource(relative);
263 result->js_scripts().push_back(UserScript::File(
264 resource.extension_root(), resource.relative_path(), url));
265 }
266 }
267
268 if (css) {
269 for (size_t script_index = 0; script_index < css->GetSize();
270 ++script_index) {
271 const Value* value;
272 std::string relative;
273 if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) {
274 *error = ErrorUtils::FormatErrorMessageUTF16(
275 errors::kInvalidCss,
276 base::IntToString(definition_index),
277 base::IntToString(script_index));
278 return false;
279 }
280 GURL url = extension->GetResourceURL(relative);
281 ExtensionResource resource = extension->GetResource(relative);
282 result->css_scripts().push_back(UserScript::File(
283 resource.extension_root(), resource.relative_path(), url));
284 }
285 }
286
287 return true;
288 }
289
290 // Returns false and sets the error if script file can't be loaded,
291 // or if it's not UTF-8 encoded.
292 static bool IsScriptValid(const base::FilePath& path,
293 const base::FilePath& relative_path,
294 int message_id,
295 std::string* error) {
296 std::string content;
297 if (!file_util::PathExists(path) ||
298 !file_util::ReadFileToString(path, &content)) {
299 *error = l10n_util::GetStringFUTF8(
300 message_id,
301 relative_path.LossyDisplayName());
302 return false;
303 }
304
305 if (!IsStringUTF8(content)) {
306 *error = l10n_util::GetStringFUTF8(
307 IDS_EXTENSION_BAD_FILE_ENCODING,
308 relative_path.LossyDisplayName());
309 return false;
310 }
311
312 return true;
313 }
314
315 struct EmptyUserScriptList {
316 UserScriptList user_script_list;
317 };
318
319 static base::LazyInstance<EmptyUserScriptList> g_empty_script_list =
320 LAZY_INSTANCE_INITIALIZER;
321
322 } // namespace
323
324 ContentScriptsInfo::ContentScriptsInfo() {
325 }
326
327 ContentScriptsInfo::~ContentScriptsInfo() {
328 }
329
330 // static
331 const UserScriptList& ContentScriptsInfo::GetContentScripts(
332 const Extension* extension) {
333 ContentScriptsInfo* info = static_cast<ContentScriptsInfo*>(
334 extension->GetManifestData(keys::kContentScripts));
335 return info ? info->content_scripts
336 : g_empty_script_list.Get().user_script_list;
337 }
338
339 // static
340 bool ContentScriptsInfo::ExtensionHasScriptAtURL(const Extension* extension,
341 const GURL& url) {
342 const UserScriptList& content_scripts = GetContentScripts(extension);
343 for (UserScriptList::const_iterator iter = content_scripts.begin();
344 iter != content_scripts.end(); ++iter) {
345 if (iter->MatchesURL(url))
346 return true;
347 }
348 return false;
349 }
350
351 ContentScriptsHandler::ContentScriptsHandler() {
352 }
353
354 ContentScriptsHandler::~ContentScriptsHandler() {
355 }
356
357 const std::vector<std::string> ContentScriptsHandler::Keys() const {
358 static const char* keys[] = {
359 keys::kContentScripts
360 };
361 return std::vector<std::string>(keys, keys + arraysize(keys));
362 }
363
364 bool ContentScriptsHandler::Parse(Extension* extension, string16* error) {
365 scoped_ptr<ContentScriptsInfo> content_scripts_info(new ContentScriptsInfo);
366 const ListValue* scripts_list = NULL;
367 if (!extension->manifest()->GetList(keys::kContentScripts, &scripts_list)) {
368 *error = ASCIIToUTF16(errors::kInvalidContentScriptsList);
369 return false;
370 }
371
372 for (size_t i = 0; i < scripts_list->GetSize(); ++i) {
373 const DictionaryValue* script_dict = NULL;
374 if (!scripts_list->GetDictionary(i, &script_dict)) {
375 *error = ErrorUtils::FormatErrorMessageUTF16(
376 errors::kInvalidContentScript,
377 base::IntToString(i));
378 return false;
379 }
380
381 UserScript user_script;
382 if (!LoadUserScriptFromDictionary(script_dict,
383 i,
384 extension,
385 error,
386 &user_script)) {
387 return false; // Failed to parse script context definition.
388 }
389
390 user_script.set_extension_id(extension->id());
391 if (extension->converted_from_user_script()) {
392 user_script.set_emulate_greasemonkey(true);
393 // Greasemonkey matches all frames.
394 user_script.set_match_all_frames(true);
395 }
396 content_scripts_info->content_scripts.push_back(user_script);
397 }
398 extension->SetManifestData(keys::kContentScripts,
399 content_scripts_info.release());
400 return true;
401 }
402
403 bool ContentScriptsHandler::Validate(
404 const Extension* extension,
405 std::string* error,
406 std::vector<InstallWarning>* warnings) const {
407 // Validate that claimed script resources actually exist,
408 // and are UTF-8 encoded.
409 ExtensionResource::SymlinkPolicy symlink_policy;
410 if ((extension->creation_flags() &
411 Extension::FOLLOW_SYMLINKS_ANYWHERE) != 0) {
412 symlink_policy = ExtensionResource::FOLLOW_SYMLINKS_ANYWHERE;
413 } else {
414 symlink_policy = ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT;
415 }
416
417 const extensions::UserScriptList& content_scripts =
418 extensions::ContentScriptsInfo::GetContentScripts(extension);
419 for (size_t i = 0; i < content_scripts.size(); ++i) {
420 const extensions::UserScript& script = content_scripts[i];
421
422 for (size_t j = 0; j < script.js_scripts().size(); j++) {
423 const extensions::UserScript::File& js_script = script.js_scripts()[j];
424 const base::FilePath& path = ExtensionResource::GetFilePath(
425 js_script.extension_root(), js_script.relative_path(),
426 symlink_policy);
427 if (!IsScriptValid(path, js_script.relative_path(),
428 IDS_EXTENSION_LOAD_JAVASCRIPT_FAILED, error))
429 return false;
430 }
431
432 for (size_t j = 0; j < script.css_scripts().size(); j++) {
433 const extensions::UserScript::File& css_script = script.css_scripts()[j];
434 const base::FilePath& path = ExtensionResource::GetFilePath(
435 css_script.extension_root(), css_script.relative_path(),
436 symlink_policy);
437 if (!IsScriptValid(path, css_script.relative_path(),
438 IDS_EXTENSION_LOAD_CSS_FAILED, error))
439 return false;
440 }
441 }
442
443 return true;
444 }
445
446 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698