Update CrOS OOBE throbber to MD throbber; delete old asset
[chromium-blink-merge.git] / chrome / common / extensions / manifest_handlers / content_scripts_handler.cc
blob6098eace3a6dfa2919413053a7021a05981b0035
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/host_id.h"
20 #include "extensions/common/manifest_constants.h"
21 #include "extensions/common/manifest_handlers/permissions_parser.h"
22 #include "extensions/common/permissions/permissions_data.h"
23 #include "extensions/common/url_pattern.h"
24 #include "extensions/common/url_pattern_set.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "url/gurl.h"
28 namespace extensions {
30 namespace keys = extensions::manifest_keys;
31 namespace values = manifest_values;
32 namespace errors = manifest_errors;
34 namespace {
36 // Helper method that loads either the include_globs or exclude_globs list
37 // from an entry in the content_script lists of the manifest.
38 bool LoadGlobsHelper(const base::DictionaryValue* content_script,
39 int content_script_index,
40 const char* globs_property_name,
41 base::string16* error,
42 void(UserScript::*add_method)(const std::string& glob),
43 UserScript* instance) {
44 if (!content_script->HasKey(globs_property_name))
45 return true; // they are optional
47 const base::ListValue* list = NULL;
48 if (!content_script->GetList(globs_property_name, &list)) {
49 *error = ErrorUtils::FormatErrorMessageUTF16(
50 errors::kInvalidGlobList,
51 base::IntToString(content_script_index),
52 globs_property_name);
53 return false;
56 for (size_t i = 0; i < list->GetSize(); ++i) {
57 std::string glob;
58 if (!list->GetString(i, &glob)) {
59 *error = ErrorUtils::FormatErrorMessageUTF16(
60 errors::kInvalidGlob,
61 base::IntToString(content_script_index),
62 globs_property_name,
63 base::IntToString(i));
64 return false;
67 (instance->*add_method)(glob);
70 return true;
73 // Helper method that loads a UserScript object from a dictionary in the
74 // content_script list of the manifest.
75 bool LoadUserScriptFromDictionary(const base::DictionaryValue* content_script,
76 int definition_index,
77 Extension* extension,
78 base::string16* error,
79 UserScript* result) {
80 // run_at
81 if (content_script->HasKey(keys::kRunAt)) {
82 std::string run_location;
83 if (!content_script->GetString(keys::kRunAt, &run_location)) {
84 *error = ErrorUtils::FormatErrorMessageUTF16(
85 errors::kInvalidRunAt,
86 base::IntToString(definition_index));
87 return false;
90 if (run_location == values::kRunAtDocumentStart) {
91 result->set_run_location(UserScript::DOCUMENT_START);
92 } else if (run_location == values::kRunAtDocumentEnd) {
93 result->set_run_location(UserScript::DOCUMENT_END);
94 } else if (run_location == values::kRunAtDocumentIdle) {
95 result->set_run_location(UserScript::DOCUMENT_IDLE);
96 } else {
97 *error = ErrorUtils::FormatErrorMessageUTF16(
98 errors::kInvalidRunAt,
99 base::IntToString(definition_index));
100 return false;
104 // all frames
105 if (content_script->HasKey(keys::kAllFrames)) {
106 bool all_frames = false;
107 if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) {
108 *error = ErrorUtils::FormatErrorMessageUTF16(
109 errors::kInvalidAllFrames, base::IntToString(definition_index));
110 return false;
112 result->set_match_all_frames(all_frames);
115 // match about blank
116 if (content_script->HasKey(keys::kMatchAboutBlank)) {
117 bool match_about_blank = false;
118 if (!content_script->GetBoolean(keys::kMatchAboutBlank,
119 &match_about_blank)) {
120 *error = ErrorUtils::FormatErrorMessageUTF16(
121 errors::kInvalidMatchAboutBlank, base::IntToString(definition_index));
122 return false;
124 result->set_match_about_blank(match_about_blank);
127 // matches (required)
128 const base::ListValue* matches = NULL;
129 if (!content_script->GetList(keys::kMatches, &matches)) {
130 *error = ErrorUtils::FormatErrorMessageUTF16(
131 errors::kInvalidMatches,
132 base::IntToString(definition_index));
133 return false;
136 if (matches->GetSize() == 0) {
137 *error = ErrorUtils::FormatErrorMessageUTF16(
138 errors::kInvalidMatchCount,
139 base::IntToString(definition_index));
140 return false;
142 for (size_t j = 0; j < matches->GetSize(); ++j) {
143 std::string match_str;
144 if (!matches->GetString(j, &match_str)) {
145 *error = ErrorUtils::FormatErrorMessageUTF16(
146 errors::kInvalidMatch,
147 base::IntToString(definition_index),
148 base::IntToString(j),
149 errors::kExpectString);
150 return false;
153 URLPattern pattern(UserScript::ValidUserScriptSchemes(
154 PermissionsData::CanExecuteScriptEverywhere(extension)));
156 URLPattern::ParseResult parse_result = pattern.Parse(match_str);
157 if (parse_result != URLPattern::PARSE_SUCCESS) {
158 *error = ErrorUtils::FormatErrorMessageUTF16(
159 errors::kInvalidMatch,
160 base::IntToString(definition_index),
161 base::IntToString(j),
162 URLPattern::GetParseResultString(parse_result));
163 return false;
166 // TODO(aboxhall): check for webstore
167 if (!PermissionsData::CanExecuteScriptEverywhere(extension) &&
168 pattern.scheme() != content::kChromeUIScheme) {
169 // Exclude SCHEME_CHROMEUI unless it's been explicitly requested.
170 // If the --extensions-on-chrome-urls flag has not been passed, requesting
171 // a chrome:// url will cause a parse failure above, so there's no need to
172 // check the flag here.
173 pattern.SetValidSchemes(
174 pattern.valid_schemes() & ~URLPattern::SCHEME_CHROMEUI);
177 if (pattern.MatchesScheme(url::kFileScheme) &&
178 !PermissionsData::CanExecuteScriptEverywhere(extension)) {
179 extension->set_wants_file_access(true);
180 if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS)) {
181 pattern.SetValidSchemes(
182 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
186 result->add_url_pattern(pattern);
189 // exclude_matches
190 if (content_script->HasKey(keys::kExcludeMatches)) { // optional
191 const base::ListValue* exclude_matches = NULL;
192 if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) {
193 *error = ErrorUtils::FormatErrorMessageUTF16(
194 errors::kInvalidExcludeMatches,
195 base::IntToString(definition_index));
196 return false;
199 for (size_t j = 0; j < exclude_matches->GetSize(); ++j) {
200 std::string match_str;
201 if (!exclude_matches->GetString(j, &match_str)) {
202 *error = ErrorUtils::FormatErrorMessageUTF16(
203 errors::kInvalidExcludeMatch,
204 base::IntToString(definition_index),
205 base::IntToString(j),
206 errors::kExpectString);
207 return false;
210 int valid_schemes = UserScript::ValidUserScriptSchemes(
211 PermissionsData::CanExecuteScriptEverywhere(extension));
212 URLPattern pattern(valid_schemes);
214 URLPattern::ParseResult parse_result = pattern.Parse(match_str);
215 if (parse_result != URLPattern::PARSE_SUCCESS) {
216 *error = ErrorUtils::FormatErrorMessageUTF16(
217 errors::kInvalidExcludeMatch,
218 base::IntToString(definition_index), base::IntToString(j),
219 URLPattern::GetParseResultString(parse_result));
220 return false;
223 result->add_exclude_url_pattern(pattern);
227 // include/exclude globs (mostly for Greasemonkey compatibility)
228 if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs,
229 error, &UserScript::add_glob, result)) {
230 return false;
233 if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs,
234 error, &UserScript::add_exclude_glob, result)) {
235 return false;
238 // js and css keys
239 const base::ListValue* js = NULL;
240 if (content_script->HasKey(keys::kJs) &&
241 !content_script->GetList(keys::kJs, &js)) {
242 *error = ErrorUtils::FormatErrorMessageUTF16(
243 errors::kInvalidJsList,
244 base::IntToString(definition_index));
245 return false;
248 const base::ListValue* css = NULL;
249 if (content_script->HasKey(keys::kCss) &&
250 !content_script->GetList(keys::kCss, &css)) {
251 *error = ErrorUtils::
252 FormatErrorMessageUTF16(errors::kInvalidCssList,
253 base::IntToString(definition_index));
254 return false;
257 // The manifest needs to have at least one js or css user script definition.
258 if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) {
259 *error = ErrorUtils::FormatErrorMessageUTF16(
260 errors::kMissingFile,
261 base::IntToString(definition_index));
262 return false;
265 if (js) {
266 for (size_t script_index = 0; script_index < js->GetSize();
267 ++script_index) {
268 const base::Value* value;
269 std::string relative;
270 if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) {
271 *error = ErrorUtils::FormatErrorMessageUTF16(
272 errors::kInvalidJs,
273 base::IntToString(definition_index),
274 base::IntToString(script_index));
275 return false;
277 GURL url = extension->GetResourceURL(relative);
278 ExtensionResource resource = extension->GetResource(relative);
279 result->js_scripts().push_back(UserScript::File(
280 resource.extension_root(), resource.relative_path(), url));
284 if (css) {
285 for (size_t script_index = 0; script_index < css->GetSize();
286 ++script_index) {
287 const base::Value* value;
288 std::string relative;
289 if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) {
290 *error = ErrorUtils::FormatErrorMessageUTF16(
291 errors::kInvalidCss,
292 base::IntToString(definition_index),
293 base::IntToString(script_index));
294 return false;
296 GURL url = extension->GetResourceURL(relative);
297 ExtensionResource resource = extension->GetResource(relative);
298 result->css_scripts().push_back(UserScript::File(
299 resource.extension_root(), resource.relative_path(), url));
303 return true;
306 // Returns false and sets the error if script file can't be loaded,
307 // or if it's not UTF-8 encoded.
308 static bool IsScriptValid(const base::FilePath& path,
309 const base::FilePath& relative_path,
310 int message_id,
311 std::string* error) {
312 std::string content;
313 if (!base::PathExists(path) ||
314 !base::ReadFileToString(path, &content)) {
315 *error = l10n_util::GetStringFUTF8(
316 message_id,
317 relative_path.LossyDisplayName());
318 return false;
321 if (!base::IsStringUTF8(content)) {
322 *error = l10n_util::GetStringFUTF8(
323 IDS_EXTENSION_BAD_FILE_ENCODING,
324 relative_path.LossyDisplayName());
325 return false;
328 return true;
331 struct EmptyUserScriptList {
332 UserScriptList user_script_list;
335 static base::LazyInstance<EmptyUserScriptList> g_empty_script_list =
336 LAZY_INSTANCE_INITIALIZER;
338 } // namespace
340 ContentScriptsInfo::ContentScriptsInfo() {
343 ContentScriptsInfo::~ContentScriptsInfo() {
346 // static
347 const UserScriptList& ContentScriptsInfo::GetContentScripts(
348 const Extension* extension) {
349 ContentScriptsInfo* info = static_cast<ContentScriptsInfo*>(
350 extension->GetManifestData(keys::kContentScripts));
351 return info ? info->content_scripts
352 : g_empty_script_list.Get().user_script_list;
355 // static
356 bool ContentScriptsInfo::ExtensionHasScriptAtURL(const Extension* extension,
357 const GURL& url) {
358 const UserScriptList& content_scripts = GetContentScripts(extension);
359 for (UserScriptList::const_iterator iter = content_scripts.begin();
360 iter != content_scripts.end(); ++iter) {
361 if (iter->MatchesURL(url))
362 return true;
364 return false;
367 // static
368 URLPatternSet ContentScriptsInfo::GetScriptableHosts(
369 const Extension* extension) {
370 const UserScriptList& content_scripts = GetContentScripts(extension);
371 URLPatternSet scriptable_hosts;
372 for (UserScriptList::const_iterator content_script =
373 content_scripts.begin();
374 content_script != content_scripts.end();
375 ++content_script) {
376 URLPatternSet::const_iterator pattern =
377 content_script->url_patterns().begin();
378 for (; pattern != content_script->url_patterns().end(); ++pattern)
379 scriptable_hosts.AddPattern(*pattern);
381 return scriptable_hosts;
384 ContentScriptsHandler::ContentScriptsHandler() {
387 ContentScriptsHandler::~ContentScriptsHandler() {
390 const std::vector<std::string> ContentScriptsHandler::Keys() const {
391 static const char* const keys[] = {
392 keys::kContentScripts
394 return std::vector<std::string>(keys, keys + arraysize(keys));
397 bool ContentScriptsHandler::Parse(Extension* extension, base::string16* error) {
398 scoped_ptr<ContentScriptsInfo> content_scripts_info(new ContentScriptsInfo);
399 const base::ListValue* scripts_list = NULL;
400 if (!extension->manifest()->GetList(keys::kContentScripts, &scripts_list)) {
401 *error = base::ASCIIToUTF16(errors::kInvalidContentScriptsList);
402 return false;
405 for (size_t i = 0; i < scripts_list->GetSize(); ++i) {
406 const base::DictionaryValue* script_dict = NULL;
407 if (!scripts_list->GetDictionary(i, &script_dict)) {
408 *error = ErrorUtils::FormatErrorMessageUTF16(
409 errors::kInvalidContentScript,
410 base::IntToString(i));
411 return false;
414 UserScript user_script;
415 if (!LoadUserScriptFromDictionary(script_dict,
417 extension,
418 error,
419 &user_script)) {
420 return false; // Failed to parse script context definition.
423 user_script.set_host_id(HostID(HostID::EXTENSIONS, extension->id()));
424 if (extension->converted_from_user_script()) {
425 user_script.set_emulate_greasemonkey(true);
426 // Greasemonkey matches all frames.
427 user_script.set_match_all_frames(true);
429 user_script.set_id(UserScript::GenerateUserScriptID());
430 content_scripts_info->content_scripts.push_back(user_script);
432 extension->SetManifestData(keys::kContentScripts,
433 content_scripts_info.release());
434 PermissionsParser::SetScriptableHosts(
435 extension, ContentScriptsInfo::GetScriptableHosts(extension));
436 return true;
439 bool ContentScriptsHandler::Validate(
440 const Extension* extension,
441 std::string* error,
442 std::vector<InstallWarning>* warnings) const {
443 // Validate that claimed script resources actually exist,
444 // and are UTF-8 encoded.
445 ExtensionResource::SymlinkPolicy symlink_policy;
446 if ((extension->creation_flags() &
447 Extension::FOLLOW_SYMLINKS_ANYWHERE) != 0) {
448 symlink_policy = ExtensionResource::FOLLOW_SYMLINKS_ANYWHERE;
449 } else {
450 symlink_policy = ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT;
453 const UserScriptList& content_scripts =
454 ContentScriptsInfo::GetContentScripts(extension);
455 for (size_t i = 0; i < content_scripts.size(); ++i) {
456 const UserScript& script = content_scripts[i];
458 for (size_t j = 0; j < script.js_scripts().size(); j++) {
459 const UserScript::File& js_script = script.js_scripts()[j];
460 const base::FilePath& path = ExtensionResource::GetFilePath(
461 js_script.extension_root(), js_script.relative_path(),
462 symlink_policy);
463 if (!IsScriptValid(path, js_script.relative_path(),
464 IDS_EXTENSION_LOAD_JAVASCRIPT_FAILED, error))
465 return false;
468 for (size_t j = 0; j < script.css_scripts().size(); j++) {
469 const UserScript::File& css_script = script.css_scripts()[j];
470 const base::FilePath& path = ExtensionResource::GetFilePath(
471 css_script.extension_root(), css_script.relative_path(),
472 symlink_policy);
473 if (!IsScriptValid(path, css_script.relative_path(),
474 IDS_EXTENSION_LOAD_CSS_FAILED, error))
475 return false;
479 return true;
482 } // namespace extensions