Delete chrome.mediaGalleriesPrivate because the functionality unique to it has since...
[chromium-blink-merge.git] / chrome / common / extensions / manifest_handlers / content_scripts_handler.cc
blob7f7cba97a23f6ed9ec4d224d4045705af49098b8
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.
5 #include "chrome/common/extensions/manifest_handlers/content_scripts_handler.h"
7 #include "base/files/file_util.h"
8 #include "base/lazy_instance.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/grit/generated_resources.h"
15 #include "content/public/common/url_constants.h"
16 #include "extensions/common/error_utils.h"
17 #include "extensions/common/extension.h"
18 #include "extensions/common/extension_resource.h"
19 #include "extensions/common/manifest_constants.h"
20 #include "extensions/common/manifest_handlers/permissions_parser.h"
21 #include "extensions/common/permissions/permissions_data.h"
22 #include "extensions/common/url_pattern.h"
23 #include "extensions/common/url_pattern_set.h"
24 #include "ui/base/l10n/l10n_util.h"
25 #include "url/gurl.h"
27 namespace extensions {
29 namespace keys = extensions::manifest_keys;
30 namespace values = manifest_values;
31 namespace errors = manifest_errors;
33 namespace {
35 // Helper method that loads either the include_globs or exclude_globs list
36 // from an entry in the content_script lists of the manifest.
37 bool LoadGlobsHelper(const base::DictionaryValue* content_script,
38 int content_script_index,
39 const char* globs_property_name,
40 base::string16* error,
41 void(UserScript::*add_method)(const std::string& glob),
42 UserScript* instance) {
43 if (!content_script->HasKey(globs_property_name))
44 return true; // they are optional
46 const base::ListValue* list = NULL;
47 if (!content_script->GetList(globs_property_name, &list)) {
48 *error = ErrorUtils::FormatErrorMessageUTF16(
49 errors::kInvalidGlobList,
50 base::IntToString(content_script_index),
51 globs_property_name);
52 return false;
55 for (size_t i = 0; i < list->GetSize(); ++i) {
56 std::string glob;
57 if (!list->GetString(i, &glob)) {
58 *error = ErrorUtils::FormatErrorMessageUTF16(
59 errors::kInvalidGlob,
60 base::IntToString(content_script_index),
61 globs_property_name,
62 base::IntToString(i));
63 return false;
66 (instance->*add_method)(glob);
69 return true;
72 // Helper method that loads a UserScript object from a dictionary in the
73 // content_script list of the manifest.
74 bool LoadUserScriptFromDictionary(const base::DictionaryValue* content_script,
75 int definition_index,
76 Extension* extension,
77 base::string16* error,
78 UserScript* result) {
79 // run_at
80 if (content_script->HasKey(keys::kRunAt)) {
81 std::string run_location;
82 if (!content_script->GetString(keys::kRunAt, &run_location)) {
83 *error = ErrorUtils::FormatErrorMessageUTF16(
84 errors::kInvalidRunAt,
85 base::IntToString(definition_index));
86 return false;
89 if (run_location == values::kRunAtDocumentStart) {
90 result->set_run_location(UserScript::DOCUMENT_START);
91 } else if (run_location == values::kRunAtDocumentEnd) {
92 result->set_run_location(UserScript::DOCUMENT_END);
93 } else if (run_location == values::kRunAtDocumentIdle) {
94 result->set_run_location(UserScript::DOCUMENT_IDLE);
95 } else {
96 *error = ErrorUtils::FormatErrorMessageUTF16(
97 errors::kInvalidRunAt,
98 base::IntToString(definition_index));
99 return false;
103 // all frames
104 if (content_script->HasKey(keys::kAllFrames)) {
105 bool all_frames = false;
106 if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) {
107 *error = ErrorUtils::FormatErrorMessageUTF16(
108 errors::kInvalidAllFrames, base::IntToString(definition_index));
109 return false;
111 result->set_match_all_frames(all_frames);
114 // match about blank
115 if (content_script->HasKey(keys::kMatchAboutBlank)) {
116 bool match_about_blank = false;
117 if (!content_script->GetBoolean(keys::kMatchAboutBlank,
118 &match_about_blank)) {
119 *error = ErrorUtils::FormatErrorMessageUTF16(
120 errors::kInvalidMatchAboutBlank, base::IntToString(definition_index));
121 return false;
123 result->set_match_about_blank(match_about_blank);
126 // matches (required)
127 const base::ListValue* matches = NULL;
128 if (!content_script->GetList(keys::kMatches, &matches)) {
129 *error = ErrorUtils::FormatErrorMessageUTF16(
130 errors::kInvalidMatches,
131 base::IntToString(definition_index));
132 return false;
135 if (matches->GetSize() == 0) {
136 *error = ErrorUtils::FormatErrorMessageUTF16(
137 errors::kInvalidMatchCount,
138 base::IntToString(definition_index));
139 return false;
141 for (size_t j = 0; j < matches->GetSize(); ++j) {
142 std::string match_str;
143 if (!matches->GetString(j, &match_str)) {
144 *error = ErrorUtils::FormatErrorMessageUTF16(
145 errors::kInvalidMatch,
146 base::IntToString(definition_index),
147 base::IntToString(j),
148 errors::kExpectString);
149 return false;
152 URLPattern pattern(UserScript::ValidUserScriptSchemes(
153 PermissionsData::CanExecuteScriptEverywhere(extension)));
155 URLPattern::ParseResult parse_result = pattern.Parse(match_str);
156 if (parse_result != URLPattern::PARSE_SUCCESS) {
157 *error = ErrorUtils::FormatErrorMessageUTF16(
158 errors::kInvalidMatch,
159 base::IntToString(definition_index),
160 base::IntToString(j),
161 URLPattern::GetParseResultString(parse_result));
162 return false;
165 // TODO(aboxhall): check for webstore
166 if (!PermissionsData::CanExecuteScriptEverywhere(extension) &&
167 pattern.scheme() != content::kChromeUIScheme) {
168 // Exclude SCHEME_CHROMEUI unless it's been explicitly requested.
169 // If the --extensions-on-chrome-urls flag has not been passed, requesting
170 // a chrome:// url will cause a parse failure above, so there's no need to
171 // check the flag here.
172 pattern.SetValidSchemes(
173 pattern.valid_schemes() & ~URLPattern::SCHEME_CHROMEUI);
176 if (pattern.MatchesScheme(url::kFileScheme) &&
177 !PermissionsData::CanExecuteScriptEverywhere(extension)) {
178 extension->set_wants_file_access(true);
179 if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS)) {
180 pattern.SetValidSchemes(
181 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
185 result->add_url_pattern(pattern);
188 // exclude_matches
189 if (content_script->HasKey(keys::kExcludeMatches)) { // optional
190 const base::ListValue* exclude_matches = NULL;
191 if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) {
192 *error = ErrorUtils::FormatErrorMessageUTF16(
193 errors::kInvalidExcludeMatches,
194 base::IntToString(definition_index));
195 return false;
198 for (size_t j = 0; j < exclude_matches->GetSize(); ++j) {
199 std::string match_str;
200 if (!exclude_matches->GetString(j, &match_str)) {
201 *error = ErrorUtils::FormatErrorMessageUTF16(
202 errors::kInvalidExcludeMatch,
203 base::IntToString(definition_index),
204 base::IntToString(j),
205 errors::kExpectString);
206 return false;
209 int valid_schemes = UserScript::ValidUserScriptSchemes(
210 PermissionsData::CanExecuteScriptEverywhere(extension));
211 URLPattern pattern(valid_schemes);
213 URLPattern::ParseResult parse_result = pattern.Parse(match_str);
214 if (parse_result != URLPattern::PARSE_SUCCESS) {
215 *error = ErrorUtils::FormatErrorMessageUTF16(
216 errors::kInvalidExcludeMatch,
217 base::IntToString(definition_index), base::IntToString(j),
218 URLPattern::GetParseResultString(parse_result));
219 return false;
222 result->add_exclude_url_pattern(pattern);
226 // include/exclude globs (mostly for Greasemonkey compatibility)
227 if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs,
228 error, &UserScript::add_glob, result)) {
229 return false;
232 if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs,
233 error, &UserScript::add_exclude_glob, result)) {
234 return false;
237 // js and css keys
238 const base::ListValue* js = NULL;
239 if (content_script->HasKey(keys::kJs) &&
240 !content_script->GetList(keys::kJs, &js)) {
241 *error = ErrorUtils::FormatErrorMessageUTF16(
242 errors::kInvalidJsList,
243 base::IntToString(definition_index));
244 return false;
247 const base::ListValue* css = NULL;
248 if (content_script->HasKey(keys::kCss) &&
249 !content_script->GetList(keys::kCss, &css)) {
250 *error = ErrorUtils::
251 FormatErrorMessageUTF16(errors::kInvalidCssList,
252 base::IntToString(definition_index));
253 return false;
256 // The manifest needs to have at least one js or css user script definition.
257 if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) {
258 *error = ErrorUtils::FormatErrorMessageUTF16(
259 errors::kMissingFile,
260 base::IntToString(definition_index));
261 return false;
264 if (js) {
265 for (size_t script_index = 0; script_index < js->GetSize();
266 ++script_index) {
267 const base::Value* value;
268 std::string relative;
269 if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) {
270 *error = ErrorUtils::FormatErrorMessageUTF16(
271 errors::kInvalidJs,
272 base::IntToString(definition_index),
273 base::IntToString(script_index));
274 return false;
276 GURL url = extension->GetResourceURL(relative);
277 ExtensionResource resource = extension->GetResource(relative);
278 result->js_scripts().push_back(UserScript::File(
279 resource.extension_root(), resource.relative_path(), url));
283 if (css) {
284 for (size_t script_index = 0; script_index < css->GetSize();
285 ++script_index) {
286 const base::Value* value;
287 std::string relative;
288 if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) {
289 *error = ErrorUtils::FormatErrorMessageUTF16(
290 errors::kInvalidCss,
291 base::IntToString(definition_index),
292 base::IntToString(script_index));
293 return false;
295 GURL url = extension->GetResourceURL(relative);
296 ExtensionResource resource = extension->GetResource(relative);
297 result->css_scripts().push_back(UserScript::File(
298 resource.extension_root(), resource.relative_path(), url));
302 return true;
305 // Returns false and sets the error if script file can't be loaded,
306 // or if it's not UTF-8 encoded.
307 static bool IsScriptValid(const base::FilePath& path,
308 const base::FilePath& relative_path,
309 int message_id,
310 std::string* error) {
311 std::string content;
312 if (!base::PathExists(path) ||
313 !base::ReadFileToString(path, &content)) {
314 *error = l10n_util::GetStringFUTF8(
315 message_id,
316 relative_path.LossyDisplayName());
317 return false;
320 if (!base::IsStringUTF8(content)) {
321 *error = l10n_util::GetStringFUTF8(
322 IDS_EXTENSION_BAD_FILE_ENCODING,
323 relative_path.LossyDisplayName());
324 return false;
327 return true;
330 struct EmptyUserScriptList {
331 UserScriptList user_script_list;
334 static base::LazyInstance<EmptyUserScriptList> g_empty_script_list =
335 LAZY_INSTANCE_INITIALIZER;
337 } // namespace
339 ContentScriptsInfo::ContentScriptsInfo() {
342 ContentScriptsInfo::~ContentScriptsInfo() {
345 // static
346 const UserScriptList& ContentScriptsInfo::GetContentScripts(
347 const Extension* extension) {
348 ContentScriptsInfo* info = static_cast<ContentScriptsInfo*>(
349 extension->GetManifestData(keys::kContentScripts));
350 return info ? info->content_scripts
351 : g_empty_script_list.Get().user_script_list;
354 // static
355 bool ContentScriptsInfo::ExtensionHasScriptAtURL(const Extension* extension,
356 const GURL& url) {
357 const UserScriptList& content_scripts = GetContentScripts(extension);
358 for (UserScriptList::const_iterator iter = content_scripts.begin();
359 iter != content_scripts.end(); ++iter) {
360 if (iter->MatchesURL(url))
361 return true;
363 return false;
366 // static
367 URLPatternSet ContentScriptsInfo::GetScriptableHosts(
368 const Extension* extension) {
369 const UserScriptList& content_scripts = GetContentScripts(extension);
370 URLPatternSet scriptable_hosts;
371 for (UserScriptList::const_iterator content_script =
372 content_scripts.begin();
373 content_script != content_scripts.end();
374 ++content_script) {
375 URLPatternSet::const_iterator pattern =
376 content_script->url_patterns().begin();
377 for (; pattern != content_script->url_patterns().end(); ++pattern)
378 scriptable_hosts.AddPattern(*pattern);
380 return scriptable_hosts;
383 ContentScriptsHandler::ContentScriptsHandler() {
386 ContentScriptsHandler::~ContentScriptsHandler() {
389 const std::vector<std::string> ContentScriptsHandler::Keys() const {
390 static const char* const keys[] = {
391 keys::kContentScripts
393 return std::vector<std::string>(keys, keys + arraysize(keys));
396 bool ContentScriptsHandler::Parse(Extension* extension, base::string16* error) {
397 scoped_ptr<ContentScriptsInfo> content_scripts_info(new ContentScriptsInfo);
398 const base::ListValue* scripts_list = NULL;
399 if (!extension->manifest()->GetList(keys::kContentScripts, &scripts_list)) {
400 *error = base::ASCIIToUTF16(errors::kInvalidContentScriptsList);
401 return false;
404 for (size_t i = 0; i < scripts_list->GetSize(); ++i) {
405 const base::DictionaryValue* script_dict = NULL;
406 if (!scripts_list->GetDictionary(i, &script_dict)) {
407 *error = ErrorUtils::FormatErrorMessageUTF16(
408 errors::kInvalidContentScript,
409 base::IntToString(i));
410 return false;
413 UserScript user_script;
414 if (!LoadUserScriptFromDictionary(script_dict,
416 extension,
417 error,
418 &user_script)) {
419 return false; // Failed to parse script context definition.
422 user_script.set_extension_id(extension->id());
423 if (extension->converted_from_user_script()) {
424 user_script.set_emulate_greasemonkey(true);
425 // Greasemonkey matches all frames.
426 user_script.set_match_all_frames(true);
428 user_script.set_id(UserScript::GenerateUserScriptID());
429 content_scripts_info->content_scripts.push_back(user_script);
431 extension->SetManifestData(keys::kContentScripts,
432 content_scripts_info.release());
433 PermissionsParser::SetScriptableHosts(
434 extension, ContentScriptsInfo::GetScriptableHosts(extension));
435 return true;
438 bool ContentScriptsHandler::Validate(
439 const Extension* extension,
440 std::string* error,
441 std::vector<InstallWarning>* warnings) const {
442 // Validate that claimed script resources actually exist,
443 // and are UTF-8 encoded.
444 ExtensionResource::SymlinkPolicy symlink_policy;
445 if ((extension->creation_flags() &
446 Extension::FOLLOW_SYMLINKS_ANYWHERE) != 0) {
447 symlink_policy = ExtensionResource::FOLLOW_SYMLINKS_ANYWHERE;
448 } else {
449 symlink_policy = ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT;
452 const UserScriptList& content_scripts =
453 ContentScriptsInfo::GetContentScripts(extension);
454 for (size_t i = 0; i < content_scripts.size(); ++i) {
455 const UserScript& script = content_scripts[i];
457 for (size_t j = 0; j < script.js_scripts().size(); j++) {
458 const UserScript::File& js_script = script.js_scripts()[j];
459 const base::FilePath& path = ExtensionResource::GetFilePath(
460 js_script.extension_root(), js_script.relative_path(),
461 symlink_policy);
462 if (!IsScriptValid(path, js_script.relative_path(),
463 IDS_EXTENSION_LOAD_JAVASCRIPT_FAILED, error))
464 return false;
467 for (size_t j = 0; j < script.css_scripts().size(); j++) {
468 const UserScript::File& css_script = script.css_scripts()[j];
469 const base::FilePath& path = ExtensionResource::GetFilePath(
470 css_script.extension_root(), css_script.relative_path(),
471 symlink_policy);
472 if (!IsScriptValid(path, css_script.relative_path(),
473 IDS_EXTENSION_LOAD_CSS_FAILED, error))
474 return false;
478 return true;
481 } // namespace extensions