summaryrefslogtreecommitdiffstats
path: root/chrome/browser/privacy_blacklist
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-15 17:32:48 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-15 17:32:48 +0000
commit42b8c0b39acc727cf56fe6e77dc9d62b6e15e25f (patch)
tree810a9138222085dba47c0c1a08668db4d5adcb78 /chrome/browser/privacy_blacklist
parent44b78f90aa7fb40d6694045f4da76c502dea2272 (diff)
downloadchromium_src-42b8c0b39acc727cf56fe6e77dc9d62b6e15e25f.zip
chromium_src-42b8c0b39acc727cf56fe6e77dc9d62b6e15e25f.tar.gz
chromium_src-42b8c0b39acc727cf56fe6e77dc9d62b6e15e25f.tar.bz2
Create BlacklistManager, which will aggregate individual blacklists into one compiled one
and maintain it. This is the first step towards shipping privacy blacklists in extensions. The next step will be to make Profile own a BlacklistManager, and make ExtensionsService a BlacklistPathsProvider. TEST=Covered by unit_tests. BUG=21541 Review URL: http://codereview.chromium.org/164535 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29135 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/privacy_blacklist')
-rw-r--r--chrome/browser/privacy_blacklist/blacklist_manager.cc189
-rw-r--r--chrome/browser/privacy_blacklist/blacklist_manager.h96
-rw-r--r--chrome/browser/privacy_blacklist/blacklist_manager_unittest.cc280
3 files changed, 565 insertions, 0 deletions
diff --git a/chrome/browser/privacy_blacklist/blacklist_manager.cc b/chrome/browser/privacy_blacklist/blacklist_manager.cc
new file mode 100644
index 0000000..4cad97b
--- /dev/null
+++ b/chrome/browser/privacy_blacklist/blacklist_manager.cc
@@ -0,0 +1,189 @@
+// 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/privacy_blacklist/blacklist_manager.h"
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "chrome/browser/privacy_blacklist/blacklist.h"
+#include "chrome/browser/privacy_blacklist/blacklist_io.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+
+// Base class for tasks that use BlacklistManager. It ensures that the
+// BlacklistManager won't get destroyed while we need it, and that it
+// will be destroyed always on the same thread. That's why we use
+// a custom Task instead of just NewRunnableMethod.
+class BlacklistManagerTask : public Task {
+ public:
+ explicit BlacklistManagerTask(BlacklistManager* manager)
+ : manager_(manager),
+ original_loop_(MessageLoop::current()) {
+ DCHECK(original_loop_);
+ manager->AddRef();
+ }
+
+ ~BlacklistManagerTask() {
+ original_loop_->ReleaseSoon(FROM_HERE, manager_);
+ }
+
+ protected:
+ BlacklistManager* blacklist_manager() const { return manager_; }
+
+ private:
+ BlacklistManager* manager_;
+
+ MessageLoop* original_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlacklistManagerTask);
+};
+
+BlacklistPathProvider::~BlacklistPathProvider() {
+}
+
+class BlacklistManager::CompileBlacklistTask : public BlacklistManagerTask {
+ public:
+ CompileBlacklistTask(BlacklistManager* manager,
+ const FilePath& destination_blacklist,
+ const std::vector<FilePath>& source_blacklists)
+ : BlacklistManagerTask(manager),
+ destination_blacklist_(destination_blacklist),
+ source_blacklists_(source_blacklists) {
+ }
+
+ virtual void Run() {
+ BlacklistIO io;
+ bool success = true;
+
+ for (std::vector<FilePath>::const_iterator i = source_blacklists_.begin();
+ i != source_blacklists_.end(); ++i) {
+ if (!io.Read(*i)) {
+ success = false;
+ break;
+ }
+ }
+
+ // Only overwrite the current compiled blacklist if we read all source
+ // files successfully.
+ if (success)
+ success = io.Write(destination_blacklist_);
+
+ blacklist_manager()->OnBlacklistCompilationFinished(success);
+ }
+
+ private:
+ FilePath destination_blacklist_;
+
+ std::vector<FilePath> source_blacklists_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompileBlacklistTask);
+};
+
+class BlacklistManager::ReadBlacklistTask : public BlacklistManagerTask {
+ public:
+ ReadBlacklistTask(BlacklistManager* manager, const FilePath& blacklist_path)
+ : BlacklistManagerTask(manager),
+ blacklist_path_(blacklist_path) {
+ }
+
+ virtual void Run() {
+ Blacklist* blacklist = new Blacklist(blacklist_path_);
+ blacklist_manager()->OnBlacklistReadFinished(blacklist);
+ }
+
+ private:
+ FilePath blacklist_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadBlacklistTask);
+};
+
+BlacklistManager::BlacklistManager(Profile* profile,
+ base::Thread* backend_thread)
+ : compiled_blacklist_path_(
+ profile->GetPath().Append(chrome::kPrivacyBlacklistFileName)),
+ compiling_blacklist_(false),
+ backend_thread_(backend_thread) {
+ registrar_.Add(this,
+ NotificationType::PRIVACY_BLACKLIST_PATH_PROVIDER_UPDATED,
+ Source<Profile>(profile));
+ ReadBlacklist();
+}
+
+void BlacklistManager::RegisterBlacklistPathProvider(
+ BlacklistPathProvider* provider) {
+ DCHECK(providers_.find(provider) == providers_.end());
+ providers_.insert(provider);
+}
+
+void BlacklistManager::UnregisterBlacklistPathProvider(
+ BlacklistPathProvider* provider) {
+ DCHECK(providers_.find(provider) != providers_.end());
+ providers_.erase(provider);
+}
+
+void BlacklistManager::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::PRIVACY_BLACKLIST_PATH_PROVIDER_UPDATED);
+ CompileBlacklist();
+}
+
+void BlacklistManager::CompileBlacklist() {
+ if (compiling_blacklist_) {
+ // If we end up here, that means that initial compile succeeded,
+ // but then we couldn't read back the resulting Blacklist. Return early
+ // to avoid a potential infinite loop.
+ // TODO(phajdan.jr): Report the error.
+ compiling_blacklist_ = false;
+ return;
+ }
+
+ compiling_blacklist_ = true;
+
+ std::vector<FilePath> source_blacklists;
+
+ for (ProvidersSet::iterator provider = providers_.begin();
+ provider != providers_.end(); ++provider) {
+ std::vector<FilePath> provided_paths((*provider)->GetBlacklistPaths());
+ source_blacklists.insert(source_blacklists.end(),
+ provided_paths.begin(), provided_paths.end());
+ }
+
+ RunTaskOnBackendThread(new CompileBlacklistTask(this, compiled_blacklist_path_,
+ source_blacklists));
+}
+
+void BlacklistManager::ReadBlacklist() {
+ RunTaskOnBackendThread(new ReadBlacklistTask(this, compiled_blacklist_path_));
+}
+
+void BlacklistManager::OnBlacklistCompilationFinished(bool success) {
+ if (success) {
+ ReadBlacklist();
+ } else {
+ // TODO(phajdan.jr): Report the error.
+ }
+}
+
+void BlacklistManager::OnBlacklistReadFinished(Blacklist* blacklist) {
+ if (blacklist->is_good()) {
+ compiled_blacklist_.reset(blacklist);
+ compiling_blacklist_ = false;
+ } else {
+ delete blacklist;
+ CompileBlacklist();
+ }
+}
+
+void BlacklistManager::RunTaskOnBackendThread(Task* task) {
+ if (backend_thread_) {
+ backend_thread_->message_loop()->PostTask(FROM_HERE, task);
+ } else {
+ task->Run();
+ delete task;
+ }
+}
diff --git a/chrome/browser/privacy_blacklist/blacklist_manager.h b/chrome/browser/privacy_blacklist/blacklist_manager.h
new file mode 100644
index 0000000..35cf37f
--- /dev/null
+++ b/chrome/browser/privacy_blacklist/blacklist_manager.h
@@ -0,0 +1,96 @@
+// 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_PRIVACY_BLACKLIST_BLACKLIST_MANAGER_H_
+#define CHROME_BROWSER_PRIVACY_BLACKLIST_BLACKLIST_MANAGER_H_
+
+#include <set>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "base/non_thread_safe.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "chrome/common/notification_registrar.h"
+
+class Blacklist;
+class MessageLoop;
+class Profile;
+class Task;
+
+namespace base {
+class Thread;
+}
+
+class BlacklistPathProvider {
+ public:
+ virtual ~BlacklistPathProvider();
+
+ virtual std::vector<FilePath> GetBlacklistPaths() = 0;
+};
+
+// Updates one compiled binary blacklist based on a list of plaintext
+// blacklists.
+class BlacklistManager : public base::RefCountedThreadSafe<BlacklistManager>,
+ public NotificationObserver,
+ public NonThreadSafe {
+ public:
+ // You must create and destroy BlacklistManager on the same thread.
+ BlacklistManager(Profile* profile, base::Thread* backend_thread);
+
+ void RegisterBlacklistPathProvider(BlacklistPathProvider* provider);
+ void UnregisterBlacklistPathProvider(BlacklistPathProvider* provider);
+
+ const Blacklist* GetCompiledBlacklist() const {
+ return compiled_blacklist_.get();
+ }
+
+ // NotificationObserver
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+#ifdef UNIT_TEST
+ const FilePath& compiled_blacklist_path() { return compiled_blacklist_path_; }
+#endif // UNIT_TEST
+
+ private:
+ class CompileBlacklistTask;
+ class ReadBlacklistTask;
+
+ typedef std::set<BlacklistPathProvider*> ProvidersSet;
+
+ void CompileBlacklist();
+ void ReadBlacklist();
+
+ void OnBlacklistCompilationFinished(bool success);
+ void OnBlacklistReadFinished(Blacklist* blacklist);
+
+ void RunTaskOnBackendThread(Task* task);
+
+ // Path where we store the compiled blacklist.
+ FilePath compiled_blacklist_path_;
+
+ // Keep the compiled blacklist object in memory.
+ scoped_ptr<Blacklist> compiled_blacklist_;
+
+ // If true, then we started compiling a blacklist and haven't yet finished
+ // successfully. This helps prevent an infinite loop in case of multiple
+ // I/O errors.
+ bool compiling_blacklist_;
+
+ // Registered blacklist paths providers.
+ ProvidersSet providers_;
+
+ // Backend thread we will execute I/O operations on (NULL means no separate
+ // thread).
+ base::Thread* backend_thread_;
+
+ NotificationRegistrar registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(BlacklistManager);
+};
+
+#endif // CHROME_BROWSER_PRIVACY_BLACKLIST_BLACKLIST_MANAGER_H_
diff --git a/chrome/browser/privacy_blacklist/blacklist_manager_unittest.cc b/chrome/browser/privacy_blacklist/blacklist_manager_unittest.cc
new file mode 100644
index 0000000..995184f57
--- /dev/null
+++ b/chrome/browser/privacy_blacklist/blacklist_manager_unittest.cc
@@ -0,0 +1,280 @@
+// 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/privacy_blacklist/blacklist_manager.h"
+
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/scoped_temp_dir.h"
+#include "base/thread.h"
+#include "chrome/browser/privacy_blacklist/blacklist.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MyTestingProfile : public TestingProfile {
+ public:
+ MyTestingProfile() {
+ EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+ path_ = temp_dir_.path();
+ }
+
+ private:
+ ScopedTempDir temp_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(MyTestingProfile);
+};
+
+class TestBlacklistPathProvider : public BlacklistPathProvider {
+ public:
+ explicit TestBlacklistPathProvider(Profile* profile) : profile_(profile) {
+ }
+
+ virtual std::vector<FilePath> GetBlacklistPaths() {
+ return paths_;
+ }
+
+ void AddPath(const FilePath& path) {
+ paths_.push_back(path);
+ NotificationService::current()->Notify(
+ NotificationType::PRIVACY_BLACKLIST_PATH_PROVIDER_UPDATED,
+ Source<Profile>(profile_),
+ Details<BlacklistPathProvider>(this));
+ }
+
+ private:
+ Profile* profile_;
+
+ std::vector<FilePath> paths_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestBlacklistPathProvider);
+};
+
+class BlacklistManagerTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_));
+ test_data_dir_ = test_data_dir_.AppendASCII("blacklist_samples");
+ }
+
+ virtual void TearDown() {
+ loop_.RunAllPending();
+ }
+
+ protected:
+ FilePath test_data_dir_;
+
+ MyTestingProfile profile_;
+
+ private:
+ MessageLoop loop_;
+};
+
+TEST_F(BlacklistManagerTest, Basic) {
+ scoped_refptr<BlacklistManager> manager(
+ new BlacklistManager(&profile_, NULL));
+
+ const Blacklist* blacklist = manager->GetCompiledBlacklist();
+
+ // We should get an empty, but valid object.
+ ASSERT_TRUE(blacklist);
+ EXPECT_TRUE(blacklist->is_good());
+
+ // Repeated invocations of GetCompiledBlacklist should return the same object.
+ EXPECT_EQ(blacklist, manager->GetCompiledBlacklist());
+}
+
+TEST_F(BlacklistManagerTest, BlacklistPathProvider) {
+ scoped_refptr<BlacklistManager> manager(
+ new BlacklistManager(&profile_, NULL));
+
+ const Blacklist* blacklist1 = manager->GetCompiledBlacklist();
+ EXPECT_FALSE(blacklist1->findMatch(GURL("http://host/annoying_ads/ad.jpg")));
+
+ TestBlacklistPathProvider provider(&profile_);
+ manager->RegisterBlacklistPathProvider(&provider);
+
+ // Blacklist should not get recompiled.
+ EXPECT_EQ(blacklist1, manager->GetCompiledBlacklist());
+
+ provider.AddPath(test_data_dir_.AppendASCII("annoying_ads.pbl"));
+
+ const Blacklist* blacklist2 = manager->GetCompiledBlacklist();
+
+ // Added a real blacklist, the manager should recompile.
+ EXPECT_NE(blacklist1, blacklist2);
+ EXPECT_TRUE(blacklist2->findMatch(GURL("http://host/annoying_ads/ad.jpg")));
+
+ manager->UnregisterBlacklistPathProvider(&provider);
+
+ // Just unregistering the provider doesn't remove the blacklist paths
+ // from the manager.
+ EXPECT_EQ(blacklist2, manager->GetCompiledBlacklist());
+}
+
+TEST_F(BlacklistManagerTest, RealThread) {
+ base::Thread backend_thread("backend_thread");
+ backend_thread.Start();
+
+ scoped_refptr<BlacklistManager> manager(
+ new BlacklistManager(&profile_, &backend_thread));
+
+ // Make sure all pending tasks run.
+ backend_thread.Stop();
+ backend_thread.Start();
+
+ const Blacklist* blacklist1 = manager->GetCompiledBlacklist();
+ EXPECT_FALSE(blacklist1->findMatch(GURL("http://host/annoying_ads/ad.jpg")));
+
+ TestBlacklistPathProvider provider(&profile_);
+ manager->RegisterBlacklistPathProvider(&provider);
+
+ // Make sure all pending tasks run.
+ backend_thread.Stop();
+ backend_thread.Start();
+
+ // Blacklist should not get recompiled.
+ EXPECT_EQ(blacklist1, manager->GetCompiledBlacklist());
+
+ provider.AddPath(test_data_dir_.AppendASCII("annoying_ads.pbl"));
+
+ // Make sure all pending tasks run.
+ backend_thread.Stop();
+ backend_thread.Start();
+
+ const Blacklist* blacklist2 = manager->GetCompiledBlacklist();
+
+ // Added a real blacklist, the manager should recompile.
+ EXPECT_NE(blacklist1, blacklist2);
+ EXPECT_TRUE(blacklist2->findMatch(GURL("http://host/annoying_ads/ad.jpg")));
+
+ manager->UnregisterBlacklistPathProvider(&provider);
+
+ // Make sure all pending tasks run.
+ backend_thread.Stop();
+ backend_thread.Start();
+
+ // Just unregistering the provider doesn't remove the blacklist paths
+ // from the manager.
+ EXPECT_EQ(blacklist2, manager->GetCompiledBlacklist());
+}
+
+TEST_F(BlacklistManagerTest, CompiledBlacklistStaysOnDisk) {
+ {
+ scoped_refptr<BlacklistManager> manager(
+ new BlacklistManager(&profile_, NULL));
+
+ TestBlacklistPathProvider provider(&profile_);
+ manager->RegisterBlacklistPathProvider(&provider);
+ provider.AddPath(test_data_dir_.AppendASCII("annoying_ads.pbl"));
+ const Blacklist* blacklist = manager->GetCompiledBlacklist();
+ EXPECT_TRUE(blacklist->findMatch(GURL("http://host/annoying_ads/ad.jpg")));
+ manager->UnregisterBlacklistPathProvider(&provider);
+ }
+
+ {
+ scoped_refptr<BlacklistManager> manager(
+ new BlacklistManager(&profile_, NULL));
+
+ // Make sure we read the compiled blacklist from disk and don't even touch
+ // the paths providers.
+ const Blacklist* blacklist = manager->GetCompiledBlacklist();
+ EXPECT_TRUE(blacklist->findMatch(GURL("http://host/annoying_ads/ad.jpg")));
+ }
+}
+
+TEST_F(BlacklistManagerTest, BlacklistPathReadError) {
+ scoped_refptr<BlacklistManager> manager(
+ new BlacklistManager(&profile_, NULL));
+
+ TestBlacklistPathProvider provider(&profile_);
+ manager->RegisterBlacklistPathProvider(&provider);
+
+ FilePath bogus_path(test_data_dir_.AppendASCII("does_not_exist_randomness"));
+ ASSERT_FALSE(file_util::PathExists(bogus_path));
+ provider.AddPath(bogus_path);
+
+ const Blacklist* blacklist = manager->GetCompiledBlacklist();
+
+ // We should get an empty, but valid object.
+ ASSERT_TRUE(blacklist);
+ EXPECT_TRUE(blacklist->is_good());
+
+ manager->UnregisterBlacklistPathProvider(&provider);
+}
+
+TEST_F(BlacklistManagerTest, CompiledBlacklistReadError) {
+ FilePath compiled_blacklist_path;
+
+ {
+ scoped_refptr<BlacklistManager> manager(
+ new BlacklistManager(&profile_, NULL));
+
+ TestBlacklistPathProvider provider(&profile_);
+ manager->RegisterBlacklistPathProvider(&provider);
+ provider.AddPath(test_data_dir_.AppendASCII("annoying_ads.pbl"));
+ const Blacklist* blacklist = manager->GetCompiledBlacklist();
+ EXPECT_TRUE(blacklist->findMatch(GURL("http://host/annoying_ads/ad.jpg")));
+ manager->UnregisterBlacklistPathProvider(&provider);
+
+ compiled_blacklist_path = manager->compiled_blacklist_path();
+ }
+
+ ASSERT_TRUE(file_util::PathExists(compiled_blacklist_path));
+ ASSERT_TRUE(file_util::Delete(compiled_blacklist_path, false));
+
+ {
+ scoped_refptr<BlacklistManager> manager(
+ new BlacklistManager(&profile_, NULL));
+
+ // Now we don't have any providers, and no compiled blacklist file. We
+ // shouldn't match any URLs.
+ const Blacklist* blacklist = manager->GetCompiledBlacklist();
+ EXPECT_FALSE(blacklist->findMatch(GURL("http://host/annoying_ads/ad.jpg")));
+ }
+}
+
+TEST_F(BlacklistManagerTest, MultipleProviders) {
+ scoped_refptr<BlacklistManager> manager(
+ new BlacklistManager(&profile_, NULL));
+
+ TestBlacklistPathProvider provider1(&profile_);
+ TestBlacklistPathProvider provider2(&profile_);
+ manager->RegisterBlacklistPathProvider(&provider1);
+ manager->RegisterBlacklistPathProvider(&provider2);
+
+ const Blacklist* blacklist1 = manager->GetCompiledBlacklist();
+ EXPECT_FALSE(blacklist1->findMatch(GURL("http://sample/annoying_ads/a.jpg")));
+ EXPECT_FALSE(blacklist1->findMatch(GURL("http://sample/other_ads/a.jpg")));
+ EXPECT_FALSE(blacklist1->findMatch(GURL("http://host/something.doc")));
+
+ provider1.AddPath(test_data_dir_.AppendASCII("annoying_ads.pbl"));
+ const Blacklist* blacklist2 = manager->GetCompiledBlacklist();
+ EXPECT_NE(blacklist1, blacklist2);
+
+ provider2.AddPath(test_data_dir_.AppendASCII("host.pbl"));
+ const Blacklist* blacklist3 = manager->GetCompiledBlacklist();
+ EXPECT_NE(blacklist2, blacklist3);
+
+ EXPECT_TRUE(blacklist3->findMatch(GURL("http://sample/annoying_ads/a.jpg")));
+ EXPECT_FALSE(blacklist3->findMatch(GURL("http://sample/other_ads/a.jpg")));
+ EXPECT_TRUE(blacklist3->findMatch(GURL("http://host/something.doc")));
+
+ provider1.AddPath(test_data_dir_.AppendASCII("other_ads.pbl"));
+
+ const Blacklist* blacklist4 = manager->GetCompiledBlacklist();
+
+ EXPECT_NE(blacklist3, blacklist4);
+ EXPECT_TRUE(blacklist4->findMatch(GURL("http://sample/annoying_ads/a.jpg")));
+ EXPECT_TRUE(blacklist4->findMatch(GURL("http://sample/other_ads/a.jpg")));
+ EXPECT_TRUE(blacklist4->findMatch(GURL("http://host/something.doc")));
+
+ manager->UnregisterBlacklistPathProvider(&provider1);
+ manager->UnregisterBlacklistPathProvider(&provider2);
+}
+
+} // namespace