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