summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvabr@chromium.org <vabr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-28 20:20:57 +0000
committervabr@chromium.org <vabr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-08-28 20:20:57 +0000
commitaae817b9e926345eb875b95db223e9897fc4f1dc (patch)
treefed37a0cdb978da17a49d1a4f8c7f459f91ef4e8
parentc51f2c7f2b44efd65df1418902cc5fa5e0a89951 (diff)
downloadchromium_src-aae817b9e926345eb875b95db223e9897fc4f1dc.zip
chromium_src-aae817b9e926345eb875b95db223e9897fc4f1dc.tar.gz
chromium_src-aae817b9e926345eb875b95db223e9897fc4f1dc.tar.bz2
Adding condition attributes for response headers to Declarative WebRequest
BUG=143662,112155 TEST=Install the extension attached to BUG 143662. If this feature is working correctly it should cancel every navigation. TBR=kathyw@chromium.org Review URL: https://chromiumcodereview.appspot.com/10874029 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@153720 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc251
-rw-r--r--chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h96
-rw-r--r--chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc263
-rw-r--r--chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.cc10
-rw-r--r--chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h10
-rw-r--r--chrome/common/extensions/api/declarative_web_request.json65
-rw-r--r--chrome/common/extensions/docs/extensions/declarativeWebRequest.html349
-rw-r--r--chrome/test/data/extensions/api_test/webrequest/declarative/headers.html.mock-http-headers5
-rw-r--r--chrome/test/data/extensions/api_test/webrequest/test_declarative.js4
9 files changed, 1011 insertions, 42 deletions
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 4d825d9..bd95cec 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
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h
index f9aa4e0..8ee69d5 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h
@@ -11,6 +11,7 @@
#include "base/basictypes.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
#include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_rule.h"
#include "chrome/common/extensions/api/events.h"
@@ -32,7 +33,8 @@ class WebRequestConditionAttribute {
public:
enum Type {
CONDITION_RESOURCE_TYPE,
- CONDITION_CONTENT_TYPE
+ CONDITION_CONTENT_TYPE,
+ CONDITION_REQUEST_HEADERS
};
WebRequestConditionAttribute();
@@ -53,7 +55,8 @@ class WebRequestConditionAttribute {
virtual int GetStages() const = 0;
// Returns whether the condition is fulfilled for this request.
- virtual bool IsFulfilled(const WebRequestRule::RequestData& request_data) = 0;
+ virtual bool IsFulfilled(
+ const WebRequestRule::RequestData& request_data) const = 0;
virtual Type GetType() const = 0;
@@ -88,8 +91,8 @@ class WebRequestConditionAttributeResourceType
// Implementation of WebRequestConditionAttribute:
virtual int GetStages() const OVERRIDE;
- virtual bool IsFulfilled(const WebRequestRule::RequestData& request_data)
- OVERRIDE;
+ virtual bool IsFulfilled(
+ const WebRequestRule::RequestData& request_data) const OVERRIDE;
virtual Type GetType() const OVERRIDE;
private:
@@ -118,8 +121,8 @@ class WebRequestConditionAttributeContentType
// Implementation of WebRequestConditionAttribute:
virtual int GetStages() const OVERRIDE;
- virtual bool IsFulfilled(const WebRequestRule::RequestData& request_data)
- OVERRIDE;
+ virtual bool IsFulfilled(
+ const WebRequestRule::RequestData& request_data) const OVERRIDE;
virtual Type GetType() const OVERRIDE;
private:
@@ -133,6 +136,87 @@ class WebRequestConditionAttributeContentType
DISALLOW_COPY_AND_ASSIGN(WebRequestConditionAttributeContentType);
};
+// Condition that performs matches against response headers' names and values.
+// In the comments below there is a distinction between when this condition is
+// "satisfied" and when it is "fulfilled". See the comments at |tests_| for
+// "satisfied" and the comment at |positive_test_| for "fulfilled".
+class WebRequestConditionAttributeResponseHeaders
+ : public WebRequestConditionAttribute {
+ public:
+ virtual ~WebRequestConditionAttributeResponseHeaders();
+
+ static bool IsMatchingType(const std::string& instance_type);
+
+ // Factory method, see WebRequestConditionAttribute::Create.
+ static scoped_ptr<WebRequestConditionAttribute> Create(
+ const std::string& name,
+ const base::Value* value,
+ std::string* error);
+
+ // Implementation of WebRequestConditionAttribute:
+ virtual int GetStages() const OVERRIDE;
+ virtual bool IsFulfilled(
+ const WebRequestRule::RequestData& request_data) const OVERRIDE;
+ virtual Type GetType() const OVERRIDE;
+
+ private:
+ enum MatchType { kPrefix, kSuffix, kEquals, kContains };
+
+ class StringMatchTest {
+ public:
+ StringMatchTest(const std::string& data, MatchType type);
+ ~StringMatchTest();
+
+ // Does |str| pass |*this| StringMatchTest?
+ bool Matches(const std::string& str) const;
+
+ private:
+ const std::string data_;
+ const MatchType type_;
+ DISALLOW_COPY_AND_ASSIGN(StringMatchTest);
+ };
+
+ class HeaderMatchTest {
+ public:
+ // Takes ownership of the content of both |name| and |value|.
+ HeaderMatchTest(ScopedVector<const StringMatchTest>* name,
+ ScopedVector<const StringMatchTest>* value);
+ ~HeaderMatchTest();
+ // Does the header |name|: |value| match all tests in this header test?
+ bool Matches(const std::string& name, const std::string& value) const;
+
+ private:
+ // Tests to be passed by a header's name.
+ const ScopedVector<const StringMatchTest> name_;
+ // Tests to be passed by a header's value.
+ const ScopedVector<const StringMatchTest> value_;
+ DISALLOW_COPY_AND_ASSIGN(HeaderMatchTest);
+ };
+
+ WebRequestConditionAttributeResponseHeaders(
+ bool positive_test, ScopedVector<const HeaderMatchTest>* tests);
+
+ // Gets the tests' description in |tests| and creates the corresponding
+ // HeaderMatchTest. Returns NULL on failure.
+ static scoped_ptr<const HeaderMatchTest> CreateTests(
+ const base::DictionaryValue* tests,
+ std::string* error);
+ // Helper to CreateTests. Never returns NULL, except for memory failures.
+ static scoped_ptr<const StringMatchTest> CreateMatchTest(const Value* content,
+ bool is_name_test,
+ MatchType match_type);
+
+ // The condition is satisfied if there is a header and its value such that for
+ // some |i| the header passes all the tests from |tests_[i]|.
+ ScopedVector<const HeaderMatchTest> tests_;
+
+ // True means that IsFulfilled() reports whether the condition is satisfied.
+ // False means that it reports whether the condition is NOT satisfied.
+ bool positive_test_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebRequestConditionAttributeResponseHeaders);
+};
+
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_DECLARATIVE_WEBREQUEST_WEBREQUEST_CONDITION_ATTRIBUTE_H_
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc
index c6027aa..d0c79b1 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute_unittest.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h"
+#include "base/basictypes.h"
#include "base/file_path.h"
#include "base/message_loop.h"
#include "base/values.h"
@@ -14,6 +15,11 @@
#include "net/test/test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
+using base::DictionaryValue;
+using base::ListValue;
+using base::StringValue;
+using base::Value;
+
namespace {
const char kUnknownConditionName[] = "unknownType";
} // namespace
@@ -156,4 +162,261 @@ TEST(WebRequestConditionAttributeTest, ContentType) {
url_request.response_headers())));
}
+namespace {
+
+// Builds a vector of vectors of string pointers from an array of strings.
+// |array| is in fact a sequence of arrays. The array |sizes| captures the sizes
+// of all parts of |array|, and |size| is the length of |sizes| itself.
+// Example (this is pseudo-code, not C++):
+// array = { "a", "b", "c", "d", "e", "f" }
+// sizes = { 2, 0, 4 }
+// size = 3
+// results in out == { {&"a", &"b"}, {}, {&"c", &"d", &"e", &"f"} }
+void GetArrayAsVector(const std::string array[],
+ const size_t sizes[],
+ const size_t size,
+ std::vector< std::vector<const std::string*> >* out) {
+ out->clear();
+ size_t next = 0;
+ for (size_t i = 0; i < size; ++i) {
+ out->push_back(std::vector<const std::string*>());
+ for (size_t j = next; j < next + sizes[i]; ++j) {
+ out->back().push_back(&(array[j]));
+ }
+ next += sizes[i];
+ }
+}
+
+// Builds a DictionaryValue from an array of the form {name1, value1, name2,
+// value2, ...}. Values for the same key are grouped in a ListValue.
+scoped_ptr<DictionaryValue> GetDictionaryFromArray(
+ const std::vector<const std::string*>& array) {
+ const size_t length = array.size();
+ CHECK(length % 2 == 0);
+
+ scoped_ptr<DictionaryValue> dictionary(new DictionaryValue);
+ for (size_t i = 0; i < length; i += 2) {
+ const std::string* name = array[i];
+ const std::string* value = array[i+1];
+ if (dictionary->HasKey(*name)) {
+ Value* entry = NULL;
+ ListValue* list = NULL;
+ if (!dictionary->GetWithoutPathExpansion(*name, &entry))
+ return scoped_ptr<DictionaryValue>(NULL);
+ switch (entry->GetType()) {
+ case Value::TYPE_STRING: // Replace the present string with a list.
+ list = new ListValue;
+ // Ignoring return value, we already verified the entry is there.
+ dictionary->RemoveWithoutPathExpansion(*name, &entry);
+ list->Append(entry);
+ list->Append(Value::CreateStringValue(*value));
+ dictionary->SetWithoutPathExpansion(*name, list);
+ break;
+ case Value::TYPE_LIST: // Just append to the list.
+ CHECK(entry->GetAsList(&list));
+ list->Append(Value::CreateStringValue(*value));
+ break;
+ default:
+ NOTREACHED(); // We never put other Values here.
+ return scoped_ptr<DictionaryValue>(NULL);
+ }
+ } else {
+ dictionary->SetString(*name, *value);
+ }
+ }
+ return dictionary.Pass();
+}
+
+// Returns whether the response headers from |url_request| satisfy the match
+// criteria given in |tests|. For at least one |i| all tests from |tests[i]|
+// must pass. If |positive_test| is true, the dictionary is interpreted as the
+// containsHeaders property of a RequestMatcher, otherwise as
+// doesNotContainHeaders.
+void MatchAndCheck(const std::vector< std::vector<const std::string*> >& tests,
+ const std::string& key,
+ net::URLRequest* url_request,
+ bool* result) {
+ ListValue contains_headers;
+ for (size_t i = 0; i < tests.size(); ++i) {
+ scoped_ptr<DictionaryValue> temp(GetDictionaryFromArray(tests[i]));
+ ASSERT_TRUE(temp.get() != NULL);
+ contains_headers.Append(temp.release());
+ }
+
+ std::string error;
+ scoped_ptr<WebRequestConditionAttribute> attribute =
+ WebRequestConditionAttribute::Create(key, &contains_headers, &error);
+ ASSERT_EQ("", error);
+ ASSERT_TRUE(attribute.get() != NULL);
+
+ *result = attribute->IsFulfilled(WebRequestRule::RequestData(
+ url_request, ON_HEADERS_RECEIVED, url_request->response_headers()));
+}
+
+} // namespace
+
+// Here we test WebRequestConditionAttributeResponseHeaders for:
+// 1. Correct implementation of prefix/suffix/contains/equals matching.
+// 2. Performing logical disjunction (||) between multiple specifications.
+// 3. Negating the match in case of 'doesNotContainHeaders'.
+TEST(WebRequestConditionAttributeTest, Headers) {
+ // Necessary for TestURLRequest.
+ MessageLoop message_loop(MessageLoop::TYPE_IO);
+
+ net::TestServer test_server(
+ net::TestServer::TYPE_HTTP,
+ net::TestServer::kLocalhost,
+ FilePath(FILE_PATH_LITERAL(
+ "chrome/test/data/extensions/api_test/webrequest/declarative")));
+ ASSERT_TRUE(test_server.Start());
+
+ TestURLRequestContext context;
+ TestDelegate delegate;
+ TestURLRequest url_request(test_server.GetURL("files/headers.html"),
+ &delegate, &context);
+ url_request.Start();
+ MessageLoop::current()->Run();
+
+ // In all the tests below we assume that the server includes the headers
+ // Custom-Header: custom/value
+ // Custom-Header-B: valueA
+ // Custom-Header-B: valueB
+ // Custom-Header-C: valueC, valueD
+ // Custom-Header-D:
+ // in the response, but does not include "Non-existing: void".
+
+ std::vector< std::vector<const std::string*> > tests;
+ bool result;
+
+ // 1.a. -- All these tests should pass.
+ const std::string kPassingCondition[] = {
+ keys::kNamePrefixKey, "Custom",
+ keys::kNameSuffixKey, "m-header", // Header names are case insensitive.
+ keys::kValueContainsKey, "alu",
+ keys::kValueEqualsKey, "custom/value"
+ };
+ const size_t kPassingConditionSizes[] = { arraysize(kPassingCondition) };
+ GetArrayAsVector(kPassingCondition, kPassingConditionSizes, 1u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_TRUE(result);
+
+ // 1.b. -- None of the following tests in the discjunction should pass.
+ const std::string kFailCondition[] = {
+ keys::kNamePrefixKey, " Custom", // Test 1.
+ keys::kNameContainsKey, " -", // Test 2.
+ keys::kValueSuffixKey, "alu", // Test 3.
+ keys::kValueEqualsKey, "custom" // Test 4.
+ };
+ const size_t kFailConditionSizes[] = { 2u, 2u, 2u, 2u };
+ GetArrayAsVector(kFailCondition, kFailConditionSizes, 4u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_FALSE(result);
+
+ // 1.c. -- This should fail (mixing name and value from different headers)
+ const std::string kMixingCondition[] = {
+ keys::kNameSuffixKey, "Header-B",
+ keys::kValueEqualsKey, "custom/value"
+ };
+ const size_t kMixingConditionSizes[] = { arraysize(kMixingCondition) };
+ GetArrayAsVector(kMixingCondition, kMixingConditionSizes, 1u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_FALSE(result);
+
+ // 1.d. -- Test handling multiple values for one header (both should pass).
+ const std::string kMoreValues1[] = {
+ keys::kNameEqualsKey, "Custom-header-b",
+ keys::kValueEqualsKey, "valueA"
+ };
+ const size_t kMoreValues1Sizes[] = { arraysize(kMoreValues1) };
+ GetArrayAsVector(kMoreValues1, kMoreValues1Sizes, 1u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_TRUE(result);
+ const std::string kMoreValues2[] = {
+ keys::kNameEqualsKey, "Custom-header-b",
+ keys::kValueEqualsKey, "valueB"
+ };
+ const size_t kMoreValues2Sizes[] = { arraysize(kMoreValues2) };
+ GetArrayAsVector(kMoreValues2, kMoreValues2Sizes, 1u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_TRUE(result);
+
+ // 1.e. -- This should fail as conjunction but pass as disjunction.
+ const std::string kConflict[] = {
+ keys::kNameSuffixKey, "Header", // True for some header.
+ keys::kNameContainsKey, "Header-B" // True for a different header.
+ };
+ // First disjunction, no conflict.
+ const size_t kNoConflictSizes[] = { 2u, 2u };
+ GetArrayAsVector(kConflict, kNoConflictSizes, 2u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_TRUE(result);
+ // Then conjunction, conflict.
+ const size_t kConflictSizes[] = { arraysize(kConflict) };
+ GetArrayAsVector(kConflict, kConflictSizes, 1u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_FALSE(result);
+
+ // 1.f. -- This should pass, checking for correct treatment of ',' in values.
+ const std::string kComma[] = {
+ keys::kNameSuffixKey, "Header-C",
+ keys::kValueEqualsKey, "valueC, valueD"
+ };
+ const size_t kCommaSizes[] = { arraysize(kComma) };
+ GetArrayAsVector(kComma, kCommaSizes, 1u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_TRUE(result);
+
+ // 1.g. -- This should pass, empty values are values as well.
+ const std::string kEmpty[] = {
+ keys::kNameEqualsKey, "custom-header-d",
+ keys::kValueEqualsKey, ""
+ };
+ const size_t kEmptySizes[] = { arraysize(kEmpty) };
+ GetArrayAsVector(kEmpty, kEmptySizes, 1u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_TRUE(result);
+
+ // 1.h. -- Values are case-sensitive, this should fail
+ const std::string kLowercase[] = {
+ keys::kNameEqualsKey, "Custom-header-b",
+ keys::kValueEqualsKey, "valuea" // valuea != valueA
+ };
+ const size_t kLowercaseSizes[] = { arraysize(kLowercase) };
+ GetArrayAsVector(kLowercase, kLowercaseSizes, 1u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_FALSE(result);
+
+ // 2.a. -- This should pass as disjunction, because one of the tests passes.
+ const std::string kDisjunction[] = {
+ keys::kNamePrefixKey, "Non-existing", // This one fails.
+ keys::kNameSuffixKey, "Non-existing", // This one fails.
+ keys::kValueEqualsKey, "void", // This one fails.
+ keys::kValueContainsKey, "alu" // This passes.
+ };
+ const size_t kDisjunctionSizes[] = { 2u, 2u, 2u, 2u };
+ GetArrayAsVector(kDisjunction, kDisjunctionSizes, 4u, &tests);
+ MatchAndCheck(tests, keys::kResponseHeadersKey, &url_request, &result);
+ EXPECT_TRUE(result);
+
+ // 3.a. -- This should pass.
+ const std::string kNonExistent[] = {
+ keys::kNameEqualsKey, "Non-existing",
+ keys::kValueEqualsKey, "void"
+ };
+ const size_t kNonExistentSizes[] = { arraysize(kNonExistent) };
+ GetArrayAsVector(kNonExistent, kNonExistentSizes, 1u, &tests);
+ MatchAndCheck(tests, keys::kExcludeResponseHeadersKey, &url_request, &result);
+ EXPECT_TRUE(result);
+
+ // 3.b. -- This should fail.
+ const std::string kExisting[] = {
+ keys::kNameEqualsKey, "custom-header-b",
+ keys::kValueEqualsKey, "valueB"
+ };
+ const size_t kExistingSize[] = { arraysize(kExisting) };
+ GetArrayAsVector(kExisting, kExistingSize, 1u, &tests);
+ MatchAndCheck(tests, keys::kExcludeResponseHeadersKey, &url_request, &result);
+ EXPECT_FALSE(result);
+}
+
} // namespace extensions
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.cc
index 852fed4..c204807 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.cc
@@ -32,6 +32,16 @@ const char kSecureKey[] = "secure";
const char kToKey[] = "to";
const char kUrlKey[] = "url";
const char kValueKey[] = "value";
+const char kResponseHeadersKey[] = "responseHeaders";
+const char kExcludeResponseHeadersKey[] = "excludeResponseHeaders";
+const char kNamePrefixKey[] = "namePrefix";
+const char kNameSuffixKey[] = "nameSuffix";
+const char kNameContainsKey[] = "nameContains";
+const char kNameEqualsKey[] = "nameEquals";
+const char kValuePrefixKey[] = "valuePrefix";
+const char kValueSuffixKey[] = "valueSuffix";
+const char kValueContainsKey[] = "valueContains";
+const char kValueEqualsKey[] = "valueEquals";
// Values of dictionaries, in particular instance types
const char kAddRequestCookieType[] = "declarativeWebRequest.AddRequestCookie";
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h b/chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h
index 7eac594..77af30d 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h
@@ -35,6 +35,16 @@ extern const char kSecureKey[];
extern const char kToKey[];
extern const char kUrlKey[];
extern const char kValueKey[];
+extern const char kResponseHeadersKey[];
+extern const char kExcludeResponseHeadersKey[];
+extern const char kNamePrefixKey[];
+extern const char kNameSuffixKey[];
+extern const char kNameContainsKey[];
+extern const char kNameEqualsKey[];
+extern const char kValuePrefixKey[];
+extern const char kValueSuffixKey[];
+extern const char kValueContainsKey[];
+extern const char kValueEqualsKey[];
// Values of dictionaries, in particular instance types
extern const char kAddRequestCookieType[];
diff --git a/chrome/common/extensions/api/declarative_web_request.json b/chrome/common/extensions/api/declarative_web_request.json
index 3a8f715..5d5943b 100644
--- a/chrome/common/extensions/api/declarative_web_request.json
+++ b/chrome/common/extensions/api/declarative_web_request.json
@@ -8,6 +8,59 @@
"documentation_permissions_required": ["declarative", "declarativeWebRequest"],
"types": [
{
+ "id": "HeaderFilter",
+ "type": "object",
+ "description": "Filters request headers for various criteria.",
+ "properties": {
+ "namePrefix": {
+ "description" : "Matches if the header name starts with the specified string.",
+ "type": "string",
+ "optional": true
+ },
+ "nameSuffix": {
+ "type": "string",
+ "optional": true,
+ "description" : "Matches if the header name ends with the specified string."
+ },
+ "nameContains": {
+ "choices": [
+ {"type": "array", "items": {"type": "string"}},
+ {"type": "string"}
+ ],
+ "optional": true,
+ "description" : "Matches if the header name contains all of the specified strings."
+ },
+ "nameEquals": {
+ "type": "string",
+ "optional": true,
+ "description" : "Matches if the header name is equal to the specified string."
+ },
+ "valuePrefix": {
+ "type": "string",
+ "optional": true,
+ "description" : "Matches if the header value starts with the specified string."
+ },
+ "valueSuffix": {
+ "type": "string",
+ "optional": true,
+ "description" : "Matches if the header value ends with the specified string."
+ },
+ "valueContains": {
+ "choices": [
+ {"type": "array", "items": {"type": "string"}},
+ {"type": "string"}
+ ],
+ "optional": true,
+ "description" : "Matches if the header value contains all of the specified strings."
+ },
+ "valueEquals": {
+ "type": "string",
+ "optional": true,
+ "description" : "Matches if the header value is equal to the specified string."
+ }
+ }
+ },
+ {
"id": "RequestMatcher",
"type": "object",
"description": "Matches network events by various criteria.",
@@ -35,6 +88,18 @@
"description": "Matches if the MIME media type of a response (from the HTTP Content-Type header) is <em>not</em> contained in the list.",
"items": { "type": "string" }
},
+ "responseHeaders": {
+ "type": "array",
+ "optional": true,
+ "description": "Matches if some of the response headers is matched by one of the HeaderFilters.",
+ "items": { "$ref": "HeaderFilter" }
+ },
+ "excludeResponseHeaders": {
+ "type": "array",
+ "optional": true,
+ "description": "Matches if none of the response headers is matched by one of the HeaderFilters.",
+ "items": { "$ref": "HeaderFilter" }
+ },
"instanceType": {
"type": "string", "enum": ["declarativeWebRequest.RequestMatcher"],
"nodoc": true
diff --git a/chrome/common/extensions/docs/extensions/declarativeWebRequest.html b/chrome/common/extensions/docs/extensions/declarativeWebRequest.html
index 5dc39c5..a93d624 100644
--- a/chrome/common/extensions/docs/extensions/declarativeWebRequest.html
+++ b/chrome/common/extensions/docs/extensions/declarativeWebRequest.html
@@ -230,7 +230,11 @@
<ol>
</ol>
</li><li>
- <a href="#type-declarativeWebRequest.RequestMatcher">RequestMatcher</a>
+ <a href="#type-declarativeWebRequest.HeaderFilter">HeaderFilter</a>
+ <ol>
+ </ol>
+ </li><li>
+ <a href="#type-declarativeWebRequest.CancelRequest">CancelRequest</a>
<ol>
</ol>
</li><li>
@@ -262,7 +266,7 @@
<ol>
</ol>
</li><li>
- <a href="#type-declarativeWebRequest.CancelRequest">CancelRequest</a>
+ <a href="#type-declarativeWebRequest.RequestMatcher">RequestMatcher</a>
<ol>
</ol>
</li><li>
@@ -698,8 +702,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
- <a name="type-declarativeWebRequest.RequestMatcher"></a>
- <h4>declarativeWebRequest.RequestMatcher</h4>
+ <a name="type-declarativeWebRequest.HeaderFilter"></a>
+ <h4>declarativeWebRequest.HeaderFilter</h4>
<div>
<dt>
<em>
@@ -715,14 +719,14 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div>
</em>
</dt>
- <dd>Matches network events by various criteria.</dd>
+ <dd>Filters request headers for various criteria.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
<div>
<div>
<dt>
- <var>url</var>
+ <var>namePrefix</var>
<em>
<!-- TYPE -->
<div style="display:inline">
@@ -730,14 +734,14 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
- <a href="events.html#type-events.UrlFilter">events.UrlFilter</a>
+ <span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
- <dd>Matches if the condition of the UrlFilter are fulfilled for the URL of the request.</dd>
+ <dd>Matches if the header name starts with the specified string.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
@@ -746,7 +750,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div><div>
<div>
<dt>
- <var>resourceType</var>
+ <var>nameSuffix</var>
<em>
<!-- TYPE -->
<div style="display:inline">
@@ -754,21 +758,38 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
- <span>
- array of <span><span>
- <span>
<span>string</span>
- <span>["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other"]</span>
</span>
- </span></span>
- </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if the header name ends with the specified string.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>nameContains</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <span>array of string or string</span>
</span>
</span>
)
</div>
</em>
</dt>
- <dd>Matches if the request type of a request is contained in the list. Requests that cannot match any of the types will be filtered out.</dd>
+ <dd>Matches if the header name contains all of the specified strings.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
@@ -777,7 +798,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div><div>
<div>
<dt>
- <var>contentType</var>
+ <var>nameEquals</var>
<em>
<!-- TYPE -->
<div style="display:inline">
@@ -785,20 +806,62 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
- <span>
- array of <span><span>
+ <span>string</span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if the header name is equal to the specified string.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>valuePrefix</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
<span>
<span>string</span>
</span>
- </span></span>
- </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if the header value starts with the specified string.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>valueSuffix</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <span>string</span>
</span>
</span>
)
</div>
</em>
</dt>
- <dd>Matches if the MIME media type of a response (from the HTTP Content-Type header) is contained in the list.</dd>
+ <dd>Matches if the header value ends with the specified string.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
@@ -807,7 +870,7 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div><div>
<div>
<dt>
- <var>excludeContentType</var>
+ <var>valueContains</var>
<em>
<!-- TYPE -->
<div style="display:inline">
@@ -815,20 +878,38 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<span class="optional">optional</span>
<span id="typeTemplate">
<span>
- <span>
- array of <span><span>
+ <span>array of string or string</span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if the header value contains all of the specified strings.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>valueEquals</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
<span>
<span>string</span>
</span>
- </span></span>
- </span>
- </span>
</span>
)
</div>
</em>
</dt>
- <dd>Matches if the MIME media type of a response (from the HTTP Content-Type header) is <em>not</em> contained in the list.</dd>
+ <dd>Matches if the header value is equal to the specified string.</dd>
<!-- OBJECT PROPERTIES -->
<!-- OBJECT METHODS -->
<!-- OBJECT EVENT FIELDS -->
@@ -842,6 +923,34 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
+ <a name="type-declarativeWebRequest.CancelRequest"></a>
+ <h4>declarativeWebRequest.CancelRequest</h4>
+ <div>
+ <dt>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span id="typeTemplate">
+ <span>
+ <span>object</span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Declarative event action that cancels a network request.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <dd>
+ <dl>
+ </dl>
+ </dd>
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div class="apiItem">
<a name="type-declarativeWebRequest.RedirectRequest"></a>
<h4>declarativeWebRequest.RedirectRequest</h4>
<div>
@@ -1227,8 +1336,8 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
<!-- FUNCTION PARAMETERS -->
</div>
</div><div class="apiItem">
- <a name="type-declarativeWebRequest.CancelRequest"></a>
- <h4>declarativeWebRequest.CancelRequest</h4>
+ <a name="type-declarativeWebRequest.RequestMatcher"></a>
+ <h4>declarativeWebRequest.RequestMatcher</h4>
<div>
<dt>
<em>
@@ -1244,10 +1353,186 @@ very fast URL matching algorithm for hundreds of thousands of URLs.
</div>
</em>
</dt>
- <dd>Declarative event action that cancels a network request.</dd>
+ <dd>Matches network events by various criteria.</dd>
<!-- OBJECT PROPERTIES -->
<dd>
<dl>
+ <div>
+ <div>
+ <dt>
+ <var>url</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <a href="events.html#type-events.UrlFilter">events.UrlFilter</a>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if the condition of the UrlFilter are fulfilled for the URL of the request.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>resourceType</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <span>
+ array of <span><span>
+ <span>
+ <span>string</span>
+ <span>["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other"]</span>
+ </span>
+ </span></span>
+ </span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if the request type of a request is contained in the list. Requests that cannot match any of the types will be filtered out.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>contentType</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <span>
+ array of <span><span>
+ <span>
+ <span>string</span>
+ </span>
+ </span></span>
+ </span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if the MIME media type of a response (from the HTTP Content-Type header) is contained in the list.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>excludeContentType</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <span>
+ array of <span><span>
+ <span>
+ <span>string</span>
+ </span>
+ </span></span>
+ </span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if the MIME media type of a response (from the HTTP Content-Type header) is <em>not</em> contained in the list.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>responseHeaders</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <span>
+ array of <span><span>
+ <span>
+ <a href="declarativeWebRequest.html#type-declarativeWebRequest.HeaderFilter">declarativeWebRequest.HeaderFilter</a>
+ </span>
+ </span></span>
+ </span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if some of the response headers is matched by one of the HeaderFilters.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>excludeResponseHeaders</var>
+ <em>
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span>
+ <span>
+ array of <span><span>
+ <span>
+ <a href="declarativeWebRequest.html#type-declarativeWebRequest.HeaderFilter">declarativeWebRequest.HeaderFilter</a>
+ </span>
+ </span></span>
+ </span>
+ </span>
+ </span>
+ )
+ </div>
+ </em>
+ </dt>
+ <dd>Matches if none of the response headers is matched by one of the HeaderFilters.</dd>
+ <!-- OBJECT PROPERTIES -->
+ <!-- OBJECT METHODS -->
+ <!-- OBJECT EVENT FIELDS -->
+ <!-- FUNCTION PARAMETERS -->
+ </div>
+ </div>
</dl>
</dd>
<!-- OBJECT METHODS -->
diff --git a/chrome/test/data/extensions/api_test/webrequest/declarative/headers.html.mock-http-headers b/chrome/test/data/extensions/api_test/webrequest/declarative/headers.html.mock-http-headers
index 2e9ea99..76c436b 100644
--- a/chrome/test/data/extensions/api_test/webrequest/declarative/headers.html.mock-http-headers
+++ b/chrome/test/data/extensions/api_test/webrequest/declarative/headers.html.mock-http-headers
@@ -1,2 +1,7 @@
HTTP/1.1 200 OK
Content-Type: text/plain; UTF-8
+Custom-Header: custom/value
+Custom-Header-B: valueA
+Custom-Header-B: valueB
+Custom-Header-C: valueC, valueD
+Custom-Header-D:
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_declarative.js b/chrome/test/data/extensions/api_test/webrequest/test_declarative.js
index 3759f7b..adbc139 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_declarative.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_declarative.js
@@ -105,7 +105,9 @@ runTests([
},
'resourceType': ["main_frame"],
'contentType': ["text/plain"],
- 'excludeContentType': ["image/png"]})],
+ 'excludeContentType': ["image/png"],
+ 'responseHeaders': [{ nameContains: ["content", "type"] } ],
+ 'excludeResponseHeaders': [{ valueContains: "nonsense" }] })],
'actions': [new CancelRequest()]}
],
function() {navigateAndWait(getURLHttpWithHeaders());}