Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / manifest_handlers / content_scripts_handler.cc
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/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"
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 // 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
45
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;
53   }
54
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;
64     }
65
66     (instance->*add_method)(glob);
67   }
68
69   return true;
70 }
71
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;
87     }
88
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;
100     }
101   }
102
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;
110     }
111     result->set_match_all_frames(all_frames);
112   }
113
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;
122     }
123     result->set_match_about_blank(match_about_blank);
124   }
125
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;
133   }
134
135   if (matches->GetSize() == 0) {
136     *error = ErrorUtils::FormatErrorMessageUTF16(
137         errors::kInvalidMatchCount,
138         base::IntToString(definition_index));
139     return false;
140   }
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;
150     }
151
152     URLPattern pattern(UserScript::ValidUserScriptSchemes(
153         PermissionsData::CanExecuteScriptEverywhere(extension)));
154
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;
163     }
164
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);
174     }
175
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);
182       }
183     }
184
185     result->add_url_pattern(pattern);
186   }
187
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;
196     }
197
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;
207       }
208
209       int valid_schemes = UserScript::ValidUserScriptSchemes(
210           PermissionsData::CanExecuteScriptEverywhere(extension));
211       URLPattern pattern(valid_schemes);
212
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;
220       }
221
222       result->add_exclude_url_pattern(pattern);
223     }
224   }
225
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;
230   }
231
232   if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs,
233                        error, &UserScript::add_exclude_glob, result)) {
234       return false;
235   }
236
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;
245   }
246
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;
254   }
255
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;
262   }
263
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;
275       }
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));
280     }
281   }
282
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;
294       }
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));
299     }
300   }
301
302   return true;
303 }
304
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;
318   }
319
320   if (!base::IsStringUTF8(content)) {
321     *error = l10n_util::GetStringFUTF8(
322         IDS_EXTENSION_BAD_FILE_ENCODING,
323         relative_path.LossyDisplayName());
324     return false;
325   }
326
327   return true;
328 }
329
330 struct EmptyUserScriptList {
331   UserScriptList user_script_list;
332 };
333
334 static base::LazyInstance<EmptyUserScriptList> g_empty_script_list =
335     LAZY_INSTANCE_INITIALIZER;
336
337 }  // namespace
338
339 ContentScriptsInfo::ContentScriptsInfo() {
340 }
341
342 ContentScriptsInfo::~ContentScriptsInfo() {
343 }
344
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;
352 }
353
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;
362   }
363   return false;
364 }
365
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);
379   }
380   return scriptable_hosts;
381 }
382
383 ContentScriptsHandler::ContentScriptsHandler() {
384 }
385
386 ContentScriptsHandler::~ContentScriptsHandler() {
387 }
388
389 const std::vector<std::string> ContentScriptsHandler::Keys() const {
390   static const char* keys[] = {
391     keys::kContentScripts
392   };
393   return std::vector<std::string>(keys, keys + arraysize(keys));
394 }
395
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;
402   }
403
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;
411     }
412
413     UserScript user_script;
414     if (!LoadUserScriptFromDictionary(script_dict,
415                                       i,
416                                       extension,
417                                       error,
418                                       &user_script)) {
419       return false;  // Failed to parse script context definition.
420     }
421
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);
427     }
428     user_script.set_id(UserScript::GenerateUserScriptID());
429     content_scripts_info->content_scripts.push_back(user_script);
430   }
431   extension->SetManifestData(keys::kContentScripts,
432                              content_scripts_info.release());
433   PermissionsParser::SetScriptableHosts(
434       extension, ContentScriptsInfo::GetScriptableHosts(extension));
435   return true;
436 }
437
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;
450   }
451
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];
456
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;
465     }
466
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;
475     }
476   }
477
478   return true;
479 }
480
481 }  // namespace extensions