Chromium Code Reviews| Index: chrome/browser/extensions/extension_blacklist_browsertest.cc |
| diff --git a/chrome/browser/extensions/extension_blacklist_browsertest.cc b/chrome/browser/extensions/extension_blacklist_browsertest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..53cc3e3cf3c8c0a1678827973e8154cc45e1edf8 |
| --- /dev/null |
| +++ b/chrome/browser/extensions/extension_blacklist_browsertest.cc |
| @@ -0,0 +1,349 @@ |
| +// Copyright 2012 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 "base/run_loop.h" |
| +#include "base/stringprintf.h" |
| +#include "chrome/browser/extensions/blacklist.h" |
| +#include "chrome/browser/extensions/extension_browsertest.h" |
| +#include "chrome/browser/extensions/extension_service.h" |
| +#include "chrome/browser/extensions/extension_system.h" |
| +#include "chrome/browser/ui/browser.h" |
| +#include "chrome/common/chrome_notification_types.h" |
| +#include "chrome/common/extensions/extension.h" |
| +#include "chrome/common/extensions/extension_constants.h" |
| +#include "content/public/browser/notification_details.h" |
| +#include "content/public/browser/notification_observer.h" |
| +#include "content/public/browser/notification_registrar.h" |
| +#include "content/public/browser/notification_source.h" |
| + |
| +namespace extensions { |
| + |
| +namespace { |
| + |
| +// Records notifications, but only for extensions with specific IDs. |
| +class FilteringNotificationObserver : public content::NotificationObserver { |
| + public: |
| + FilteringNotificationObserver( |
| + content::NotificationSource source, |
| + const std::set<std::string>& extension_ids) |
| + : extension_ids_(extension_ids) { |
| + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, source); |
| + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, source); |
| + registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, source); |
| + } |
| + |
| + // Checks then clears notifications for our extensions. |
| + testing::AssertionResult CheckNotifications(chrome::NotificationType type) { |
| + return CheckNotifications(std::vector<chrome::NotificationType>(1, type)); |
| + } |
| + |
| + // Checks then clears notifications for our extensions. |
| + testing::AssertionResult CheckNotifications(chrome::NotificationType t1, |
| + chrome::NotificationType t2) { |
| + std::vector<chrome::NotificationType> types; |
| + types.push_back(t1); |
| + types.push_back(t2); |
| + return CheckNotifications(types); |
| + } |
| + |
| + // Checks then clears notifications for our extensions. |
| + testing::AssertionResult CheckNotifications(chrome::NotificationType t1, |
| + chrome::NotificationType t2, |
| + chrome::NotificationType t3, |
| + chrome::NotificationType t4) { |
| + std::vector<chrome::NotificationType> types; |
| + types.push_back(t1); |
| + types.push_back(t2); |
| + types.push_back(t3); |
| + types.push_back(t4); |
| + return CheckNotifications(types); |
| + } |
| + |
| + private: |
| + // content::NotificationObserver implementation. |
| + virtual void Observe(int type, |
| + const content::NotificationSource& source, |
| + const content::NotificationDetails& details) OVERRIDE { |
| + switch (type) { |
| + case chrome::NOTIFICATION_EXTENSION_INSTALLED: { |
| + const Extension* extension = |
| + content::Details<const Extension>(details).ptr(); |
| + if (extension_ids_.count(extension->id())) |
| + notifications_.push_back(static_cast<chrome::NotificationType>(type)); |
| + break; |
| + } |
| + |
| + case chrome::NOTIFICATION_EXTENSION_LOADED: { |
| + const Extension* extension = |
| + content::Details<const Extension>(details).ptr(); |
| + if (extension_ids_.count(extension->id())) |
| + notifications_.push_back(static_cast<chrome::NotificationType>(type)); |
| + break; |
| + } |
| + |
| + case chrome::NOTIFICATION_EXTENSION_UNLOADED: { |
| + UnloadedExtensionInfo* reason = |
| + content::Details<UnloadedExtensionInfo>(details).ptr(); |
| + if (extension_ids_.count(reason->extension->id())) { |
| + notifications_.push_back(static_cast<chrome::NotificationType>(type)); |
| + // The only way that extensions are unloaded in these tests is |
| + // by blacklisting. |
| + EXPECT_EQ(extension_misc::UNLOAD_REASON_BLACKLIST, |
| + reason->reason); |
| + } |
| + break; |
| + } |
| + |
| + default: |
| + NOTREACHED(); |
| + break; |
| + } |
| + } |
| + |
| + // Checks then clears notifications for our extensions. |
| + testing::AssertionResult CheckNotifications( |
| + const std::vector<chrome::NotificationType>& types) { |
| + testing::AssertionResult result = (notifications_ == types) ? |
| + testing::AssertionSuccess() : |
| + testing::AssertionFailure() << "Expected " << Str(types) << ", " << |
| + "Got " << Str(notifications_); |
| + notifications_.clear(); |
| + return result; |
| + } |
| + |
| + std::string Str(const std::vector<chrome::NotificationType>& types) { |
| + std::string str = "["; |
| + bool needs_comma = false; |
| + for (std::vector<chrome::NotificationType>::const_iterator it = |
| + types.begin(); it != types.end(); ++it) { |
| + if (needs_comma) |
| + str += ","; |
| + needs_comma = true; |
| + str += base::StringPrintf("%d", *it); |
| + } |
| + return str + "]"; |
| + } |
| + |
| + const std::set<std::string> extension_ids_; |
| + |
| + std::vector<chrome::NotificationType> notifications_; |
| + |
| + content::NotificationRegistrar registrar_; |
| +}; |
| + |
| +// Stores the paths to CRX files of extensions, and the extension's ID. |
| +// Use arbitrary extensions; we're just testing blacklisting behavior. |
| +class CrxInfo { |
| + public: |
| + CrxInfo(const std::string& path, const std::string& id) |
| + : path_(path), id_(id) {} |
| + |
| + const std::string& path() { return path_; } |
| + const std::string& id() { return id_; } |
| + |
| + private: |
| + const std::string path_; |
| + const std::string id_; |
| +}; |
| + |
| +} // namespace |
| + |
| +class ExtensionBlacklistBrowserTest : public ExtensionBrowserTest { |
| + public: |
| + ExtensionBlacklistBrowserTest() |
| + : info_a_("install/install.crx", "ogdbpbegnmindpdjfafpmpicikegejdj"), |
| + info_b_("autoupdate/v1.crx", "ogjcoiohnmldgjemafoockdghcjciccf") {} |
| + |
| + virtual ~ExtensionBlacklistBrowserTest() {} |
| + |
| + protected: |
| + // Returns whether |extension| is strictly installed: in ExtensionService's |
| + // installed extensions, and not in its blacklisted extensions. |
| + testing::AssertionResult IsInstalled(const Extension* extension) { |
| + std::string id = extension->id(); |
| + if (!extension_service()->extensions()->Contains(id)) |
| + return testing::AssertionFailure() << id << " is not in extensions"; |
| + return IsInValidState(extension); |
| + } |
| + |
| + // Returns whether |extension| is strictly blacklisted: in ExtensionService's |
| + // blacklist, and not installed. |
| + testing::AssertionResult IsBlacklisted(const Extension* extension) { |
| + std::string id = extension->id(); |
| + if (!extension_service()->blacklisted_extensions()->Contains(id)) |
| + return testing::AssertionFailure() << id << " is not in blacklisted"; |
| + return IsInValidState(extension); |
| + } |
| + |
| + std::set<std::string> GetTestExtensionIDs() { |
| + std::set<std::string> extension_ids; |
| + extension_ids.insert(info_a_.id()); |
| + extension_ids.insert(info_b_.id()); |
| + return extension_ids; |
| + } |
| + |
| + Profile* profile() { |
|
asargent_no_longer_on_chrome
2012/11/30 21:44:22
optional suggestion: It might be worth hoisting th
not at google - send to devlin
2012/11/30 23:09:54
Done.
|
| + return browser()->profile(); |
| + } |
| + |
| + ExtensionSystem* extension_system() { |
| + return ExtensionSystem::Get(profile()); |
| + } |
| + |
| + ExtensionService* extension_service() { |
|
asargent_no_longer_on_chrome
2012/11/30 21:44:22
same for this one
not at google - send to devlin
2012/11/30 23:09:54
Done.
|
| + return extension_system()->extension_service(); |
| + } |
| + |
| + CrxInfo info_a_; |
| + |
| + CrxInfo info_b_; |
| + |
| + private: |
| + // Returns whether |extension| is either installed or blacklisted, but |
| + // neither both nor neither. |
| + testing::AssertionResult IsInValidState(const Extension* extension) { |
| + std::string id = extension->id(); |
| + bool is_blacklisted = |
| + extension_service()->blacklisted_extensions()->Contains(id); |
| + bool is_installed = extension_service()->GetInstalledExtension(id); |
| + if (is_blacklisted && is_installed) { |
| + return testing::AssertionFailure() << |
| + id << " is both installed and in blacklisted_extensions"; |
| + } |
| + if (!is_blacklisted && !is_installed) { |
| + return testing::AssertionFailure() << |
| + id << " is neither installed nor in blacklisted_extensions"; |
| + } |
| + return testing::AssertionSuccess(); |
| + } |
| +}; |
| + |
| +// Stage 1: blacklisting when there weren't any extensions installed when the |
| +// browser started. |
| +IN_PROC_BROWSER_TEST_F(ExtensionBlacklistBrowserTest, PRE_Blacklist) { |
| + //FilteringNotificationObserver notifications( |
| + // content::Source<Profile>(profile()), GetTestExtensionIDs()); |
| + FilteringNotificationObserver notifications( |
| + content::NotificationService::AllSources(), GetTestExtensionIDs()); |
| + |
| + scoped_refptr<const Extension> extension_a = |
| + InstallExtension(test_data_dir_.AppendASCII(info_a_.path()), 1); |
| + scoped_refptr<const Extension> extension_b = |
| + InstallExtension(test_data_dir_.AppendASCII(info_b_.path()), 1); |
| + |
| + EXPECT_TRUE(notifications.CheckNotifications( |
| + chrome::NOTIFICATION_EXTENSION_INSTALLED, |
| + chrome::NOTIFICATION_EXTENSION_LOADED, |
| + chrome::NOTIFICATION_EXTENSION_INSTALLED, |
| + chrome::NOTIFICATION_EXTENSION_LOADED)); |
| + |
| + ASSERT_TRUE(extension_a); |
| + ASSERT_TRUE(extension_b); |
| + ASSERT_EQ(info_a_.id(), extension_a->id()); |
| + ASSERT_EQ(info_b_.id(), extension_b->id()); |
| + |
| + std::vector<std::string> empty_vector; |
| + std::vector<std::string> vector_a(1, info_a_.id()); |
| + std::vector<std::string> vector_b(1, info_b_.id()); |
| + std::vector<std::string> vector_ab(1, info_a_.id()); |
| + vector_ab.push_back(info_b_.id()); |
| + |
| + EXPECT_TRUE(IsInstalled(extension_a)); |
| + EXPECT_TRUE(IsInstalled(extension_b)); |
| + |
| + // Blacklist a. |
| + extension_system()->blacklist()->SetFromUpdater(vector_a, "1"); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_TRUE(IsBlacklisted(extension_a)); |
| + EXPECT_TRUE(IsInstalled(extension_b)); |
| + EXPECT_TRUE(notifications.CheckNotifications( |
| + chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| + |
| + // Un-blacklist a. |
| + extension_system()->blacklist()->SetFromUpdater(empty_vector, "2"); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_TRUE(IsInstalled(extension_a)); |
| + EXPECT_TRUE(IsInstalled(extension_b)); |
| + EXPECT_TRUE(notifications.CheckNotifications( |
| + chrome::NOTIFICATION_EXTENSION_LOADED)); |
| + |
| + // Blacklist a then switch with b. |
| + extension_system()->blacklist()->SetFromUpdater(vector_a, "3"); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_TRUE(IsBlacklisted(extension_a)); |
| + EXPECT_TRUE(IsInstalled(extension_b)); |
| + EXPECT_TRUE(notifications.CheckNotifications( |
| + chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| + |
| + extension_system()->blacklist()->SetFromUpdater(vector_b, "4"); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_TRUE(IsInstalled(extension_a)); |
| + EXPECT_TRUE(IsBlacklisted(extension_b)); |
| + EXPECT_TRUE(notifications.CheckNotifications( |
| + chrome::NOTIFICATION_EXTENSION_LOADED, |
| + chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| + |
| + // Add a to blacklist. |
| + extension_system()->blacklist()->SetFromUpdater(vector_ab, "5"); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_TRUE(IsBlacklisted(extension_a)); |
| + EXPECT_TRUE(IsBlacklisted(extension_b)); |
| + EXPECT_TRUE(notifications.CheckNotifications( |
| + chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| + |
| + // Clear blacklist. |
| + extension_system()->blacklist()->SetFromUpdater(empty_vector, "6"); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_TRUE(IsInstalled(extension_a)); |
| + EXPECT_TRUE(IsInstalled(extension_b)); |
| + EXPECT_TRUE(notifications.CheckNotifications( |
| + chrome::NOTIFICATION_EXTENSION_LOADED, |
| + chrome::NOTIFICATION_EXTENSION_LOADED)); |
| + |
| + // Add b back again for the next test. |
| + extension_system()->blacklist()->SetFromUpdater(vector_b, "7"); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_TRUE(IsInstalled(extension_a)); |
| + EXPECT_TRUE(IsBlacklisted(extension_b)); |
| + EXPECT_TRUE(notifications.CheckNotifications( |
| + chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| +} |
| + |
| +// Stage 2: blacklisting with extensions A and B having been installed, |
| +// with B actually in the blacklist. |
| +IN_PROC_BROWSER_TEST_F(ExtensionBlacklistBrowserTest, Blacklist) { |
| + FilteringNotificationObserver notifications( |
| + content::Source<Profile>(profile()), GetTestExtensionIDs()); |
| + |
| + scoped_refptr<const Extension> extension_a = |
| + extension_service()->extensions()->GetByID(info_a_.id()); |
| + ASSERT_TRUE(extension_a); |
| + |
| + scoped_refptr<const Extension> extension_b = |
| + extension_service()->blacklisted_extensions()->GetByID(info_b_.id()); |
| + ASSERT_TRUE(extension_b); |
| + |
| + EXPECT_TRUE(IsInstalled(extension_a)); |
| + EXPECT_TRUE(IsBlacklisted(extension_b)); |
| + |
| + // Make sure that we can still blacklist a and unblacklist b. |
| + std::vector<std::string> vector_a(1, extension_a->id()); |
| + extension_system()->blacklist()->SetFromUpdater(vector_a, "8"); |
| + base::RunLoop().RunUntilIdle(); |
| + |
| + EXPECT_TRUE(IsBlacklisted(extension_a)); |
| + EXPECT_TRUE(IsInstalled(extension_b)); |
| + EXPECT_TRUE(notifications.CheckNotifications( |
| + chrome::NOTIFICATION_EXTENSION_LOADED, |
| + chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| +} |
| + |
| +} // namespace extensions |