Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(661)

Unified Diff: chrome/browser/policy/url_blacklist_manager.cc

Issue 7716003: WIP: URL blacklisting by policy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Reviewed Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/policy/url_blacklist_manager.cc
diff --git a/chrome/browser/policy/url_blacklist_manager.cc b/chrome/browser/policy/url_blacklist_manager.cc
new file mode 100644
index 0000000000000000000000000000000000000000..9dfaa384e3c68dd85ae02d9b2296bb910fd9e8cb
--- /dev/null
+++ b/chrome/browser/policy/url_blacklist_manager.cc
@@ -0,0 +1,439 @@
+// Copyright (c) 2011 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/policy/url_blacklist_manager.h"
+
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/net/url_fixer_upper.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/chrome_notification_types.h"
+#include "chrome/common/pref_names.h"
+#include "content/browser/browser_thread.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_source.h"
+#include "googleurl/src/gurl.h"
+
+namespace policy {
+
+namespace {
+
+// Time to wait before starting an update of the blacklist. Scheduling another
+// update during this period will reset the timer.
+const int64 kUpdateDelayMs = 1000;
+
+// Maximum filters per policy. Filters over this index are ignored.
+const size_t kMaxFiltersPerPolicy = 100;
+
+typedef std::vector<std::string> StringVector;
+
+StringVector* ListValueToStringVector(const base::ListValue* list) {
+ StringVector* vector = new StringVector;
+
+ if (!list)
+ return vector;
+
+ vector->reserve(list->GetSize());
+ std::string s;
+ for (base::ListValue::const_iterator it = list->begin();
+ it != list->end() && vector->size() < kMaxFiltersPerPolicy; ++it) {
+ if ((*it)->GetAsString(&s))
+ vector->push_back(s);
+ }
+
+ return vector;
+}
+
+// A task that owns the Blacklist, and passes it to the URLBlacklistManager
+// on the IO thread, if the URLBlacklistManager still exists.
+class SetBlacklistTask : public Task {
+ public:
+ SetBlacklistTask(
+ base::WeakPtr<URLBlacklistManager> url_blacklist_manager,
+ Blacklist* blacklist)
+ : url_blacklist_manager_(url_blacklist_manager),
+ blacklist_(blacklist) {
+ }
+
+ virtual void Run() OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (url_blacklist_manager_)
+ url_blacklist_manager_->SetBlacklist(blacklist_.release());
+ }
+
+ private:
+ base::WeakPtr<URLBlacklistManager> url_blacklist_manager_;
+ scoped_ptr<Blacklist> blacklist_;
+ DISALLOW_COPY_AND_ASSIGN(SetBlacklistTask);
+};
+
+// A task that builds the blacklist on the FILE thread, and then posts another
+// task to pass it to the URLBlacklistManager on the IO thread.
+class BuildBlacklistTask : public Task {
+ public:
+ BuildBlacklistTask(
+ base::WeakPtr<URLBlacklistManager> url_blacklist_manager,
+ StringVector* blacklist,
+ StringVector* whitelist)
+ : url_blacklist_manager_(url_blacklist_manager),
+ blacklist_(blacklist),
+ whitelist_(whitelist) {
+ }
+
+ virtual void Run() OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ Blacklist* blacklist = NULL;
+
+ if (!blacklist_->empty()) {
+ blacklist = new Blacklist;
+ for (StringVector::iterator it = blacklist_->begin();
+ it != blacklist_->end(); ++it) {
+ blacklist->Block(*it);
+ }
+ for (StringVector::iterator it = whitelist_->begin();
+ it != whitelist_->end(); ++it) {
+ blacklist->Allow(*it);
+ }
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ new SetBlacklistTask(url_blacklist_manager_, blacklist));
+ }
+
+ private:
+ base::WeakPtr<URLBlacklistManager> url_blacklist_manager_;
+ scoped_ptr<StringVector> blacklist_;
+ scoped_ptr<StringVector> whitelist_;
+ DISALLOW_COPY_AND_ASSIGN(BuildBlacklistTask);
+};
+
+} // namespace
+
+Blacklist::Blacklist() {
+}
+
+Blacklist::~Blacklist() {
+}
+
+void Blacklist::AddFilter(const std::string& filter, bool block) {
+ std::string scheme;
+ std::string host;
+ uint16 port;
+ std::string path;
+ SchemeFlag flag;
+ bool match_subdomains = true;
+
+ if (!FilterToComponents(filter, &scheme, &host, &port, &path)) {
+ LOG(WARNING) << "Invalid filter, ignoring: " << filter;
+ return;
+ }
+
+ if (!SchemeToFlag(scheme, &flag)) {
+ LOG(WARNING) << "Unsupported scheme in filter, ignoring filter: " << filter;
+ return;
+ }
+
+ // Special syntax to disable subdomain matching.
+ if (!host.empty() && host[0] == '.') {
+ host.erase(0, 1);
+ match_subdomains = false;
+ }
+
+ // Try to find an existing PathFilter with the same path prefix, port and
+ // match_subdomains value.
+ PathFilterList& list = host_filters_[host];
+ PathFilterList::iterator it;
+ for (it = list.begin(); it != list.end(); ++it) {
+ if (it->port == port && it->match_subdomains == match_subdomains &&
+ it->path_prefix == path)
+ break;
+ }
+ PathFilter* path_filter;
+ if (it == list.end()) {
+ list.push_back(PathFilter(path, port, match_subdomains));
+ path_filter = &list.back();
+ } else {
+ path_filter = &(*it);
+ }
+
+ if (block)
+ path_filter->blocked_schemes |= flag;
+ else
+ path_filter->allowed_schemes |= flag;
+}
+
+void Blacklist::Block(const std::string& filter) {
+ AddFilter(filter, true);
+}
+
+void Blacklist::Allow(const std::string& filter) {
+ AddFilter(filter, false);
+}
+
+bool Blacklist::IsURLBlocked(const GURL& url) const {
+ SchemeFlag flag;
+ if (!SchemeToFlag(url.scheme(), &flag)) {
+ // Not a scheme that can be filtered.
+ return false;
+ }
+
+ std::string host(url.host());
+ int int_port = url.EffectiveIntPort();
+ const uint16 port = int_port > 0 ? int_port : 0;
+ const std::string& path = url.path();
+
+ // The first iteration through the loop will be an exact host match.
+ // Subsequent iterations are subdomain matches, and some filters don't apply
+ // to those.
+ bool is_matching_subdomains = false;
+ const bool host_is_ip = url.HostIsIPAddress();
+ for (;;) {
+ HostFilterTable::const_iterator host_filter = host_filters_.find(host);
+ if (host_filter != host_filters_.end()) {
+ const PathFilterList& v = host_filter->second;
+ size_t longest_length = 0;
+ bool is_blocked = false;
+ bool has_match = false;
+ bool has_exact_host_match = false;
+ for (PathFilterList::const_iterator it = v.begin(); it != v.end(); ++it) {
+ // Filters that apply to an exact hostname only take precedence over
+ // filters that can apply to subdomains too.
+ // E.g. ".google.com" filters take priority over "google.com".
+ if (has_exact_host_match && it->match_subdomains)
+ continue;
+
+ // Skip if filter doesn't apply to subdomains, and this is a subdomain.
+ if (is_matching_subdomains && !it->match_subdomains)
+ continue;
+
+ if (it->port != 0 && it->port != port)
+ continue;
+
+ // If this match can't be longer than the current match, skip it.
+ // For same size matches, the first rule to match takes precedence.
+ // If this is an exact host match, it can be actually shorter than
+ // a previous, non-exact match.
+ if ((has_match && it->path_prefix.length() <= longest_length) &&
+ (has_exact_host_match || it->match_subdomains)) {
+ continue;
+ }
+
+ // Skip if the filter's |path_prefix| is not a prefix of |path|.
+ if (path.compare(0, it->path_prefix.length(), it->path_prefix) != 0)
+ continue;
+
+ // Check if there is a blocked or allowed bit set for the scheme.
+ if ((it->allowed_schemes & flag) || (it->blocked_schemes & flag)) {
+ // This is the best match so far.
+ has_match = true;
+ has_exact_host_match = !it->match_subdomains;
+ longest_length = it->path_prefix.length();
+ // If both blocked and allowed bits are set, allowed takes precedence.
+ is_blocked = !(it->allowed_schemes & flag);
+ }
+ }
+ // If a match was found, return its decision.
+ if (has_match)
+ return is_blocked;
+ }
+
+ // Quit after trying the empty string (corresponding to host '*').
+ // Also skip subdomain matching for IP addresses.
+ if (host.empty() || host_is_ip)
+ break;
+
+ // No match found for this host. Try a subdomain match, by removing the
+ // leftmost subdomain from the hostname.
+ is_matching_subdomains = true;
+ size_t i = host.find('.');
+ if (i != std::string::npos)
+ ++i;
+ host.erase(0, i);
+ }
+
+ // Default is to allow.
+ return false;
+}
+
+// static
+bool Blacklist::SchemeToFlag(const std::string& scheme, SchemeFlag* flag) {
+ if (scheme.empty())
+ *flag = SCHEME_ALL;
+ else if (scheme == "http")
+ *flag = SCHEME_HTTP;
+ else if (scheme == "https")
+ *flag = SCHEME_HTTPS;
+ else if (scheme == "ftp")
+ *flag = SCHEME_FTP;
+ else
+ return false;
+ return true;
+}
+
+// static
+bool Blacklist::FilterToComponents(const std::string& filter,
+ std::string* scheme,
+ std::string* host,
+ uint16* port,
+ std::string* path) {
+ url_parse::Parsed parsed;
+ URLFixerUpper::SegmentURL(filter, &parsed);
+
+ if (!parsed.host.is_nonempty())
+ return false;
+
+ if (parsed.scheme.is_nonempty())
+ scheme->assign(filter, parsed.scheme.begin, parsed.scheme.len);
+ else
+ scheme->clear();
+
+ host->assign(filter, parsed.host.begin, parsed.host.len);
+ // Special '*' host, matches all hosts.
+ if (*host == "*")
+ host->clear();
+
+ if (parsed.port.is_nonempty()) {
+ int int_port;
+ if (!base::StringToInt(filter.substr(parsed.port.begin, parsed.port.len),
+ &int_port)) {
+ return false;
+ }
+ if (int_port <= 0 || int_port > kuint16max)
+ return false;
+ *port = int_port;
+ } else {
+ // Match any port.
+ *port = 0;
+ }
+
+ if (parsed.path.is_nonempty())
+ path->assign(filter, parsed.path.begin, parsed.path.len);
+ else
+ path->clear();
+
+ return true;
+}
+
+URLBlacklistManager::URLBlacklistManager(Profile* profile)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(ui_method_factory_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(io_weak_ptr_factory_(this)),
+ pref_service_(profile->GetPrefs()) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // This has to be created on the UI thread, but is only posted later, after
+ // the initialization on the IO thread is complete.
+ // Posting a task to invoke InitializeOnIOThread from here isn't safe, since
+ // we can't get weak ptrs for the IO thread yet.
+ initialize_on_ui_task_ = ui_method_factory_.NewRunnableMethod(
+ &URLBlacklistManager::InitializeOnUIThread);
+}
+
+void URLBlacklistManager::InitializeOnIOThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(initialize_on_ui_task_ != NULL);
+ io_weak_ptr_factory_.DetachFromThread();
+ weak_ptr_ = io_weak_ptr_factory_.GetWeakPtr();
+
+ // It is now safe to resume initialization on the UI thread, since it is now
+ // possible to get weak refs to self to use on the IO thread.
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, initialize_on_ui_task_);
+}
+
+void URLBlacklistManager::InitializeOnUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(initialize_on_ui_task_ != NULL);
+ initialize_on_ui_task_ = NULL;
+
+ pref_change_registrar_.Init(pref_service_);
+ pref_change_registrar_.Add(prefs::kUrlBlacklist, this);
+ pref_change_registrar_.Add(prefs::kUrlWhitelist, this);
+
+ // Start enforcing the policies without a delay when they are present at
+ // startup.
+ if (pref_service_->HasPrefPath(prefs::kUrlBlacklist))
+ Update();
+}
+
+void URLBlacklistManager::ShutdownOnUIThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Cancel any pending updates, and stop listening for pref change updates.
+ // This also cancels a pending InitializeOnUIThread, if it hasn't executed
+ // yet.
+ ui_method_factory_.RevokeAll();
+ pref_change_registrar_.RemoveAll();
+}
+
+URLBlacklistManager::~URLBlacklistManager() {
+ // Cancel any weak ptrs on the IO thread. Pending tasks won't execute their
+ // updates anymore.
+ io_weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void URLBlacklistManager::Observe(int type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(type == chrome::NOTIFICATION_PREF_CHANGED);
+ PrefService* prefs = Source<PrefService>(source).ptr();
+ DCHECK(prefs == pref_service_);
+ std::string* pref_name = Details<std::string>(details).ptr();
+ DCHECK(*pref_name == prefs::kUrlBlacklist ||
+ *pref_name == prefs::kUrlWhitelist);
+ ScheduleUpdate();
+}
+
+void URLBlacklistManager::ScheduleUpdate() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Cancel pending updates, if any.
+ ui_method_factory_.RevokeAll();
+ PostUpdateTask(
+ ui_method_factory_.NewRunnableMethod(&URLBlacklistManager::Update));
+}
+
+void URLBlacklistManager::PostUpdateTask(Task* task) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // This is overriden in tests to post the task without the delay.
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, task, kUpdateDelayMs);
+}
+
+void URLBlacklistManager::Update() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // The preferences can only be read on the UI thread.
+ StringVector* blacklist = ListValueToStringVector(
+ pref_service_->GetList(prefs::kUrlBlacklist));
+ StringVector* whitelist = ListValueToStringVector(
+ pref_service_->GetList(prefs::kUrlWhitelist));
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ new BuildBlacklistTask(weak_ptr_, blacklist, whitelist));
+}
+
+void URLBlacklistManager::SetBlacklist(Blacklist* blacklist) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ blacklist_.reset(blacklist);
+}
+
+bool URLBlacklistManager::IsURLBlocked(const GURL& url) const {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (!blacklist_.get())
+ return false;
+
+ return blacklist_->IsURLBlocked(url);
+}
+
+// static
+void URLBlacklistManager::RegisterPrefs(PrefService* pref_service) {
+ pref_service->RegisterListPref(prefs::kUrlBlacklist,
+ PrefService::UNSYNCABLE_PREF);
+ pref_service->RegisterListPref(prefs::kUrlWhitelist,
+ PrefService::UNSYNCABLE_PREF);
+}
+
+} // namespace policy
« no previous file with comments | « chrome/browser/policy/url_blacklist_manager.h ('k') | chrome/browser/policy/url_blacklist_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698