summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authoraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-23 20:48:59 +0000
committeraa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-23 20:48:59 +0000
commiteab9b45f79f18963793e0f4d666a83b311caabd3 (patch)
tree8ba9b44119deb7652f55838e494664cf395ae94c /chrome/browser
parent3418ebed0b079864abd37cf3d6d622ef4aa3b42c (diff)
downloadchromium_src-eab9b45f79f18963793e0f4d666a83b311caabd3.zip
chromium_src-eab9b45f79f18963793e0f4d666a83b311caabd3.tar.gz
chromium_src-eab9b45f79f18963793e0f4d666a83b311caabd3.tar.bz2
Try again to commit r8486, which updates the manifest
parsing in the Extension class to also parse out user script details. The problem was that extension_protocols.cc appears to link OK if you don't refer to anything in it, but as soon as you try to use something defined in it, a bunch of other symbols it refers to turn up undefined. To avoid this problem in the future, I stopped compiling extensions_protocol.cc so that we would realize if we tried to use it. Also in this change, I absolutified paths in ExtensionsService before creating Extension instances. On Linux, the path that PathService was giving us was not absolute. Review URL: http://codereview.chromium.org/18704 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8576 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/browser.scons2
-rw-r--r--chrome/browser/extensions/extension.cc231
-rw-r--r--chrome/browser/extensions/extension.h64
-rw-r--r--chrome/browser/extensions/extension_protocols.cc48
-rw-r--r--chrome/browser/extensions/extension_protocols.h12
-rw-r--r--chrome/browser/extensions/extension_protocols_unittest.cc33
-rw-r--r--chrome/browser/extensions/extension_unittest.cc226
-rw-r--r--chrome/browser/extensions/extensions_service.cc6
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc24
-rw-r--r--chrome/browser/extensions/user_script_master.cc5
-rw-r--r--chrome/browser/extensions/user_script_master.h8
11 files changed, 440 insertions, 219 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.