Enable Enterprise enrollment on desktop builds.
[chromium-blink-merge.git] / chrome / common / extensions / manifest_handlers / content_scripts_handler.cc
blob2753614d523e78fa55ff29da77a7d02dba4a6e56
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/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 "content/public/common/url_constants.h"
15 #include "extensions/common/error_utils.h"
16 #include "extensions/common/extension.h"
17 #include "extensions/common/extension_resource.h"
18 #include "extensions/common/manifest_constants.h"
19 #include "extensions/common/permissions/permissions_data.h"
20 #include "extensions/common/url_pattern.h"
21 #include "extensions/common/url_pattern_set.h"
22 #include "grit/generated_resources.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "url/gurl.h"
26 namespace extensions {
28 namespace keys = extensions::manifest_keys;
29 namespace values = manifest_values;
30 namespace errors = manifest_errors;
32 namespace {
34 // Helper method that loads either the include_globs or exclude_globs list
35 // from an entry in the content_script lists of the manifest.
36 bool LoadGlobsHelper(const base::DictionaryValue* content_script,
37 int content_script_index,
38 const char* globs_property_name,
39 base::string16* error,
40 void(UserScript::*add_method)(const std::string& glob),
41 UserScript* instance) {
42 if (!content_script->HasKey(globs_property_name))
43 return true; // they are optional
45 const base::ListValue* list = NULL;
46 if (!content_script->GetList(globs_property_name, &list)) {
47 *error = ErrorUtils::FormatErrorMessageUTF16(
48 errors::kInvalidGlobList,
49 base::IntToString(content_script_index),
50 globs_property_name);
51 return false;
54 for (size_t i = 0; i < list->GetSize(); ++i) {
55 std::string glob;
56 if (!list->GetString(i, &glob)) {
57 *error = ErrorUtils::FormatErrorMessageUTF16(
58 errors::kInvalidGlob,
59 base::IntToString(content_script_index),
60 globs_property_name,
61 base::IntToString(i));
62 return false;
65 (instance->*add_method)(glob);
68 return true;
71 // Helper method that loads a UserScript object from a dictionary in the
72 // content_script list of the manifest.
73 bool LoadUserScriptFromDictionary(const base::DictionaryValue* content_script,
74 int definition_index,
75 Extension* extension,
76 base::string16* error,
77 UserScript* result) {
78 // run_at
79 if (content_script->HasKey(keys::kRunAt)) {
80 std::string run_location;
81 if (!content_script->GetString(keys::kRunAt, &run_location)) {
82 *error = ErrorUtils::FormatErrorMessageUTF16(
83 errors::kInvalidRunAt,
84 base::IntToString(definition_index));
85 return false;
88 if (run_location == values::kRunAtDocumentStart) {
89 result->set_run_location(UserScript::DOCUMENT_START);
90 } else if (run_location == values::kRunAtDocumentEnd) {
91 result->set_run_location(UserScript::DOCUMENT_END);
92 } else if (run_location == values::kRunAtDocumentIdle) {
93 result->set_run_location(UserScript::DOCUMENT_IDLE);
94 } else {
95 *error = ErrorUtils::FormatErrorMessageUTF16(
96 errors::kInvalidRunAt,
97 base::IntToString(definition_index));
98 return false;
102 // all frames
103 if (content_script->HasKey(keys::kAllFrames)) {
104 bool all_frames = false;
105 if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) {
106 *error = ErrorUtils::FormatErrorMessageUTF16(
107 errors::kInvalidAllFrames, base::IntToString(definition_index));
108 return false;
110 result->set_match_all_frames(all_frames);
113 // matches (required)
114 const base::ListValue* matches = NULL;
115 if (!content_script->GetList(keys::kMatches, &matches)) {
116 *error = ErrorUtils::FormatErrorMessageUTF16(
117 errors::kInvalidMatches,
118 base::IntToString(definition_index));
119 return false;
122 if (matches->GetSize() == 0) {
123 *error = ErrorUtils::FormatErrorMessageUTF16(
124 errors::kInvalidMatchCount,
125 base::IntToString(definition_index));
126 return false;
128 for (size_t j = 0; j < matches->GetSize(); ++j) {
129 std::string match_str;
130 if (!matches->GetString(j, &match_str)) {
131 *error = ErrorUtils::FormatErrorMessageUTF16(
132 errors::kInvalidMatch,
133 base::IntToString(definition_index),
134 base::IntToString(j),
135 errors::kExpectString);
136 return false;
139 URLPattern pattern(UserScript::ValidUserScriptSchemes(
140 PermissionsData::CanExecuteScriptEverywhere(extension)));
142 URLPattern::ParseResult parse_result = pattern.Parse(match_str);
143 if (parse_result != URLPattern::PARSE_SUCCESS) {
144 *error = ErrorUtils::FormatErrorMessageUTF16(
145 errors::kInvalidMatch,
146 base::IntToString(definition_index),
147 base::IntToString(j),
148 URLPattern::GetParseResultString(parse_result));
149 return false;
152 // TODO(aboxhall): check for webstore
153 if (!PermissionsData::CanExecuteScriptEverywhere(extension) &&
154 pattern.scheme() != content::kChromeUIScheme) {
155 // Exclude SCHEME_CHROMEUI unless it's been explicitly requested.
156 // If the --extensions-on-chrome-urls flag has not been passed, requesting
157 // a chrome:// url will cause a parse failure above, so there's no need to
158 // check the flag here.
159 pattern.SetValidSchemes(
160 pattern.valid_schemes() & ~URLPattern::SCHEME_CHROMEUI);
163 if (pattern.MatchesScheme(content::kFileScheme) &&
164 !PermissionsData::CanExecuteScriptEverywhere(extension)) {
165 extension->set_wants_file_access(true);
166 if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS)) {
167 pattern.SetValidSchemes(
168 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
172 result->add_url_pattern(pattern);
175 // exclude_matches
176 if (content_script->HasKey(keys::kExcludeMatches)) { // optional
177 const base::ListValue* exclude_matches = NULL;
178 if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) {
179 *error = ErrorUtils::FormatErrorMessageUTF16(
180 errors::kInvalidExcludeMatches,
181 base::IntToString(definition_index));
182 return false;
185 for (size_t j = 0; j < exclude_matches->GetSize(); ++j) {
186 std::string match_str;
187 if (!exclude_matches->GetString(j, &match_str)) {
188 *error = ErrorUtils::FormatErrorMessageUTF16(
189 errors::kInvalidExcludeMatch,
190 base::IntToString(definition_index),
191 base::IntToString(j),
192 errors::kExpectString);
193 return false;
196 int valid_schemes = UserScript::ValidUserScriptSchemes(
197 PermissionsData::CanExecuteScriptEverywhere(extension));
198 URLPattern pattern(valid_schemes);
200 URLPattern::ParseResult parse_result = pattern.Parse(match_str);
201 if (parse_result != URLPattern::PARSE_SUCCESS) {
202 *error = ErrorUtils::FormatErrorMessageUTF16(
203 errors::kInvalidExcludeMatch,
204 base::IntToString(definition_index), base::IntToString(j),
205 URLPattern::GetParseResultString(parse_result));
206 return false;
209 result->add_exclude_url_pattern(pattern);
213 // include/exclude globs (mostly for Greasemonkey compatibility)
214 if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs,
215 error, &UserScript::add_glob, result)) {
216 return false;
219 if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs,
220 error, &UserScript::add_exclude_glob, result)) {
221 return false;
224 // js and css keys
225 const base::ListValue* js = NULL;
226 if (content_script->HasKey(keys::kJs) &&
227 !content_script->GetList(keys::kJs, &js)) {
228 *error = ErrorUtils::FormatErrorMessageUTF16(
229 errors::kInvalidJsList,
230 base::IntToString(definition_index));
231 return false;
234 const base::ListValue* css = NULL;
235 if (content_script->HasKey(keys::kCss) &&
236 !content_script->GetList(keys::kCss, &css)) {
237 *error = ErrorUtils::
238 FormatErrorMessageUTF16(errors::kInvalidCssList,
239 base::IntToString(definition_index));
240 return false;
243 // The manifest needs to have at least one js or css user script definition.
244 if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) {
245 *error = ErrorUtils::FormatErrorMessageUTF16(
246 errors::kMissingFile,
247 base::IntToString(definition_index));
248 return false;
251 if (js) {
252 for (size_t script_index = 0; script_index < js->GetSize();
253 ++script_index) {
254 const base::Value* value;
255 std::string relative;
256 if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) {
257 *error = ErrorUtils::FormatErrorMessageUTF16(
258 errors::kInvalidJs,
259 base::IntToString(definition_index),
260 base::IntToString(script_index));
261 return false;
263 GURL url = extension->GetResourceURL(relative);
264 ExtensionResource resource = extension->GetResource(relative);
265 result->js_scripts().push_back(UserScript::File(
266 resource.extension_root(), resource.relative_path(), url));
270 if (css) {
271 for (size_t script_index = 0; script_index < css->GetSize();
272 ++script_index) {
273 const base::Value* value;
274 std::string relative;
275 if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) {
276 *error = ErrorUtils::FormatErrorMessageUTF16(
277 errors::kInvalidCss,
278 base::IntToString(definition_index),
279 base::IntToString(script_index));
280 return false;
282 GURL url = extension->GetResourceURL(relative);
283 ExtensionResource resource = extension->GetResource(relative);
284 result->css_scripts().push_back(UserScript::File(
285 resource.extension_root(), resource.relative_path(), url));
289 return true;
292 // Returns false and sets the error if script file can't be loaded,
293 // or if it's not UTF-8 encoded.
294 static bool IsScriptValid(const base::FilePath& path,
295 const base::FilePath& relative_path,
296 int message_id,
297 std::string* error) {
298 std::string content;
299 if (!base::PathExists(path) ||
300 !base::ReadFileToString(path, &content)) {
301 *error = l10n_util::GetStringFUTF8(
302 message_id,
303 relative_path.LossyDisplayName());
304 return false;
307 if (!IsStringUTF8(content)) {
308 *error = l10n_util::GetStringFUTF8(
309 IDS_EXTENSION_BAD_FILE_ENCODING,
310 relative_path.LossyDisplayName());
311 return false;
314 return true;
317 struct EmptyUserScriptList {
318 UserScriptList user_script_list;
321 static base::LazyInstance<EmptyUserScriptList> g_empty_script_list =
322 LAZY_INSTANCE_INITIALIZER;
324 } // namespace
326 ContentScriptsInfo::ContentScriptsInfo() {
329 ContentScriptsInfo::~ContentScriptsInfo() {
332 // static
333 const UserScriptList& ContentScriptsInfo::GetContentScripts(
334 const Extension* extension) {
335 ContentScriptsInfo* info = static_cast<ContentScriptsInfo*>(
336 extension->GetManifestData(keys::kContentScripts));
337 return info ? info->content_scripts
338 : g_empty_script_list.Get().user_script_list;
341 // static
342 bool ContentScriptsInfo::ExtensionHasScriptAtURL(const Extension* extension,
343 const GURL& url) {
344 const UserScriptList& content_scripts = GetContentScripts(extension);
345 for (UserScriptList::const_iterator iter = content_scripts.begin();
346 iter != content_scripts.end(); ++iter) {
347 if (iter->MatchesURL(url))
348 return true;
350 return false;
353 // static
354 URLPatternSet ContentScriptsInfo::GetScriptableHosts(
355 const Extension* extension) {
356 const UserScriptList& content_scripts = GetContentScripts(extension);
357 URLPatternSet scriptable_hosts;
358 for (UserScriptList::const_iterator content_script =
359 content_scripts.begin();
360 content_script != content_scripts.end();
361 ++content_script) {
362 URLPatternSet::const_iterator pattern =
363 content_script->url_patterns().begin();
364 for (; pattern != content_script->url_patterns().end(); ++pattern)
365 scriptable_hosts.AddPattern(*pattern);
367 return scriptable_hosts;
370 ContentScriptsHandler::ContentScriptsHandler() {
373 ContentScriptsHandler::~ContentScriptsHandler() {
376 const std::vector<std::string> ContentScriptsHandler::Keys() const {
377 static const char* keys[] = {
378 keys::kContentScripts
380 return std::vector<std::string>(keys, keys + arraysize(keys));
383 bool ContentScriptsHandler::Parse(Extension* extension, base::string16* error) {
384 scoped_ptr<ContentScriptsInfo> content_scripts_info(new ContentScriptsInfo);
385 const base::ListValue* scripts_list = NULL;
386 if (!extension->manifest()->GetList(keys::kContentScripts, &scripts_list)) {
387 *error = base::ASCIIToUTF16(errors::kInvalidContentScriptsList);
388 return false;
391 for (size_t i = 0; i < scripts_list->GetSize(); ++i) {
392 const base::DictionaryValue* script_dict = NULL;
393 if (!scripts_list->GetDictionary(i, &script_dict)) {
394 *error = ErrorUtils::FormatErrorMessageUTF16(
395 errors::kInvalidContentScript,
396 base::IntToString(i));
397 return false;
400 UserScript user_script;
401 if (!LoadUserScriptFromDictionary(script_dict,
403 extension,
404 error,
405 &user_script)) {
406 return false; // Failed to parse script context definition.
409 user_script.set_extension_id(extension->id());
410 if (extension->converted_from_user_script()) {
411 user_script.set_emulate_greasemonkey(true);
412 // Greasemonkey matches all frames.
413 user_script.set_match_all_frames(true);
415 content_scripts_info->content_scripts.push_back(user_script);
417 extension->SetManifestData(keys::kContentScripts,
418 content_scripts_info.release());
419 PermissionsData::SetInitialScriptableHosts(
420 extension,
421 ContentScriptsInfo::GetScriptableHosts(extension));
422 return true;
425 bool ContentScriptsHandler::Validate(
426 const Extension* extension,
427 std::string* error,
428 std::vector<InstallWarning>* warnings) const {
429 // Validate that claimed script resources actually exist,
430 // and are UTF-8 encoded.
431 ExtensionResource::SymlinkPolicy symlink_policy;
432 if ((extension->creation_flags() &
433 Extension::FOLLOW_SYMLINKS_ANYWHERE) != 0) {
434 symlink_policy = ExtensionResource::FOLLOW_SYMLINKS_ANYWHERE;
435 } else {
436 symlink_policy = ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT;
439 const UserScriptList& content_scripts =
440 ContentScriptsInfo::GetContentScripts(extension);
441 for (size_t i = 0; i < content_scripts.size(); ++i) {
442 const UserScript& script = content_scripts[i];
444 for (size_t j = 0; j < script.js_scripts().size(); j++) {
445 const UserScript::File& js_script = script.js_scripts()[j];
446 const base::FilePath& path = ExtensionResource::GetFilePath(
447 js_script.extension_root(), js_script.relative_path(),
448 symlink_policy);
449 if (!IsScriptValid(path, js_script.relative_path(),
450 IDS_EXTENSION_LOAD_JAVASCRIPT_FAILED, error))
451 return false;
454 for (size_t j = 0; j < script.css_scripts().size(); j++) {
455 const UserScript::File& css_script = script.css_scripts()[j];
456 const base::FilePath& path = ExtensionResource::GetFilePath(
457 css_script.extension_root(), css_script.relative_path(),
458 symlink_policy);
459 if (!IsScriptValid(path, css_script.relative_path(),
460 IDS_EXTENSION_LOAD_CSS_FAILED, error))
461 return false;
465 return true;
468 } // namespace extensions