Chromium Code Reviews| Index: chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc |
| diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc |
| index 4d825d9e154226af331e555bf84de0a77d9073df..1f619b4e245c889c8c7df867c8c82d6426682d3b 100644 |
| --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc |
| +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc |
| @@ -7,6 +7,7 @@ |
| #include <algorithm> |
| #include "base/logging.h" |
| +#include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "base/values.h" |
| #include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h" |
| @@ -18,6 +19,11 @@ |
| #include "net/http/http_request_headers.h" |
| #include "net/url_request/url_request.h" |
| +using base::DictionaryValue; |
| +using base::ListValue; |
| +using base::StringValue; |
| +using base::Value; |
| + |
| namespace { |
| // Error messages. |
| const char kUnknownConditionAttribute[] = "Unknown matching condition: '*'"; |
| @@ -43,7 +49,9 @@ bool WebRequestConditionAttribute::IsKnownType( |
| const std::string& instance_type) { |
| return |
| WebRequestConditionAttributeResourceType::IsMatchingType(instance_type) || |
| - WebRequestConditionAttributeContentType::IsMatchingType(instance_type); |
| + WebRequestConditionAttributeContentType::IsMatchingType(instance_type) || |
| + WebRequestConditionAttributeResponseHeaders::IsMatchingType( |
| + instance_type); |
| } |
| // static |
| @@ -52,10 +60,15 @@ WebRequestConditionAttribute::Create( |
| const std::string& name, |
| const base::Value* value, |
| std::string* error) { |
| + CHECK(value != NULL && error != NULL); |
| if (WebRequestConditionAttributeResourceType::IsMatchingType(name)) { |
| return WebRequestConditionAttributeResourceType::Create(name, value, error); |
| } else if (WebRequestConditionAttributeContentType::IsMatchingType(name)) { |
| return WebRequestConditionAttributeContentType::Create(name, value, error); |
| + } else if (WebRequestConditionAttributeResponseHeaders::IsMatchingType( |
| + name)) { |
| + return WebRequestConditionAttributeResponseHeaders::Create( |
| + name, value, error); |
| } |
| *error = ExtensionErrorUtils::FormatErrorMessage(kUnknownConditionAttribute, |
| @@ -122,7 +135,7 @@ int WebRequestConditionAttributeResourceType::GetStages() const { |
| } |
| bool WebRequestConditionAttributeResourceType::IsFulfilled( |
| - const WebRequestRule::RequestData& request_data) { |
| + const WebRequestRule::RequestData& request_data) const { |
| if (!(request_data.stage & GetStages())) |
| return false; |
| const content::ResourceRequestInfo* info = |
| @@ -193,7 +206,7 @@ int WebRequestConditionAttributeContentType::GetStages() const { |
| } |
| bool WebRequestConditionAttributeContentType::IsFulfilled( |
| - const WebRequestRule::RequestData& request_data) { |
| + const WebRequestRule::RequestData& request_data) const { |
| if (!(request_data.stage & GetStages())) |
| return false; |
| std::string content_type; |
| @@ -219,4 +232,234 @@ WebRequestConditionAttributeContentType::GetType() const { |
| return CONDITION_CONTENT_TYPE; |
| } |
| +// |
| +// WebRequestConditionAttributeResponseHeaders |
| +// |
| + |
| +WebRequestConditionAttributeResponseHeaders::MatchTest::MatchTest( |
| + const std::string& data, |
| + MatchType type) |
| + : data_(data), |
| + type_(type) {} |
| + |
| +WebRequestConditionAttributeResponseHeaders::MatchTest::~MatchTest() {} |
| + |
| +WebRequestConditionAttributeResponseHeaders::TestGroup::TestGroup( |
| + ScopedVector<const MatchTest>* name, |
| + ScopedVector<const MatchTest>* value) |
| + : name_(name->Pass()), |
| + value_(value->Pass()) {} |
| + |
| +WebRequestConditionAttributeResponseHeaders::TestGroup::~TestGroup() {} |
| + |
| +WebRequestConditionAttributeResponseHeaders:: |
| +WebRequestConditionAttributeResponseHeaders( |
| + bool positive_test, ScopedVector<const TestGroup>* tests) |
| + : tests_(tests->Pass()), |
| + positive_test_(positive_test) {} |
| + |
| +WebRequestConditionAttributeResponseHeaders:: |
| +~WebRequestConditionAttributeResponseHeaders() {} |
| + |
| +// static |
| +bool WebRequestConditionAttributeResponseHeaders::IsMatchingType( |
| + const std::string& instance_type) { |
| + return instance_type == keys::kResponseHeadersKey || |
| + instance_type == keys::kExcludeResponseHeadersKey; |
| +} |
| + |
| +// static |
| +scoped_ptr<WebRequestConditionAttribute> |
| +WebRequestConditionAttributeResponseHeaders::Create( |
| + const std::string& name, |
| + const base::Value* value, |
| + std::string* error) { |
| + DCHECK(IsMatchingType(name)); |
| + |
| + const ListValue* value_as_list = NULL; |
| + if (!value->GetAsList(&value_as_list)) { |
| + *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); |
| + return scoped_ptr<WebRequestConditionAttribute>(NULL); |
| + } |
| + |
| + ScopedVector<const TestGroup> groups; |
| + for (ListValue::const_iterator it = value_as_list->begin(); |
| + it != value_as_list->end(); ++it) { |
| + const DictionaryValue* tests = NULL; |
| + if (!(*it)->GetAsDictionary(&tests)) { |
| + *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); |
| + return scoped_ptr<WebRequestConditionAttribute>(NULL); |
| + } |
| + |
| + scoped_ptr<const TestGroup> group(CreateTests(tests, error)); |
| + if (group.get() == NULL) |
| + return scoped_ptr<WebRequestConditionAttribute>(NULL); |
| + groups.push_back(group.release()); |
| + } |
| + |
| + scoped_ptr<WebRequestConditionAttributeResponseHeaders> result; |
| + result.reset(new WebRequestConditionAttributeResponseHeaders( |
| + name == keys::kResponseHeadersKey, &groups)); |
| + |
| + return result.PassAs<WebRequestConditionAttribute>(); |
| +} |
| + |
| +int WebRequestConditionAttributeResponseHeaders::GetStages() const { |
| + return ON_HEADERS_RECEIVED; |
| +} |
| + |
| +bool WebRequestConditionAttributeResponseHeaders::IsFulfilled( |
| + const WebRequestRule::RequestData& request_data) const { |
| + if (!(request_data.stage & GetStages())) |
| + return false; |
| + |
| + const net::HttpResponseHeaders* headers = |
| + request_data.original_response_headers; |
| + if (headers == NULL) { |
| + // Each header of an empty set satisfies (the negation of) everything; |
| + // OTOH, there is no header to satisfy even the most permissive test. |
| + return !positive_test_; |
| + } |
| + |
| + // Has some header already passed some test group? |
| + bool header_found = false; |
| + |
| + for (size_t i = 0; !header_found && i < tests_.size(); ++i) { |
| + std::string name; |
| + std::string value; |
| + |
| + for (void* iter = NULL; |
| + !header_found && headers->EnumerateHeaderLines(&iter, &name, &value); |
| + /* No increment here, this is done inside EnumerateHeaderLines. */) { |
|
battre
2012/08/27 17:16:25
opt:
void* iter = NULL;
while (!header_found && he
vabr (Chromium)
2012/08/28 17:59:09
Done.
|
| + StringToLowerASCII(&name); // Header names are case-insensitive. |
| + header_found |= tests_[i]->Matches(name, value); |
| + } |
| + } |
| + |
| + return (positive_test_ ? header_found : !header_found); |
| +} |
| + |
| +WebRequestConditionAttribute::Type |
| +WebRequestConditionAttributeResponseHeaders::GetType() const { |
| + return CONDITION_REQUEST_HEADERS; |
| +} |
| + |
| +bool WebRequestConditionAttributeResponseHeaders::MatchTest::Matches( |
| + const std::string& str) const { |
| + switch (type_) { |
| + case kPrefix: |
| + return StartsWithASCII(str, data_, true /*case_sensitive*/); |
| + case kSuffix: |
| + return EndsWith(str, data_, true /*case_sensitive*/); |
| + case kEquals: |
| + return data_ == str; |
| + case kContains: |
| + return str.find(data_) != std::string::npos; |
| + } |
| + // We never get past the "switch", but the compiler worries about no return. |
| + NOTREACHED(); |
| + return false; |
| +} |
| + |
| +bool WebRequestConditionAttributeResponseHeaders::TestGroup::Matches( |
| + const std::string& name, |
| + const std::string& value) const { |
| + for (size_t i = 0; i < name_.size(); ++i) { |
| + if (!name_[i]->Matches(name)) |
| + return false; |
| + } |
| + |
| + for (size_t i = 0; i < value_.size(); ++i) { |
| + if (!value_[i]->Matches(value)) |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| + |
| +// static |
| +scoped_ptr<const WebRequestConditionAttributeResponseHeaders::TestGroup> |
| +WebRequestConditionAttributeResponseHeaders::CreateTests( |
| + const DictionaryValue* tests, |
| + std::string* error) { |
| + ScopedVector<const MatchTest> name; |
| + ScopedVector<const MatchTest> value; |
| + |
| + for (DictionaryValue::key_iterator key = tests->begin_keys(); |
| + key != tests->end_keys(); |
| + ++key) { |
| + bool is_name = false; // Is this test for header name? |
| + MatchType match_type; |
| + if (*key == keys::kNamePrefixKey) { |
| + is_name = true; |
| + match_type = kPrefix; |
| + } else if (*key == keys::kNameSuffixKey) { |
| + is_name = true; |
| + match_type = kSuffix; |
| + } else if (*key == keys::kNameContainsKey) { |
| + is_name = true; |
| + match_type = kContains; |
| + } else if (*key == keys::kNameEqualsKey) { |
| + is_name = true; |
| + match_type = kEquals; |
| + } else if (*key == keys::kValuePrefixKey) { |
| + match_type = kPrefix; |
| + } else if (*key == keys::kValueSuffixKey) { |
| + match_type = kSuffix; |
| + } else if (*key == keys::kValueContainsKey) { |
| + match_type = kContains; |
| + } else if (*key == keys::kValueEqualsKey) { |
| + match_type = kEquals; |
| + } else { |
| + NOTREACHED(); // JSON schema type checking should prevent this. |
| + *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); |
| + return scoped_ptr<const TestGroup>(NULL); |
| + } |
| + const Value* content = NULL; |
| + // This should not fire, we already checked that |key| is there. |
| + CHECK(tests->Get(*key, &content)); |
| + |
| + switch (content->GetType()) { |
| + case Value::TYPE_LIST: { |
| + const ListValue* list = NULL; |
| + CHECK(content->GetAsList(&list)); |
| + for (ListValue::const_iterator it = list->begin(); |
| + it != list->end(); ++it) { |
| + ScopedVector<const MatchTest>* tests = is_name ? &name : &value; |
| + tests->push_back(CreateMatchTest(*it, is_name, match_type).release()); |
| + } |
| + break; |
| + } |
| + case Value::TYPE_STRING: { |
| + ScopedVector<const MatchTest>* tests = is_name ? &name : &value; |
| + tests->push_back( |
| + CreateMatchTest(content, is_name, match_type).release()); |
| + break; |
| + } |
| + default: { |
| + NOTREACHED(); // JSON schema type checking should prevent this. |
| + *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); |
| + return scoped_ptr<const TestGroup>(NULL); |
| + } |
| + } |
| + } |
| + |
| + return scoped_ptr<const TestGroup>(new TestGroup(&name, &value)); |
| +} |
| + |
| +// static |
| +scoped_ptr<const WebRequestConditionAttributeResponseHeaders::MatchTest> |
| +WebRequestConditionAttributeResponseHeaders::CreateMatchTest( |
| + const Value* content, bool is_name_test, MatchType match_type) { |
| + std::string str; |
| + |
| + CHECK(content->GetAsString(&str)); |
| + if (is_name_test) // Header names are case-insensitive. |
| + StringToLowerASCII(&str); |
| + |
| + return scoped_ptr<const MatchTest>( |
| + new MatchTest(str, match_type)); |
|
battre
2012/08/27 17:16:25
nit: single line
vabr (Chromium)
2012/08/28 17:59:09
Does not fit any more after renaming MatchTest to
|
| +} |
| + |
| } // namespace extensions |