diff options
Diffstat (limited to 'chrome/browser/extensions/extension.cc')
-rw-r--r-- | chrome/browser/extensions/extension.cc | 231 |
1 files changed, 179 insertions, 52 deletions
diff --git a/chrome/browser/extensions/extension.cc b/chrome/browser/extensions/extension.cc index e8d6513..b5918d5 100644 --- a/chrome/browser/extensions/extension.cc +++ b/chrome/browser/extensions/extension.cc @@ -6,32 +6,143 @@ #include "base/logging.h" #include "base/string_util.h" +#include "net/base/net_util.h" + +const char kExtensionURLScheme[] = "chrome-extension"; +const char kUserScriptURLScheme[] = "chrome-user-script"; const char Extension::kManifestFilename[] = "manifest"; +const wchar_t* Extension::kDescriptionKey = L"description"; +const wchar_t* Extension::kFilesKey = L"files"; const wchar_t* Extension::kFormatVersionKey = L"format_version"; const wchar_t* Extension::kIdKey = L"id"; +const wchar_t* Extension::kMatchesKey = L"matches"; const wchar_t* Extension::kNameKey = L"name"; -const wchar_t* Extension::kDescriptionKey = L"description"; -const wchar_t* Extension::kContentScriptsKey = L"content_scripts"; +const wchar_t* Extension::kUserScriptsKey = L"user_scripts"; const wchar_t* Extension::kVersionKey = L"version"; -const char* Extension::kInvalidManifestError = - "Manifest is missing or invalid."; +// Extension-related error messages. Some of these are simple patterns, where a +// '*' is replaced at runtime with a specific value. This is used instead of +// printf because we want to unit test them and scanf is hard to make +// cross-platform. +const char* Extension::kInvalidDescriptionError = + "Invalid value for 'description'."; +const char* Extension::kInvalidFileCountError = + "Invalid value for 'user_scripts[*].files. Only one file is currently " + "supported per-user script."; +const char* Extension::kInvalidFileError = + "Invalid value for 'user_scripts[*].files[*]'."; +const char* Extension::kInvalidFilesError = + "Required value 'user_scripts[*].files is missing or invalid."; const char* Extension::kInvalidFormatVersionError = - "Required key 'format_version' is missing or invalid."; + "Required value 'format_version' is missing or invalid."; const char* Extension::kInvalidIdError = - "Required key 'id' is missing or invalid."; + "Required value 'id' is missing or invalid."; +const char* Extension::kInvalidManifestError = + "Manifest is missing or invalid."; +const char* Extension::kInvalidMatchCountError = + "Invalid value for 'user_scripts[*].matches. There must be at least one " + "match specified."; +const char* Extension::kInvalidMatchError = + "Invalid value for 'user_scripts[*].matches[*]'."; +const char* Extension::kInvalidMatchesError = + "Required value 'user_scripts[*].matches' is missing or invalid."; const char* Extension::kInvalidNameError = - "Required key 'name' is missing or has invalid type."; -const char* Extension::kInvalidDescriptionError = - "Invalid type for 'description' key."; -const char* Extension::kInvalidContentScriptsListError = - "Invalid type for 'content_scripts' key."; -const char* Extension::kInvalidContentScriptError = - "Invalid type for content_scripts at index "; + "Required value 'name' is missing or invalid."; +const char* Extension::kInvalidUserScriptError = + "Invalid value for 'user_scripts[*]'."; +const char* Extension::kInvalidUserScriptsListError = + "Invalid value for 'user_scripts'."; const char* Extension::kInvalidVersionError = - "Required key 'version' is missing or invalid."; + "Required value 'version' is missing or invalid."; + +// Defined in extension_protocols.h. +extern const char kExtensionURLScheme[]; + +// static +GURL Extension::GetResourceURL(const GURL& extension_url, + const std::string& relative_path) { + DCHECK(extension_url.SchemeIs(kExtensionURLScheme)); + DCHECK(extension_url.path() == "/"); + + GURL ret_val = GURL(extension_url.spec() + relative_path); + DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false)); + + return ret_val; +} + +// static +FilePath Extension::GetResourcePath(const FilePath& extension_path, + const std::string& relative_path) { + // Build up a file:// URL and convert that back to a FilePath. This avoids + // URL encoding and path separator issues. + + // Convert the extension's root to a file:// URL. + GURL extension_url = net::FilePathToFileURL(extension_path); + if (!extension_url.is_valid()) + return FilePath(); + + // Append the requested path. + GURL::Replacements replacements; + std::string new_path(extension_url.path()); + new_path += "/"; + new_path += relative_path; + replacements.SetPathStr(new_path); + GURL file_url = extension_url.ReplaceComponents(replacements); + if (!file_url.is_valid()) + return FilePath(); + + // Convert the result back to a FilePath. + FilePath ret_val; + if (!net::FileURLToFilePath(file_url, &ret_val)) + return FilePath(); + + // Double-check that the path we ended up with is actually inside the + // extension root. We can do this with a simple prefix match because: + // a) We control the prefix on both sides, and they should match. + // b) GURL normalizes things like "../" and "//" before it gets to us. + if (ret_val.value().find(extension_path.value() + + FilePath::kSeparators[0]) != 0) + return FilePath(); + + return ret_val; +} + +// Creates an error messages from a pattern. +static std::string FormatErrorMessage(const std::string& format, + const std::string s1) { + std::string ret_val = format; + ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); + return ret_val; +} + +static std::string FormatErrorMessage(const std::string& format, + const std::string s1, + const std::string s2) { + std::string ret_val = format; + ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s1); + ReplaceFirstSubstringAfterOffset(&ret_val, 0, "*", s2); + return ret_val; +} + +Extension::Extension(const FilePath& path) { + DCHECK(path.IsAbsolute()); + +#if defined(OS_WIN) + // Normalize any drive letter to upper-case. We do this for consistency with + // net_utils::FilePathToFileURL(), which does the same thing, to make string + // comparisons simpler. + std::wstring path_str = path.value(); + if (path_str.size() >= 2 && path_str[0] >= L'a' && path_str[0] <= L'z' && + path_str[1] == ':') + path_str[0] += ('A' - 'a'); + + path_ = FilePath(path_str); +#else + path_ = path; +#endif +} bool Extension::InitFromValue(const DictionaryValue& source, std::string* error) { @@ -49,6 +160,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, return false; } + // Initialize URL. + extension_url_ = GURL(std::string(kExtensionURLScheme) + "://" + id_ + "/"); + // Initialize version. if (!source.GetString(kVersionKey, &version_)) { *error = kInvalidVersionError; @@ -62,64 +176,77 @@ bool Extension::InitFromValue(const DictionaryValue& source, } // Initialize description (optional). - Value* value = NULL; - if (source.Get(kDescriptionKey, &value)) { - if (!value->GetAsString(&description_)) { + if (source.HasKey(kDescriptionKey)) { + if (!source.GetString(kDescriptionKey, &description_)) { *error = kInvalidDescriptionError; return false; } } - // Initialize content scripts (optional). - if (source.Get(kContentScriptsKey, &value)) { - ListValue* list_value = NULL; - if (value->GetType() != Value::TYPE_LIST) { - *error = kInvalidContentScriptsListError; + // Initialize user scripts (optional). + if (source.HasKey(kUserScriptsKey)) { + ListValue* list_value; + if (!source.GetList(kUserScriptsKey, &list_value)) { + *error = kInvalidUserScriptsListError; return false; - } else { - list_value = static_cast<ListValue*>(value); } for (size_t i = 0; i < list_value->GetSize(); ++i) { - std::string content_script; - if (!list_value->Get(i, &value) || !value->GetAsString(&content_script)) { - *error = kInvalidContentScriptError; - *error += IntToString(i); + DictionaryValue* user_script; + if (!list_value->GetDictionary(i, &user_script)) { + *error = FormatErrorMessage(kInvalidUserScriptError, IntToString(i)); return false; } - content_scripts_.push_back(content_script); - } - } + ListValue* matches; + ListValue* files; - return true; -} + if (!user_script->GetList(kMatchesKey, &matches)) { + *error = FormatErrorMessage(kInvalidMatchesError, IntToString(i)); + return false; + } -void Extension::CopyToValue(DictionaryValue* destination) { - // Set format version - destination->SetInteger(kFormatVersionKey, - kExpectedFormatVersion); + if (!user_script->GetList(kFilesKey, &files)) { + *error = FormatErrorMessage(kInvalidFilesError, IntToString(i)); + return false; + } - // Copy id. - destination->SetString(kIdKey, id_); + if (matches->GetSize() == 0) { + *error = FormatErrorMessage(kInvalidMatchCountError, IntToString(i)); + return false; + } - // Copy version. - destination->SetString(kVersionKey, version_); + // NOTE: Only one file is supported right now. + if (files->GetSize() != 1) { + *error = FormatErrorMessage(kInvalidFileCountError, IntToString(i)); + return false; + } - // Copy name. - destination->SetString(kNameKey, name_); + UserScriptInfo script_info; + for (size_t j = 0; j < matches->GetSize(); ++j) { + std::string match; + if (!matches->GetString(j, &match)) { + *error = FormatErrorMessage(kInvalidMatchError, IntToString(i), + IntToString(j)); + return false; + } - // Copy description (optional). - if (description_.size() > 0) - destination->SetString(kDescriptionKey, description_); + script_info.matches.push_back(match); + } - // Copy content scripts (optional). - if (content_scripts_.size() > 0) { - ListValue* list_value = new ListValue(); - destination->Set(kContentScriptsKey, list_value); + // TODO(aa): Support multiple files. + std::string file; + if (!files->GetString(0, &file)) { + *error = FormatErrorMessage(kInvalidFileError, IntToString(i), + IntToString(0)); + return false; + } + script_info.path = Extension::GetResourcePath(path(), file); + script_info.url = Extension::GetResourceURL(url(), file); - for (size_t i = 0; i < content_scripts_.size(); ++i) { - list_value->Set(i, Value::CreateStringValue(content_scripts_[i])); + user_scripts_.push_back(script_info); } } + + return true; } |