Exempt component and policy-installed extensions from policy blacklist check.

Component extensions and policy-installed extensions shouldn't be affected by the policy blacklist: The former are considered internal UI that happens to be part of an extension, and they should have first-class policy knobs if we want to disable them. For the latter, the admin specifying them is enough indication that they should be installed, and requiring them to explicitly white-listed in some cases is redundant.

BUG=chromium-os:16702, chromium-os:21281
TEST=On ChromeOS, configure ExtensionInstallBlacklist = * and load chrome://bookmarks


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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@107707 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index 514ebe2..c348719 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -378,7 +378,7 @@
   }
 
   if (!frontend_weak_->extension_prefs()->IsExtensionAllowedByPolicy(
-      extension_->id())) {
+          extension_->id(), install_source_)) {
     ReportFailureFromUIThread(
         l10n_util::GetStringUTF8(IDS_EXTENSION_CANT_INSTALL_POLICY_BLACKLIST));
     return;
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 8485f88..9241264 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -11,10 +11,10 @@
 #include "chrome/browser/prefs/pref_notifier.h"
 #include "chrome/browser/prefs/scoped_user_pref_update.h"
 #include "chrome/common/chrome_notification_types.h"
-#include "chrome/common/url_constants.h"
 #include "chrome/common/extensions/extension.h"
 #include "chrome/common/extensions/url_pattern.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
 #include "content/public/browser/notification_service.h"
 
 using base::Time;
@@ -619,9 +619,15 @@
 }
 
 bool ExtensionPrefs::IsExtensionAllowedByPolicy(
-    const std::string& extension_id) {
+    const std::string& extension_id,
+    Extension::Location location) {
   std::string string_value;
 
+  if (location == Extension::COMPONENT ||
+      location == Extension::EXTERNAL_POLICY_DOWNLOAD) {
+    return true;
+  }
+
   const ListValue* blacklist =
       prefs_->GetList(prefs::kExtensionInstallDenyList);
   if (!blacklist || blacklist->empty())
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index d993914..d7760a7 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -182,7 +182,8 @@
 
   // Is the extension with |extension_id| allowed by policy (checking both
   // whitelist and blacklist).
-  bool IsExtensionAllowedByPolicy(const std::string& extension_id);
+  bool IsExtensionAllowedByPolicy(const std::string& extension_id,
+                                  Extension::Location location);
 
   // Returns the last value set via SetLastPingDay. If there isn't such a
   // pref, the returned Time will return true for is_null().
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 53fac60c..531d3b6 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -7,8 +7,8 @@
 #include <algorithm>
 #include <set>
 
-#include "base/bind.h"
 #include "base/basictypes.h"
+#include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
 #include "base/file_util.h"
@@ -92,8 +92,8 @@
 #include "content/browser/plugin_service.h"
 #include "content/browser/renderer_host/render_process_host.h"
 #include "content/browser/user_metrics.h"
-#include "content/public/browser/notification_service.h"
 #include "content/common/pepper_plugin_registry.h"
+#include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
 #include "googleurl/src/gurl.h"
 #include "net/base/registry_controlled_domain.h"
@@ -1400,7 +1400,12 @@
                                               bool write_to_prefs) {
   std::string error;
   scoped_refptr<const Extension> extension(NULL);
-  if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id)) {
+
+  // An explicit check against policy is required to behave correctly during
+  // startup.  This is because extensions that were previously OK might have
+  // been blacklisted in policy while Chrome was not running.
+  if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id,
+                                                    info.extension_location)) {
     error = errors::kDisabledByPolicy;
   } else if (info.extension_manifest.get()) {
     extension = Extension::Create(
@@ -1694,8 +1699,10 @@
   for (ExtensionList::const_iterator iter = extensions_.begin();
        iter != extensions_.end(); ++iter) {
     const Extension* extension = (*iter);
-    if (!extension_prefs_->IsExtensionAllowedByPolicy(extension->id()))
+    if (!extension_prefs_->IsExtensionAllowedByPolicy(extension->id(),
+                                                      extension->location())) {
       to_be_removed.push_back(extension->id());
+    }
   }
 
   // UnloadExtension will change the extensions_ list. So, we should
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index b2da4aa..b9083a2 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -2599,6 +2599,93 @@
   EXPECT_EQ(0u, service_->extensions()->size());
 }
 
+// Tests that component extensions are not blacklisted by policy.
+TEST_F(ExtensionServiceTest, ComponentExtensionWhitelisted) {
+  InitializeEmptyExtensionService();
+
+  // Blacklist everything.
+  {
+    ListPrefUpdate update(profile_->GetPrefs(),
+                          prefs::kExtensionInstallDenyList);
+    ListValue* blacklist = update.Get();
+    blacklist->Append(Value::CreateStringValue("*"));
+  }
+
+  // Install a component extension.
+  FilePath path = data_dir_
+      .AppendASCII("good")
+      .AppendASCII("Extensions")
+      .AppendASCII(good0)
+      .AppendASCII("1.0.0.0");
+  std::string manifest;
+  ASSERT_TRUE(file_util::ReadFileToString(
+      path.Append(Extension::kManifestFilename), &manifest));
+  service_->register_component_extension(
+      ExtensionService::ComponentExtensionInfo(manifest, path));
+  service_->Init();
+
+  // Extension should be installed despite blacklist.
+  ASSERT_EQ(1u, service_->extensions()->size());
+  EXPECT_EQ(good0, service_->extensions()->at(0)->id());
+
+  // Poke external providers and make sure the extension is still present.
+  service_->CheckForExternalUpdates();
+  ASSERT_EQ(1u, service_->extensions()->size());
+  EXPECT_EQ(good0, service_->extensions()->at(0)->id());
+
+  // Extension should not be uninstalled on blacklist changes.
+  {
+    ListPrefUpdate update(profile_->GetPrefs(),
+                          prefs::kExtensionInstallDenyList);
+    ListValue* blacklist = update.Get();
+    blacklist->Append(Value::CreateStringValue(good0));
+  }
+  loop_.RunAllPending();
+  ASSERT_EQ(1u, service_->extensions()->size());
+  EXPECT_EQ(good0, service_->extensions()->at(0)->id());
+}
+
+// Tests that policy-installed extensions are not blacklisted by policy.
+TEST_F(ExtensionServiceTest, PolicyInstalledExtensionsWhitelisted) {
+  InitializeEmptyExtensionService();
+
+  // Blacklist everything.
+  {
+    ListPrefUpdate update(profile_->GetPrefs(),
+                          prefs::kExtensionInstallDenyList);
+    ListValue* blacklist = update.Get();
+    blacklist->Append(Value::CreateStringValue("*"));
+  }
+
+  // Have policy force-install an extension.
+  MockExtensionProvider* provider =
+      new MockExtensionProvider(service_,
+                                Extension::EXTERNAL_POLICY_DOWNLOAD);
+  AddMockExternalProvider(provider);
+  provider->UpdateOrAddExtension(good_crx, "1.0.0.0",
+                                 data_dir_.AppendASCII("good.crx"));
+
+  // Reloading extensions should find our externally registered extension
+  // and install it.
+  service_->CheckForExternalUpdates();
+  loop_.RunAllPending();
+
+  // Extension should be installed despite blacklist.
+  ASSERT_EQ(1u, service_->extensions()->size());
+  EXPECT_EQ(good_crx, service_->extensions()->at(0)->id());
+
+  // Blacklist update should not uninstall the extension.
+  {
+    ListPrefUpdate update(profile_->GetPrefs(),
+                          prefs::kExtensionInstallDenyList);
+    ListValue* blacklist = update.Get();
+    blacklist->Append(Value::CreateStringValue(good0));
+  }
+  loop_.RunAllPending();
+  ASSERT_EQ(1u, service_->extensions()->size());
+  EXPECT_EQ(good_crx, service_->extensions()->at(0)->id());
+}
+
 // Tests disabling extensions
 TEST_F(ExtensionServiceTest, DisableExtension) {
   InitializeEmptyExtensionService();
@@ -3145,8 +3232,7 @@
 
     // Now test the case where user uninstalls and then the extension is removed
     // from the external provider.
-
-  provider->UpdateOrAddExtension(good_crx, "1.0.0.1", source_path);
+    provider->UpdateOrAddExtension(good_crx, "1.0.0.1", source_path);
     service_->CheckForExternalUpdates();
     loop_.RunAllPending();