| Index: chrome/browser/extensions/extension.cc
|
| diff --git a/chrome/browser/extensions/extension.cc b/chrome/browser/extensions/extension.cc
|
| index d93cacdf96110b1b8fd1f6911c4e3b1d75c1ce0c..db5f637b36b2613fb73286f7affe86bc429bfb72 100644
|
| --- a/chrome/browser/extensions/extension.cc
|
| +++ b/chrome/browser/extensions/extension.cc
|
| @@ -6,33 +6,141 @@
|
|
|
| #include "base/logging.h"
|
| #include "base/string_util.h"
|
| +#include "net/base/net_util.h"
|
|
|
| const FilePath::CharType* Extension::kManifestFilename =
|
| FILE_PATH_LITERAL("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 =
|
| +// 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 value 'format_version' is missing or invalid.";
|
| +const char* Extension::kInvalidIdError
|
| + "Required value 'id' is missing or invalid.";
|
| +const char* Extension::kInvalidManifestError
|
| "Manifest is missing or invalid.";
|
| -const char* Extension::kInvalidFormatVersionError =
|
| - "Required key 'format_version' is missing or invalid.";
|
| -const char* Extension::kInvalidIdError =
|
| - "Required key 'id' 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 ";
|
| -const char* Extension::kInvalidVersionError =
|
| - "Required key 'version' 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 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 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;
|
| + ReplaceSubstringsAfterOffset(&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;
|
| + ReplaceSubstringsAfterOffset(&ret_val, 0, "*", s1);
|
| + ReplaceSubstringsAfterOffset(&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) {
|
| @@ -50,6 +158,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;
|
| @@ -63,64 +174,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;
|
| }
|
|
|