diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-22 19:27:56 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-22 19:27:56 +0000 |
commit | 91c394a8ab7a4a3acd1721ccc314498f0f726c8f (patch) | |
tree | 5b6fb9aa84bb2654b2655b528d3887699d30fb9a /chrome | |
parent | f1cbc4644f1c41954e5f3562d723a471139ff465 (diff) | |
download | chromium_src-91c394a8ab7a4a3acd1721ccc314498f0f726c8f.zip chromium_src-91c394a8ab7a4a3acd1721ccc314498f0f726c8f.tar.gz chromium_src-91c394a8ab7a4a3acd1721ccc314498f0f726c8f.tar.bz2 |
Parse more user script info out of the manifest and expose
it on the Extension class.
Removed Extension::CopyToValue() because it was only being
used in unit tests.
Centralize functions for creation of absolute URLs and
paths to extension resources in Extension class and move
corresponding unit tests.
Various other minor nitpickery.
Review URL: http://codereview.chromium.org/18352
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8486 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/extensions/extension.cc | 228 | ||||
-rw-r--r-- | chrome/browser/extensions/extension.h | 72 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_protocols.cc | 43 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_protocols.h | 6 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_protocols_unittest.cc | 33 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_unittest.cc | 226 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service_unittest.cc | 24 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_master.h | 8 | ||||
-rwxr-xr-x | chrome/test/data/extensions/extension1/manifest | 12 | ||||
-rw-r--r-- | chrome/test/unit/unit_tests.scons | 1 | ||||
-rw-r--r-- | chrome/test/unit/unittests.vcproj | 4 |
11 files changed, 433 insertions, 224 deletions
diff --git a/chrome/browser/extensions/extension.cc b/chrome/browser/extensions/extension.cc index e8d6513..4dfac73 100644 --- a/chrome/browser/extensions/extension.cc +++ b/chrome/browser/extensions/extension.cc @@ -6,32 +6,140 @@ #include "base/logging.h" #include "base/string_util.h" +#include "net/base/net_util.h" 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 +157,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 +173,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; } diff --git a/chrome/browser/extensions/extension.h b/chrome/browser/extensions/extension.h index 8a82547..7887f87 100644 --- a/chrome/browser/extensions/extension.h +++ b/chrome/browser/extensions/extension.h @@ -11,12 +11,13 @@ #include "base/file_path.h" #include "base/string16.h" #include "base/values.h" +#include "chrome/browser/extensions/user_script_master.h" +#include "googleurl/src/gurl.h" // Represents a Chromium extension. class Extension { public: - Extension(){}; - Extension(const FilePath& path) : path_(path) {}; + Extension(const FilePath& path); // The format for extension manifests that this code understands. static const int kExpectedFormatVersion = 1; @@ -25,26 +26,53 @@ class Extension { static const char kManifestFilename[]; // Keys used in JSON representation of extensions. - static const wchar_t* kFormatVersionKey; - static const wchar_t* kIdKey; - static const wchar_t* kNameKey; - static const wchar_t* kDescriptionKey; - static const wchar_t* kContentScriptsKey; - static const wchar_t* kVersionKey; + static const wchar_t* kDescriptionKey;
+ static const wchar_t* kFilesKey;
+ static const wchar_t* kFormatVersionKey;
+ static const wchar_t* kIdKey;
+ static const wchar_t* kMatchesKey;
+ static const wchar_t* kNameKey;
+ static const wchar_t* kUserScriptsKey;
+ static const wchar_t* kVersionKey;
// Error messages returned from InitFromValue(). - static const char* kInvalidFormatVersionError; - static const char* kInvalidManifestError; - static const char* kInvalidIdError; - static const char* kInvalidNameError; - static const char* kInvalidDescriptionError; - static const char* kInvalidContentScriptsListError; - static const char* kInvalidContentScriptError; - static const char* kInvalidVersionError; + static const char* kInvalidDescriptionError;
+ static const char* kInvalidFileCountError;
+ static const char* kInvalidFileError;
+ static const char* kInvalidFilesError;
+ static const char* kInvalidFormatVersionError;
+ static const char* kInvalidIdError;
+ static const char* kInvalidManifestError;
+ static const char* kInvalidMatchCountError;
+ static const char* kInvalidMatchError;
+ static const char* kInvalidMatchesError;
+ static const char* kInvalidNameError;
+ static const char* kInvalidUserScriptError;
+ static const char* kInvalidUserScriptsListError;
+ static const char* kInvalidVersionError;
+ + // Creates an absolute url to a resource inside an extension. The + // |extension_url| argument should be the url() from an Extension object. The + // |relative_path| can be untrusted user input. The returned URL will either + // be invalid() or a child of |extension_url|. + // NOTE: Static so that it can be used from multiple threads. + static GURL GetResourceURL(const GURL& extension_url, + const std::string& relative_path); + + // Creates an absolute path to a resource inside an extension. The + // |extension_path| argument should be the path() from an Extension object. + // The |relative_path| can be untrusted user input. The returned path will + // either be empty or a child of extension_path. + // NOTE: Static so that it can be used from multiple threads. + static FilePath GetResourcePath(const FilePath& extension_path, + const std::string& relative_path); // The path to the folder the extension is stored in. const FilePath& path() const { return path_; } + // The base URL for the extension. + const GURL& url() const { return extension_url_; } + // A human-readable ID for the extension. The convention is to use something // like 'com.example.myextension', but this is not currently enforced. An // extension's ID is used in things like directory structures and URLs, and @@ -63,20 +91,20 @@ class Extension { const std::string& description() const { return description_; } // Paths to the content scripts that the extension contains. - const std::vector<std::string>& content_scripts() const { - return content_scripts_; + const UserScriptList& user_scripts() const { + return user_scripts_; } // Initialize the extension from a parsed manifest. bool InitFromValue(const DictionaryValue& value, std::string* error); - // Serialize the extension to a DictionaryValue. - void CopyToValue(DictionaryValue* value); - private: // The path to the directory the extension is stored in. FilePath path_; + // The base extension url for the extension. + GURL extension_url_; + // The extension's ID. std::string id_; @@ -90,7 +118,7 @@ class Extension { std::string description_; // Paths to the content scripts the extension contains. - std::vector<std::string> content_scripts_; + UserScriptList user_scripts_; DISALLOW_COPY_AND_ASSIGN(Extension); }; diff --git a/chrome/browser/extensions/extension_protocols.cc b/chrome/browser/extensions/extension_protocols.cc index 1643c4d6..4d83129 100644 --- a/chrome/browser/extensions/extension_protocols.cc +++ b/chrome/browser/extensions/extension_protocols.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/extension_protocols.h" #include "base/string_util.h" +#include "chrome/browser/extensions/extension.h" #include "chrome/browser/net/chrome_url_request_context.h" #include "googleurl/src/url_util.h" #include "net/base/net_util.h" @@ -13,44 +14,6 @@ const char kExtensionURLScheme[] = "chrome-extension"; const char kUserScriptURLScheme[] = "chrome-user-script"; -FilePath GetPathForExtensionResource(const FilePath& extension_path, - const std::string& url_path) { - DCHECK(extension_path.IsAbsolute()); - DCHECK(url_path.length() > 0 && url_path[0] == '/'); - - // 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 += url_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; -} - // Factory registered with URLRequest to create URLRequestJobs for extension:// // URLs. static URLRequestJob* CreateExtensionURLRequestJob(URLRequest* request, @@ -64,7 +27,7 @@ static URLRequestJob* CreateExtensionURLRequestJob(URLRequest* request, return NULL; std::string resource = request->url().path(); - FilePath path = GetPathForExtensionResource(directory_path, resource); + FilePath path = Extension::GetResourcePath(directory_path, resource); return new URLRequestFileJob(request, path); } @@ -80,7 +43,7 @@ static URLRequestJob* CreateUserScriptURLRequestJob(URLRequest* request, FilePath directory_path = context->user_script_dir_path(); std::string resource = request->url().path(); - FilePath path = GetPathForExtensionResource(directory_path, resource); + FilePath path = Extension::GetResourcePath(directory_path, resource); return new URLRequestFileJob(request, path); } diff --git a/chrome/browser/extensions/extension_protocols.h b/chrome/browser/extensions/extension_protocols.h index 25f9fda..28adb24 100644 --- a/chrome/browser/extensions/extension_protocols.h +++ b/chrome/browser/extensions/extension_protocols.h @@ -13,12 +13,6 @@ extern const char kExtensionURLScheme[]; // The URL scheme Chromium user scripts are served from. extern const char kUserScriptURLScheme[]; -// Gets a FilePath for a resource inside an extension. |extension_path| is the -// full path to the extension directory. |resource_path| is the path to the -// resource from the extension root, including the leading '/'. -FilePath GetPathForExtensionResource(const FilePath& extension_path, - const std::string& resource_path); - // Registers support for the extension URL scheme. void RegisterExtensionProtocols(); diff --git a/chrome/browser/extensions/extension_protocols_unittest.cc b/chrome/browser/extensions/extension_protocols_unittest.cc deleted file mode 100644 index dee70a8..0000000 --- a/chrome/browser/extensions/extension_protocols_unittest.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2006-2008 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/browser/extensions/extension_protocols.h" -#include "testing/gtest/include/gtest/gtest.h" - -class ExtensionProtocolsTest : public testing::Test { -}; - -TEST(ExtensionProtocolsTest, GetPathForExtensionResource) { -#if defined(OS_WIN) - FilePath extension_path(FILE_PATH_LITERAL("C:\\myextension")); - EXPECT_EQ(std::wstring(L"C:\\myextension\\foo\\bar.gif"), - GetPathForExtensionResource(extension_path, "/foo/bar.gif").value()); - EXPECT_EQ(std::wstring(L"C:\\myextension\\"), - GetPathForExtensionResource(extension_path, "/").value()); - EXPECT_EQ(std::wstring(L"C:\\myextension\\c:\\foo.gif"), - GetPathForExtensionResource(extension_path, "/c:/foo.gif").value()); - EXPECT_EQ(std::wstring(L""), - GetPathForExtensionResource(extension_path, "/../foo.gif").value()); -#else - FilePath extension_path(FILE_PATH_LITERAL("/myextension")); - EXPECT_EQ(std::wstring("/myextension/foo/bar.gif"), - GetPathForExtensionResource(extension_path, "/foo/bar.gif").value()); - EXPECT_EQ(std::wstring("/myextension/"), - GetPathForExtensionResource(extension_path, "/").value()); - EXPECT_EQ(std::wstring(""), - GetPathForExtensionResource(extension_path, "/../foo.gif").value()); -#endif - - -} diff --git a/chrome/browser/extensions/extension_unittest.cc b/chrome/browser/extensions/extension_unittest.cc index 71812b9..83973dd 100644 --- a/chrome/browser/extensions/extension_unittest.cc +++ b/chrome/browser/extensions/extension_unittest.cc @@ -3,95 +3,211 @@ // found in the LICENSE file. #include "base/string_util.h" +#include "base/path_service.h" #include "chrome/browser/extensions/extension.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/json_value_serializer.h" #include "testing/gtest/include/gtest/gtest.h" class ExtensionTest : public testing::Test { }; TEST(ExtensionTest, InitFromValueInvalid) { - Extension extension; +#if defined(OS_WIN) + FilePath path(FILE_PATH_LITERAL("c:\\foo")); +#elif defined(OS_POSIX) + FilePath path(FILE_PATH_LITERAL("/foo")); +#endif + Extension extension(path); std::string error; - // Test invalid format version - DictionaryValue input_value; - input_value.SetInteger(Extension::kFormatVersionKey, 2); - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); + // Start with a valid extension manifest + std::wstring extensions_dir; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_dir)); + FilePath extensions_path = FilePath::FromWStringHack(extensions_dir) + .Append(FILE_PATH_LITERAL("extensions")) + .Append(FILE_PATH_LITERAL("extension1")) + .Append(FILE_PATH_LITERAL("manifest")); + + JSONFileValueSerializer serializer(extensions_path.ToWStringHack()); + scoped_ptr<DictionaryValue> valid_value( + static_cast<DictionaryValue*>(serializer.Deserialize(&error))); + ASSERT_TRUE(valid_value.get()); + ASSERT_EQ("", error); + ASSERT_TRUE(extension.InitFromValue(*valid_value, &error)); + ASSERT_EQ("", error); + + scoped_ptr<DictionaryValue> input_value; + + // Test missing and invalid format versions + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + input_value->Remove(Extension::kFormatVersionKey, NULL); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_EQ(Extension::kInvalidFormatVersionError, error); + + input_value->SetString(Extension::kFormatVersionKey, "foo"); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_EQ(Extension::kInvalidFormatVersionError, error); + + input_value->SetInteger(Extension::kFormatVersionKey, 2); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); EXPECT_EQ(Extension::kInvalidFormatVersionError, error); - input_value.SetInteger(Extension::kFormatVersionKey, 1); // Test missing and invalid ids - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + input_value->Remove(Extension::kIdKey, NULL); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); EXPECT_EQ(Extension::kInvalidIdError, error); - input_value.SetInteger(Extension::kIdKey, 42); - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); + + input_value->SetInteger(Extension::kIdKey, 42); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); EXPECT_EQ(Extension::kInvalidIdError, error); - input_value.SetString(Extension::kIdKey, L"com.google.myextension"); // Test missing and invalid versions - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + input_value->Remove(Extension::kVersionKey, NULL); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); EXPECT_EQ(Extension::kInvalidVersionError, error); - input_value.SetInteger(Extension::kVersionKey, 42); - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); + + input_value->SetInteger(Extension::kVersionKey, 42); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); EXPECT_EQ(Extension::kInvalidVersionError, error); - input_value.SetString(Extension::kVersionKey, L"1.0"); // Test missing and invalid names - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + input_value->Remove(Extension::kNameKey, NULL); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); EXPECT_EQ(Extension::kInvalidNameError, error); - input_value.SetInteger(Extension::kNameKey, 42); - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); + + input_value->SetInteger(Extension::kNameKey, 42); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); EXPECT_EQ(Extension::kInvalidNameError, error); - input_value.SetString(Extension::kNameKey, L"my extension"); // Test invalid description - input_value.SetInteger(Extension::kDescriptionKey, 42); - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + input_value->SetInteger(Extension::kDescriptionKey, 42); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); EXPECT_EQ(Extension::kInvalidDescriptionError, error); - input_value.Remove(Extension::kDescriptionKey, NULL); - - // Test invalid content scripts list - input_value.SetInteger(Extension::kContentScriptsKey, 42); - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); - EXPECT_EQ(Extension::kInvalidContentScriptsListError, error); - - // Test invalid content script item - ListValue* content_scripts = new ListValue; - input_value.Set(Extension::kContentScriptsKey, content_scripts); - content_scripts->Set(0, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(input_value, &error)); - EXPECT_EQ(0u, error.find(Extension::kInvalidContentScriptError)); + + // Test invalid user scripts list + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + input_value->SetInteger(Extension::kUserScriptsKey, 42); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_EQ(Extension::kInvalidUserScriptsListError, error); + + // Test invalid user script item + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + ListValue* user_scripts = NULL; + input_value->GetList(Extension::kUserScriptsKey, &user_scripts); + user_scripts->Set(0, Value::CreateIntegerValue(42)); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidUserScriptError)); + + // Test missing and invalid matches array + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + input_value->GetList(Extension::kUserScriptsKey, &user_scripts); + DictionaryValue* user_script = NULL; + user_scripts->GetDictionary(0, &user_script); + user_script->Remove(Extension::kMatchesKey, NULL); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidMatchesError)); + + user_script->Set(Extension::kMatchesKey, Value::CreateIntegerValue(42)); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidMatchesError)); + + ListValue* matches = new ListValue; + user_script->Set(Extension::kMatchesKey, matches); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidMatchCountError)); + + // Test invalid match element + matches->Set(0, Value::CreateIntegerValue(42)); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidMatchError)); + + // Test missing and invalid files array + input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy())); + input_value->GetList(Extension::kUserScriptsKey, &user_scripts); + user_scripts->GetDictionary(0, &user_script); + user_script->Remove(Extension::kFilesKey, NULL); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidFilesError)); + + user_script->Set(Extension::kFilesKey, Value::CreateIntegerValue(42)); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidFilesError)); + + ListValue* files = new ListValue; + user_script->Set(Extension::kFilesKey, files); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidFileCountError)); + + // Test invalid file element + files->Set(0, Value::CreateIntegerValue(42)); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidFileError)); + + // Test too many file elements (more than one not yet supported) + files->Set(0, Value::CreateStringValue("foo.js")); + files->Set(1, Value::CreateStringValue("bar.js")); + EXPECT_FALSE(extension.InitFromValue(*input_value, &error)); + EXPECT_TRUE(MatchPattern(error, Extension::kInvalidFileCountError)); } TEST(ExtensionTest, InitFromValueValid) { - Extension extension; +#if defined(OS_WIN) + FilePath path(FILE_PATH_LITERAL("C:\\foo")); +#elif defined(OS_POSIX) + FilePath path(FILE_PATH_LITERAL("/foo")); +#endif + Extension extension(path); std::string error; DictionaryValue input_value; - DictionaryValue output_value; // Test minimal extension input_value.SetInteger(Extension::kFormatVersionKey, 1); - input_value.SetString(Extension::kIdKey, L"com.google.myextension"); - input_value.SetString(Extension::kVersionKey, L"1.0"); - input_value.SetString(Extension::kNameKey, L"my extension"); + input_value.SetString(Extension::kIdKey, "com.google.myextension"); + input_value.SetString(Extension::kVersionKey, "1.0"); + input_value.SetString(Extension::kNameKey, "my extension"); EXPECT_TRUE(extension.InitFromValue(input_value, &error)); - extension.CopyToValue(&output_value); - EXPECT_TRUE(input_value.Equals(&output_value)); + EXPECT_EQ("", error); + EXPECT_EQ("com.google.myextension", extension.id()); + EXPECT_EQ("1.0", extension.version()); + EXPECT_EQ("my extension", extension.name()); + EXPECT_EQ("chrome-extension://com.google.myextension/", + extension.url().spec()); + EXPECT_EQ(path.value(), extension.path().value()); +} - // Test with a description - input_value.SetString(Extension::kDescriptionKey, - L"my extension does things"); - EXPECT_TRUE(extension.InitFromValue(input_value, &error)); - extension.CopyToValue(&output_value); - EXPECT_TRUE(input_value.Equals(&output_value)); - - // Test content_scripts - ListValue* content_scripts = new ListValue(); - input_value.Set(Extension::kContentScriptsKey, content_scripts); - content_scripts->Set(0, Value::CreateStringValue(L"foo/bar.js")); - content_scripts->Set(1, Value::CreateStringValue(L"hot/dog.js")); - EXPECT_TRUE(extension.InitFromValue(input_value, &error)); - extension.CopyToValue(&output_value); - EXPECT_TRUE(input_value.Equals(&output_value)); +TEST(ExtensionTest, GetResourceURLAndPath) { +#if defined(OS_WIN) + FilePath path(FILE_PATH_LITERAL("C:\\foo")); +#elif defined(OS_POSIX) + FilePath path(FILE_PATH_LITERAL("/foo")); +#endif + Extension extension(path); + DictionaryValue input_value; + input_value.SetInteger(Extension::kFormatVersionKey, 1); + input_value.SetString(Extension::kIdKey, "com.google.myextension"); + input_value.SetString(Extension::kVersionKey, "1.0"); + input_value.SetString(Extension::kNameKey, "my extension"); + EXPECT_TRUE(extension.InitFromValue(input_value, NULL)); + + EXPECT_EQ(extension.url().spec() + "bar/baz.js", + Extension::GetResourceURL(extension.url(), "bar/baz.js").spec()); + EXPECT_EQ(extension.url().spec() + "baz.js", + Extension::GetResourceURL(extension.url(), "bar/../baz.js").spec()); + EXPECT_EQ(extension.url().spec() + "baz.js", + Extension::GetResourceURL(extension.url(), "../baz.js").spec()); + + EXPECT_EQ(path.Append(FILE_PATH_LITERAL("bar")) + .Append(FILE_PATH_LITERAL("baz.js")).value(), + Extension::GetResourcePath(extension.path(), "bar/baz.js").value()); + EXPECT_EQ(path.Append(FILE_PATH_LITERAL("baz.js")).value(), + Extension::GetResourcePath(extension.path(), "bar/../baz.js") + .value()); + EXPECT_EQ(FilePath().value(), + Extension::GetResourcePath(extension.path(), "../baz.js").value()); } diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc index 19794a1..bfc5664 100644 --- a/chrome/browser/extensions/extensions_service_unittest.cc +++ b/chrome/browser/extensions/extensions_service_unittest.cc @@ -78,7 +78,7 @@ typedef PlatformTest ExtensionsServiceTest; TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectory) { std::wstring extensions_dir; ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &extensions_dir)); - FilePath manifest_path = FilePath::FromWStringHack(extensions_dir).Append( + FilePath extensions_path = FilePath::FromWStringHack(extensions_dir).Append( FILE_PATH_LITERAL("extensions")); scoped_refptr<ExtensionsServiceBackend> backend(new ExtensionsServiceBackend); @@ -86,7 +86,7 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectory) { new ExtensionsServiceTestFrontend); std::vector<Extension*> extensions; - EXPECT_TRUE(backend->LoadExtensionsFromDirectory(manifest_path, + EXPECT_TRUE(backend->LoadExtensionsFromDirectory(extensions_path, scoped_refptr<ExtensionsServiceFrontendInterface>(frontend.get()))); frontend->GetMessageLoop()->RunAllPending(); @@ -101,11 +101,19 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectory) { frontend->extensions()->at(0)->name()); EXPECT_EQ(std::string("The first extension that I made."), frontend->extensions()->at(0)->description()); - ASSERT_EQ(2u, frontend->extensions()->at(0)->content_scripts().size()); - EXPECT_EQ(std::string("script1.user.js"), - frontend->extensions()->at(0)->content_scripts().at(0)); - EXPECT_EQ(std::string("script2.user.js"), - frontend->extensions()->at(0)->content_scripts().at(1)); + + Extension* extension = frontend->extensions()->at(0); + const UserScriptList& scripts = extension->user_scripts(); + ASSERT_EQ(2u, scripts.size()); + EXPECT_EQ(2u, scripts[0].matches.size()); + EXPECT_EQ("http://*.google.com/*", scripts[0].matches[0]); + EXPECT_EQ("https://*.google.com/*", scripts[0].matches[1]); + EXPECT_EQ(extension->path().Append(FILE_PATH_LITERAL("script1.js")).value(), + scripts[0].path.value()); + EXPECT_EQ(1u, scripts[1].matches.size()); + EXPECT_EQ("http://*.yahoo.com/*", scripts[1].matches[0]); + EXPECT_EQ(extension->path().Append(FILE_PATH_LITERAL("script2.js")).value(), + scripts[1].path.value()); EXPECT_EQ(std::string("com.google.myextension2"), frontend->extensions()->at(1)->id()); @@ -113,5 +121,5 @@ TEST_F(ExtensionsServiceTest, LoadAllExtensionsFromDirectory) { frontend->extensions()->at(1)->name()); EXPECT_EQ(std::string(""), frontend->extensions()->at(1)->description()); - ASSERT_EQ(0u, frontend->extensions()->at(1)->content_scripts().size()); + ASSERT_EQ(0u, frontend->extensions()->at(1)->user_scripts().size()); }; diff --git a/chrome/browser/extensions/user_script_master.h b/chrome/browser/extensions/user_script_master.h index 8df49be..5167a65 100644 --- a/chrome/browser/extensions/user_script_master.h +++ b/chrome/browser/extensions/user_script_master.h @@ -12,6 +12,14 @@ #include "base/scoped_ptr.h" #include "base/shared_memory.h" #include "base/string_piece.h" +#include "googleurl/src/gurl.h" + +struct UserScriptInfo { + GURL url; + FilePath path; + std::vector<std::string> matches; +}; +typedef std::vector<UserScriptInfo> UserScriptList; // Manages a segment of shared memory that contains the user scripts the user // has installed. Lives on the UI thread. diff --git a/chrome/test/data/extensions/extension1/manifest b/chrome/test/data/extensions/extension1/manifest index bfde176..a7b3d74 100755 --- a/chrome/test/data/extensions/extension1/manifest +++ b/chrome/test/data/extensions/extension1/manifest @@ -4,8 +4,14 @@ "version": "1.0",
"name": "My extension 1",
"description": "The first extension that I made.",
- "content_scripts": [
- "script1.user.js",
- "script2.user.js"
+ "user_scripts": [
+ {
+ "matches": ["http://*.google.com/*", "https://*.google.com/*"],
+ "files": ["script1.js"]
+ },
+ {
+ "matches": ["http://*.yahoo.com/*"],
+ "files": ["script2.js"]
+ }
]
}
diff --git a/chrome/test/unit/unit_tests.scons b/chrome/test/unit/unit_tests.scons index efe4442..32ac31a 100644 --- a/chrome/test/unit/unit_tests.scons +++ b/chrome/test/unit/unit_tests.scons @@ -134,7 +134,6 @@ input_files = ChromeFileList([ '$CHROME_DIR/browser/download/download_request_manager_unittest.cc', '$CHROME_DIR/browser/password_manager/encryptor_unittest.cc', '$CHROME_DIR/browser/history/expire_history_backend_unittest.cc', - '$CHROME_DIR/browser/extensions/extension_protocols_unittest.cc', '$CHROME_DIR/browser/extensions/extension_unittest.cc', '$CHROME_DIR/browser/extensions/extensions_service_unittest.cc', '$CHROME_DIR/browser/importer/firefox_importer_unittest.cc', diff --git a/chrome/test/unit/unittests.vcproj b/chrome/test/unit/unittests.vcproj index ad1b852..817c1c7 100644 --- a/chrome/test/unit/unittests.vcproj +++ b/chrome/test/unit/unittests.vcproj @@ -455,10 +455,6 @@ > </File> <File - RelativePath="..\..\browser\extensions\extension_protocols_unittest.cc" - > - </File> - <File RelativePath="..\..\browser\extensions\extension_unittest.cc" > </File> |