diff options
-rw-r--r-- | chrome/browser/browser.scons | 2 | ||||
-rw-r--r-- | chrome/browser/extensions/extension.cc | 231 | ||||
-rw-r--r-- | chrome/browser/extensions/extension.h | 64 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_protocols.cc | 48 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_protocols.h | 12 | ||||
-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.cc | 6 | ||||
-rw-r--r-- | chrome/browser/extensions/extensions_service_unittest.cc | 24 | ||||
-rw-r--r-- | chrome/browser/extensions/user_script_master.cc | 5 | ||||
-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 |
14 files changed, 449 insertions, 227 deletions
diff --git a/chrome/browser/browser.scons b/chrome/browser/browser.scons index d171277..0dea200 100644 --- a/chrome/browser/browser.scons +++ b/chrome/browser/browser.scons @@ -505,7 +505,6 @@ input_files = ChromeFileList([ MSVSFilter('Extensions', [ 'extensions/extension.cc', 'extensions/extension.h', - 'extensions/extension_protocols.cc', 'extensions/extension_protocols.h', 'extensions/extensions_service.cc', 'extensions/extensions_service.h', @@ -724,6 +723,7 @@ if not env.Bit('windows'): 'download/save_item.cc', 'download/save_package.cc', 'download/save_page_model.cc', + 'extensions/extension_protocols.cc', 'drag_utils.cc', 'encoding_menu_controller_delegate.cc', 'external_protocol_handler.cc', 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; } diff --git a/chrome/browser/extensions/extension.h b/chrome/browser/extensions/extension.h index 8a82547..e6a2ca8 100644 --- a/chrome/browser/extensions/extension.h +++ b/chrome/browser/extensions/extension.h @@ -11,12 +11,21 @@ #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" + +// The URL schemes Chromium extensions and user scripts are served from. These +// really should be in extension_protocols.h, but that causes link errors on +// linux because extension_protocols.cc refers to undefined symbols. +// TODO(aa): Move these back to extension_protocols.h when more of Linux and +// Mac are up and running. +extern const char kExtensionURLScheme[]; +extern const char kUserScriptURLScheme[]; // 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 +34,53 @@ class Extension { static const char kManifestFilename[]; // Keys used in JSON representation of extensions. + 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* kDescriptionKey; - static const wchar_t* kContentScriptsKey; + static const wchar_t* kUserScriptsKey; static const wchar_t* kVersionKey; // Error messages returned from InitFromValue(). + static const char* kInvalidDescriptionError; + static const char* kInvalidFileCountError; + static const char* kInvalidFileError; + static const char* kInvalidFilesError; static const char* kInvalidFormatVersionError; - static const char* kInvalidManifestError; 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* kInvalidDescriptionError; - static const char* kInvalidContentScriptsListError; - static const char* kInvalidContentScriptError; + 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 +99,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 +126,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..bc33592 100644 --- a/chrome/browser/extensions/extension_protocols.cc +++ b/chrome/browser/extensions/extension_protocols.cc @@ -5,51 +5,15 @@ #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" #include "net/url_request/url_request_file_job.h" -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; -} +// Defined in extension.h. +extern const char kExtensionURLScheme[]; +extern const char kUserScriptURLScheme[]; // Factory registered with URLRequest to create URLRequestJobs for extension:// // URLs. @@ -64,7 +28,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 +44,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..b76c31c 100644 --- a/chrome/browser/extensions/extension_protocols.h +++ b/chrome/browser/extensions/extension_protocols.h @@ -7,18 +7,6 @@ #include "base/file_path.h" -// The URL scheme Chromium extensions are served from. -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.cc b/chrome/browser/extensions/extensions_service.cc index cbefb3f..85d4fe8 100644 --- a/chrome/browser/extensions/extensions_service.cc +++ b/chrome/browser/extensions/extensions_service.cc @@ -70,8 +70,12 @@ void ExtensionsService::OnExtensionLoadError(const std::string& error) { // ExtensionsServicesBackend bool ExtensionsServiceBackend::LoadExtensionsFromDirectory( - const FilePath& path, + const FilePath& path_in, scoped_refptr<ExtensionsServiceFrontendInterface> frontend) { + FilePath path = path_in; + if (!file_util::AbsolutePath(&path)) + NOTREACHED(); + // Find all child directories in the install directory and load their // manifests. Post errors and results to the frontend. scoped_ptr<ExtensionList> extensions(new ExtensionList); 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.cc b/chrome/browser/extensions/user_script_master.cc index b95a1d3..6efb577 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -12,11 +12,14 @@ #include "base/path_service.h" #include "base/pickle.h" #include "base/string_util.h" -#include "chrome/browser/extensions/extension_protocols.h" #include "chrome/common/notification_service.h" #include "googleurl/src/gurl.h" #include "net/base/net_util.h" +// Defined in extension.h. +extern const char kExtensionURLScheme[]; +extern const char kUserScriptURLScheme[]; + // static void UserScriptMaster::ScriptReloader::ParseMetadataHeader( const StringPiece& script_text, std::vector<std::string> *includes) { 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 1cfae9f..21763ce 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> |