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 a0ca0d986ecede3a99c906d0f65bb4f9047af90c..3c686164c69d8753a846bda27318aa61e6d7c3ba 100644 |
| --- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc |
| +++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc |
| @@ -232,160 +232,128 @@ 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; |
| -} |
| +// Manages a set of tests to be applied to name-value pairs representing |
| +// headers. This is a helper class to header-related condition attributes. |
| +// It contains a set of test groups. A name-value pair satisfies the whole |
| +// set of test groups iff it passes at least one test group. |
| +class TestManager { |
| + public: |
| + ~TestManager(); |
| + |
| + // Creates an instance based on a list |tests| of test groups, encoded as |
| + // dictionaries of the type declarativeWebRequest.HeaderFilter (see |
| + // declarative_web_request.json). |
| + static scoped_ptr<const TestManager> Create(const base::ListValue* tests); |
| + |
| + bool TestNameValue(const std::string& name, const std::string& value) const; |
| + |
| + private: |
| + // Represents a single string-matching test. |
| + class StringMatchTest { |
| + public: |
| + enum MatchType { kPrefix, kSuffix, kEquals, kContains }; |
| + |
| + StringMatchTest(const std::string& data, MatchType type); |
| + ~StringMatchTest(); |
| + |
| + // Takes a string to be matched, as a StringValue in |content|, an |
| + // indication |is_name_test| of whether the test will be used for matching |
| + // against header values or names, and a |match_type|. Never returns NULL, |
| + // except for memory failures. |
| + static scoped_ptr<const StringMatchTest> Create(const Value* content, |
| + bool is_name_test, |
| + MatchType match_type); |
| + |
| + // 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); |
| + }; |
| + |
| + // Represents a test group -- a set of string matching tests to be applied to |
| + // both the header name and value. |
| + class HeaderMatchTest { |
| + public: |
| + // Takes ownership of the content of both |name| and |value|. |
| + HeaderMatchTest(ScopedVector<const StringMatchTest>* name, |
| + ScopedVector<const StringMatchTest>* value); |
| + ~HeaderMatchTest(); |
| + |
| + // Gets the test group description in |tests| and creates the corresponding |
| + // HeaderMatchTest. On failure returns NULL. |
| + static scoped_ptr<const HeaderMatchTest> Create( |
| + const base::DictionaryValue* tests); |
| + |
| + // 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); |
| + }; |
| + |
| + explicit TestManager(ScopedVector<const HeaderMatchTest>* tests); |
| + |
| + const ScopedVector<const HeaderMatchTest> tests_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestManager); |
| +}; |
| + |
| +// TestManager implementation |
| +TestManager::~TestManager() {} |
| // 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); |
| - } |
| - |
| +scoped_ptr<const TestManager> TestManager::Create( |
| + const base::ListValue* tests) { |
| ScopedVector<const HeaderMatchTest> header_tests; |
| - for (ListValue::const_iterator it = value_as_list->begin(); |
| - it != value_as_list->end(); ++it) { |
| + for (ListValue::const_iterator it = tests->begin(); |
| + it != tests->end(); ++it) { |
| const DictionaryValue* tests = NULL; |
| - if (!(*it)->GetAsDictionary(&tests)) { |
| - *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); |
| - return scoped_ptr<WebRequestConditionAttribute>(NULL); |
| - } |
| + if (!(*it)->GetAsDictionary(&tests)) |
| + return scoped_ptr<const TestManager>(NULL); |
| scoped_ptr<const HeaderMatchTest> header_test( |
| - CreateHeaderMatchTest(tests, error)); |
| + HeaderMatchTest::Create(tests)); |
| if (header_test.get() == NULL) |
| - return scoped_ptr<WebRequestConditionAttribute>(NULL); |
| + return scoped_ptr<const TestManager>(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; |
| + return scoped_ptr<const TestManager>( |
| + new TestManager(&header_tests)); |
| } |
| -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_RESPONSE_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; |
| +bool TestManager::TestNameValue(const std::string& name, |
| + const std::string& value) const { |
| + for (size_t i = 0; i < tests_.size(); ++i) { |
| + if (tests_[i]->Matches(name, value)) |
| + return true; |
| } |
| - // 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; |
| - } |
| +TestManager::TestManager(ScopedVector<const HeaderMatchTest>* tests) |
| + : tests_(tests->Pass()) {} |
| - for (size_t i = 0; i < value_.size(); ++i) { |
| - if (!value_[i]->Matches(value)) |
| - return false; |
| - } |
| +// TestManager::HeaderMatchTest implementation. |
| - return true; |
| -} |
| +TestManager::HeaderMatchTest::HeaderMatchTest( |
| + ScopedVector<const StringMatchTest>* name, |
| + ScopedVector<const StringMatchTest>* value) |
| + : name_(name->Pass()), |
| + value_(value->Pass()) {} |
| +TestManager::HeaderMatchTest::~HeaderMatchTest() {} |
| // static |
| -scoped_ptr<const WebRequestConditionAttributeResponseHeaders::HeaderMatchTest> |
| -WebRequestConditionAttributeResponseHeaders::CreateHeaderMatchTest( |
| - const DictionaryValue* tests, |
| - std::string* error) { |
| +scoped_ptr<const TestManager::HeaderMatchTest> |
| +TestManager::HeaderMatchTest::Create(const base::DictionaryValue* tests) { |
| ScopedVector<const StringMatchTest> name; |
| ScopedVector<const StringMatchTest> value; |
| @@ -393,30 +361,29 @@ WebRequestConditionAttributeResponseHeaders::CreateHeaderMatchTest( |
| key != tests->end_keys(); |
| ++key) { |
| bool is_name = false; // Is this test for header name? |
| - MatchType match_type; |
| + StringMatchTest::MatchType match_type; |
| if (*key == keys::kNamePrefixKey) { |
| is_name = true; |
| - match_type = kPrefix; |
| + match_type = StringMatchTest::kPrefix; |
| } else if (*key == keys::kNameSuffixKey) { |
| is_name = true; |
| - match_type = kSuffix; |
| + match_type = StringMatchTest::kSuffix; |
| } else if (*key == keys::kNameContainsKey) { |
| is_name = true; |
| - match_type = kContains; |
| + match_type = StringMatchTest::kContains; |
| } else if (*key == keys::kNameEqualsKey) { |
| is_name = true; |
| - match_type = kEquals; |
| + match_type = StringMatchTest::kEquals; |
| } else if (*key == keys::kValuePrefixKey) { |
| - match_type = kPrefix; |
| + match_type = StringMatchTest::kPrefix; |
| } else if (*key == keys::kValueSuffixKey) { |
| - match_type = kSuffix; |
| + match_type = StringMatchTest::kSuffix; |
| } else if (*key == keys::kValueContainsKey) { |
| - match_type = kContains; |
| + match_type = StringMatchTest::kContains; |
| } else if (*key == keys::kValueEqualsKey) { |
| - match_type = kEquals; |
| + match_type = StringMatchTest::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; |
| @@ -431,19 +398,18 @@ WebRequestConditionAttributeResponseHeaders::CreateHeaderMatchTest( |
| it != list->end(); ++it) { |
| ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; |
| tests->push_back( |
| - CreateStringMatchTest(*it, is_name, match_type).release()); |
| + StringMatchTest::Create(*it, is_name, match_type).release()); |
| } |
| break; |
| } |
| case Value::TYPE_STRING: { |
| ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; |
| tests->push_back( |
| - CreateStringMatchTest(content, is_name, match_type).release()); |
| + StringMatchTest::Create(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); |
| } |
| } |
| @@ -452,12 +418,36 @@ WebRequestConditionAttributeResponseHeaders::CreateHeaderMatchTest( |
| return scoped_ptr<const HeaderMatchTest>(new HeaderMatchTest(&name, &value)); |
| } |
| +bool TestManager::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; |
| +} |
| + |
| +// TestManager::StringMatchTest implementation. |
| + |
| +TestManager::StringMatchTest::StringMatchTest(const std::string& data, |
| + MatchType type) |
| + : data_(data), |
| + type_(type) {} |
| + |
| +TestManager::StringMatchTest::~StringMatchTest() {} |
| + |
| // static |
| -scoped_ptr<const WebRequestConditionAttributeResponseHeaders::StringMatchTest> |
| -WebRequestConditionAttributeResponseHeaders::CreateStringMatchTest( |
| - const Value* content, |
| - bool is_name_test, |
| - MatchType match_type) { |
| +scoped_ptr<const TestManager::StringMatchTest> |
| +TestManager::StringMatchTest::Create(const Value* content, |
| + bool is_name_test, |
| + MatchType match_type) { |
| std::string str; |
| CHECK(content->GetAsString(&str)); |
| @@ -468,4 +458,101 @@ WebRequestConditionAttributeResponseHeaders::CreateStringMatchTest( |
| new StringMatchTest(str, match_type)); |
| } |
| +bool TestManager::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. |
|
Yoyo Zhou
2012/09/01 19:44:50
I think this is usually in a default: section (as
vabr (Chromium)
2012/09/03 07:37:05
Done.
|
| + NOTREACHED(); |
| + return false; |
| +} |
| + |
| +// |
| +// WebRequestConditionAttributeResponseHeaders |
| +// |
| + |
| +WebRequestConditionAttributeResponseHeaders:: |
| +WebRequestConditionAttributeResponseHeaders( |
| + scoped_ptr<const TestManager>* test_manager, bool positive) |
| + : test_manager_(test_manager->Pass()), |
| + positive_(positive) {} |
| + |
| +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); |
| + } |
| + |
| + scoped_ptr<const TestManager> test_manager( |
| + TestManager::Create(value_as_list)); |
| + if (test_manager.get() == NULL) { |
| + *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); |
| + return scoped_ptr<WebRequestConditionAttribute>(NULL); |
| + } |
| + |
| + const bool positive = name == keys::kResponseHeadersKey; |
| + return scoped_ptr<WebRequestConditionAttribute>( |
| + new WebRequestConditionAttributeResponseHeaders(&test_manager, positive)); |
| +} |
| + |
| +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_; |
| + } |
| + |
| + bool passed = false; // Did some header pass TestNameValue? |
| + std::string name; |
| + std::string value; |
| + void* iter = NULL; |
| + while (!passed && headers->EnumerateHeaderLines(&iter, &name, &value)) { |
| + StringToLowerASCII(&name); // Header names are case-insensitive. |
| + passed |= test_manager_->TestNameValue(name, value); |
| + } |
| + |
| + return (positive_ ? passed : !passed); |
| +} |
| + |
| +WebRequestConditionAttribute::Type |
| +WebRequestConditionAttributeResponseHeaders::GetType() const { |
| + return CONDITION_RESPONSE_HEADERS; |
| +} |
| + |
| } // namespace extensions |