Reenable disabled component extensions with profile resetter.
Sice disabled extensions are synced and component extensions do not
have a UI for reenabling them, once disabled will stay disabled forever.
It happened that for some reason some component extensions were
disabled. The reason is unknown, but at least users will be able to
recover from this situation by using the profile resetter.
Note, that we allow to disable component extensions programatically,
which was used at least for the Hotword component extension.
TEST=Disable a component extension programatically, then reset the
profile in chrome://settings.
BUG=680429
Review-Url: https://codereview.chromium.org/2647783003
Cr-Commit-Position: refs/heads/master@{#446575}
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index abaae912..2b7ba6f 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -259,6 +259,19 @@
return bubble_count;
}
+scoped_refptr<Extension> CreateExtension(const base::string16& name,
+ const base::FilePath& path,
+ Manifest::Location location) {
+ base::DictionaryValue manifest;
+ manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
+ manifest.SetString(extensions::manifest_keys::kName, name);
+ std::string error;
+ scoped_refptr<Extension> extension =
+ Extension::Create(path, location, manifest, Extension::NO_FLAGS, &error);
+ EXPECT_TRUE(extension.get() != nullptr) << error;
+ return extension;
+}
+
} // namespace
class MockExtensionProvider : public extensions::ExternalProviderInterface {
@@ -6409,6 +6422,36 @@
EXPECT_TRUE(service()->IsExtensionEnabled(page_action));
}
+// As for components, only external component extensions can be disabled.
+TEST_F(ExtensionServiceTest, DisablingComponentExtensions) {
+ InitializeEmptyExtensionService();
+ service_->Init();
+
+ scoped_refptr<Extension> external_component_extension = CreateExtension(
+ base::ASCIIToUTF16("external_component_extension"),
+ base::FilePath(FILE_PATH_LITERAL("//external_component_extension")),
+ Manifest::EXTERNAL_COMPONENT);
+ service_->AddExtension(external_component_extension.get());
+ EXPECT_TRUE(registry()->enabled_extensions().Contains(
+ external_component_extension->id()));
+ service_->DisableExtension(external_component_extension->id(),
+ extensions::Extension::DISABLE_USER_ACTION);
+ EXPECT_TRUE(registry()->disabled_extensions().Contains(
+ external_component_extension->id()));
+
+ scoped_refptr<Extension> component_extension = CreateExtension(
+ base::ASCIIToUTF16("component_extension"),
+ base::FilePath(FILE_PATH_LITERAL("//component_extension")),
+ Manifest::COMPONENT);
+ service_->AddExtension(component_extension.get());
+ EXPECT_TRUE(
+ registry()->enabled_extensions().Contains(component_extension->id()));
+ service_->DisableExtension(component_extension->id(),
+ extensions::Extension::DISABLE_USER_ACTION);
+ EXPECT_FALSE(
+ registry()->disabled_extensions().Contains(component_extension->id()));
+}
+
// Test that installing multiple external extensions works.
// Flaky on windows; http://crbug.com/295757 .
// Causes race conditions with an in-process utility thread, so disable under
diff --git a/chrome/browser/profile_resetter/profile_resetter.cc b/chrome/browser/profile_resetter/profile_resetter.cc
index 105779cf..cf97bf1 100644
--- a/chrome/browser/profile_resetter/profile_resetter.cc
+++ b/chrome/browser/profile_resetter/profile_resetter.cc
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <string>
+#include <vector>
#include "base/macros.h"
#include "base/synchronization/cancellation_flag.h"
@@ -37,8 +38,11 @@
#include "components/search_engines/template_url_prepopulate_data.h"
#include "components/search_engines/template_url_service.h"
#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/management_policy.h"
+#include "extensions/common/extension_id.h"
+#include "extensions/common/manifest.h"
#if defined(OS_WIN)
#include "base/base_paths.h"
@@ -271,6 +275,24 @@
DCHECK(extension_service);
extension_service->DisableUserExtensionsExcept(brandcode_extensions);
+ // Reenable all disabled external component extensions.
+ // BrandcodedDefaultSettings does not contain information about component
+ // extensions, so fetch them from the existing registry. This may be not very
+ // robust, as the profile resetter may be invoked when the registry is in some
+ // iffy state. However, we can't enable an extension which is not in the
+ // registry anyway.
+ extensions::ExtensionRegistry* extension_registry =
+ extensions::ExtensionRegistry::Get(profile_);
+ DCHECK(extension_registry);
+ std::vector<extensions::ExtensionId> extension_ids_to_reenable;
+ for (const auto& extension : extension_registry->disabled_extensions()) {
+ if (extension->location() == extensions::Manifest::EXTERNAL_COMPONENT)
+ extension_ids_to_reenable.push_back(extension->id());
+ }
+ for (const auto& extension_id : extension_ids_to_reenable) {
+ extension_service->EnableExtension(extension_id);
+ }
+
MarkAsDone(EXTENSIONS);
}
diff --git a/chrome/browser/profile_resetter/profile_resetter_unittest.cc b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
index 017f4696..5bbfd4a6 100644
--- a/chrome/browser/profile_resetter/profile_resetter_unittest.cc
+++ b/chrome/browser/profile_resetter/profile_resetter_unittest.cc
@@ -689,6 +689,29 @@
EXPECT_TRUE(theme_service->UsingDefaultTheme());
}
+TEST_F(ProfileResetterTest, ResetExtensionsByReenablingExternalComponents) {
+ service_->Init();
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ scoped_refptr<Extension> ext =
+ CreateExtension(base::ASCIIToUTF16("example"),
+ base::FilePath(FILE_PATH_LITERAL("//nonexistent")),
+ Manifest::EXTERNAL_COMPONENT,
+ extensions::Manifest::TYPE_EXTENSION, false);
+ service_->AddExtension(ext.get());
+
+ service_->DisableExtension(ext->id(),
+ extensions::Extension::DISABLE_USER_ACTION);
+ EXPECT_FALSE(registry()->enabled_extensions().Contains(ext->id()));
+ EXPECT_TRUE(registry()->disabled_extensions().Contains(ext->id()));
+
+ ResetAndWait(ProfileResetter::EXTENSIONS);
+ EXPECT_TRUE(registry()->enabled_extensions().Contains(ext->id()));
+ EXPECT_FALSE(registry()->disabled_extensions().Contains(ext->id()));
+}
+
TEST_F(ProfileResetterTest, ResetStartPageNonOrganic) {
PrefService* prefs = profile()->GetPrefs();
DCHECK(prefs);