summaryrefslogtreecommitdiffstats
path: root/chrome/browser/extensions/extension.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/extensions/extension.cc')
-rw-r--r--chrome/browser/extensions/extension.cc231
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;
}