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

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

Issue 10692158: Refactor URLBlacklistManager to use URLMatcher. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: review Created 8 years, 5 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
index e553d75153d9dd0227b2dca84b5ca316fb49916b..e2307fe8f32c71bba4b30ae420b379a1acd5c010 100644
--- a/chrome/browser/policy/url_blacklist_manager.cc
+++ b/chrome/browser/policy/url_blacklist_manager.cc
@@ -19,6 +19,12 @@
#include "googleurl/src/gurl.h"
using content::BrowserThread;
+using extensions::URLMatcher;
+using extensions::URLMatcherCondition;
+using extensions::URLMatcherConditionFactory;
+using extensions::URLMatcherConditionSet;
+using extensions::URLMatcherPortFilter;
+using extensions::URLMatcherSchemeFilter;
namespace policy {
@@ -27,201 +33,126 @@ namespace {
// 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 == NULL)
- return vector;
+const char* kStandardSchemes[] = {
+ "http",
+ "https",
+ "file",
+ "ftp",
+ "gopher",
+ "ws",
+ "wss"
+};
- 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);
+bool IsStandardScheme(const std::string& scheme) {
+ for (size_t i = 0; i < arraysize(kStandardSchemes); ++i) {
+ if (scheme == kStandardSchemes[i])
+ return true;
}
-
- return vector;
+ return false;
}
-// A task that builds the blacklist on the FILE thread. Takes ownership
-// of |block| and |allow| but not of |blacklist|.
-void BuildBlacklist(URLBlacklist* blacklist,
- StringVector* block,
- StringVector* allow) {
+// A task that builds the blacklist on the FILE thread.
+scoped_ptr<URLBlacklist> BuildBlacklist(scoped_ptr<base::ListValue> block,
+ scoped_ptr<base::ListValue> allow) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- scoped_ptr<StringVector> scoped_block(block);
- scoped_ptr<StringVector> scoped_allow(allow);
-
- for (StringVector::iterator it = block->begin(); it != block->end(); ++it) {
- blacklist->Block(*it);
- }
- for (StringVector::iterator it = allow->begin(); it != allow->end(); ++it) {
- blacklist->Allow(*it);
- }
-}
-
-// A task that owns the URLBlacklist, and passes it to the URLBlacklistManager
-// on the IO thread, if the URLBlacklistManager still exists.
-void SetBlacklistOnIO(base::WeakPtr<URLBlacklistManager> blacklist_manager,
- URLBlacklist* blacklist) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- if (blacklist_manager) {
- blacklist_manager->SetBlacklist(blacklist);
- } else {
- delete blacklist;
- }
+ scoped_ptr<URLBlacklist> blacklist(new URLBlacklist);
+ blacklist->Block(block.get());
+ blacklist->Allow(allow.get());
+ return blacklist.Pass();
}
} // namespace
-struct URLBlacklist::PathFilter {
- explicit PathFilter(const std::string& path, uint16 port, bool match)
- : path_prefix(path),
- port(port),
- blocked_schemes(0),
- allowed_schemes(0),
- match_subdomains(match) {}
+struct URLBlacklist::FilterComponents {
+ FilterComponents() : match_subdomains(true), allow(true) {}
+ ~FilterComponents() {}
- std::string path_prefix;
+ std::string scheme;
+ std::string host;
uint16 port;
- uint8 blocked_schemes;
- uint8 allowed_schemes;
+ std::string path;
bool match_subdomains;
+ bool allow;
};
-URLBlacklist::URLBlacklist() {
+URLBlacklist::URLBlacklist() : id_(0),
+ url_matcher_(new URLMatcher) {
}
URLBlacklist::~URLBlacklist() {
- STLDeleteValues(&host_filters_);
}
-void URLBlacklist::Block(const std::string& filter) {
- AddFilter(filter, true);
+void URLBlacklist::AddFilters(bool allow,
+ const base::ListValue* list) {
+ URLMatcherConditionSet::Vector all_conditions;
+ size_t size = std::min(kMaxFiltersPerPolicy, list->GetSize());
+ for (size_t i = 0; i < size; ++i) {
+ std::string pattern;
+ bool success = list->GetString(i, &pattern);
+ DCHECK(success);
+ FilterComponents components;
+ components.allow = allow;
+ if (!FilterToComponents(pattern, &components.scheme, &components.host,
+ &components.match_subdomains, &components.port,
+ &components.path)) {
+ LOG(ERROR) << "Invalid pattern " << pattern;
+ continue;
+ }
+
+ all_conditions.push_back(
+ CreateConditionSet(url_matcher_.get(), ++id_, components.scheme,
+ components.host, components.match_subdomains,
+ components.port, components.path));
+ filters_[id_] = components;
+ }
+ url_matcher_->AddConditionSets(all_conditions);
+}
+
+void URLBlacklist::Block(const base::ListValue* filters) {
+ AddFilters(false, filters);
}
-void URLBlacklist::Allow(const std::string& filter) {
- AddFilter(filter, false);
+void URLBlacklist::Allow(const base::ListValue* filters) {
+ AddFilters(true, filters);
}
bool URLBlacklist::IsURLBlocked(const GURL& url) const {
- SchemeFlag flag = SCHEME_ALL;
- if (!SchemeToFlag(url.scheme(), &flag)) {
- // Not a scheme that can be filtered.
+ if (!HasStandardScheme(url))
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();
- while (1) {
- HostFilterTable::const_iterator host_filter = host_filters_.find(host);
- if (host_filter != host_filters_.end()) {
- const PathFilterList* list = 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 = list->begin();
- it != list->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;
-
- // Skip if the filter doesn't apply to the scheme.
- if ((it->allowed_schemes & flag) == 0 &&
- (it->blocked_schemes & flag) == 0)
- 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;
-
- // 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);
+ std::set<URLMatcherConditionSet::ID> matching_ids =
+ url_matcher_->MatchURL(url);
+
+ const FilterComponents* max = NULL;
+ for (std::set<URLMatcherConditionSet::ID>::iterator id = matching_ids.begin();
+ id != matching_ids.end(); ++id) {
+ std::map<int, FilterComponents>::const_iterator it = filters_.find(*id);
+ DCHECK(it != filters_.end());
+ const FilterComponents& filter = it->second;
+ if (!max || FilterTakesPrecedence(filter, *max))
+ max = &filter;
}
- // Default is to allow.
- return false;
+ // Default to allow.
+ if (!max)
+ return false;
+
+ return !max->allow;
}
// static
-bool URLBlacklist::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 if (scheme == "ws") {
- *flag = SCHEME_WS;
- } else if (scheme == "wss") {
- *flag = SCHEME_WSS;
- } else {
- return false;
- }
- return true;
+bool URLBlacklist::HasStandardScheme(const GURL& url) {
+ return IsStandardScheme(url.scheme());
}
// static
bool URLBlacklist::FilterToComponents(const std::string& filter,
- std::string* scheme,
- std::string* host,
- uint16* port,
- std::string* path) {
+ std::string* scheme,
+ std::string* host,
+ bool* match_subdomains,
+ uint16* port,
+ std::string* path) {
url_parse::Parsed parsed;
URLFixerUpper::SegmentURL(filter, &parsed);
@@ -235,8 +166,28 @@ bool URLBlacklist::FilterToComponents(const std::string& filter,
host->assign(filter, parsed.host.begin, parsed.host.len);
// Special '*' host, matches all hosts.
- if (*host == "*")
+ if (*host == "*") {
host->clear();
+ *match_subdomains = true;
+ } else if ((*host)[0] == '.') {
+ // A leading dot in the pattern syntax means that we don't want to match
+ // subdomains.
+ host->erase(0, 1);
+ *match_subdomains = false;
+ } else {
+ url_canon::RawCanonOutputT<char> output;
+ url_canon::CanonHostInfo host_info;
+ url_canon::CanonicalizeHostVerbose(filter.c_str(), parsed.host,
+ &output, &host_info);
+ if (host_info.family == url_canon::CanonHostInfo::NEUTRAL) {
+ // We want to match subdomains. Add a dot in front to make sure we only
+ // match at domain component boundaries.
+ *host = "." + *host;
+ *match_subdomains = true;
+ } else {
+ *match_subdomains = false;
+ }
+ }
if (parsed.port.is_nonempty()) {
int int_port;
@@ -257,62 +208,70 @@ bool URLBlacklist::FilterToComponents(const std::string& filter,
else
path->clear();
+ if (!scheme->empty() && !IsStandardScheme(*scheme))
+ return false;
+
return true;
}
-void URLBlacklist::AddFilter(const std::string& filter, bool block) {
- std::string scheme;
- std::string host;
- uint16 port;
- std::string path;
- if (!FilterToComponents(filter, &scheme, &host, &port, &path)) {
- LOG(WARNING) << "Invalid filter, ignoring: " << filter;
- return;
+// static
+scoped_refptr<extensions::URLMatcherConditionSet>
+URLBlacklist::CreateConditionSet(
+ extensions::URLMatcher* url_matcher,
+ int id,
+ const std::string& scheme,
+ const std::string& host,
+ bool match_subdomains,
+ uint16 port,
+ const std::string& path) {
+ URLMatcherConditionFactory* condition_factory =
+ url_matcher->condition_factory();
+ std::set<URLMatcherCondition> conditions;
+ conditions.insert(match_subdomains ?
+ condition_factory->CreateHostSuffixPathPrefixCondition(host, path) :
+ condition_factory->CreateHostEqualsPathPrefixCondition(host, path));
+
+ scoped_ptr<URLMatcherSchemeFilter> scheme_filter;
+ if (!scheme.empty())
+ scheme_filter.reset(new URLMatcherSchemeFilter(scheme));
+
+ scoped_ptr<URLMatcherPortFilter> port_filter;
+ if (port != 0) {
+ std::vector<URLMatcherPortFilter::Range> ranges;
+ ranges.push_back(URLMatcherPortFilter::CreateRange(port));
+ port_filter.reset(new URLMatcherPortFilter(ranges));
}
- SchemeFlag flag;
- if (!SchemeToFlag(scheme, &flag)) {
- LOG(WARNING) << "Unsupported scheme in filter, ignoring filter: " << filter;
- return;
- }
+ return new URLMatcherConditionSet(id, conditions,
+ scheme_filter.Pass(), port_filter.Pass());
+}
- bool match_subdomains = true;
- // Special syntax to disable subdomain matching.
- if (!host.empty() && host[0] == '.') {
- host.erase(0, 1);
- match_subdomains = false;
- }
+// static
+bool URLBlacklist::FilterTakesPrecedence(const FilterComponents& lhs,
+ const FilterComponents& rhs) {
+ if (lhs.match_subdomains && !rhs.match_subdomains)
+ return false;
+ if (!lhs.match_subdomains && rhs.match_subdomains)
+ return true;
- // Try to find an existing PathFilter with the same path prefix, port and
- // match_subdomains value.
- PathFilterList* list;
- HostFilterTable::iterator host_filter = host_filters_.find(host);
- if (host_filter == host_filters_.end()) {
- list = new PathFilterList;
- host_filters_[host] = list;
- } else {
- list = host_filter->second;
- }
- 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);
- }
+ size_t host_length = lhs.host.length();
+ size_t other_host_length = rhs.host.length();
+ if (host_length < other_host_length)
+ return false;
+ if (host_length > other_host_length)
+ return true;
Joao da Silva 2012/07/16 11:26:43 Suggestion: if (host_length != other_host_length)
Bernhard Bauer 2012/07/16 11:31:58 Done.
- if (block) {
- path_filter->blocked_schemes |= flag;
- } else {
- path_filter->allowed_schemes |= flag;
- }
+ size_t path_length = lhs.path.length();
+ size_t other_path_length = rhs.path.length();
+ if (path_length < other_path_length)
+ return false;
+ if (path_length > other_path_length)
+ return true;
+
+ if (lhs.allow && !rhs.allow)
+ return true;
+
+ return false;
}
URLBlacklistManager::URLBlacklistManager(PrefService* pref_service)
@@ -371,37 +330,38 @@ void URLBlacklistManager::Update() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// The preferences can only be read on the UI thread.
- StringVector* block =
- ListValueToStringVector(pref_service_->GetList(prefs::kUrlBlacklist));
- StringVector* allow =
- ListValueToStringVector(pref_service_->GetList(prefs::kUrlWhitelist));
+ scoped_ptr<base::ListValue> block(
+ pref_service_->GetList(prefs::kUrlBlacklist)->DeepCopy());
+ scoped_ptr<base::ListValue> allow(
+ pref_service_->GetList(prefs::kUrlWhitelist)->DeepCopy());
// Go through the IO thread to grab a WeakPtr to |this|. This is safe from
// here, since this task will always execute before a potential deletion of
// ProfileIOData on IO.
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&URLBlacklistManager::UpdateOnIO,
- base::Unretained(this), block, allow));
+ base::Unretained(this),
+ base::Passed(&block),
+ base::Passed(&allow)));
}
-void URLBlacklistManager::UpdateOnIO(StringVector* block, StringVector* allow) {
+void URLBlacklistManager::UpdateOnIO(scoped_ptr<base::ListValue> block,
+ scoped_ptr<base::ListValue> allow) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- URLBlacklist* blacklist = new URLBlacklist;
// The URLBlacklist is built on the FILE thread. Once it's ready, it is passed
// to the URLBlacklistManager on IO.
- // |blacklist|, |block| and |allow| can leak on the unlikely event of a
- // policy update and shutdown happening at the same time.
- BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE,
- base::Bind(&BuildBlacklist,
- blacklist, block, allow),
- base::Bind(&SetBlacklistOnIO,
- io_weak_ptr_factory_.GetWeakPtr(),
- blacklist));
+ BrowserThread::PostTaskAndReplyWithResult(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&BuildBlacklist,
+ base::Passed(&block),
+ base::Passed(&allow)),
+ base::Bind(&URLBlacklistManager::SetBlacklist,
+ io_weak_ptr_factory_.GetWeakPtr()));
}
-void URLBlacklistManager::SetBlacklist(URLBlacklist* blacklist) {
+void URLBlacklistManager::SetBlacklist(scoped_ptr<URLBlacklist> blacklist) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- blacklist_.reset(blacklist);
+ blacklist_ = blacklist.Pass();
}
bool URLBlacklistManager::IsURLBlocked(const GURL& url) const {
« 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