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

Unified Diff: chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc

Issue 10874029: Adding condition attributes for response headers to Declarative WebRequest (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Some typos corrected Created 8 years, 4 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/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..c4170c131543fd6d33991185818efce7900e2a65 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc
@@ -5,8 +5,11 @@
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h"
#include <algorithm>
+#include <set>
+#include <string.h>
#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 +21,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 +51,9 @@ bool WebRequestConditionAttribute::IsKnownType(
const std::string& instance_type) {
return
WebRequestConditionAttributeResourceType::IsMatchingType(instance_type) ||
- WebRequestConditionAttributeContentType::IsMatchingType(instance_type);
+ WebRequestConditionAttributeContentType::IsMatchingType(instance_type) ||
+ WebRequestConditionAttributeContainsHeaders::IsMatchingType(
+ instance_type);
}
// static
@@ -52,10 +62,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 (WebRequestConditionAttributeContainsHeaders::IsMatchingType(
+ name)) {
+ return WebRequestConditionAttributeContainsHeaders::Create(
+ name, value, error);
}
*error = ExtensionErrorUtils::FormatErrorMessage(kUnknownConditionAttribute,
@@ -219,4 +234,255 @@ WebRequestConditionAttributeContentType::GetType() const {
return CONDITION_CONTENT_TYPE;
}
+//
+// WebRequestConditionAttributeContainsHeaders
+//
+
+WebRequestConditionAttributeContainsHeaders::TestGroup::TestGroup() {}
+WebRequestConditionAttributeContainsHeaders::TestGroup::~TestGroup() {}
+
+WebRequestConditionAttributeContainsHeaders::
+WebRequestConditionAttributeContainsHeaders(bool positive_test)
+ : positive_test_(positive_test) {}
+
+WebRequestConditionAttributeContainsHeaders::
+~WebRequestConditionAttributeContainsHeaders() {}
+
+// static
+bool WebRequestConditionAttributeContainsHeaders::IsMatchingType(
+ const std::string& instance_type) {
+ return instance_type == keys::kContainsHeadersKey ||
+ instance_type == keys::kDoesNotContainHeadersKey;
+}
+
+// static
+scoped_ptr<WebRequestConditionAttribute>
+WebRequestConditionAttributeContainsHeaders::Create(
+ const std::string& name,
+ const base::Value* value,
+ std::string* error) {
+ DCHECK(IsMatchingType(name));
+
+ enum { kList, kDictionary, kNone } obtained_value = kNone;
+
+ const DictionaryValue* value_as_dictionary = NULL;
+ const ListValue* value_as_list = NULL;
+ if (value->GetAsList(&value_as_list))
+ obtained_value = kList;
+ else if (value->GetAsDictionary(&value_as_dictionary))
+ obtained_value = kDictionary;
+
+ scoped_ptr<WebRequestConditionAttributeContainsHeaders> result;
+ if (obtained_value != kNone) {
+ // Only construct the condition after we know we have a valid |value|.
+ result.reset(new WebRequestConditionAttributeContainsHeaders(
+ name == keys::kContainsHeadersKey));
+ }
+ switch (obtained_value) {
+ case kList:
+ 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);
+ }
+
+ if (!result->PushMatchTests(tests, error)) {
+ return scoped_ptr<WebRequestConditionAttribute>(NULL);
+ }
+ }
+ break;
+ case kDictionary:
+ if (!result->PushMatchTests(value_as_dictionary, error)) {
+ return scoped_ptr<WebRequestConditionAttribute>(NULL);
+ }
battre 2012/08/23 12:32:11 nit: no {}, also in 291
vabr (Chromium) 2012/08/24 14:48:59 Done, and subsequently deleted altogether. Case kD
+ break;
+ case kNone:
+ *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name);
+ return scoped_ptr<WebRequestConditionAttribute>(NULL);
+ }
+
+
+ return result.PassAs<WebRequestConditionAttribute>();
+}
+
+int WebRequestConditionAttributeContainsHeaders::GetStages() const {
+ return ON_HEADERS_RECEIVED;
+}
+
+bool WebRequestConditionAttributeContainsHeaders::IsFulfilled(
+ const WebRequestRule::RequestData& request_data) {
+ if (!(request_data.stage & GetStages()))
+ return false;
+
+ const net::HttpResponseHeaders*
+ headers = request_data.original_response_headers;
battre 2012/08/23 12:32:11 "headers =" goes into previous line.
vabr (Chromium) 2012/08/24 14:48:59 Done.
+ 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_;
+ }
+
+ // |success| == for some |i|, has there been a name-value header pair
+ // satisfying all tests from |tests_[i]|?
+ bool success = false;
+
+ // First we get all header names. Unfortunately, this is unnecessarily
+ // costly, because the values are copied along anyway. But we need to know
+ // the header names so that we can call EnumerateHeader instead of
+ // EnumerateHeaderLines.
+ // TODO(vabr) -- check whether you could implement this header enumeration
+ // directly in HttpResponseHeaders instead.
+ std::set<std::string> header_names;
+ std::string name;
+ std::string value;
+ for (void* iter = NULL;
+ headers->EnumerateHeaderLines(&iter, &name, &value);
+ /* No increment here, this is done inside EnumerateHeaderLines. */) {
+ StringToLowerASCII(&name); // Header names are case-insensitive.
+ header_names.insert(name);
+ }
+
+ for (std::set<std::string>::const_iterator it = header_names.begin();
+ !success && it != header_names.end();
+ ++it) {
+ for (size_t i = 0; !success && i < tests_.size(); ++i) {
battre 2012/08/23 12:32:11 How about moving this loop to become the outer loo
vabr (Chromium) 2012/08/24 14:48:59 Done.
+ // |header_success| == have all tests for this header passed so far?
+ bool header_success = true;
+ for (size_t j = 0; header_success && j < tests_[i].name.size(); ++j) {
+ header_success &= tests_[i].name[j].Matches(*it);
+ }
+ if (!header_success)
+ continue;
+
+ // Is there a value of this header such that all tests pass for it?
+ bool found_value = false;
+ for (void* iter = NULL;
+ !found_value && headers->EnumerateHeader(&iter, *it, &value);
+ /* No increment here, done inside EnumerateHeader. */) {
+ // |value_success| == have all tests for this value passed so far?
+ bool value_success = true;
+ for (size_t j = 0; value_success && j < tests_[i].value.size(); ++j) {
+ value_success &= tests_[i].value[j].Matches(value);
+ }
+ found_value |= value_success;
+ }
+
+ // Here we already know that header_success == true, just check values.
+ success |= found_value;
+ }
+ }
+
+ return (positive_test_ ? success : !success);
+}
+
+WebRequestConditionAttribute::Type
+WebRequestConditionAttributeContainsHeaders::GetType() const {
+ return CONDITION_CONTAINS_HEADERS;
+}
+
+bool WebRequestConditionAttributeContainsHeaders::MatchTest::Matches(
+ const std::string& str) {
+ switch (type_) {
+ case kPrefix: {
battre 2012/08/23 12:32:11 You can use StartsWithASCII and EndsWith for these
vabr (Chromium) 2012/08/24 14:48:59 Done. Thanks for making me aware of those function
+ if (data_.size() > str.size())
+ return false;
+ return strncmp(data_.data(), str.data(), data_.size()) == 0;
+ }
+ case kSuffix: {
+ if (data_.size() > str.size())
+ return false;
+ const char* start = str.data() + str.size() - data_.size();
+ return strncmp(data_.data(), start, data_.size()) == 0;
+ }
+ case kEqual: {
+ 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 WebRequestConditionAttributeContainsHeaders::PushMatchTests(
+ const DictionaryValue* tests, std::string* error) {
battre 2012/08/23 12:32:11 How about a functional style of programming? I.e.
vabr (Chromium) 2012/08/24 14:48:59 Done.
+ tests_.push_back(TestGroup());
+
+ 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 = kEqual;
+ } 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 = kEqual;
+ } else {
+ NOTREACHED(); // JSON schema type checking should prevent this.
+ *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key);
+ return false;
+ }
+ const Value* content;
battre 2012/08/23 12:32:11 nit: = NULL
vabr (Chromium) 2012/08/24 14:48:59 Done.
+ tests->Get(*key, &content); // We already checked that |key| is there.
battre 2012/08/23 12:32:11 CHECK?
vabr (Chromium) 2012/08/24 14:48:59 Done.
+ 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) {
+ PushOneMatchTest(*it, is_name, match_type);
+ }
+ break;
+ }
+ case Value::TYPE_STRING: {
+ PushOneMatchTest(content, is_name, match_type);
+ break;
+ }
+ default: {
+ NOTREACHED(); // JSON schema type checking should prevent this.
+ *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void WebRequestConditionAttributeContainsHeaders::PushOneMatchTest(
+ const Value* content, bool is_name_test, MatchType match_type) {
+ std::string* data = NULL;
+ if (is_name_test) {
+ std::vector<MatchTest>* current_name_tests = &(tests_.back().name);
+ current_name_tests->push_back(MatchTest(match_type));
+ data = &(current_name_tests->back().data());
+ } else {
+ std::vector<MatchTest>* current_value_tests = &(tests_.back().value);
+ current_value_tests->push_back(MatchTest(match_type));
+ data = &(current_value_tests->back().data());
+ }
+ CHECK(content->GetAsString(data));
+ if (is_name_test) // Header names are case-insensitive.
+ StringToLowerASCII(data);
+}
+
} // namespace extensions

Powered by Google App Engine
This is Rietveld 408576698