summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfinnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-12 02:51:34 +0000
committerfinnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-12 02:51:34 +0000
commita1257b12bd288161e1eadeff9f2378de9b6e029a (patch)
tree36352e0325d128786131a059a501bae9c1f46967
parentb5110bcd688f2d19b48af67e255273be2b94e68b (diff)
downloadchromium_src-a1257b12bd288161e1eadeff9f2378de9b6e029a.zip
chromium_src-a1257b12bd288161e1eadeff9f2378de9b6e029a.tar.gz
chromium_src-a1257b12bd288161e1eadeff9f2378de9b6e029a.tar.bz2
Factor out the code that deals with installing external extensions through Registry and Preferences into separate classes. While I was at it, I removed the EXTERNAL_INSTALL file, since we now keep track of this in the Preferences.
This also takes care of the virtualization problem in the unit tests for vista (see bug). BUG=13063 TEST=None, covered by automated tests. Review URL: http://codereview.chromium.org/122004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18247 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extensions_service.cc299
-rw-r--r--chrome/browser/extensions/extensions_service.h66
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc165
-rw-r--r--chrome/browser/extensions/external_extension_provider.h46
-rw-r--r--chrome/browser/extensions/external_pref_extension_provider.cc84
-rw-r--r--chrome/browser/extensions/external_pref_extension_provider.h33
-rw-r--r--chrome/browser/extensions/external_registry_extension_provider_win.cc88
-rw-r--r--chrome/browser/extensions/external_registry_extension_provider_win.h30
-rw-r--r--chrome/browser/profile.cc3
-rw-r--r--chrome/chrome.gyp5
10 files changed, 583 insertions, 236 deletions
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index 94c1dd5..487d617 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -22,6 +22,8 @@
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/extensions/extension_browser_event_router.h"
#include "chrome/browser/extensions/extension_process_manager.h"
+#include "chrome/browser/extensions/external_extension_provider.h"
+#include "chrome/browser/extensions/external_pref_extension_provider.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/utility_process_host.h"
#include "chrome/common/chrome_switches.h"
@@ -40,8 +42,8 @@
#if defined(OS_WIN)
#include "app/win_util.h"
-#include "base/registry.h"
#include "base/win_util.h"
+#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
#endif
// ExtensionsService.
@@ -60,25 +62,6 @@ const wchar_t kExternalExtensionsPref[] = L"extensions.settings";
const wchar_t kLocation[] = L"location";
const wchar_t kState[] = L"state";
-// Registry key where registry defined extension installers live.
-// TODO(port): Assuming this becomes a similar key into the appropriate
-// platform system.
-const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";
-
-#if defined(OS_WIN)
-
-// Registry value of of that key that defines the path to the .crx file.
-const wchar_t kRegistryExtensionPath[] = L"path";
-
-// Registry value of that key that defines the current version of the .crx file.
-const wchar_t kRegistryExtensionVersion[] = L"version";
-
-#endif
-
-// A marker file to indicate that an extension was installed from an external
-// source.
-const char kExternalInstallFile[] = "EXTERNAL_INSTALL";
-
// A temporary subdirectory where we unpack extensions.
const char* kUnpackExtensionDir = "TEMP_UNPACK";
@@ -216,19 +199,21 @@ class ExtensionsServiceBackend::UnpackerClient
ExtensionsService::ExtensionsService(Profile* profile,
MessageLoop* frontend_loop,
- MessageLoop* backend_loop,
- const std::string& registry_path)
+ MessageLoop* backend_loop)
: prefs_(profile->GetPrefs()),
backend_loop_(backend_loop),
install_directory_(profile->GetPath().AppendASCII(kInstallDirectoryName)),
extensions_enabled_(
CommandLine::ForCurrentProcess()->
HasSwitch(switches::kEnableExtensions)),
- show_extensions_prompts_(true),
- backend_(new ExtensionsServiceBackend(
- install_directory_, g_browser_process->resource_dispatcher_host(),
- frontend_loop, registry_path)) {
+ show_extensions_prompts_(true) {
+ // We pass ownership of this object to the Backend.
+ DictionaryValue* external_extensions = new DictionaryValue;
prefs_->RegisterDictionaryPref(kExternalExtensionsPref);
+ GetExternalExtensions(external_extensions, NULL);
+ backend_ = new ExtensionsServiceBackend(
+ install_directory_, g_browser_process->resource_dispatcher_host(),
+ frontend_loop, external_extensions);
}
ExtensionsService::~ExtensionsService() {
@@ -252,7 +237,6 @@ bool ExtensionsService::Init() {
backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
&ExtensionsServiceBackend::CheckForExternalUpdates,
*killed_extensions.get(),
- external_extensions.get(),
scoped_refptr<ExtensionsService>(this)));
backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
@@ -322,7 +306,7 @@ void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
// Filter out any extensions we don't want to enable. Themes are always
// enabled, but other extensions are only loaded if --enable-extensions is
// present.
- ExtensionList enabled_extensions;
+ ExtensionList enabled_extensions;
for (ExtensionList::iterator iter = new_extensions->begin();
iter != new_extensions->end(); ++iter) {
if (extensions_enabled() || (*iter)->IsTheme()) {
@@ -340,7 +324,7 @@ void ExtensionsService::OnExtensionsLoaded(ExtensionList* new_extensions) {
DictionaryValue* pref = GetOrCreateExtensionPref(extension_id);
int location;
int state;
-
+
// Ensure all loaded extensions have a preference set. This deals with a
// legacy problem where some extensions were installed before we were
// storing state in the preferences.
@@ -469,6 +453,18 @@ DictionaryValue* ExtensionsService::GetOrCreateExtensionPref(
return extension;
}
+void ExtensionsService::ClearProvidersForTesting() {
+ backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
+ &ExtensionsServiceBackend::ClearProvidersForTesting));
+}
+
+void ExtensionsService::SetProviderForTesting(
+ Extension::Location location, ExternalExtensionProvider* test_provider) {
+ backend_loop_->PostTask(FROM_HERE, NewRunnableMethod(backend_.get(),
+ &ExtensionsServiceBackend::SetProviderForTesting,
+ location, test_provider));
+}
+
bool ExtensionsService::UpdateExtensionPref(const std::wstring& extension_id,
const std::wstring& key,
Value* data_value,
@@ -489,17 +485,23 @@ bool ExtensionsService::UpdateExtensionPref(const std::wstring& extension_id,
ExtensionsServiceBackend::ExtensionsServiceBackend(
const FilePath& install_directory, ResourceDispatcherHost* rdh,
- MessageLoop* frontend_loop, const std::string& registry_path)
+ MessageLoop* frontend_loop, DictionaryValue* extension_prefs)
: frontend_(NULL),
install_directory_(install_directory),
resource_dispatcher_host_(rdh),
alert_on_error_(false),
- frontend_loop_(frontend_loop),
- registry_path_(registry_path) {
- // Default the registry path if unspecified.
- if (registry_path_.empty()) {
- registry_path_ = kRegistryExtensions;
- }
+ frontend_loop_(frontend_loop) {
+ external_extension_providers_[Extension::EXTERNAL_PREF] =
+ new ExternalPrefExtensionProvider(extension_prefs);
+#if defined(OS_WIN)
+ external_extension_providers_[Extension::EXTERNAL_REGISTRY] =
+ new ExternalRegistryExtensionProvider();
+#endif
+}
+
+ExtensionsServiceBackend::~ExtensionsServiceBackend() {
+ STLDeleteContainerPairSecondPointers(external_extension_providers_.begin(),
+ external_extension_providers_.end());
}
void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
@@ -539,7 +541,7 @@ void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
// Find all child directories in the install directory and load their
// manifests. Post errors and results to the frontend.
file_util::FileEnumerator enumerator(install_directory_,
- false, // not recursive
+ false, // Not recursive.
file_util::FileEnumerator::DIRECTORIES);
FilePath extension_path;
for (extension_path = enumerator.Next(); !extension_path.value().empty();
@@ -567,17 +569,21 @@ void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
if (!ReadCurrentVersion(extension_path, &current_version))
continue;
- int location;
+ Extension::Location location = Extension::INVALID;
+ int location_value;
DictionaryValue* pref = NULL;
external_extensions->GetDictionary(ASCIIToWide(extension_id), &pref);
if (!pref ||
- !pref->GetInteger(kLocation, &location)) {
- location = Extension::INTERNAL;
+ !pref->GetInteger(kLocation, &location_value)) {
+ // Check with the external providers.
+ if (!LookupExternalExtension(extension_id, NULL, &location))
+ location = Extension::INTERNAL;
+ } else {
+ location = static_cast<Extension::Location>(location_value);
}
- Extension::Location ext_location =
- static_cast<Extension::Location>(location);
+
FilePath version_path = extension_path.AppendASCII(current_version);
- if (Extension::IsExternalLocation(ext_location) &&
+ if (Extension::IsExternalLocation(location) &&
CheckExternalUninstall(external_extensions.get(),
version_path, extension_id)) {
// TODO(erikkay): Possibly defer this operation to avoid slowing initial
@@ -589,7 +595,9 @@ void ExtensionsServiceBackend::LoadExtensionsFromInstallDirectory(
continue;
}
- Extension* extension = LoadExtension(version_path, true); // require id
+ Extension* extension = LoadExtension(version_path,
+ location,
+ true); // require id
if (extension)
extensions->push_back(extension);
}
@@ -613,9 +621,9 @@ void ExtensionsServiceBackend::LoadSingleExtension(
WideToASCII(extension_path.BaseName().ToWStringHack());
Extension* extension = LoadExtension(extension_path,
+ Extension::LOAD,
false); // don't require ID
if (extension) {
- extension->set_location(Extension::LOAD);
ExtensionList* extensions = new ExtensionList;
extensions->push_back(extension);
ReportExtensionsLoaded(extensions);
@@ -623,7 +631,9 @@ void ExtensionsServiceBackend::LoadSingleExtension(
}
Extension* ExtensionsServiceBackend::LoadExtension(
- const FilePath& extension_path, bool require_id) {
+ const FilePath& extension_path,
+ Extension::Location location,
+ bool require_id) {
FilePath manifest_path =
extension_path.AppendASCII(Extension::kManifestFilename);
if (!file_util::PathExists(manifest_path)) {
@@ -651,13 +661,7 @@ Extension* ExtensionsServiceBackend::LoadExtension(
return NULL;
}
- FilePath external_marker = extension_path.AppendASCII(kExternalInstallFile);
- if (file_util::PathExists(external_marker)) {
- extension->set_location(
- extension->ExternalExtensionInstallType(registry_path_));
- } else {
- extension->set_location(Extension::INTERNAL);
- }
+ extension->set_location(location);
// Theme resource validation.
if (extension->IsTheme()) {
@@ -909,7 +913,7 @@ void ExtensionsServiceBackend::OnExtensionUnpacked(
}
#if defined(OS_WIN)
- if (!extension.IsTheme() &&
+ if (!extension.IsTheme() && !from_external &&
frontend_->show_extensions_prompts() &&
win_util::MessageBox(GetActiveWindow(),
L"Are you sure you want to install this extension?\n\n"
@@ -1020,14 +1024,11 @@ void ExtensionsServiceBackend::OnExtensionUnpacked(
if (!SetCurrentVersion(dest_dir, version))
return;
- // To mark that this extension was installed from an external source, create a
- // zero-length file. At load time, this is used to indicate that the
- // extension should be uninstalled.
- // TODO(erikkay): move this into per-extension config storage when it appears.
- if (from_external) {
- FilePath marker = version_dir.AppendASCII(kExternalInstallFile);
- file_util::WriteFile(marker, NULL, 0);
- }
+ Extension::Location location = Extension::INVALID;
+ if (from_external)
+ LookupExternalExtension(extension.id(), NULL, &location);
+ else
+ location = Extension::INTERNAL;
// Load the extension immediately and then report installation success. We
// don't load extensions for external installs because external installation
@@ -1036,7 +1037,9 @@ void ExtensionsServiceBackend::OnExtensionUnpacked(
// the preferences for these extensions to reflect that they've just been
// installed.
if (!from_external) {
- Extension* extension = LoadExtension(version_dir, true); // require id
+ Extension* extension = LoadExtension(version_dir,
+ location,
+ true); // require id
CHECK(extension);
frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
@@ -1053,8 +1056,7 @@ void ExtensionsServiceBackend::OnExtensionUnpacked(
} else {
frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(
frontend_, &ExtensionsService::OnExternalExtensionInstalled,
- extension.id(),
- extension.ExternalExtensionInstallType(registry_path_)));
+ extension.id(), location));
}
scoped_version_dir.Take();
@@ -1088,12 +1090,28 @@ bool ExtensionsServiceBackend::ShouldSkipInstallingExtension(
}
void ExtensionsServiceBackend::CheckVersionAndInstallExtension(
- const std::string& id, const std::string& extension_version,
+ const std::string& id, const Version* extension_version,
const FilePath& extension_path, bool from_external) {
if (ShouldInstall(id, extension_version))
InstallOrUpdateExtension(FilePath(extension_path), id, from_external);
}
+bool ExtensionsServiceBackend::LookupExternalExtension(
+ const std::string& id, Version** version, Extension::Location* location) {
+ scoped_ptr<Version> extension_version;
+ for (ProviderMap::const_iterator i = external_extension_providers_.begin();
+ i != external_extension_providers_.end(); ++i) {
+ const ExternalExtensionProvider* provider = i->second;
+ extension_version.reset(provider->RegisteredVersion(id, location));
+ if (extension_version.get()) {
+ if (version)
+ *version = extension_version.release();
+ return true;
+ }
+ }
+ return false;
+}
+
// Some extensions will autoupdate themselves externally from Chrome. These
// are typically part of some larger client application package. To support
// these, the extension will register its location in the the preferences file
@@ -1102,7 +1120,6 @@ void ExtensionsServiceBackend::CheckVersionAndInstallExtension(
// a new version is available.
void ExtensionsServiceBackend::CheckForExternalUpdates(
std::set<std::string> ids_to_ignore,
- DictionaryValue* extension_prefs,
scoped_refptr<ExtensionsService> frontend) {
// Note that this installation is intentionally silent (since it didn't
// go through the front-end). Extensions that are registered in this
@@ -1112,115 +1129,43 @@ void ExtensionsServiceBackend::CheckForExternalUpdates(
alert_on_error_ = false;
frontend_ = frontend;
- for (DictionaryValue::key_iterator i = extension_prefs->begin_keys();
- i != extension_prefs->end_keys(); ++i) {
- const std::wstring& extension_id = *i;
- if (ShouldSkipInstallingExtension(ids_to_ignore, WideToASCII(extension_id)))
- continue;
-
- DictionaryValue* extension = NULL;
- if (!extension_prefs->GetDictionary(extension_id, &extension)) {
- NOTREACHED() << "Cannot read extension " << extension_id.c_str()
- << " from dictionary.";
- continue;
- }
-
- int location;
- if (extension->GetInteger(kLocation, &location) &&
- location != Extension::EXTERNAL_PREF) {
- continue;
- }
- int state;
- if (extension->GetInteger(kState, &state) &&
- state == Extension::KILLBIT) {
- continue;
- }
-
- FilePath::StringType external_crx;
- std::string external_version;
- if (!extension->GetString(L"external_crx", &external_crx) ||
- !extension->GetString(L"external_version", &external_version)) {
- LOG(WARNING) << "Malformed extension dictionary for extension: "
- << extension_id.c_str();
- continue;
- }
-
- bool from_external = true;
- CheckVersionAndInstallExtension(WideToASCII(extension_id), external_version,
- FilePath(external_crx), from_external);
- }
-
-#if defined(OS_WIN)
- // TODO(port): Pull this out into an interface. That will also allow us to
- // test the behavior of external extensions.
- HKEY reg_root = HKEY_LOCAL_MACHINE;
- RegistryKeyIterator iterator(reg_root, ASCIIToWide(registry_path_).c_str());
- while (iterator.Valid()) {
- // Fold
- std::string id = StringToLowerASCII(WideToASCII(iterator.Name()));
- if (ShouldSkipInstallingExtension(ids_to_ignore, id)) {
- ++iterator;
- continue;
- }
-
- RegKey key;
- std::wstring key_path = ASCIIToWide(registry_path_);
- key_path.append(L"\\");
- key_path.append(iterator.Name());
- if (key.Open(reg_root, key_path.c_str())) {
- std::wstring extension_path;
- if (key.ReadValue(kRegistryExtensionPath, &extension_path)) {
- std::wstring extension_version;
- if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
- bool from_external = true;
- CheckVersionAndInstallExtension(
- id, WideToASCII(extension_version), FilePath(extension_path),
- from_external);
- } else {
- // TODO(erikkay): find a way to get this into about:extensions
- LOG(WARNING) << "Missing value " << kRegistryExtensionVersion <<
- " for key " << key_path;
- }
- } else {
- // TODO(erikkay): find a way to get this into about:extensions
- LOG(WARNING) << "Missing value " << kRegistryExtensionPath <<
- " for key " << key_path;
- }
- }
- ++iterator;
+ // Ask each external extension provider to give us a call back for each
+ // extension they know about. See OnExternalExtensionFound.
+ for (ProviderMap::const_iterator i = external_extension_providers_.begin();
+ i != external_extension_providers_.end(); ++i) {
+ ExternalExtensionProvider* provider = i->second;
+ provider->VisitRegisteredExtension(this, ids_to_ignore);
}
-#endif
}
bool ExtensionsServiceBackend::CheckExternalUninstall(
DictionaryValue* extension_prefs, const FilePath& version_path,
const std::string& id) {
// First check the preferences for the kill-bit.
- int location = Extension::INVALID;
+ int location_value = Extension::INVALID;
DictionaryValue* extension = NULL;
- if (extension_prefs->GetDictionary(ASCIIToWide(id), &extension)) {
- int state;
- if (extension->GetInteger(kLocation, &location) &&
- location == Extension::EXTERNAL_PREF) {
- return extension->GetInteger(kState, &state) &&
- state == Extension::KILLBIT;
- }
+ if (!extension_prefs->GetDictionary(ASCIIToWide(id), &extension))
+ return false;
+ int state;
+ if (extension->GetInteger(kLocation, &location_value) &&
+ location_value == Extension::EXTERNAL_PREF) {
+ return extension->GetInteger(kState, &state) &&
+ state == Extension::KILLBIT;
}
-#if defined(OS_WIN)
- if (location == Extension::EXTERNAL_REGISTRY) {
- HKEY reg_root = HKEY_LOCAL_MACHINE;
- RegKey key;
- std::wstring key_path = ASCIIToWide(registry_path_);
- key_path.append(L"\\");
- key_path.append(ASCIIToWide(id));
-
- // If the key doesn't exist, then we should uninstall.
- return !key.Open(reg_root, key_path.c_str());
+ Extension::Location location =
+ static_cast<Extension::Location>(location_value);
+
+ // Check if the providers know about this extension.
+ ProviderMap::const_iterator i = external_extension_providers_.find(location);
+ if (i != external_extension_providers_.end()) {
+ scoped_ptr<Version> version;
+ version.reset(i->second->RegisteredVersion(id, NULL));
+ if (version.get())
+ return false; // Yup, known extension, don't uninstall.
}
-#endif
- return false;
+ return true; // This is not a known extension, uninstall.
}
// Assumes that the extension isn't currently loaded or in use.
@@ -1248,7 +1193,7 @@ void ExtensionsServiceBackend::UninstallExtension(
}
}
- // Ok, now try and delete the entire rest of the directory. One major place
+ // OK, now try and delete the entire rest of the directory. One major place
// this can fail is if the extension contains a plugin (stupid plugins). It's
// not a big deal though, because we'll notice next time we startup that the
// Current Version file is gone and finish the delete then.
@@ -1258,12 +1203,28 @@ void ExtensionsServiceBackend::UninstallExtension(
}
}
+void ExtensionsServiceBackend::ClearProvidersForTesting() {
+ external_extension_providers_.clear();
+}
+
+void ExtensionsServiceBackend::SetProviderForTesting(
+ Extension::Location location,
+ ExternalExtensionProvider* test_provider) {
+ DCHECK(test_provider);
+ external_extension_providers_[location] = test_provider;
+}
+
+void ExtensionsServiceBackend::OnExternalExtensionFound(
+ const std::string& id, const Version* version, const FilePath& path) {
+ bool from_external = true;
+ CheckVersionAndInstallExtension(id, version, path, from_external);
+}
+
bool ExtensionsServiceBackend::ShouldInstall(const std::string& id,
- const std::string& version) {
+ const Version* version) {
FilePath dir(install_directory_.AppendASCII(id.c_str()));
std::string current_version;
- if (ReadCurrentVersion(dir, &current_version)) {
- return CheckCurrentVersion(version, current_version, dir);
- }
+ if (ReadCurrentVersion(dir, &current_version))
+ return CheckCurrentVersion(version->GetString(), current_version, dir);
return true;
}
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index f72a63a..fb0feaf 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_EXTENSIONS_EXTENSIONS_SERVICE_H_
#define CHROME_BROWSER_EXTENSIONS_EXTENSIONS_SERVICE_H_
+#include <list>
#include <set>
#include <string>
#include <vector>
@@ -15,6 +16,7 @@
#include "base/ref_counted.h"
#include "base/task.h"
#include "base/values.h"
+#include "chrome/browser/extensions/external_extension_provider.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
@@ -38,8 +40,7 @@ class ExtensionsService
public:
ExtensionsService(Profile* profile,
MessageLoop* frontend_loop,
- MessageLoop* backend_loop,
- const std::string& registry_path);
+ MessageLoop* backend_loop);
~ExtensionsService();
// Gets the list of currently installed extensions.
@@ -86,6 +87,14 @@ class ExtensionsService
Value* data_value,
bool schedule_save);
+ // Clear all ExternalExtensionProviders.
+ void ClearProvidersForTesting();
+
+ // Sets an ExternalExtensionProvider for the service to use during testing.
+ // |location| specifies what type of provider should be added.
+ void SetProviderForTesting(Extension::Location location,
+ ExternalExtensionProvider* test_provider);
+
// The name of the file that the current active version number is stored in.
static const char* kCurrentVersionFileName;
@@ -148,15 +157,18 @@ class ExtensionsService
// Implements IO for the ExtensionsService.
// TODO(aa): This can probably move into the .cc file.
class ExtensionsServiceBackend
- : public base::RefCountedThreadSafe<ExtensionsServiceBackend> {
+ : public base::RefCountedThreadSafe<ExtensionsServiceBackend>,
+ public ExternalExtensionProvider::Visitor {
public:
// |rdh| can be NULL in the case of test environment.
- // |registry_path| can be NULL *except* in the case of the test environment,
- // where it is specified to a temp location.
+ // |extension_prefs| contains a dictionary value that points to the extension
+ // preferences.
ExtensionsServiceBackend(const FilePath& install_directory,
- ResourceDispatcherHost* rdh,
- MessageLoop* frontend_loop,
- const std::string& registry_path);
+ ResourceDispatcherHost* rdh,
+ MessageLoop* frontend_loop,
+ DictionaryValue* extension_prefs);
+
+ virtual ~ExtensionsServiceBackend();
// Loads extensions from the install directory. The extensions are assumed to
// be unpacked in directories that are direct children of the specified path.
@@ -186,7 +198,6 @@ class ExtensionsServiceBackend
// Errors are reported through ExtensionErrorReporter. Succcess is not
// reported.
void CheckForExternalUpdates(std::set<std::string> ids_to_ignore,
- DictionaryValue* extension_prefs,
scoped_refptr<ExtensionsService> frontend);
// Deletes all versions of the extension from the filesystem. Note that only
@@ -194,13 +205,27 @@ class ExtensionsServiceBackend
// uninstall other extensions will silently fail.
void UninstallExtension(const std::string& extension_id);
+ // Clear all ExternalExtensionProviders.
+ void ClearProvidersForTesting();
+
+ // Sets an ExternalExtensionProvider for the service to use during testing.
+ // |location| specifies what type of provider should be added.
+ void SetProviderForTesting(Extension::Location location,
+ ExternalExtensionProvider* test_provider);
+
+ // ExternalExtensionProvider::Visitor implementation.
+ virtual void OnExternalExtensionFound(const std::string& id,
+ const Version* version,
+ const FilePath& path);
private:
class UnpackerClient;
friend class UnpackerClient;
// Load a single extension from |extension_path|, the top directory of
// a specific extension where its manifest file lives.
- Extension* LoadExtension(const FilePath& extension_path, bool require_id);
+ Extension* LoadExtension(const FilePath& extension_path,
+ Extension::Location location,
+ bool require_id);
// Load a single extension from |extension_path|, the top directory of
// a versioned extension where its Current Version file lives.
@@ -251,10 +276,20 @@ class ExtensionsServiceBackend
// Installs the extension if the extension is a newer version or if the
// extension hasn't been installed before.
void CheckVersionAndInstallExtension(const std::string& id,
- const std::string& extension_version,
+ const Version* extension_version,
const FilePath& extension_path,
bool from_external);
+ // Lookup an external extension by |id| by going through all registered
+ // external extension providers until we find a provider that contains an
+ // extension that matches. If |version| is not NULL, the extension version
+ // will be returned (caller is responsible for deleting that pointer).
+ // |location| can also be null, if not needed. Returns true if extension is
+ // found, false otherwise.
+ bool LookupExternalExtension(const std::string& id,
+ Version** version,
+ Extension::Location* location);
+
// Read the manifest from the front of the extension file.
// Caller takes ownership of return value.
DictionaryValue* ReadManifest(const FilePath& extension_path);
@@ -286,7 +321,7 @@ class ExtensionsServiceBackend
// Should an extension of |id| and |version| be installed?
// Returns true if no extension of type |id| is installed or if |version|
// is greater than the current installed version.
- bool ShouldInstall(const std::string& id, const std::string& version);
+ bool ShouldInstall(const std::string& id, const Version* version);
// The name of a temporary directory to install an extension into for
// validation before finalizing install.
@@ -308,10 +343,9 @@ class ExtensionsServiceBackend
// The message loop to use to call the frontend.
MessageLoop* frontend_loop_;
- // The path to look for externally registered extensions in. This is a
- // registry key on windows, but it could be a similar string for the
- // appropriate system on other platforms in the future.
- std::string registry_path_;
+ // A map of all external extension providers.
+ typedef std::map<Extension::Location, ExternalExtensionProvider*> ProviderMap;
+ ProviderMap external_extension_providers_;
DISALLOW_COPY_AND_ASSIGN(ExtensionsServiceBackend);
};
diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc
index ef41940..1411d78 100644
--- a/chrome/browser/extensions/extensions_service_unittest.cc
+++ b/chrome/browser/extensions/extensions_service_unittest.cc
@@ -14,6 +14,7 @@
#include "base/time.h"
#include "chrome/browser/extensions/extension_creator.h"
#include "chrome/browser/extensions/extensions_service.h"
+#include "chrome/browser/extensions/external_extension_provider.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/url_pattern.h"
#include "chrome/common/chrome_paths.h"
@@ -27,10 +28,6 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
-#if defined(OS_WIN)
-#include "base/registry.h"
-#endif
-
namespace {
// Extension ids used during testing.
@@ -71,6 +68,54 @@ static std::vector<std::string> GetErrors() {
} // namespace
+class MockExtensionProvider : public ExternalExtensionProvider {
+ public:
+ explicit MockExtensionProvider(Extension::Location location)
+ : location_(location) {}
+ virtual ~MockExtensionProvider() {}
+
+ void UpdateOrAddExtension(const std::string& id,
+ const std::string& version,
+ FilePath path) {
+ extension_map_[id] = std::make_pair(version, path);
+ }
+
+ void RemoveExtension(const std::string& id) {
+ extension_map_.erase(id);
+ }
+
+ // ExternalExtensionProvider implementation:
+ virtual void VisitRegisteredExtension(
+ Visitor* visitor, const std::set<std::string>& ids_to_ignore) const {
+ for (DataMap::const_iterator i = extension_map_.begin();
+ i != extension_map_.end(); ++i) {
+ if (ids_to_ignore.find(i->first) != ids_to_ignore.end())
+ continue;
+ scoped_ptr<Version> version;
+ version.reset(Version::GetVersionFromString(i->second.first));
+
+ visitor->OnExternalExtensionFound(
+ i->first, version.get(), i->second.second);
+ }
+ }
+
+ virtual Version* RegisteredVersion(std::string id,
+ Extension::Location* location) const {
+ DataMap::const_iterator it = extension_map_.find(id);
+ if (it == extension_map_.end())
+ return NULL;
+
+ if (location)
+ *location = location_;
+ return Version::GetVersionFromString(it->second.first);
+ }
+
+ private:
+ typedef std::map< std::string, std::pair<std::string, FilePath> > DataMap;
+ DataMap extension_map_;
+ Extension::Location location_;
+};
+
class ExtensionsServiceTest
: public testing::Test, public NotificationObserver {
public:
@@ -84,16 +129,18 @@ class ExtensionsServiceTest
registrar_.Add(this, NotificationType::THEME_INSTALLED,
NotificationService::AllSources());
- // Create a temporary area in the registry to test external extensions.
- registry_path_ = "Software\\Google\\Chrome\\ExtensionsServiceTest_";
- registry_path_ += IntToString(
- static_cast<int>(base::Time::Now().ToDoubleT()));
-
profile_.reset(new TestingProfile());
- service_ = new ExtensionsService(profile_.get(), &loop_, &loop_,
- registry_path_);
+ service_ = new ExtensionsService(profile_.get(), &loop_, &loop_);
service_->set_extensions_enabled(true);
service_->set_show_extensions_prompts(false);
+
+ // When we start up, we want to make sure there is no external provider,
+ // since the ExtensionService on Windows will use the Registry as a default
+ // provider and if there is something already registered there then it will
+ // interfere with the tests. Those tests that need an external provider
+ // will register one specifically.
+ service_->ClearProvidersForTesting();
+
total_successes_ = 0;
}
@@ -139,6 +186,11 @@ class ExtensionsServiceTest
service_->set_extensions_enabled(enabled);
}
+ void SetMockExternalProvider(Extension::Location location,
+ ExternalExtensionProvider* provider) {
+ service_->SetProviderForTesting(location, provider);
+ }
+
protected:
void InstallExtension(const FilePath& path,
bool should_succeed) {
@@ -151,7 +203,7 @@ class ExtensionsServiceTest
EXPECT_TRUE(installed_) << path.value();
- EXPECT_EQ(1u, loaded_.size()) << path.value();
+ ASSERT_EQ(1u, loaded_.size()) << path.value();
EXPECT_EQ(0u, errors.size()) << path.value();
EXPECT_EQ(total_successes_, service_->extensions()->size()) <<
path.value();
@@ -200,6 +252,23 @@ class ExtensionsServiceTest
EXPECT_EQ(must_equal, val) << msg;
}
+ void SetPref(std::string extension_id, std::wstring pref_path, int value) {
+ std::wstring msg = L" while setting: ";
+ msg += ASCIIToWide(extension_id);
+ msg += L" ";
+ msg += pref_path;
+ msg += L" = ";
+ msg += IntToWString(value);
+
+ const DictionaryValue* dict =
+ profile_->GetPrefs()->GetMutableDictionary(L"extensions.settings");
+ ASSERT_TRUE(dict != NULL) << msg;
+ DictionaryValue* pref = NULL;
+ ASSERT_TRUE(dict->GetDictionary(ASCIIToWide(extension_id), &pref)) << msg;
+ EXPECT_TRUE(pref != NULL) << msg;
+ pref->SetInteger(pref_path, value);
+ }
+
protected:
scoped_ptr<TestingProfile> profile_;
scoped_refptr<ExtensionsService> service_;
@@ -208,7 +277,6 @@ class ExtensionsServiceTest
std::vector<Extension*> loaded_;
std::string unloaded_id_;
Extension* installed_;
- std::string registry_path_;
private:
NotificationRegistrar registrar_;
@@ -457,9 +525,9 @@ TEST_F(ExtensionsServiceTest, PackExtension) {
// Test Packaging and installing an extension using an openssl generated key.
// The openssl is generated with the following:
-// > openssl genrsa -out privkey.pem 1024
+// > openssl genrsa -out privkey.pem 1024
// > openssl pkcs8 -topk8 -nocrypt -in privkey.pem -out privkey_asn1.pem
-// The privkey.pem is a PrivateKey, and the pcks8 -topk8 creates a
+// The privkey.pem is a PrivateKey, and the pcks8 -topk8 creates a
// PrivateKeyInfo ASN.1 structure, we our RSAPrivateKey expects.
TEST_F(ExtensionsServiceTest, PackExtensionOpenSSLKey) {
SetExtensionsEnabled(true);
@@ -488,7 +556,7 @@ TEST_F(ExtensionsServiceTest, PackExtensionOpenSSLKey) {
file_util::Delete(crx_path, false);
}
-#endif // defined(OS_WIN)
+#endif // defined(OS_WIN)
TEST_F(ExtensionsServiceTest, InstallTheme) {
FilePath extensions_path;
@@ -690,17 +758,23 @@ TEST_F(ExtensionsServiceTest, GenerateID) {
#if defined(OS_WIN)
TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
- // Register a test extension externally using the registry.
+ // Verify that starting with no providers loads no extensions.
+ service_->Init();
+ loop_.RunAllPending();
+ ASSERT_EQ(0u, loaded_.size());
+
+ // Now add providers. Extension system takes ownership of the objects.
+ MockExtensionProvider* reg_provider =
+ new MockExtensionProvider(Extension::EXTERNAL_REGISTRY);
+ SetMockExternalProvider(Extension::EXTERNAL_REGISTRY, reg_provider);
+
+ // Register a test extension externally using the mock registry provider.
FilePath source_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &source_path));
source_path = source_path.AppendASCII("extensions").AppendASCII("good.crx");
- RegKey key;
- std::wstring reg_path = ASCIIToWide(registry_path_);
- reg_path += L"\\00123456789ABCDEF0123456789ABCDEF0123456";
- ASSERT_TRUE(key.Create(HKEY_LOCAL_MACHINE, reg_path.c_str(), KEY_WRITE));
- ASSERT_TRUE(key.WriteValue(L"path", source_path.ToWStringHack().c_str()));
- ASSERT_TRUE(key.WriteValue(L"version", L"1.0.0.0"));
+ // Add the extension.
+ reg_provider->UpdateOrAddExtension(good_crx, "1.0.0.0", source_path);
// Start up the service, it should find our externally registered extension
// and install it.
@@ -728,8 +802,7 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
// Now update the extension with a new version. We should get upgraded.
source_path = source_path.DirName().AppendASCII("good2.crx");
- ASSERT_TRUE(key.WriteValue(L"path", source_path.ToWStringHack().c_str()));
- ASSERT_TRUE(key.WriteValue(L"version", L"1.0.0.1"));
+ reg_provider->UpdateOrAddExtension(good_crx, "1.0.0.1", source_path);
loaded_.clear();
service_->Init();
@@ -762,13 +835,7 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
// Now clear the preference, reinstall, then remove the reg key. The extension
// should be uninstalled.
- std::wstring pref_path = L"extensions.settings.";
- pref_path += ASCIIToWide(good_crx);
- profile_->GetPrefs()->RegisterDictionaryPref(pref_path.c_str());
- DictionaryValue* extension_prefs;
- extension_prefs =
- profile_->GetPrefs()->GetMutableDictionary(pref_path.c_str());
- extension_prefs->SetInteger(L"state", 0);
+ SetPref(good_crx, L"state", Extension::ENABLED);
profile_->GetPrefs()->ScheduleSavePersistentPrefs();
loaded_.clear();
@@ -779,9 +846,8 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
ValidatePref(good_crx, L"state", Extension::ENABLED);
ValidatePref(good_crx, L"location", Extension::EXTERNAL_REGISTRY);
- RegKey parent_key;
- key.Open(HKEY_LOCAL_MACHINE, ASCIIToWide(registry_path_).c_str(), KEY_WRITE);
- key.DeleteKey(ASCIIToWide(id).c_str());
+ reg_provider->RemoveExtension(good_crx);
+
loaded_.clear();
service_->Init();
loop_.RunAllPending();
@@ -791,20 +857,23 @@ TEST_F(ExtensionsServiceTest, ExternalInstallRegistry) {
#endif
TEST_F(ExtensionsServiceTest, ExternalInstallPref) {
+ // Verify that starting with no providers loads no extensions.
+ service_->Init();
+ loop_.RunAllPending();
+ ASSERT_EQ(0u, loaded_.size());
+
+ // Now add providers. Extension system takes ownership of the objects.
+ MockExtensionProvider* pref_provider =
+ new MockExtensionProvider(Extension::EXTERNAL_PREF);
+ SetMockExternalProvider(Extension::EXTERNAL_PREF, pref_provider);
+
// Register a external extension using preinstalled preferences.
FilePath source_path;
ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &source_path));
source_path = source_path.AppendASCII("extensions").AppendASCII("good.crx");
- DictionaryValue* extension_prefs;
- extension_prefs =
- profile_->GetPrefs()->GetMutableDictionary(L"extensions.settings");
-
- DictionaryValue* extension = new DictionaryValue();
- ASSERT_TRUE(extension->SetString(L"external_crx",
- source_path.ToWStringHack()));
- ASSERT_TRUE(extension->SetString(L"external_version", L"1.0"));
- ASSERT_TRUE(extension_prefs->Set(ASCIIToWide(good_crx), extension));
+ // Add the extension.
+ pref_provider->UpdateOrAddExtension(good_crx, "1.0", source_path);
// Start up the service, it should find our externally registered extension
// and install it.
@@ -834,9 +903,7 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) {
// Now update the extension with a new version. We should get upgraded.
source_path = source_path.DirName().AppendASCII("good2.crx");
- ASSERT_TRUE(extension->SetString(L"external_crx",
- source_path.ToWStringHack()));
- ASSERT_TRUE(extension->SetString(L"external_version", L"1.0.0.1"));
+ pref_provider->UpdateOrAddExtension(good_crx, "1.0.0.1", source_path);
loaded_.clear();
service_->Init();
@@ -870,7 +937,7 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) {
ValidatePref(good_crx, L"location", Extension::EXTERNAL_PREF);
// Now clear the preference and reinstall.
- extension->SetInteger(L"state", Extension::ENABLED);
+ SetPref(good_crx, L"state", Extension::ENABLED);
profile_->GetPrefs()->ScheduleSavePersistentPrefs();
loaded_.clear();
@@ -883,7 +950,7 @@ TEST_F(ExtensionsServiceTest, ExternalInstallPref) {
ValidatePref(good_crx, L"location", Extension::EXTERNAL_PREF);
// Now set the kill bit and watch the extension go away.
- extension->SetInteger(L"state", Extension::KILLBIT);
+ SetPref(good_crx, L"state", Extension::KILLBIT);
profile_->GetPrefs()->ScheduleSavePersistentPrefs();
loaded_.clear();
diff --git a/chrome/browser/extensions/external_extension_provider.h b/chrome/browser/extensions/external_extension_provider.h
new file mode 100644
index 0000000..dafa665
--- /dev/null
+++ b/chrome/browser/extensions/external_extension_provider.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTERNAL_EXTENSION_PROVIDER_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTERNAL_EXTENSION_PROVIDER_H_
+
+#include <set>
+#include <string>
+
+#include "base/version.h"
+#include "chrome/common/extensions/extension.h"
+
+class FilePath;
+
+// This class is an abstract class for implementing external extensions
+// providers.
+class ExternalExtensionProvider {
+ public:
+ // ExternalExtensionProvider uses this interface to communicate back to the
+ // caller what extensions are registered, and which |id|, |version| and |path|
+ // they have. See also VisitRegisteredExtension below. Ownership of |version|
+ // is not transferred to the visitor.
+ class Visitor {
+ public:
+ virtual void OnExternalExtensionFound(const std::string& id,
+ const Version* version,
+ const FilePath& path) = 0;
+ };
+
+ virtual ~ExternalExtensionProvider() {}
+
+ // Enumerate registered extension, calling OnExternalExtensionFound on
+ // the |visitor| object for each registered extension found. |ids_to_ignore|
+ // contains a list of extension ids that should not result in a call back.
+ virtual void VisitRegisteredExtension(
+ Visitor* visitor, const std::set<std::string>& ids_to_ignore) const = 0;
+
+ // Gets the version of extension with |id| and its |location|. |location| can
+ // be NULL. The caller is responsible for cleaning up the Version object
+ // returned. This function returns NULL if the extension is not found.
+ virtual Version* RegisteredVersion(std::string id,
+ Extension::Location* location) const = 0;
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTERNAL_EXTENSION_PROVIDER_H_
diff --git a/chrome/browser/extensions/external_pref_extension_provider.cc b/chrome/browser/extensions/external_pref_extension_provider.cc
new file mode 100644
index 0000000..6db3355
--- /dev/null
+++ b/chrome/browser/extensions/external_pref_extension_provider.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2009 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/external_pref_extension_provider.h"
+
+#include "base/file_path.h"
+#include "base/string_util.h"
+#include "base/version.h"
+
+// Constants for keeping track of extension preferences.
+const wchar_t kLocation[] = L"location";
+const wchar_t kState[] = L"state";
+const wchar_t kExternalCrx[] = L"external_crx";
+const wchar_t kExternalVersion[] = L"external_version";
+
+ExternalPrefExtensionProvider::ExternalPrefExtensionProvider(
+ DictionaryValue* prefs) {
+ DCHECK(prefs);
+ prefs_.reset(prefs);
+}
+
+ExternalPrefExtensionProvider::~ExternalPrefExtensionProvider() {
+}
+
+void ExternalPrefExtensionProvider::VisitRegisteredExtension(
+ Visitor* visitor, const std::set<std::string>& ids_to_ignore) const {
+ for (DictionaryValue::key_iterator i = prefs_->begin_keys();
+ i != prefs_->end_keys(); ++i) {
+ const std::wstring& extension_id = *i;
+ if (ids_to_ignore.find(WideToASCII(extension_id)) != ids_to_ignore.end())
+ continue;
+
+ DictionaryValue* extension = NULL;
+ if (!prefs_->GetDictionary(extension_id, &extension)) {
+ NOTREACHED() << "Cannot read extension " << extension_id.c_str()
+ << " from dictionary.";
+ continue;
+ }
+
+ int location;
+ if (extension->GetInteger(kLocation, &location) &&
+ location != Extension::EXTERNAL_PREF) {
+ continue;
+ }
+ int state;
+ if (extension->GetInteger(kState, &state) &&
+ state == Extension::KILLBIT) {
+ continue;
+ }
+
+ FilePath::StringType external_crx;
+ std::string external_version;
+ if (!extension->GetString(kExternalCrx, &external_crx) ||
+ !extension->GetString(kExternalVersion, &external_version)) {
+ LOG(WARNING) << "Malformed extension dictionary for extension: "
+ << extension_id.c_str();
+ continue;
+ }
+
+ scoped_ptr<Version> version;
+ version.reset(Version::GetVersionFromString(external_version));
+ visitor->OnExternalExtensionFound(
+ WideToASCII(extension_id), version.get(), FilePath(external_crx));
+ }
+}
+
+Version* ExternalPrefExtensionProvider::RegisteredVersion(
+ std::string id, Extension::Location* location) const {
+ DictionaryValue* extension = NULL;
+ if (!prefs_->GetDictionary(ASCIIToWide(id), &extension)) {
+ NOTREACHED() << "Cannot read extension " << id.c_str()
+ << " from dictionary.";
+ return NULL;
+ }
+
+ std::string external_version;
+ if (!extension->GetString(kExternalVersion, &external_version))
+ return NULL;
+
+ if (location)
+ *location = Extension::EXTERNAL_PREF;
+ return Version::GetVersionFromString(external_version);
+}
diff --git a/chrome/browser/extensions/external_pref_extension_provider.h b/chrome/browser/extensions/external_pref_extension_provider.h
new file mode 100644
index 0000000..a52be411
--- /dev/null
+++ b/chrome/browser/extensions/external_pref_extension_provider.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2009 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTERNAL_PREF_EXTENSION_PROVIDER_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTERNAL_PREF_EXTENSION_PROVIDER_H_
+
+#include <set>
+#include <string>
+
+#include "chrome/browser/extensions/external_extension_provider.h"
+
+class DictionaryValue;
+class Version;
+
+// A specialization of the ExternalExtensionProvider that uses preferences to
+// look up which external extensions are registered.
+class ExternalPrefExtensionProvider : public ExternalExtensionProvider {
+ public:
+ explicit ExternalPrefExtensionProvider(DictionaryValue* prefs);
+ virtual ~ExternalPrefExtensionProvider();
+
+ // ExternalExtensionProvider implementation:
+ virtual void VisitRegisteredExtension(
+ Visitor* visitor, const std::set<std::string>& ids_to_ignore) const;
+
+ virtual Version* RegisteredVersion(std::string id,
+ Extension::Location* location) const;
+ protected:
+ scoped_ptr<DictionaryValue> prefs_;
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTERNAL_PREF_EXTENSION_PROVIDER_H_
diff --git a/chrome/browser/extensions/external_registry_extension_provider_win.cc b/chrome/browser/extensions/external_registry_extension_provider_win.cc
new file mode 100644
index 0000000..f820be4
--- /dev/null
+++ b/chrome/browser/extensions/external_registry_extension_provider_win.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2009 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/external_registry_extension_provider_win.h"
+
+#include "base/file_path.h"
+#include "base/registry.h"
+#include "base/string_util.h"
+#include "base/version.h"
+
+// The Registry hive where to look for external extensions.
+const HKEY kRegRoot = HKEY_LOCAL_MACHINE;
+
+// The Registry subkey that contains information about external extensions.
+const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";
+
+// Registry value of of that key that defines the path to the .crx file.
+const wchar_t kRegistryExtensionPath[] = L"path";
+
+// Registry value of that key that defines the current version of the .crx file.
+const wchar_t kRegistryExtensionVersion[] = L"version";
+
+ExternalRegistryExtensionProvider::ExternalRegistryExtensionProvider() {
+}
+
+ExternalRegistryExtensionProvider::~ExternalRegistryExtensionProvider() {
+}
+
+void ExternalRegistryExtensionProvider::VisitRegisteredExtension(
+ Visitor* visitor, const std::set<std::string>& ids_to_ignore) const {
+ RegistryKeyIterator iterator(kRegRoot,
+ ASCIIToWide(kRegistryExtensions).c_str());
+ while (iterator.Valid()) {
+ RegKey key;
+ std::wstring key_path = ASCIIToWide(kRegistryExtensions);
+ key_path.append(L"\\");
+ key_path.append(iterator.Name());
+ if (key.Open(kRegRoot, key_path.c_str())) {
+ std::wstring extension_path;
+ if (key.ReadValue(kRegistryExtensionPath, &extension_path)) {
+ std::wstring extension_version;
+ if (key.ReadValue(kRegistryExtensionVersion, &extension_version)) {
+ std::string id = WideToASCII(iterator.Name());
+ StringToLowerASCII(&id);
+ if (ids_to_ignore.find(id) != ids_to_ignore.end()) {
+ ++iterator;
+ continue;
+ }
+
+ scoped_ptr<Version> version;
+ version.reset(Version::GetVersionFromString(extension_version));
+ FilePath path = FilePath::FromWStringHack(extension_path);
+ visitor->OnExternalExtensionFound(id, version.get(), path);
+ } else {
+ // TODO(erikkay): find a way to get this into about:extensions
+ LOG(WARNING) << "Missing value " << kRegistryExtensionVersion <<
+ " for key " << key_path;
+ }
+ } else {
+ // TODO(erikkay): find a way to get this into about:extensions
+ LOG(WARNING) << "Missing value " << kRegistryExtensionPath <<
+ " for key " << key_path;
+ }
+ }
+ ++iterator;
+ }
+}
+
+Version* ExternalRegistryExtensionProvider::RegisteredVersion(
+ std::string id,
+ Extension::Location* location) const {
+ RegKey key;
+ std::wstring key_path = ASCIIToWide(kRegistryExtensions);
+ key_path.append(L"\\");
+ key_path.append(ASCIIToWide(id));
+
+ if (!key.Open(kRegRoot, key_path.c_str()))
+ return NULL;
+
+ std::wstring extension_version;
+ if (!key.ReadValue(kRegistryExtensionVersion, &extension_version))
+ return NULL;
+
+ if (location)
+ *location = Extension::EXTERNAL_REGISTRY;
+ return Version::GetVersionFromString(extension_version);
+}
diff --git a/chrome/browser/extensions/external_registry_extension_provider_win.h b/chrome/browser/extensions/external_registry_extension_provider_win.h
new file mode 100644
index 0000000..88f472f
--- /dev/null
+++ b/chrome/browser/extensions/external_registry_extension_provider_win.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2009 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.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_EXTERNAL_REGISTRY_EXTENSION_PROVIDER_WIN_H_
+#define CHROME_BROWSER_EXTENSIONS_EXTERNAL_REGISTRY_EXTENSION_PROVIDER_WIN_H_
+
+#include <set>
+#include <string>
+
+#include "chrome/browser/extensions/external_extension_provider.h"
+
+class Version;
+
+// A specialization of the ExternalExtensionProvider that uses the Registry to
+// look up which external extensions are registered.
+class ExternalRegistryExtensionProvider : public ExternalExtensionProvider {
+ public:
+ ExternalRegistryExtensionProvider();
+ virtual ~ExternalRegistryExtensionProvider();
+
+ // ExternalExtensionProvider implementation:
+ virtual void VisitRegisteredExtension(
+ Visitor* visitor, const std::set<std::string>& ids_to_ignore) const;
+
+ virtual Version* RegisteredVersion(std::string id,
+ Extension::Location* location) const;
+};
+
+#endif // CHROME_BROWSER_EXTENSIONS_EXTERNAL_REGISTRY_EXTENSION_PROVIDER_WIN_H_
diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc
index 1aed4ee..b19fb2d7 100644
--- a/chrome/browser/profile.cc
+++ b/chrome/browser/profile.cc
@@ -475,8 +475,7 @@ void ProfileImpl::InitExtensions() {
script_dir);
extensions_service_ = new ExtensionsService(
this, MessageLoop::current(),
- g_browser_process->file_thread()->message_loop(),
- std::string()); // Use default registry path
+ g_browser_process->file_thread()->message_loop());
extensions_service_->Init();
}
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 38881f1..583cafa 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -871,6 +871,11 @@
'browser/extensions/extensions_service.h',
'browser/extensions/extensions_ui.cc',
'browser/extensions/extensions_ui.h',
+ 'browser/extensions/external_extension_provider.h',
+ 'browser/extensions/external_registry_extension_provider_win.cc',
+ 'browser/extensions/external_registry_extension_provider_win.h',
+ 'browser/extensions/external_pref_extension_provider.cc',
+ 'browser/extensions/external_pref_extension_provider.h',
'browser/extensions/user_script_master.cc',
'browser/extensions/user_script_master.h',
'browser/external_protocol_handler.cc',