Commit f0841cdf authored by skerner@google.com's avatar skerner@google.com

Allow relative paths to external extension files for some providers, error out for others.

This change will enable a fix for issue 57289, which requires a different base directory for extensions.

BUG=70111
TEST=ExtensionServiceTest.ExternalPrefProvider

Review URL: http://codereview.chromium.org/6293006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@71792 0039d316-1c4b-4281-b951-d872f2087c98
parent c4656efa
...@@ -408,10 +408,10 @@ bool ExtensionService::UninstallExtensionHelper( ...@@ -408,10 +408,10 @@ bool ExtensionService::UninstallExtensionHelper(
} }
ExtensionService::ExtensionService(Profile* profile, ExtensionService::ExtensionService(Profile* profile,
const CommandLine* command_line, const CommandLine* command_line,
const FilePath& install_directory, const FilePath& install_directory,
ExtensionPrefs* extension_prefs, ExtensionPrefs* extension_prefs,
bool autoupdate_enabled) bool autoupdate_enabled)
: profile_(profile), : profile_(profile),
extension_prefs_(extension_prefs), extension_prefs_(extension_prefs),
install_directory_(install_directory), install_directory_(install_directory),
......
...@@ -209,14 +209,20 @@ class MockExtensionProvider : public ExternalExtensionProviderInterface { ...@@ -209,14 +209,20 @@ class MockExtensionProvider : public ExternalExtensionProviderInterface {
class MockProviderVisitor class MockProviderVisitor
: public ExternalExtensionProviderInterface::VisitorInterface { : public ExternalExtensionProviderInterface::VisitorInterface {
public: public:
MockProviderVisitor() : ids_found_(0) {
// The provider will return |fake_base_path| from
// GetBaseCrxFilePath(). User can test the behavior with
// and without an empty path using this parameter.
explicit MockProviderVisitor(FilePath fake_base_path)
: ids_found_(0),
fake_base_path_(fake_base_path) {
} }
int Visit(const std::string& json_data) { int Visit(const std::string& json_data) {
// Give the test json file to the provider for parsing. // Give the test json file to the provider for parsing.
provider_.reset(new ExternalExtensionProviderImpl( provider_.reset(new ExternalExtensionProviderImpl(
this, this,
new ExternalTestingExtensionLoader(json_data), new ExternalTestingExtensionLoader(json_data, fake_base_path_),
Extension::EXTERNAL_PREF, Extension::EXTERNAL_PREF,
Extension::EXTERNAL_PREF_DOWNLOAD)); Extension::EXTERNAL_PREF_DOWNLOAD));
...@@ -254,6 +260,10 @@ class MockProviderVisitor ...@@ -254,6 +260,10 @@ class MockProviderVisitor
EXPECT_TRUE(prefs_->GetDictionary(id, &pref)) EXPECT_TRUE(prefs_->GetDictionary(id, &pref))
<< "Got back ID (" << id.c_str() << ") we weren't expecting"; << "Got back ID (" << id.c_str() << ") we weren't expecting";
EXPECT_TRUE(path.IsAbsolute());
if (!fake_base_path_.empty())
EXPECT_TRUE(fake_base_path_.IsParent(path));
if (pref) { if (pref) {
EXPECT_TRUE(provider_->HasExtension(id)); EXPECT_TRUE(provider_->HasExtension(id));
...@@ -309,7 +319,7 @@ class MockProviderVisitor ...@@ -309,7 +319,7 @@ class MockProviderVisitor
private: private:
int ids_found_; int ids_found_;
FilePath fake_base_path_;
scoped_ptr<ExternalExtensionProviderImpl> provider_; scoped_ptr<ExternalExtensionProviderImpl> provider_;
scoped_ptr<DictionaryValue> prefs_; scoped_ptr<DictionaryValue> prefs_;
...@@ -3004,9 +3014,16 @@ TEST_F(ExtensionServiceTest, ExternalUninstall) { ...@@ -3004,9 +3014,16 @@ TEST_F(ExtensionServiceTest, ExternalUninstall) {
TEST_F(ExtensionServiceTest, ExternalPrefProvider) { TEST_F(ExtensionServiceTest, ExternalPrefProvider) {
InitializeEmptyExtensionService(); InitializeEmptyExtensionService();
// Test some valid extension records.
// Set a base path to avoid erroring out on relative paths.
// Paths starting with // are absolute on every platform we support.
FilePath base_path(FILE_PATH_LITERAL("//base/path"));
ASSERT_TRUE(base_path.IsAbsolute());
MockProviderVisitor visitor(base_path);
std::string json_data = std::string json_data =
"{" "{"
" \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\": {" " \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\": {"
" \"external_crx\": \"RandomExtension.crx\"," " \"external_crx\": \"RandomExtension.crx\","
" \"external_version\": \"1.0\"" " \"external_version\": \"1.0\""
" }," " },"
...@@ -3018,11 +3035,10 @@ TEST_F(ExtensionServiceTest, ExternalPrefProvider) { ...@@ -3018,11 +3035,10 @@ TEST_F(ExtensionServiceTest, ExternalPrefProvider) {
" \"external_update_url\": \"http:\\\\foo.com/update\"" " \"external_update_url\": \"http:\\\\foo.com/update\""
" }" " }"
"}"; "}";
EXPECT_EQ(3, visitor.Visit(json_data));
MockProviderVisitor visitor;
// Simulate an external_extensions.json file that contains seven invalid // Simulate an external_extensions.json file that contains seven invalid
// extensions: // records:
// - One that is missing the 'external_crx' key. // - One that is missing the 'external_crx' key.
// - One that is missing the 'external_version' key. // - One that is missing the 'external_version' key.
// - One that is specifying .. in the path. // - One that is specifying .. in the path.
...@@ -3068,6 +3084,35 @@ TEST_F(ExtensionServiceTest, ExternalPrefProvider) { ...@@ -3068,6 +3084,35 @@ TEST_F(ExtensionServiceTest, ExternalPrefProvider) {
" }" " }"
"}"; "}";
EXPECT_EQ(1, visitor.Visit(json_data)); EXPECT_EQ(1, visitor.Visit(json_data));
// Check that if a base path is not provided, use of a relative
// path fails.
FilePath empty;
MockProviderVisitor visitor_no_relative_paths(empty);
// Use absolute paths. Expect success.
json_data =
"{"
" \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\": {"
" \"external_crx\": \"//RandomExtension1.crx\","
" \"external_version\": \"3.0\""
" },"
" \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\": {"
" \"external_crx\": \"//path/to/RandomExtension2.crx\","
" \"external_version\": \"3.0\""
" }"
"}";
EXPECT_EQ(2, visitor_no_relative_paths.Visit(json_data));
// Use a relative path. Expect that it will error out.
json_data =
"{"
" \"cccccccccccccccccccccccccccccccc\": {"
" \"external_crx\": \"RandomExtension2.crx\","
" \"external_version\": \"3.0\""
" }"
"}";
EXPECT_EQ(0, visitor_no_relative_paths.Visit(json_data));
} }
// Test loading good extensions from the profile directory. // Test loading good extensions from the profile directory.
......
...@@ -15,6 +15,14 @@ void ExternalExtensionLoader::Init( ...@@ -15,6 +15,14 @@ void ExternalExtensionLoader::Init(
owner_ = owner; owner_ = owner;
} }
const FilePath ExternalExtensionLoader::GetBaseCrxFilePath() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// By default, relative paths are not supported.
// Subclasses that wish to support them should override this method.
return FilePath();
}
void ExternalExtensionLoader::OwnerShutdown() { void ExternalExtensionLoader::OwnerShutdown() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
owner_ = NULL; owner_ = NULL;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CHROME_BROWSER_EXTENSIONS_EXTERNAL_EXTENSION_LOADER_H_ #define CHROME_BROWSER_EXTENSIONS_EXTERNAL_EXTENSION_LOADER_H_
#pragma once #pragma once
#include "base/file_path.h"
#include "base/ref_counted.h" #include "base/ref_counted.h"
#include "base/scoped_ptr.h" #include "base/scoped_ptr.h"
...@@ -41,6 +42,13 @@ class ExternalExtensionLoader ...@@ -41,6 +42,13 @@ class ExternalExtensionLoader
// in prefs_ and then call LoadFinished. // in prefs_ and then call LoadFinished.
virtual void StartLoading() = 0; virtual void StartLoading() = 0;
// Some external providers allow relative file paths to local CRX files.
// Subclasses that want this behavior should override this method to
// return the absolute path from which relative paths should be resolved.
// By default, return an empty path, which indicates that relative paths
// are not allowed.
virtual const FilePath GetBaseCrxFilePath();
protected: protected:
virtual ~ExternalExtensionLoader() {} virtual ~ExternalExtensionLoader() {}
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "chrome/browser/extensions/external_policy_extension_loader.h" #include "chrome/browser/extensions/external_policy_extension_loader.h"
#include "chrome/browser/extensions/external_pref_extension_loader.h" #include "chrome/browser/extensions/external_pref_extension_loader.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_paths.h"
#if defined(OS_WIN) #if defined(OS_WIN)
#include "chrome/browser/extensions/external_registry_extension_loader_win.h" #include "chrome/browser/extensions/external_registry_extension_loader_win.h"
...@@ -120,12 +121,16 @@ void ExternalExtensionProviderImpl::SetPrefs(DictionaryValue* prefs) { ...@@ -120,12 +121,16 @@ void ExternalExtensionProviderImpl::SetPrefs(DictionaryValue* prefs) {
continue; continue;
} }
// If the path is relative, make it absolute. // If the path is relative, and the provider has a base path,
// build the absolute path to the crx file.
FilePath path(external_crx); FilePath path(external_crx);
if (!path.IsAbsolute()) { if (!path.IsAbsolute()) {
// Try path as relative path from external extension dir. FilePath base_path = loader_->GetBaseCrxFilePath();
FilePath base_path; if (base_path.empty()) {
PathService::Get(app::DIR_EXTERNAL_EXTENSIONS, &base_path); LOG(WARNING) << "File path " << external_crx.c_str()
<< " is relative. An absolute path is required.";
continue;
}
path = base_path.Append(external_crx); path = base_path.Append(external_crx);
} }
...@@ -222,7 +227,7 @@ void ExternalExtensionProviderImpl::CreateExternalProviders( ...@@ -222,7 +227,7 @@ void ExternalExtensionProviderImpl::CreateExternalProviders(
linked_ptr<ExternalExtensionProviderInterface>( linked_ptr<ExternalExtensionProviderInterface>(
new ExternalExtensionProviderImpl( new ExternalExtensionProviderImpl(
service, service,
new ExternalPrefExtensionLoader, new ExternalPrefExtensionLoader(app::DIR_EXTERNAL_EXTENSIONS),
Extension::EXTERNAL_PREF, Extension::EXTERNAL_PREF,
Extension::EXTERNAL_PREF_DOWNLOAD))); Extension::EXTERNAL_PREF_DOWNLOAD)));
#if defined(OS_WIN) #if defined(OS_WIN)
......
...@@ -32,10 +32,22 @@ DictionaryValue* ExtractPrefs(ValueSerializer* serializer) { ...@@ -32,10 +32,22 @@ DictionaryValue* ExtractPrefs(ValueSerializer* serializer) {
} // namespace } // namespace
ExternalPrefExtensionLoader::ExternalPrefExtensionLoader() { ExternalPrefExtensionLoader::ExternalPrefExtensionLoader(int base_path_key)
: base_path_key_(base_path_key) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
} }
const FilePath ExternalPrefExtensionLoader::GetBaseCrxFilePath() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// LoadOnFileThread() should set |external_file_path_| to a non-empty
// path. This function should not be called until after LoadOnFileThread()
// is complete.
CHECK(!base_path_.empty());
return base_path_;
}
void ExternalPrefExtensionLoader::StartLoading() { void ExternalPrefExtensionLoader::StartLoading() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask( BrowserThread::PostTask(
...@@ -47,11 +59,13 @@ void ExternalPrefExtensionLoader::StartLoading() { ...@@ -47,11 +59,13 @@ void ExternalPrefExtensionLoader::StartLoading() {
void ExternalPrefExtensionLoader::LoadOnFileThread() { void ExternalPrefExtensionLoader::LoadOnFileThread() {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
CHECK(PathService::Get(base_path_key_, &base_path_));
FilePath json_file; FilePath json_file;
PathService::Get(app::DIR_EXTERNAL_EXTENSIONS, &json_file); json_file = base_path_.Append(FILE_PATH_LITERAL("external_extensions.json"));
json_file = json_file.Append(FILE_PATH_LITERAL("external_extensions.json"));
scoped_ptr<DictionaryValue> prefs;
scoped_ptr<DictionaryValue> prefs;
if (file_util::PathExists(json_file)) { if (file_util::PathExists(json_file)) {
JSONFileValueSerializer serializer(json_file); JSONFileValueSerializer serializer(json_file);
prefs.reset(ExtractPrefs(&serializer)); prefs.reset(ExtractPrefs(&serializer));
...@@ -68,7 +82,9 @@ void ExternalPrefExtensionLoader::LoadOnFileThread() { ...@@ -68,7 +82,9 @@ void ExternalPrefExtensionLoader::LoadOnFileThread() {
} }
ExternalTestingExtensionLoader::ExternalTestingExtensionLoader( ExternalTestingExtensionLoader::ExternalTestingExtensionLoader(
const std::string& json_data) { const std::string& json_data,
const FilePath& fake_base_path)
: fake_base_path_(fake_base_path) {
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
JSONStringValueSerializer serializer(json_data); JSONStringValueSerializer serializer(json_data);
testing_prefs_.reset(ExtractPrefs(&serializer)); testing_prefs_.reset(ExtractPrefs(&serializer));
...@@ -79,3 +95,7 @@ void ExternalTestingExtensionLoader::StartLoading() { ...@@ -79,3 +95,7 @@ void ExternalTestingExtensionLoader::StartLoading() {
prefs_.reset(testing_prefs_->DeepCopy()); prefs_.reset(testing_prefs_->DeepCopy());
LoadFinished(); LoadFinished();
} }
const FilePath ExternalTestingExtensionLoader::GetBaseCrxFilePath() {
return fake_base_path_;
}
...@@ -19,7 +19,12 @@ ...@@ -19,7 +19,12 @@
// thread and they are expecting public method calls from the UI thread. // thread and they are expecting public method calls from the UI thread.
class ExternalPrefExtensionLoader : public ExternalExtensionLoader { class ExternalPrefExtensionLoader : public ExternalExtensionLoader {
public: public:
ExternalPrefExtensionLoader(); // |base_path_key| is the directory containing the external_extensions.json
// file. Relative file paths to extension files are resolved relative
// to this path.
explicit ExternalPrefExtensionLoader(int base_path_key);
virtual const FilePath GetBaseCrxFilePath();
protected: protected:
virtual void StartLoading(); virtual void StartLoading();
...@@ -31,6 +36,9 @@ class ExternalPrefExtensionLoader : public ExternalExtensionLoader { ...@@ -31,6 +36,9 @@ class ExternalPrefExtensionLoader : public ExternalExtensionLoader {
void LoadOnFileThread(); void LoadOnFileThread();
int base_path_key_;
FilePath base_path_;
DISALLOW_COPY_AND_ASSIGN(ExternalPrefExtensionLoader); DISALLOW_COPY_AND_ASSIGN(ExternalPrefExtensionLoader);
}; };
...@@ -38,7 +46,11 @@ class ExternalPrefExtensionLoader : public ExternalExtensionLoader { ...@@ -38,7 +46,11 @@ class ExternalPrefExtensionLoader : public ExternalExtensionLoader {
// from json data specified in a string. // from json data specified in a string.
class ExternalTestingExtensionLoader : public ExternalExtensionLoader { class ExternalTestingExtensionLoader : public ExternalExtensionLoader {
public: public:
explicit ExternalTestingExtensionLoader(const std::string& json_data); ExternalTestingExtensionLoader(
const std::string& json_data,
const FilePath& fake_base_path);
virtual const FilePath GetBaseCrxFilePath();
protected: protected:
virtual void StartLoading(); virtual void StartLoading();
...@@ -48,6 +60,7 @@ class ExternalTestingExtensionLoader : public ExternalExtensionLoader { ...@@ -48,6 +60,7 @@ class ExternalTestingExtensionLoader : public ExternalExtensionLoader {
virtual ~ExternalTestingExtensionLoader() {} virtual ~ExternalTestingExtensionLoader() {}
FilePath fake_base_path_;
scoped_ptr<DictionaryValue> testing_prefs_; scoped_ptr<DictionaryValue> testing_prefs_;
DISALLOW_COPY_AND_ASSIGN(ExternalTestingExtensionLoader); DISALLOW_COPY_AND_ASSIGN(ExternalTestingExtensionLoader);
......
...@@ -49,10 +49,18 @@ void ExternalRegistryExtensionLoader::LoadOnFileThread() { ...@@ -49,10 +49,18 @@ void ExternalRegistryExtensionLoader::LoadOnFileThread() {
std::wstring key_path = ASCIIToWide(kRegistryExtensions); std::wstring key_path = ASCIIToWide(kRegistryExtensions);
key_path.append(L"\\"); key_path.append(L"\\");
key_path.append(iterator.Name()); key_path.append(iterator.Name());
if (key.Open(kRegRoot, key_path.c_str(), KEY_READ) == ERROR_SUCCESS) { if (key.Open(kRegRoot, key_path.c_str(), KEY_READ) == ERROR_SUCCESS) {
std::wstring extension_path; std::wstring extension_path_str;
if (key.ReadValue(kRegistryExtensionPath, &extension_path) if (key.ReadValue(kRegistryExtensionPath, &extension_path_str)
== ERROR_SUCCESS) { == ERROR_SUCCESS) {
FilePath extension_path(extension_path_str);
if (!extension_path.IsAbsolute()) {
LOG(ERROR) << "Path " << extension_path_str
<< " needs to be absolute in key "
<< key_path;
++iterator;
continue;
}
std::wstring extension_version; std::wstring extension_version;
if (key.ReadValue(kRegistryExtensionVersion, &extension_version) if (key.ReadValue(kRegistryExtensionVersion, &extension_version)
== ERROR_SUCCESS) { == ERROR_SUCCESS) {
...@@ -81,7 +89,7 @@ void ExternalRegistryExtensionLoader::LoadOnFileThread() { ...@@ -81,7 +89,7 @@ void ExternalRegistryExtensionLoader::LoadOnFileThread() {
WideToASCII(extension_version)); WideToASCII(extension_version));
prefs->SetString( prefs->SetString(
id + "." + ExternalExtensionProviderImpl::kExternalCrx, id + "." + ExternalExtensionProviderImpl::kExternalCrx,
extension_path); extension_path_str);
} else { } else {
// TODO(erikkay): find a way to get this into about:extensions // TODO(erikkay): find a way to get this into about:extensions
LOG(ERROR) << "Missing value " << kRegistryExtensionVersion LOG(ERROR) << "Missing value " << kRegistryExtensionVersion
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment