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..bd95cec8622b1aeb0ca6b282e42c92982ca3c013 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,236 @@ WebRequestConditionAttributeContentType::GetType() const { |
return CONDITION_CONTENT_TYPE; |
} |
+// |
+// WebRequestConditionAttributeResponseHeaders |
+// |
+ |
+WebRequestConditionAttributeResponseHeaders::StringMatchTest::StringMatchTest( |
+ const std::string& data, |
+ MatchType type) |
+ : data_(data), |
+ type_(type) {} |
+ |
+WebRequestConditionAttributeResponseHeaders::StringMatchTest::~StringMatchTest() |
+{} |
+ |
+WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::HeaderMatchTest( |
+ ScopedVector<const StringMatchTest>* name, |
+ ScopedVector<const StringMatchTest>* value) |
+ : name_(name->Pass()), |
+ value_(value->Pass()) {} |
+ |
+WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::~HeaderMatchTest() |
+{} |
+ |
+WebRequestConditionAttributeResponseHeaders:: |
+WebRequestConditionAttributeResponseHeaders( |
+ bool positive_test, ScopedVector<const HeaderMatchTest>* 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 HeaderMatchTest> header_tests; |
+ 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 HeaderMatchTest> header_test(CreateTests(tests, error)); |
+ if (header_test.get() == NULL) |
+ return scoped_ptr<WebRequestConditionAttribute>(NULL); |
+ header_tests.push_back(header_test.release()); |
+ } |
+ |
+ scoped_ptr<WebRequestConditionAttributeResponseHeaders> result; |
+ result.reset(new WebRequestConditionAttributeResponseHeaders( |
+ name == keys::kResponseHeadersKey, &header_tests)); |
+ |
+ 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 header test? |
+ bool header_found = false; |
+ |
+ for (size_t i = 0; !header_found && i < tests_.size(); ++i) { |
+ std::string name; |
+ std::string value; |
+ |
+ void* iter = NULL; |
+ while (!header_found && |
+ headers->EnumerateHeaderLines(&iter, &name, &value)) { |
+ 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::StringMatchTest::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::HeaderMatchTest::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::HeaderMatchTest> |
+WebRequestConditionAttributeResponseHeaders::CreateTests( |
+ const DictionaryValue* tests, |
+ std::string* error) { |
+ ScopedVector<const StringMatchTest> name; |
+ ScopedVector<const StringMatchTest> 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 HeaderMatchTest>(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 StringMatchTest>* tests = is_name ? &name : &value; |
+ tests->push_back(CreateMatchTest(*it, is_name, match_type).release()); |
+ } |
+ break; |
+ } |
+ case Value::TYPE_STRING: { |
+ ScopedVector<const StringMatchTest>* 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 HeaderMatchTest>(NULL); |
+ } |
+ } |
+ } |
+ |
+ return scoped_ptr<const HeaderMatchTest>(new HeaderMatchTest(&name, &value)); |
+} |
+ |
+// static |
+scoped_ptr<const WebRequestConditionAttributeResponseHeaders::StringMatchTest> |
+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 StringMatchTest>( |
+ new StringMatchTest(str, match_type)); |
+} |
+ |
} // namespace extensions |