summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-21 03:09:14 +0000
committerjamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-21 03:09:14 +0000
commitbb1bc9b30627f0f454c89ac91637c89a17352769 (patch)
tree2141a8a6ae3a14d89ff7a18001e0bac5c1140ae7
parentddc78c802e48e2084ffccf537b7aca7c6f2c825c (diff)
downloadchromium_src-bb1bc9b30627f0f454c89ac91637c89a17352769.zip
chromium_src-bb1bc9b30627f0f454c89ac91637c89a17352769.tar.gz
chromium_src-bb1bc9b30627f0f454c89ac91637c89a17352769.tar.bz2
Extract an ExtensionRegistry from ExtensionService
This new class holds the various sets of enabled/disabled/terminated/etc. extensions. It lives in src/extensions/browser so it can be used by app_shell. This CL renames some members and methods to clarify which set of extensions is the enabled set and also renames some methods that are only used in test code. BUG=none TEST=unit_tests ExtensionRegistryTest and ExtensionServiceTest Review URL: https://codereview.chromium.org/111873008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242248 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_crash_recovery_browsertest.cc2
-rw-r--r--chrome/browser/extensions/extension_service.cc209
-rw-r--r--chrome/browser/extensions/extension_service.h32
-rw-r--r--chrome/browser/extensions/extension_service_unittest.cc59
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--extensions/browser/extension_registry.cc60
-rw-r--r--extensions/browser/extension_registry.h94
-rw-r--r--extensions/browser/extension_registry_unittest.cc102
-rw-r--r--extensions/extensions.gyp2
9 files changed, 433 insertions, 128 deletions
diff --git a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
index f5eac57..4b49a8d 100644
--- a/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
+++ b/chrome/browser/extensions/extension_crash_recovery_browsertest.cc
@@ -534,7 +534,7 @@ IN_PROC_BROWSER_TEST_F(MAYBE_ExtensionCrashRecoveryTest,
ASSERT_EQ(crash_size_before + 1,
GetExtensionService()->terminated_extensions()->size());
- GetExtensionService()->UnloadAllExtensions();
+ GetExtensionService()->UnloadAllExtensionsForTest();
ASSERT_EQ(crash_size_before,
GetExtensionService()->terminated_extensions()->size());
}
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 730bda5..8b931f13 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -76,6 +76,7 @@
#include "content/public/browser/url_data_source.h"
#include "extensions/browser/app_sorting.h"
#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/external_provider_interface.h"
#include "extensions/browser/management_policy.h"
@@ -274,7 +275,7 @@ bool ExtensionService::OnExternalExtensionUpdateUrlFound(
const Extension* ExtensionService::GetInstalledExtensionByUrl(
const GURL& url) const {
- return extensions_.GetExtensionOrAppByURL(url);
+ return registry_->enabled_extensions().GetExtensionOrAppByURL(url);
}
const Extension* ExtensionService::GetInstalledApp(const GURL& url) const {
@@ -328,6 +329,7 @@ ExtensionService::ExtensionService(Profile* profile,
blacklist_(blacklist),
settings_frontend_(extensions::SettingsFrontend::Create(profile)),
extension_sync_service_(NULL),
+ registry_(new extensions::ExtensionRegistry),
pending_extension_manager_(*this),
install_directory_(install_directory),
extensions_enabled_(extensions_enabled),
@@ -409,19 +411,19 @@ ExtensionService::ExtensionService(Profile* profile,
}
const ExtensionSet* ExtensionService::extensions() const {
- return &extensions_;
+ return &registry_->enabled_extensions();
}
const ExtensionSet* ExtensionService::disabled_extensions() const {
- return &disabled_extensions_;
+ return &registry_->disabled_extensions();
}
const ExtensionSet* ExtensionService::terminated_extensions() const {
- return &terminated_extensions_;
+ return &registry_->terminated_extensions();
}
const ExtensionSet* ExtensionService::blacklisted_extensions() const {
- return &blacklisted_extensions_;
+ return &registry_->blacklisted_extensions();
}
const ExtensionSet* ExtensionService::delayed_installs() const {
@@ -431,11 +433,11 @@ const ExtensionSet* ExtensionService::delayed_installs() const {
scoped_ptr<ExtensionSet>
ExtensionService::GenerateInstalledExtensionsSet() const {
scoped_ptr<ExtensionSet> installed_extensions(new ExtensionSet());
- installed_extensions->InsertAll(extensions_);
- installed_extensions->InsertAll(disabled_extensions_);
- installed_extensions->InsertAll(terminated_extensions_);
- installed_extensions->InsertAll(blacklisted_extensions_);
- return installed_extensions.PassAs<ExtensionSet>();
+ installed_extensions->InsertAll(registry_->enabled_extensions());
+ installed_extensions->InsertAll(registry_->disabled_extensions());
+ installed_extensions->InsertAll(registry_->terminated_extensions());
+ installed_extensions->InsertAll(registry_->blacklisted_extensions());
+ return installed_extensions.Pass();
}
extensions::PendingExtensionManager*
@@ -482,22 +484,26 @@ const Extension* ExtensionService::GetExtensionById(
const std::string& id, int include_mask) const {
std::string lowercase_id = StringToLowerASCII(id);
if (include_mask & INCLUDE_ENABLED) {
- const Extension* extension = extensions_.GetByID(lowercase_id);
+ const Extension* extension =
+ registry_->enabled_extensions().GetByID(lowercase_id);
if (extension)
return extension;
}
if (include_mask & INCLUDE_DISABLED) {
- const Extension* extension = disabled_extensions_.GetByID(lowercase_id);
+ const Extension* extension =
+ registry_->disabled_extensions().GetByID(lowercase_id);
if (extension)
return extension;
}
if (include_mask & INCLUDE_TERMINATED) {
- const Extension* extension = terminated_extensions_.GetByID(lowercase_id);
+ const Extension* extension =
+ registry_->terminated_extensions().GetByID(lowercase_id);
if (extension)
return extension;
}
if (include_mask & INCLUDE_BLACKLISTED) {
- const Extension* extension = blacklisted_extensions_.GetByID(lowercase_id);
+ const Extension* extension =
+ registry_->blacklisted_extensions().GetByID(lowercase_id);
if (extension)
return extension;
}
@@ -510,7 +516,7 @@ void ExtensionService::Init() {
base::Time begin_time = base::Time::Now();
DCHECK(!is_ready()); // Can't redo init.
- DCHECK_EQ(extensions_.size(), 0u);
+ DCHECK_EQ(registry_->enabled_extensions().size(), 0u);
const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
if (cmd_line->HasSwitch(switches::kInstallFromWebstore) ||
@@ -529,8 +535,9 @@ void ExtensionService::Init() {
// Attempt to re-enable extensions whose only disable reason is reloading.
std::vector<std::string> extensions_to_enable;
- for (ExtensionSet::const_iterator iter = disabled_extensions_.begin();
- iter != disabled_extensions_.end(); ++iter) {
+ const ExtensionSet& disabled_extensions = registry_->disabled_extensions();
+ for (ExtensionSet::const_iterator iter = disabled_extensions.begin();
+ iter != disabled_extensions.end(); ++iter) {
const Extension* e = iter->get();
if (extension_prefs_->GetDisableReasons(e->id()) ==
Extension::DISABLE_RELOAD) {
@@ -620,8 +627,9 @@ void ExtensionService::FinishVerifyAllExtensions(bool success) {
// Check to see if any currently unverified extensions became verified.
InstallVerifier* verifier =
extensions::ExtensionSystem::Get(profile_)->install_verifier();
- for (ExtensionSet::const_iterator i = disabled_extensions_.begin();
- i != disabled_extensions_.end(); ++i) {
+ const ExtensionSet& disabled_extensions = registry_->disabled_extensions();
+ for (ExtensionSet::const_iterator i = disabled_extensions.begin();
+ i != disabled_extensions.end(); ++i) {
const Extension& extension = **i;
int disable_reasons = extension_prefs_->GetDisableReasons(extension.id());
if (disable_reasons & Extension::DISABLE_NOT_VERIFIED &&
@@ -921,13 +929,13 @@ bool ExtensionService::UninstallExtension(
bool ExtensionService::IsExtensionEnabled(
const std::string& extension_id) const {
- if (extensions_.Contains(extension_id) ||
- terminated_extensions_.Contains(extension_id)) {
+ if (registry_->enabled_extensions().Contains(extension_id) ||
+ registry_->terminated_extensions().Contains(extension_id)) {
return true;
}
- if (disabled_extensions_.Contains(extension_id) ||
- blacklisted_extensions_.Contains(extension_id)) {
+ if (registry_->disabled_extensions().Contains(extension_id) ||
+ registry_->blacklisted_extensions().Contains(extension_id)) {
return false;
}
@@ -947,7 +955,8 @@ void ExtensionService::EnableExtension(const std::string& extension_id) {
if (IsExtensionEnabled(extension_id))
return;
- const Extension* extension = disabled_extensions_.GetByID(extension_id);
+ const Extension* extension =
+ registry_->disabled_extensions().GetByID(extension_id);
ManagementPolicy* policy = system_->management_policy();
if (extension && policy->MustRemainDisabled(extension, NULL, NULL)) {
@@ -980,8 +989,8 @@ void ExtensionService::EnableExtension(const std::string& extension_id) {
}
// Move it over to the enabled list.
- extensions_.Insert(make_scoped_refptr(extension));
- disabled_extensions_.Remove(extension->id());
+ registry_->AddEnabled(make_scoped_refptr(extension));
+ registry_->RemoveDisabled(extension->id());
NotifyExtensionLoaded(extension);
@@ -1027,12 +1036,12 @@ void ExtensionService::DisableExtension(
// Move it over to the disabled list. Don't send a second unload notification
// for terminated extensions being disabled.
- disabled_extensions_.Insert(make_scoped_refptr(extension));
- if (extensions_.Contains(extension->id())) {
- extensions_.Remove(extension->id());
+ registry_->AddDisabled(make_scoped_refptr(extension));
+ if (registry_->enabled_extensions().Contains(extension->id())) {
+ registry_->RemoveEnabled(extension->id());
NotifyExtensionUnloaded(extension, UnloadedExtensionInfo::REASON_DISABLE);
} else {
- terminated_extensions_.Remove(extension->id());
+ registry_->RemoveTerminated(extension->id());
}
if (extension_sync_service_)
@@ -1045,13 +1054,15 @@ void ExtensionService::DisableUserExtensions(
system_->management_policy();
extensions::ExtensionList to_disable;
- for (ExtensionSet::const_iterator extension = extensions_.begin();
- extension != extensions_.end(); ++extension) {
+ const ExtensionSet& enabled_set = registry_->enabled_extensions();
+ for (ExtensionSet::const_iterator extension = enabled_set.begin();
+ extension != enabled_set.end(); ++extension) {
if (management_policy->UserMayModifySettings(extension->get(), NULL))
to_disable.push_back(*extension);
}
- for (ExtensionSet::const_iterator extension = terminated_extensions_.begin();
- extension != terminated_extensions_.end(); ++extension) {
+ const ExtensionSet& terminated_set = registry_->terminated_extensions();
+ for (ExtensionSet::const_iterator extension = terminated_set.begin();
+ extension != terminated_set.end(); ++extension) {
if (management_policy->UserMayModifySettings(extension->get(), NULL))
to_disable.push_back(*extension);
}
@@ -1272,8 +1283,9 @@ void ExtensionService::CheckManagementPolicy() {
// Loop through the extensions list, finding extensions we need to unload or
// disable.
- for (ExtensionSet::const_iterator iter = extensions_.begin();
- iter != extensions_.end(); ++iter) {
+ const ExtensionSet& extensions = registry_->enabled_extensions();
+ for (ExtensionSet::const_iterator iter = extensions.begin();
+ iter != extensions.end(); ++iter) {
const Extension* extension = (iter->get());
if (!system_->management_policy()->UserMayLoad(extension, NULL))
to_unload.push_back(extension->id());
@@ -1431,8 +1443,9 @@ bool ExtensionService::PopulateExtensionErrorUI(
bool needs_alert = false;
// Extensions that are blacklisted.
- for (ExtensionSet::const_iterator it = blacklisted_extensions_.begin();
- it != blacklisted_extensions_.end(); ++it) {
+ const ExtensionSet& blacklisted_set = registry_->blacklisted_extensions();
+ for (ExtensionSet::const_iterator it = blacklisted_set.begin();
+ it != blacklisted_set.end(); ++it) {
std::string id = (*it)->id();
if (!extension_prefs_->IsBlacklistedExtensionAcknowledged(id)) {
extension_error_ui->AddBlacklistedExtension(id);
@@ -1440,8 +1453,9 @@ bool ExtensionService::PopulateExtensionErrorUI(
}
}
- for (ExtensionSet::const_iterator iter = extensions_.begin();
- iter != extensions_.end(); ++iter) {
+ const ExtensionSet& enabled_set = registry_->enabled_extensions();
+ for (ExtensionSet::const_iterator iter = enabled_set.begin();
+ iter != enabled_set.end(); ++iter) {
const Extension* e = iter->get();
// Skip for extensions that have pending updates. They will be checked again
@@ -1495,19 +1509,21 @@ bool ExtensionService::IsUnacknowledgedExternalExtension(
void ExtensionService::ReconcileKnownDisabled() {
ExtensionIdSet known_disabled_ids;
if (!extension_prefs_->GetKnownDisabled(&known_disabled_ids)) {
- extension_prefs_->SetKnownDisabled(disabled_extensions_.GetIDs());
+ extension_prefs_->SetKnownDisabled(
+ registry_->disabled_extensions().GetIDs());
UMA_HISTOGRAM_BOOLEAN("Extensions.KnownDisabledInitialized", true);
return;
}
- // Both |known_disabled_ids| and |extensions_| are ordered (by definition
+ // Both |known_disabled_ids| and |extensions| are ordered (by definition
// of std::map and std::set). Iterate forward over both sets in parallel
// to find matching IDs and disable the corresponding extensions.
- ExtensionSet::const_iterator extensions_it = extensions_.begin();
+ const ExtensionSet& enabled_set = registry_->enabled_extensions();
+ ExtensionSet::const_iterator extensions_it = enabled_set.begin();
ExtensionIdSet::const_iterator known_disabled_ids_it =
known_disabled_ids.begin();
int known_disabled_count = 0;
- while (extensions_it != extensions_.end() &&
+ while (extensions_it != enabled_set.end() &&
known_disabled_ids_it != known_disabled_ids.end()) {
const std::string& extension_id = extensions_it->get()->id();
const int comparison = extension_id.compare(*known_disabled_ids_it);
@@ -1529,7 +1545,7 @@ void ExtensionService::ReconcileKnownDisabled() {
// Update the list of known disabled to reflect every change to
// |disabled_extensions_| from this point forward.
- disabled_extensions_.set_modification_callback(
+ registry_->SetDisabledModificationCallback(
base::Bind(&extensions::ExtensionPrefs::SetKnownDisabled,
base::Unretained(extension_prefs_)));
}
@@ -1547,8 +1563,9 @@ void ExtensionService::UpdateExternalExtensionAlert() {
return;
const Extension* extension = NULL;
- for (ExtensionSet::const_iterator iter = disabled_extensions_.begin();
- iter != disabled_extensions_.end(); ++iter) {
+ const ExtensionSet& disabled_extensions = registry_->disabled_extensions();
+ for (ExtensionSet::const_iterator iter = disabled_extensions.begin();
+ iter != disabled_extensions.end(); ++iter) {
const Extension* e = iter->get();
if (IsUnacknowledgedExternalExtension(e)) {
extension = e;
@@ -1620,15 +1637,15 @@ void ExtensionService::UnloadExtension(
// Clean up runtime data.
extension_runtime_data_.erase(extension_id);
- if (disabled_extensions_.Contains(extension->id())) {
- disabled_extensions_.Remove(extension->id());
+ if (registry_->disabled_extensions().Contains(extension->id())) {
+ registry_->RemoveDisabled(extension->id());
// Make sure the profile cleans up its RequestContexts when an already
// disabled extension is unloaded (since they are also tracking the disabled
// extensions).
system_->UnregisterExtensionWithRequestContexts(extension_id, reason);
} else {
- // Remove the extension from our list.
- extensions_.Remove(extension->id());
+ // Remove the extension from the enabled list.
+ registry_->RemoveEnabled(extension->id());
NotifyExtensionUnloaded(extension.get(), reason);
}
@@ -1649,21 +1666,14 @@ void ExtensionService::RemoveComponentExtension(
content::Details<const Extension>(extension.get()));
}
-void ExtensionService::UnloadAllExtensions() {
- profile_->GetExtensionSpecialStoragePolicy()->RevokeRightsForAllExtensions();
-
- extensions_.Clear();
- disabled_extensions_.Clear();
- terminated_extensions_.Clear();
- extension_runtime_data_.clear();
-
- // TODO(erikkay) should there be a notification for this? We can't use
- // EXTENSION_UNLOADED since that implies that the extension has been disabled
- // or uninstalled, and UnloadAll is just part of shutdown.
+void ExtensionService::UnloadAllExtensionsForTest() {
+ UnloadAllExtensionsInternal();
}
-void ExtensionService::ReloadExtensions() {
- UnloadAllExtensions();
+void ExtensionService::ReloadExtensionsForTest() {
+ // Calling UnloadAllExtensionsForTest here triggers a false-positive presubmit
+ // warning about calling test code in production.
+ UnloadAllExtensionsInternal();
component_loader_->LoadAll();
extensions::InstalledLoader(this).LoadAllExtensions();
// Don't call SetReadyAndNotifyListeners() since tests call this multiple
@@ -1778,10 +1788,10 @@ void ExtensionService::AddExtension(const Extension* extension) {
// blacklist before calling into here, e.g. CrxInstaller checks before
// installation then threads through the install and pending install flow
// of this class, and we check when loading installed extensions.
- blacklisted_extensions_.Insert(extension);
+ registry_->AddBlacklisted(extension);
} else if (!reloading &&
extension_prefs_->IsExtensionDisabled(extension->id())) {
- disabled_extensions_.Insert(extension);
+ registry_->AddDisabled(extension);
if (extension_sync_service_)
extension_sync_service_->SyncExtensionChangeIfNeeded(*extension);
content::NotificationService::current()->Notify(
@@ -1797,7 +1807,7 @@ void ExtensionService::AddExtension(const Extension* extension) {
}
} else if (reloading) {
// Replace the old extension with the new version.
- CHECK(!disabled_extensions_.Insert(extension));
+ CHECK(!registry_->AddDisabled(extension));
EnableExtension(extension->id());
} else {
// All apps that are displayed in the launcher are ordered by their ordinals
@@ -1810,7 +1820,7 @@ void ExtensionService::AddExtension(const Extension* extension) {
extension->id(), syncer::StringOrdinal());
}
- extensions_.Insert(extension);
+ registry_->AddEnabled(extension);
if (extension_sync_service_)
extension_sync_service_->SyncExtensionChangeIfNeeded(*extension);
NotifyExtensionLoaded(extension);
@@ -1973,8 +1983,9 @@ void ExtensionService::CheckPermissionsIncrease(const Extension* extension,
void ExtensionService::UpdateActiveExtensionsInCrashReporter() {
std::set<std::string> extension_ids;
- for (ExtensionSet::const_iterator iter = extensions_.begin();
- iter != extensions_.end(); ++iter) {
+ const ExtensionSet& extensions = registry_->enabled_extensions();
+ for (ExtensionSet::const_iterator iter = extensions.begin();
+ iter != extensions.end(); ++iter) {
const Extension* extension = iter->get();
if (!extension->is_theme() && extension->location() != Manifest::COMPONENT)
extension_ids.insert(extension->id());
@@ -2047,9 +2058,9 @@ scoped_ptr<const ExtensionSet>
scoped_ptr<ExtensionSet> dependents(new ExtensionSet());
scoped_ptr<ExtensionSet> set_to_check(new ExtensionSet());
if (SharedModuleInfo::IsSharedModule(extension)) {
- set_to_check->InsertAll(disabled_extensions_);
+ set_to_check->InsertAll(registry_->disabled_extensions());
set_to_check->InsertAll(delayed_installs_);
- set_to_check->InsertAll(extensions_);
+ set_to_check->InsertAll(registry_->enabled_extensions());
for (ExtensionSet::const_iterator iter = set_to_check->begin();
iter != set_to_check->end(); ++iter) {
if (SharedModuleInfo::ImportsExtensionById(iter->get(),
@@ -2340,16 +2351,16 @@ const Extension* ExtensionService::GetPendingExtensionUpdate(
}
void ExtensionService::TrackTerminatedExtension(const Extension* extension) {
- if (!terminated_extensions_.Contains(extension->id()))
- terminated_extensions_.Insert(make_scoped_refptr(extension));
-
+ // No need to check for duplicates; inserting a duplicate is a no-op.
+ registry_->AddTerminated(make_scoped_refptr(extension));
UnloadExtension(extension->id(), UnloadedExtensionInfo::REASON_TERMINATE);
}
void ExtensionService::UntrackTerminatedExtension(const std::string& id) {
std::string lowercase_id = StringToLowerASCII(id);
- const Extension* extension = terminated_extensions_.GetByID(lowercase_id);
- terminated_extensions_.Remove(lowercase_id);
+ const Extension* extension =
+ registry_->terminated_extensions().GetByID(lowercase_id);
+ registry_->RemoveTerminated(lowercase_id);
if (extension) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_REMOVED,
@@ -2374,13 +2385,15 @@ const Extension* ExtensionService::GetInstalledExtension(
bool ExtensionService::ExtensionBindingsAllowed(const GURL& url) {
// Allow bindings for all packaged extensions and component hosted apps.
- const Extension* extension = extensions_.GetExtensionOrAppByURL(url);
+ const Extension* extension =
+ registry_->enabled_extensions().GetExtensionOrAppByURL(url);
return extension && (!extension->is_hosted_app() ||
extension->location() == Manifest::COMPONENT);
}
bool ExtensionService::ShouldBlockUrlInBrowserTab(GURL* url) {
- const Extension* extension = extensions_.GetExtensionOrAppByURL(*url);
+ const Extension* extension =
+ registry_->enabled_extensions().GetExtensionOrAppByURL(*url);
if (extension && extension->is_platform_app()) {
*url = GURL(chrome::kExtensionInvalidRequestURL);
return true;
@@ -2465,7 +2478,8 @@ bool ExtensionService::OnExternalExtensionFileFound(
scoped_ptr<DictionaryValue> ExtensionService::GetExtensionInfo(
const std::string& extension_id) const {
scoped_ptr<DictionaryValue> dictionary(new DictionaryValue);
- const extensions::Extension* extension = extensions_.GetByID(extension_id);
+ const extensions::Extension* extension =
+ registry_->enabled_extensions().GetByID(extension_id);
if (extension) {
GURL icon = extensions::ExtensionIconSource::GetIconURL(
extension, extension_misc::EXTENSION_ICON_SMALLISH,
@@ -2569,8 +2583,9 @@ void ExtensionService::Observe(int type,
// Loaded extensions.
std::vector<ExtensionMsg_Loaded_Params> loaded_extensions;
- for (ExtensionSet::const_iterator iter = extensions_.begin();
- iter != extensions_.end(); ++iter) {
+ const ExtensionSet& extensions = registry_->enabled_extensions();
+ for (ExtensionSet::const_iterator iter = extensions.begin();
+ iter != extensions.end(); ++iter) {
// Renderers don't need to know about themes.
if (!(*iter)->is_theme())
loaded_extensions.push_back(ExtensionMsg_Loaded_Params(iter->get()));
@@ -2635,8 +2650,9 @@ bool ExtensionService::HasApps() const {
ExtensionIdSet ExtensionService::GetAppIds() const {
ExtensionIdSet result;
- for (ExtensionSet::const_iterator it = extensions_.begin();
- it != extensions_.end(); ++it) {
+ const ExtensionSet& extensions = registry_->enabled_extensions();
+ for (ExtensionSet::const_iterator it = extensions.begin();
+ it != extensions.end(); ++it) {
if ((*it)->is_app() && (*it)->location() != Manifest::COMPONENT)
result.insert((*it)->id());
}
@@ -2751,8 +2767,9 @@ bool ExtensionService::ShouldDelayExtensionUpdate(
void ExtensionService::GarbageCollectIsolatedStorage() {
scoped_ptr<base::hash_set<base::FilePath> > active_paths(
new base::hash_set<base::FilePath>());
- for (ExtensionSet::const_iterator it = extensions_.begin();
- it != extensions_.end(); ++it) {
+ const ExtensionSet& extensions = registry_->enabled_extensions();
+ for (ExtensionSet::const_iterator it = extensions.begin();
+ it != extensions.end(); ++it) {
if (extensions::AppIsolationInfo::HasIsolatedStorage(it->get())) {
active_paths->insert(BrowserContext::GetStoragePartitionForSite(
profile_, GetSiteForExtensionId((*it)->id()))->GetPath());
@@ -2799,7 +2816,7 @@ void ExtensionService::OnBlacklistUpdated() {
void ExtensionService::ManageBlacklist(const std::set<std::string>& updated) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- std::set<std::string> before = blacklisted_extensions_.GetIDs();
+ std::set<std::string> before = registry_->blacklisted_extensions().GetIDs();
std::set<std::string> no_longer_blacklisted =
base::STLSetDifference<std::set<std::string> >(before, updated);
std::set<std::string> not_yet_blacklisted =
@@ -2808,13 +2825,13 @@ void ExtensionService::ManageBlacklist(const std::set<std::string>& updated) {
for (std::set<std::string>::iterator it = no_longer_blacklisted.begin();
it != no_longer_blacklisted.end(); ++it) {
scoped_refptr<const Extension> extension =
- blacklisted_extensions_.GetByID(*it);
+ registry_->blacklisted_extensions().GetByID(*it);
if (!extension.get()) {
NOTREACHED() << "Extension " << *it << " no longer blacklisted, "
<< "but it was never blacklisted.";
continue;
}
- blacklisted_extensions_.Remove(*it);
+ registry_->RemoveBlacklisted(*it);
extension_prefs_->SetExtensionBlacklisted(extension->id(), false);
AddExtension(extension.get());
UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.UnblacklistInstalled",
@@ -2830,7 +2847,7 @@ void ExtensionService::ManageBlacklist(const std::set<std::string>& updated) {
<< "blacklisted, but it's not installed.";
continue;
}
- blacklisted_extensions_.Insert(extension);
+ registry_->AddBlacklisted(extension);
extension_prefs_->SetExtensionBlacklisted(extension->id(), true);
UnloadExtension(*it, UnloadedExtensionInfo::REASON_BLACKLIST);
UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlacklistInstalled",
@@ -2848,3 +2865,15 @@ void ExtensionService::RemoveUpdateObserver(
extensions::UpdateObserver* observer) {
update_observers_.RemoveObserver(observer);
}
+
+// Used only by test code.
+void ExtensionService::UnloadAllExtensionsInternal() {
+ profile_->GetExtensionSpecialStoragePolicy()->RevokeRightsForAllExtensions();
+
+ registry_->ClearAll();
+ extension_runtime_data_.clear();
+
+ // TODO(erikkay) should there be a notification for this? We can't use
+ // EXTENSION_UNLOADED since that implies that the extension has been disabled
+ // or uninstalled.
+}
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 17d62ec..f8d86c1 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -55,6 +55,7 @@ class ComponentLoader;
class ContentSettingsStore;
class CrxInstaller;
class ExtensionActionStorageManager;
+class ExtensionRegistry;
class ExtensionSystem;
class ExtensionUpdater;
class PendingExtensionManager;
@@ -325,12 +326,11 @@ class ExtensionService
virtual void RemoveComponentExtension(const std::string& extension_id)
OVERRIDE;
- // Unload all extensions. This is currently only called on shutdown, and
- // does not send notifications.
- void UnloadAllExtensions();
+ // Unload all extensions. Does not send notifications.
+ void UnloadAllExtensionsForTest();
- // Called only by testing.
- void ReloadExtensions();
+ // Reloads all extensions. Does not notify that extensions are ready.
+ void ReloadExtensionsForTest();
// Scan the extension directory and clean up the cruft.
void GarbageCollectExtensions();
@@ -718,6 +718,9 @@ class ExtensionService
}
bool installs_delayed_for_gc() const { return installs_delayed_for_gc_; }
+ // Used only by test code.
+ void UnloadAllExtensionsInternal();
+
// The normal profile associated with this ExtensionService.
Profile* profile_;
@@ -736,23 +739,12 @@ class ExtensionService
// The ExtensionSyncService that is used by this ExtensionService.
ExtensionSyncService* extension_sync_service_;
- // The current list of installed extensions.
- extensions::ExtensionSet extensions_;
-
- // The list of installed extensions that have been disabled.
- extensions::ExtensionSet disabled_extensions_;
-
- // The list of installed extensions that have been terminated.
- extensions::ExtensionSet terminated_extensions_;
-
- // The list of installed extensions that have been blacklisted. Generally
- // these shouldn't be considered as installed by the extension platform: we
- // only keep them around so that if extensions are blacklisted by mistake
- // they can easily be un-blacklisted.
- extensions::ExtensionSet blacklisted_extensions_;
+ // TODO(jamescook): Convert this to a BrowserContextKeyedService.
+ scoped_ptr<extensions::ExtensionRegistry> registry_;
// The list of extension installs delayed for various reasons. The reason
- // for delayed install is stored in ExtensionPrefs.
+ // for delayed install is stored in ExtensionPrefs. These are not part of
+ // ExtensionRegistry because they are not yet installed.
extensions::ExtensionSet delayed_installs_;
// Hold the set of pending extensions.
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index 2b659c9..2700d22 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -2158,7 +2158,7 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) {
// updating the browser to a version which recognizes a new API permission).
SetPref(extension_id, "granted_permissions.api",
new ListValue(), "granted_permissions.api");
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
EXPECT_EQ(1u, service_->disabled_extensions()->size());
extension = service_->disabled_extensions()->begin()->get();
@@ -2201,7 +2201,7 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) {
SetPrefStringSet(
extension_id, "granted_permissions.scriptable_host", host_permissions);
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
EXPECT_EQ(1u, service_->disabled_extensions()->size());
extension = service_->disabled_extensions()->begin()->get();
@@ -2538,7 +2538,7 @@ TEST_F(ExtensionServiceTest, UnpackedExtensionCanChangeID) {
loaded_.clear();
// Reload the extensions.
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
const Extension* extension = service_->GetExtensionById(unpacked, false);
EXPECT_EQ(unpacked, extension->id());
ASSERT_EQ(1u, loaded_.size());
@@ -2837,7 +2837,7 @@ TEST_F(ExtensionServiceTest, FromWebStore) {
ASSERT_TRUE(ValidateBooleanPref(good_crx, "from_webstore", true));
// Reload so extension gets reinitialized with new value.
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
extension = service_->GetExtensionById(id, false);
ASSERT_TRUE(extension->from_webstore());
@@ -3913,18 +3913,39 @@ TEST_F(ExtensionServiceTest, DisableExtension) {
InitializeEmptyExtensionService();
InstallCRX(data_dir_.AppendASCII("good.crx"), INSTALL_NEW);
- EXPECT_FALSE(service_->extensions()->is_empty());
EXPECT_TRUE(service_->GetExtensionById(good_crx, true));
EXPECT_TRUE(service_->GetExtensionById(good_crx, false));
- EXPECT_TRUE(service_->disabled_extensions()->is_empty());
+ EXPECT_EQ(1u, service_->extensions()->size());
+ EXPECT_EQ(0u, service_->disabled_extensions()->size());
+ EXPECT_EQ(0u, service_->terminated_extensions()->size());
+ EXPECT_EQ(0u, service_->blacklisted_extensions()->size());
// Disable it.
service_->DisableExtension(good_crx, Extension::DISABLE_USER_ACTION);
- EXPECT_TRUE(service_->extensions()->is_empty());
EXPECT_TRUE(service_->GetExtensionById(good_crx, true));
EXPECT_FALSE(service_->GetExtensionById(good_crx, false));
- EXPECT_FALSE(service_->disabled_extensions()->is_empty());
+ EXPECT_EQ(0u, service_->extensions()->size());
+ EXPECT_EQ(1u, service_->disabled_extensions()->size());
+ EXPECT_EQ(0u, service_->terminated_extensions()->size());
+ EXPECT_EQ(0u, service_->blacklisted_extensions()->size());
+}
+
+TEST_F(ExtensionServiceTest, TerminateExtension) {
+ InitializeEmptyExtensionService();
+
+ InstallCRX(data_dir_.AppendASCII("good.crx"), INSTALL_NEW);
+ EXPECT_EQ(1u, service_->extensions()->size());
+ EXPECT_EQ(0u, service_->disabled_extensions()->size());
+ EXPECT_EQ(0u, service_->terminated_extensions()->size());
+ EXPECT_EQ(0u, service_->blacklisted_extensions()->size());
+
+ TerminateExtension(good_crx);
+
+ EXPECT_EQ(0u, service_->extensions()->size());
+ EXPECT_EQ(0u, service_->disabled_extensions()->size());
+ EXPECT_EQ(1u, service_->terminated_extensions()->size());
+ EXPECT_EQ(0u, service_->blacklisted_extensions()->size());
}
TEST_F(ExtensionServiceTest, DisableTerminatedExtension) {
@@ -3939,7 +3960,11 @@ TEST_F(ExtensionServiceTest, DisableTerminatedExtension) {
EXPECT_FALSE(service_->GetTerminatedExtension(good_crx));
EXPECT_TRUE(service_->GetExtensionById(good_crx, true));
- EXPECT_FALSE(service_->disabled_extensions()->is_empty());
+
+ EXPECT_EQ(0u, service_->extensions()->size());
+ EXPECT_EQ(1u, service_->disabled_extensions()->size());
+ EXPECT_EQ(0u, service_->terminated_extensions()->size());
+ EXPECT_EQ(0u, service_->blacklisted_extensions()->size());
}
// Tests disabling all extensions (simulating --disable-extensions flag).
@@ -3954,7 +3979,7 @@ TEST_F(ExtensionServiceTest, DisableAllExtensions) {
// Disable extensions.
service_->set_extensions_enabled(false);
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
// There shouldn't be extensions in either list.
EXPECT_EQ(0u, service_->extensions()->size());
@@ -3962,7 +3987,7 @@ TEST_F(ExtensionServiceTest, DisableAllExtensions) {
// This shouldn't do anything when all extensions are disabled.
service_->EnableExtension(good_crx);
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
// There still shouldn't be extensions in either list.
EXPECT_EQ(0u, service_->extensions()->size());
@@ -3970,7 +3995,7 @@ TEST_F(ExtensionServiceTest, DisableAllExtensions) {
// And then re-enable the extensions.
service_->set_extensions_enabled(true);
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
EXPECT_EQ(1u, service_->extensions()->size());
EXPECT_EQ(0u, service_->disabled_extensions()->size());
@@ -3990,7 +4015,7 @@ TEST_F(ExtensionServiceTest, ReloadExtensions) {
EXPECT_EQ(0u, service_->extensions()->size());
EXPECT_EQ(1u, service_->disabled_extensions()->size());
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
// The creation flags should not change when reloading the extension.
const Extension* extension = service_->GetExtensionById(good_crx, true);
@@ -4011,7 +4036,7 @@ TEST_F(ExtensionServiceTest, ReloadExtensions) {
// EnableExtension() call above inserted into it and
// UnloadAllExtensions() doesn't send out notifications.
loaded_.clear();
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
// Extension counts shouldn't change.
EXPECT_EQ(1u, service_->extensions()->size());
@@ -4553,7 +4578,7 @@ void ExtensionServiceTest::TestExternalProvider(
// Reload extensions without changing anything. The extension should be
// loaded again.
loaded_.clear();
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
base::RunLoop().RunUntilIdle();
ASSERT_EQ(0u, GetErrors().size());
ASSERT_EQ(1u, loaded_.size());
@@ -4751,7 +4776,7 @@ TEST_F(ExtensionServiceTest, ExternalUninstall) {
// Verify that it's not the disabled extensions flag causing it not to load.
set_extensions_enabled(true);
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
base::RunLoop().RunUntilIdle();
ASSERT_EQ(0u, GetErrors().size());
@@ -5211,7 +5236,7 @@ TEST_F(ExtensionServiceTest, ComponentExtensions) {
// Reload all extensions, and make sure it comes back.
std::string extension_id = (*service_->extensions()->begin())->id();
loaded_.clear();
- service_->ReloadExtensions();
+ service_->ReloadExtensionsForTest();
ASSERT_EQ(1u, service_->extensions()->size());
EXPECT_EQ(extension_id, (*service_->extensions()->begin())->id());
}
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 7fcf4c4..7f3393b 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -529,6 +529,7 @@
'../extensions/browser/admin_policy_unittest.cc',
'../extensions/browser/event_listener_map_unittest.cc',
'../extensions/browser/event_router_unittest.cc',
+ '../extensions/browser/extension_registry_unittest.cc',
'../extensions/browser/file_highlighter_unittest.cc',
'../extensions/browser/file_reader_unittest.cc',
'../extensions/browser/info_map_unittest.cc',
diff --git a/extensions/browser/extension_registry.cc b/extensions/browser/extension_registry.cc
new file mode 100644
index 0000000..9391b40
--- /dev/null
+++ b/extensions/browser/extension_registry.cc
@@ -0,0 +1,60 @@
+// Copyright 2013 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 "extensions/browser/extension_registry.h"
+
+namespace extensions {
+
+ExtensionRegistry::ExtensionRegistry() {}
+ExtensionRegistry::~ExtensionRegistry() {}
+
+bool ExtensionRegistry::AddEnabled(
+ const scoped_refptr<const Extension>& extension) {
+ return enabled_extensions_.Insert(extension);
+}
+
+bool ExtensionRegistry::RemoveEnabled(const std::string& id) {
+ return enabled_extensions_.Remove(id);
+}
+
+bool ExtensionRegistry::AddDisabled(
+ const scoped_refptr<const Extension>& extension) {
+ return disabled_extensions_.Insert(extension);
+}
+
+bool ExtensionRegistry::RemoveDisabled(const std::string& id) {
+ return disabled_extensions_.Remove(id);
+}
+
+bool ExtensionRegistry::AddTerminated(
+ const scoped_refptr<const Extension>& extension) {
+ return terminated_extensions_.Insert(extension);
+}
+
+bool ExtensionRegistry::RemoveTerminated(const std::string& id) {
+ return terminated_extensions_.Remove(id);
+}
+
+bool ExtensionRegistry::AddBlacklisted(
+ const scoped_refptr<const Extension>& extension) {
+ return blacklisted_extensions_.Insert(extension);
+}
+
+bool ExtensionRegistry::RemoveBlacklisted(const std::string& id) {
+ return blacklisted_extensions_.Remove(id);
+}
+
+void ExtensionRegistry::ClearAll() {
+ enabled_extensions_.Clear();
+ disabled_extensions_.Clear();
+ terminated_extensions_.Clear();
+ blacklisted_extensions_.Clear();
+}
+
+void ExtensionRegistry::SetDisabledModificationCallback(
+ const ExtensionSet::ModificationCallback& callback) {
+ disabled_extensions_.set_modification_callback(callback);
+}
+
+} // namespace extensions
diff --git a/extensions/browser/extension_registry.h b/extensions/browser/extension_registry.h
new file mode 100644
index 0000000..c051fbb
--- /dev/null
+++ b/extensions/browser/extension_registry.h
@@ -0,0 +1,94 @@
+// Copyright 2013 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 EXTENSIONS_BROWSER_EXTENSION_REGISTRY_H_
+#define EXTENSIONS_BROWSER_EXTENSION_REGISTRY_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "extensions/common/extension_set.h"
+
+namespace extensions {
+class Extension;
+
+// ExtensionRegistry holds sets of the installed extensions for a given
+// BrowserContext.
+// TODO(jamescook): Convert this to a BrowserContextKeyedService.
+class ExtensionRegistry {
+ public:
+ ExtensionRegistry();
+ ~ExtensionRegistry();
+
+ // NOTE: These sets are *eventually* mututally exclusive, but an extension can
+ // appear in two sets for short periods of time.
+ const ExtensionSet& enabled_extensions() const {
+ return enabled_extensions_;
+ }
+ const ExtensionSet& disabled_extensions() const {
+ return disabled_extensions_;
+ }
+ const ExtensionSet& terminated_extensions() const {
+ return terminated_extensions_;
+ }
+ const ExtensionSet& blacklisted_extensions() const {
+ return blacklisted_extensions_;
+ }
+
+ // Adds the specified extension to the enabled set. The registry becomes an
+ // owner. Any previous extension with the same ID is removed.
+ // Returns true if there is no previous extension.
+ // NOTE: You probably want to use ExtensionService instead of calling this
+ // method directly.
+ bool AddEnabled(const scoped_refptr<const Extension>& extension);
+
+ // Removes the specified extension from the enabled set.
+ // Returns true if the set contained the specified extension.
+ // NOTE: You probably want to use ExtensionService instead of calling this
+ // method directly.
+ bool RemoveEnabled(const std::string& id);
+
+ // As above, but for the disabled set.
+ bool AddDisabled(const scoped_refptr<const Extension>& extension);
+ bool RemoveDisabled(const std::string& id);
+
+ // As above, but for the terminated set.
+ bool AddTerminated(const scoped_refptr<const Extension>& extension);
+ bool RemoveTerminated(const std::string& id);
+
+ // As above, but for the blacklisted set.
+ bool AddBlacklisted(const scoped_refptr<const Extension>& extension);
+ bool RemoveBlacklisted(const std::string& id);
+
+ // Removes all extensions from all sets.
+ void ClearAll();
+
+ // Sets a callback to run when the disabled extension set is modified.
+ // TODO(jamescook): This is too specific for a generic registry; find some
+ // other way to do this.
+ void SetDisabledModificationCallback(
+ const ExtensionSet::ModificationCallback& callback);
+
+ private:
+ // Extensions that are installed, enabled and not terminated.
+ ExtensionSet enabled_extensions_;
+
+ // Extensions that are installed and disabled.
+ ExtensionSet disabled_extensions_;
+
+ // Extensions that are installed and terminated.
+ ExtensionSet terminated_extensions_;
+
+ // Extensions that are installed and blacklisted. Generally these shouldn't be
+ // considered as installed by the extension platform: we only keep them around
+ // so that if extensions are blacklisted by mistake they can easily be
+ // un-blacklisted.
+ ExtensionSet blacklisted_extensions_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExtensionRegistry);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_EXTENSION_REGISTRY_H_
diff --git a/extensions/browser/extension_registry_unittest.cc b/extensions/browser/extension_registry_unittest.cc
new file mode 100644
index 0000000..4243dea
--- /dev/null
+++ b/extensions/browser/extension_registry_unittest.cc
@@ -0,0 +1,102 @@
+// Copyright 2013 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 "extensions/browser/extension_registry.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/value_builder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+namespace {
+
+// Creates a very simple extension.
+scoped_refptr<Extension> CreateExtensionWithID(const std::string& id) {
+ return ExtensionBuilder()
+ .SetManifest(
+ DictionaryBuilder().Set("name", "Echo").Set("version", "1.0"))
+ .SetID(id)
+ .Build();
+}
+
+typedef testing::Test ExtensionRegistryTest;
+
+TEST_F(ExtensionRegistryTest, FillAndClearRegistry) {
+ ExtensionRegistry registry;
+ scoped_refptr<Extension> extension1 = CreateExtensionWithID("id1");
+ scoped_refptr<Extension> extension2 = CreateExtensionWithID("id2");
+ scoped_refptr<Extension> extension3 = CreateExtensionWithID("id3");
+ scoped_refptr<Extension> extension4 = CreateExtensionWithID("id4");
+
+ // All the sets start empty.
+ EXPECT_EQ(0u, registry.enabled_extensions().size());
+ EXPECT_EQ(0u, registry.disabled_extensions().size());
+ EXPECT_EQ(0u, registry.terminated_extensions().size());
+ EXPECT_EQ(0u, registry.blacklisted_extensions().size());
+
+ // Extensions can be added to each set.
+ registry.AddEnabled(extension1);
+ registry.AddDisabled(extension2);
+ registry.AddTerminated(extension3);
+ registry.AddBlacklisted(extension4);
+
+ EXPECT_EQ(1u, registry.enabled_extensions().size());
+ EXPECT_EQ(1u, registry.disabled_extensions().size());
+ EXPECT_EQ(1u, registry.terminated_extensions().size());
+ EXPECT_EQ(1u, registry.blacklisted_extensions().size());
+
+ // Clearing the registry clears all sets.
+ registry.ClearAll();
+
+ EXPECT_EQ(0u, registry.enabled_extensions().size());
+ EXPECT_EQ(0u, registry.disabled_extensions().size());
+ EXPECT_EQ(0u, registry.terminated_extensions().size());
+ EXPECT_EQ(0u, registry.blacklisted_extensions().size());
+}
+
+// A simple test of adding and removing things from sets.
+TEST_F(ExtensionRegistryTest, AddAndRemoveExtensionFromRegistry) {
+ ExtensionRegistry registry;
+
+ // Adding an extension works.
+ scoped_refptr<Extension> extension = CreateExtensionWithID("id");
+ EXPECT_TRUE(registry.AddEnabled(extension));
+ EXPECT_EQ(1u, registry.enabled_extensions().size());
+
+ // The extension was only added to one set.
+ EXPECT_EQ(0u, registry.disabled_extensions().size());
+ EXPECT_EQ(0u, registry.terminated_extensions().size());
+ EXPECT_EQ(0u, registry.blacklisted_extensions().size());
+
+ // Removing an extension works.
+ EXPECT_TRUE(registry.RemoveEnabled(extension->id()));
+ EXPECT_EQ(0u, registry.enabled_extensions().size());
+
+ // Trying to remove an extension that isn't in the set fails cleanly.
+ EXPECT_FALSE(registry.RemoveEnabled(extension->id()));
+}
+
+TEST_F(ExtensionRegistryTest, AddExtensionToRegistryTwice) {
+ ExtensionRegistry registry;
+ scoped_refptr<Extension> extension = CreateExtensionWithID("id");
+
+ // An extension can exist in two sets at once. It would be nice to eliminate
+ // this functionality, but some users of ExtensionRegistry need it.
+ EXPECT_TRUE(registry.AddEnabled(extension));
+ EXPECT_TRUE(registry.AddDisabled(extension));
+
+ EXPECT_EQ(1u, registry.enabled_extensions().size());
+ EXPECT_EQ(1u, registry.disabled_extensions().size());
+ EXPECT_EQ(0u, registry.terminated_extensions().size());
+ EXPECT_EQ(0u, registry.blacklisted_extensions().size());
+}
+
+} // namespace
+} // namespace extensions
diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp
index 4609d46..fa07292 100644
--- a/extensions/extensions.gyp
+++ b/extensions/extensions.gyp
@@ -169,6 +169,8 @@
'browser/extension_error.h',
'browser/extension_function.cc',
'browser/extension_function.h',
+ 'browser/extension_registry.cc',
+ 'browser/extension_registry.h',
'browser/extensions_browser_client.cc',
'browser/extensions_browser_client.h',
'browser/external_provider_interface.h',