Chromium Code Reviews| Index: chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.cc |
| diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e181495edf0579d5816b98649f044ca67ccdbbf8 |
| --- /dev/null |
| +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.cc |
| @@ -0,0 +1,296 @@ |
| +// Copyright (c) 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 "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/lazy_instance.h" |
| +#include "base/logging.h" |
| +#include "base/stringprintf.h" |
| +#include "base/values.h" |
| +#include "chrome/browser/extensions/api/declarative_webrequest/request_stages.h" |
| +#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h" |
| +#include "net/url_request/url_request.h" |
| + |
| +namespace { |
| +static extensions::URLMatcherConditionSet::ID g_next_id = 0; |
| + |
| +// TODO(battre): improve error messaging to give more meaningful messages |
| +// to the extension developer. |
| +// Error messages: |
| +const char kExpectedDictionary[] = "A condition has to be a dictionary."; |
| +const char kConditionWithoutInstanceType[] = "A condition had no instanceType"; |
| +const char kExpectedOtherConditionType[] = "Expected a condition of type " |
| + "experimental.webRequest.RequestMatcher"; |
| +const char kUnknownConditionAttribute[] = "UnKnown condition attribute '%s'"; |
| +const char kConditionExpectedString[] = |
| + "Condition '%s' expected a string value"; |
| + |
| +// String literals from the JavaScript API: |
| +const char kRequestMatcher[] = "experimental.webRequest.RequestMatcher"; |
| +const char kInstanceType[] = "instanceType"; |
| + |
| +const char kHostContainsKey[] = "host_contains"; |
| +const char kHostEqualsKey[] = "host_equals"; |
| +const char kHostPrefixKey[] = "host_prefix"; |
| +const char kHostSuffixKey[] = "host_suffix"; |
| +const char kHostSuffixPathPrefixKey[] = "host_suffix_path_prefix"; |
| +const char kPathContainsKey[] = "path_contains"; |
| +const char kPathEqualsKey[] = "path_equals"; |
| +const char kPathPrefixKey[] = "path_prefix"; |
| +const char kPathSuffixKey[] = "path_suffix"; |
| +const char kQueryContainsKey[] = "query_contains"; |
| +const char kQueryEqualsKey[] = "query_equals"; |
| +const char kQueryPrefixKey[] = "query_prefix"; |
| +const char kQuerySuffixKey[] = "query_suffix"; |
| +const char kURLContainsKey[] = "url_contains"; |
| +const char kURLEqualsKey[] = "url_equals"; |
| +const char kURLPrefixKey[] = "url_prefix"; |
| +const char kURLSuffixKey[] = "url_suffix"; |
| + |
| +// Registry for all factory methods of extensions::URLMatcherConditionFactory |
| +// that allows translating string literals from the extension API into |
| +// the corresponding factory method to be called. |
| +class URLMatcherConditionFactoryMethods { |
| + public: |
| + URLMatcherConditionFactoryMethods() { |
| + typedef extensions::URLMatcherConditionFactory F; |
| + factory_methods_[kHostContainsKey] = &F::CreateHostContainsCondition; |
| + factory_methods_[kHostEqualsKey] = &F::CreateHostEqualsCondition; |
| + factory_methods_[kHostPrefixKey] = &F::CreateHostPrefixCondition; |
| + factory_methods_[kHostSuffixKey] = &F::CreateHostSuffixCondition; |
| + factory_methods_[kPathContainsKey] = &F::CreatePathContainsCondition; |
| + factory_methods_[kPathEqualsKey] = &F::CreatePathEqualsCondition; |
| + factory_methods_[kPathPrefixKey] = &F::CreatePathPrefixCondition; |
| + factory_methods_[kPathSuffixKey] = &F::CreatePathSuffixCondition; |
| + factory_methods_[kQueryContainsKey] = &F::CreateQueryContainsCondition; |
| + factory_methods_[kQueryEqualsKey] = &F::CreateQueryEqualsCondition; |
| + factory_methods_[kQueryPrefixKey] = &F::CreateQueryPrefixCondition; |
| + factory_methods_[kQuerySuffixKey] = &F::CreateQuerySuffixCondition; |
| + factory_methods_[kURLContainsKey] = &F::CreateURLContainsCondition; |
| + factory_methods_[kURLEqualsKey] = &F::CreateURLEqualsCondition; |
| + factory_methods_[kURLPrefixKey] = &F::CreateURLPrefixCondition; |
| + factory_methods_[kURLSuffixKey] = &F::CreateURLSuffixCondition; |
| + } |
| + |
| + // Returns whether a factory method for the specified |pattern_type| (e.g. |
| + // "host_suffix") is known. |
| + bool Contains(const std::string& pattern_type) const { |
| + return factory_methods_.find(pattern_type) != factory_methods_.end(); |
| + } |
| + |
| + // Creates a URLMatcherCondition instance from |url_matcher_condition_factory| |
| + // of the given |pattern_type| (e.g. "host_suffix") for the given |
| + // |pattern_value| (e.g. "example.com"). |
| + // The |pattern_type| needs to be known to this class (see Contains()) or |
| + // a CHECK is triggered. |
| + extensions::URLMatcherCondition Call( |
| + extensions::URLMatcherConditionFactory* url_matcher_condition_factory, |
| + const std::string& pattern_type, |
| + const std::string& pattern_value) const { |
| + FactoryMethods::const_iterator i = factory_methods_.find(pattern_type); |
| + CHECK(i != factory_methods_.end()); |
| + const FactoryMethod& method = i->second; |
| + return base::Bind(method, |
| + base::Unretained(url_matcher_condition_factory)).Run(pattern_value); |
|
Matt Perry
2012/03/26 20:21:20
You don't need to use Bind to invoke here. The syn
battre
2012/03/26 20:38:10
Done.
|
| + } |
| + |
| + private: |
| + typedef extensions::URLMatcherCondition |
| + (extensions::URLMatcherConditionFactory::* FactoryMethod) |
| + (const std::string& prefix); |
| + typedef std::map<std::string, FactoryMethod> FactoryMethods; |
| + |
| + FactoryMethods factory_methods_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(URLMatcherConditionFactoryMethods); |
| +}; |
| + |
| +static base::LazyInstance<URLMatcherConditionFactoryMethods> |
| + g_url_matcher_condition_factory_methods = LAZY_INSTANCE_INITIALIZER; |
| + |
| +} // namespace |
| + |
| +namespace extensions { |
| + |
| +// |
| +// WebRequestCondition |
| +// |
| + |
| +WebRequestCondition::WebRequestCondition( |
| + const URLMatcherConditionSet& url_matcher_conditions, |
| + const WebRequestConditionAttributes& condition_attributes) |
| + : url_matcher_conditions_(url_matcher_conditions), |
| + condition_attributes_(condition_attributes) {} |
| + |
| +WebRequestCondition::~WebRequestCondition() {} |
| + |
| +bool WebRequestCondition::IsFulfilled(net::URLRequest* request) const { |
| + // All condition attributes must be fulfilled for a fulfilled condition. |
| + for (WebRequestConditionAttributes::const_iterator i = |
| + condition_attributes_.begin(); i != condition_attributes_.end(); ++i) { |
| + if (!(*i)->IsFulfilled(request)) |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +// static |
| +scoped_ptr<WebRequestCondition> WebRequestCondition::Create( |
| + URLMatcherConditionFactory* url_matcher_condition_factory, |
| + const base::Value& condition, |
| + std::string* error) { |
| + const base::DictionaryValue* condition_dict = NULL; |
| + if (!condition.GetAsDictionary(&condition_dict)) { |
| + *error = kExpectedDictionary; |
| + return scoped_ptr<WebRequestCondition>(NULL); |
| + } |
| + |
| + // Verify that we are dealing with a Condition whose type we understand. |
| + std::string instance_type; |
| + if (!condition_dict->GetString(kInstanceType, &instance_type)) { |
| + *error = kConditionWithoutInstanceType; |
| + return scoped_ptr<WebRequestCondition>(NULL); |
| + } |
| + if (instance_type != kRequestMatcher) { |
| + *error = kExpectedOtherConditionType; |
| + return scoped_ptr<WebRequestCondition>(NULL); |
| + } |
| + |
| + WebRequestConditionAttributes attributes; |
| + URLMatcherConditionSet::Conditions url_matcher_conditions; |
| + |
| + for (base::DictionaryValue::Iterator iter(*condition_dict); |
| + iter.HasNext(); iter.Advance()) { |
| + const std::string& condition_attribute_name = iter.key(); |
| + const Value& condition_attribute_value = iter.value(); |
| + if (condition_attribute_name == kInstanceType) { |
| + // Skip this. |
| + } else if (IsURLMatcherConditionAttribute(condition_attribute_name)) { |
| + URLMatcherCondition url_matcher_condition = |
| + CreateURLMatcherCondition( |
| + url_matcher_condition_factory, |
| + condition_attribute_name, |
| + &condition_attribute_value, |
| + error); |
| + if (!error->empty()) |
| + return scoped_ptr<WebRequestCondition>(NULL); |
| + url_matcher_conditions.insert(url_matcher_condition); |
| + } else if (WebRequestConditionAttribute::IsKnownType( |
| + condition_attribute_name)) { |
| + scoped_ptr<WebRequestConditionAttribute> attribute = |
| + WebRequestConditionAttribute::Create( |
| + condition_attribute_name, |
| + &condition_attribute_value, |
| + error); |
| + if (!error->empty()) |
| + return scoped_ptr<WebRequestCondition>(NULL); |
| + attributes.push_back(make_linked_ptr(attribute.release())); |
| + } else { |
| + *error = base::StringPrintf(kUnknownConditionAttribute, |
| + condition_attribute_name.c_str()); |
| + return scoped_ptr<WebRequestCondition>(NULL); |
| + } |
| + } |
| + |
| + // As the URL is the preliminary matching criterion that triggers the tests |
| + // for the remaining condition attributes, we insert an empty URL match if |
| + // no other url match conditions were specified. Such an empty URL is always |
| + // matched. |
| + if (url_matcher_conditions.empty()) { |
| + url_matcher_conditions.insert( |
| + url_matcher_condition_factory->CreateHostPrefixCondition("")); |
| + } |
| + |
| + URLMatcherConditionSet url_matcher_condition_set(++g_next_id, |
| + url_matcher_conditions); |
| + return scoped_ptr<WebRequestCondition>( |
| + new WebRequestCondition(url_matcher_condition_set, attributes)); |
| +} |
| + |
| +// static |
| +bool WebRequestCondition::IsURLMatcherConditionAttribute( |
| + const std::string& condition_attribute_name) { |
| + return g_url_matcher_condition_factory_methods.Get().Contains( |
| + condition_attribute_name); |
| +} |
| + |
| +// static |
| +URLMatcherCondition WebRequestCondition::CreateURLMatcherCondition( |
| + URLMatcherConditionFactory* url_matcher_condition_factory, |
| + const std::string& condition_attribute_name, |
| + const base::Value* value, |
| + std::string* error) { |
| + std::string str_value; |
| + if (!value->GetAsString(&str_value)) { |
| + *error = base::StringPrintf(kConditionExpectedString, |
| + condition_attribute_name.c_str()); |
| + return URLMatcherCondition(); |
| + } |
| + return g_url_matcher_condition_factory_methods.Get().Call( |
| + url_matcher_condition_factory, condition_attribute_name, str_value); |
| +} |
| + |
| + |
| +// |
| +// WebRequestConditionCollection |
| +// |
| + |
| +WebRequestConditionCollection::WebRequestConditionCollection( |
| + const std::vector<linked_ptr<WebRequestCondition> >& conditions) |
| + : conditions_(conditions) { |
| + for (Collection::iterator i = conditions_.begin(); i != conditions_.end(); |
| + ++i) { |
| + URLMatcherConditionSet::ID trigger_id = |
| + (*i)->url_matcher_condition_set_id(); |
| + match_triggers_[trigger_id] = i->get(); |
| + } |
| +} |
| + |
| +WebRequestConditionCollection::~WebRequestConditionCollection() {} |
| + |
| +bool WebRequestConditionCollection::IsFulfilled( |
| + URLMatcherConditionSet::ID url_match, |
| + net::URLRequest* request) const { |
| + MatchTriggers::const_iterator trigger = match_triggers_.find(url_match); |
| + if (trigger == match_triggers_.end()) |
|
Matt Perry
2012/03/26 20:21:20
Should this be an error (DCHECK)? The caller knows
battre
2012/03/26 20:38:10
Done.
|
| + return false; |
| + DCHECK_EQ(url_match, trigger->second->url_matcher_condition_set_id()); |
| + if (trigger->second->IsFulfilled(request)) |
|
Matt Perry
2012/03/26 20:21:20
nit: return trigger->second->IsFulfilled(request)
battre
2012/03/26 20:38:10
Done.
|
| + return true; |
| + return false; |
| +} |
| + |
| +void WebRequestConditionCollection::AppendURLMatcherConditionSets( |
| + std::vector<URLMatcherConditionSet>* condition_sets) const { |
| + for (Collection::const_iterator i = conditions_.begin(); |
| + i != conditions_.end(); ++i) { |
| + condition_sets->push_back((*i)->url_matcher_condition_set()); |
| + } |
| +} |
| + |
| +// static |
| +scoped_ptr<WebRequestConditionCollection> |
| +WebRequestConditionCollection::Create( |
| + URLMatcherConditionFactory* url_matcher_condition_factory, |
| + const AnyVector& conditions, |
| + std::string* error) { |
| + std::vector<linked_ptr<WebRequestCondition> > result; |
| + |
| + for (AnyVector::const_iterator i = conditions.begin(); |
| + i != conditions.end(); ++i) { |
| + CHECK(i->get()); |
| + scoped_ptr<WebRequestCondition> condition = |
| + WebRequestCondition::Create(url_matcher_condition_factory, |
| + (*i)->value(), error); |
| + if (!error->empty()) |
| + return scoped_ptr<WebRequestConditionCollection>(NULL); |
| + result.push_back(make_linked_ptr(condition.release())); |
| + } |
| + |
| + return scoped_ptr<WebRequestConditionCollection>( |
| + new WebRequestConditionCollection(result)); |
| +} |
| + |
| +} // namespace extensions |