diff options
Diffstat (limited to 'chrome/browser/privacy_blacklist')
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 |