Chromium Code Reviews| Index: chrome/common/extensions/api/content_scripts/content_scripts_handler.cc |
| diff --git a/chrome/common/extensions/api/content_scripts/content_scripts_handler.cc b/chrome/common/extensions/api/content_scripts/content_scripts_handler.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..2a9761e4f57b01b5d188ff1e673844bc1d63b7d5 |
| --- /dev/null |
| +++ b/chrome/common/extensions/api/content_scripts/content_scripts_handler.cc |
| @@ -0,0 +1,341 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/common/extensions/api/content_scripts/content_scripts_handler.h" |
| + |
| +#include "base/lazy_instance.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "base/string_number_conversions.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "base/values.h" |
| +#include "chrome/common/extensions/extension.h" |
| +#include "chrome/common/extensions/extension_manifest_constants.h" |
| +#include "chrome/common/extensions/extension_resource.h" |
| +#include "content/public/common/url_constants.h" |
| +#include "extensions/common/error_utils.h" |
| +#include "extensions/common/url_pattern.h" |
| +#include "googleurl/src/gurl.h" |
| + |
| +namespace extensions { |
| + |
| +namespace keys = extension_manifest_keys; |
| +namespace values = extension_manifest_values; |
| +namespace errors = extension_manifest_errors; |
| + |
| +namespace { |
| + |
| +// Helper method that loads either the include_globs or exclude_globs list |
| +// from an entry in the content_script lists of the manifest. |
| +bool LoadGlobsHelper(const DictionaryValue* content_script, |
| + int content_script_index, |
| + const char* globs_property_name, |
| + string16* error, |
| + void(UserScript::*add_method)(const std::string& glob), |
| + UserScript* instance) { |
| + if (!content_script->HasKey(globs_property_name)) |
| + return true; // they are optional |
| + |
| + const ListValue* list = NULL; |
| + if (!content_script->GetList(globs_property_name, &list)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidGlobList, |
| + base::IntToString(content_script_index), |
| + globs_property_name); |
| + return false; |
| + } |
| + |
| + for (size_t i = 0; i < list->GetSize(); ++i) { |
| + std::string glob; |
| + if (!list->GetString(i, &glob)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidGlob, |
| + base::IntToString(content_script_index), |
| + globs_property_name, |
| + base::IntToString(i)); |
| + return false; |
| + } |
| + |
| + (instance->*add_method)(glob); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +// Helper method that loads a UserScript object from a dictionary in the |
| +// content_script list of the manifest. |
| +bool LoadUserScriptFromDictionary(const DictionaryValue* content_script, |
| + int definition_index, |
| + Extension* extension, |
| + string16* error, |
| + UserScript* result) { |
| + // run_at |
| + if (content_script->HasKey(keys::kRunAt)) { |
| + std::string run_location; |
| + if (!content_script->GetString(keys::kRunAt, &run_location)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidRunAt, |
| + base::IntToString(definition_index)); |
| + return false; |
| + } |
| + |
| + if (run_location == values::kRunAtDocumentStart) { |
| + result->set_run_location(UserScript::DOCUMENT_START); |
| + } else if (run_location == values::kRunAtDocumentEnd) { |
| + result->set_run_location(UserScript::DOCUMENT_END); |
| + } else if (run_location == values::kRunAtDocumentIdle) { |
| + result->set_run_location(UserScript::DOCUMENT_IDLE); |
| + } else { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidRunAt, |
| + base::IntToString(definition_index)); |
| + return false; |
| + } |
| + } |
| + |
| + // all frames |
| + if (content_script->HasKey(keys::kAllFrames)) { |
| + bool all_frames = false; |
| + if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidAllFrames, base::IntToString(definition_index)); |
| + return false; |
| + } |
| + result->set_match_all_frames(all_frames); |
| + } |
| + |
| + // matches (required) |
| + const ListValue* matches = NULL; |
| + if (!content_script->GetList(keys::kMatches, &matches)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidMatches, |
| + base::IntToString(definition_index)); |
| + return false; |
| + } |
| + |
| + if (matches->GetSize() == 0) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidMatchCount, |
| + base::IntToString(definition_index)); |
| + return false; |
| + } |
| + for (size_t j = 0; j < matches->GetSize(); ++j) { |
| + std::string match_str; |
| + if (!matches->GetString(j, &match_str)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidMatch, |
| + base::IntToString(definition_index), |
| + base::IntToString(j), |
| + errors::kExpectString); |
| + return false; |
| + } |
| + |
| + URLPattern pattern(UserScript::kValidUserScriptSchemes); |
| + if (extension->CanExecuteScriptEverywhere()) |
| + pattern.SetValidSchemes(URLPattern::SCHEME_ALL); |
| + |
| + URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
| + if (parse_result != URLPattern::PARSE_SUCCESS) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidMatch, |
| + base::IntToString(definition_index), |
| + base::IntToString(j), |
| + URLPattern::GetParseResultString(parse_result)); |
| + return false; |
| + } |
| + |
| + if (pattern.MatchesScheme(chrome::kFileScheme) && |
| + !extension->CanExecuteScriptEverywhere()) { |
| + extension->set_wants_file_access(true); |
|
Devlin
2012/12/29 21:18:38
This is bad. Do we know how we want to handle thin
Yoyo Zhou
2013/01/04 00:13:30
I'd put a TODO saying what's wrong, at least until
Devlin
2013/01/11 20:30:02
Done (in Extension.h, with the method).
|
| + if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS)) { |
| + pattern.SetValidSchemes( |
| + pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); |
| + } |
| + } |
| + |
| + result->add_url_pattern(pattern); |
| + } |
| + |
| + // exclude_matches |
| + if (content_script->HasKey(keys::kExcludeMatches)) { // optional |
| + const ListValue* exclude_matches = NULL; |
| + if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidExcludeMatches, |
| + base::IntToString(definition_index)); |
| + return false; |
| + } |
| + |
| + for (size_t j = 0; j < exclude_matches->GetSize(); ++j) { |
| + std::string match_str; |
| + if (!exclude_matches->GetString(j, &match_str)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidExcludeMatch, |
| + base::IntToString(definition_index), |
| + base::IntToString(j), |
| + errors::kExpectString); |
| + return false; |
| + } |
| + |
| + URLPattern pattern(UserScript::kValidUserScriptSchemes); |
| + if (extension->CanExecuteScriptEverywhere()) |
| + pattern.SetValidSchemes(URLPattern::SCHEME_ALL); |
| + |
| + URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
| + if (parse_result != URLPattern::PARSE_SUCCESS) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidExcludeMatch, |
| + base::IntToString(definition_index), base::IntToString(j), |
| + URLPattern::GetParseResultString(parse_result)); |
| + return false; |
| + } |
| + |
| + result->add_exclude_url_pattern(pattern); |
| + } |
| + } |
| + |
| + // include/exclude globs (mostly for Greasemonkey compatibility) |
| + if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs, |
| + error, &UserScript::add_glob, result)) { |
| + return false; |
| + } |
| + |
| + if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs, |
| + error, &UserScript::add_exclude_glob, result)) { |
| + return false; |
| + } |
| + |
| + // js and css keys |
| + const ListValue* js = NULL; |
| + if (content_script->HasKey(keys::kJs) && |
| + !content_script->GetList(keys::kJs, &js)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidJsList, |
| + base::IntToString(definition_index)); |
| + return false; |
| + } |
| + |
| + const ListValue* css = NULL; |
| + if (content_script->HasKey(keys::kCss) && |
| + !content_script->GetList(keys::kCss, &css)) { |
| + *error = ErrorUtils:: |
| + FormatErrorMessageUTF16(errors::kInvalidCssList, |
| + base::IntToString(definition_index)); |
| + return false; |
| + } |
| + |
| + // The manifest needs to have at least one js or css user script definition. |
| + if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kMissingFile, |
| + base::IntToString(definition_index)); |
| + return false; |
| + } |
| + |
| + if (js) { |
| + for (size_t script_index = 0; script_index < js->GetSize(); |
| + ++script_index) { |
| + const Value* value; |
| + std::string relative; |
| + if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidJs, |
| + base::IntToString(definition_index), |
| + base::IntToString(script_index)); |
| + return false; |
| + } |
| + GURL url = extension->GetResourceURL(relative); |
| + ExtensionResource resource = extension->GetResource(relative); |
| + result->js_scripts().push_back(UserScript::File( |
| + resource.extension_root(), resource.relative_path(), url)); |
| + } |
| + } |
| + |
| + if (css) { |
| + for (size_t script_index = 0; script_index < css->GetSize(); |
| + ++script_index) { |
| + const Value* value; |
| + std::string relative; |
| + if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidCss, |
| + base::IntToString(definition_index), |
| + base::IntToString(script_index)); |
| + return false; |
| + } |
| + GURL url = extension->GetResourceURL(relative); |
| + ExtensionResource resource = extension->GetResource(relative); |
| + result->css_scripts().push_back(UserScript::File( |
| + resource.extension_root(), resource.relative_path(), url)); |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +struct EmptyUserScriptList { |
| + UserScriptList user_script_list; |
| +}; |
| + |
| +static base::LazyInstance<EmptyUserScriptList> g_empty_script_list = |
| + LAZY_INSTANCE_INITIALIZER; |
| + |
| +} // namespace |
| + |
| +// static |
| +const UserScriptList& ContentScriptsInfo::GetContentScripts( |
| + const Extension* extension) { |
| + ContentScriptsInfo* info = static_cast<ContentScriptsInfo*>( |
| + extension->GetManifestData(keys::kContentScripts)); |
| + return info ? info->content_scripts |
| + : g_empty_script_list.Get().user_script_list; |
| +} |
| + |
| +ContentScriptsHandler::ContentScriptsHandler() { |
| +} |
| + |
| +ContentScriptsHandler::~ContentScriptsHandler() { |
| +} |
| + |
| +bool ContentScriptsHandler::Parse(const base::Value* value, |
| + Extension* extension, |
| + string16* error) { |
| + scoped_ptr<ContentScriptsInfo> content_scripts_info(new ContentScriptsInfo); |
| + const ListValue* scripts_list = NULL; |
| + if (!value->GetAsList(&scripts_list)) { |
| + *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); |
| + return false; |
| + } |
| + |
| + for (size_t i = 0; i < scripts_list->GetSize(); ++i) { |
| + const DictionaryValue* script_dict = NULL; |
| + if (!scripts_list->GetDictionary(i, &script_dict)) { |
| + *error = ErrorUtils::FormatErrorMessageUTF16( |
| + extension_manifest_errors::kInvalidContentScript, |
| + base::IntToString(i)); |
| + return false; |
| + } |
| + |
| + UserScript user_script; |
| + if (!LoadUserScriptFromDictionary(script_dict, |
| + i, |
| + extension, |
| + error, |
| + &user_script)) { |
| + return false; // Failed to parse script context definition. |
| + } |
| + user_script.set_extension_id(extension->id()); |
| + if (extension->converted_from_user_script()) { |
| + user_script.set_emulate_greasemonkey(true); |
| + // Greasemonkey matches all frames. |
| + user_script.set_match_all_frames(true); |
| + } |
| + content_scripts_info->content_scripts.push_back(user_script); |
| + } |
| + extension->SetManifestData(keys::kContentScripts, |
| + content_scripts_info.release()); |
| + return true; |
| +} |
| + |
| +} // namespace extensions |
| + |