Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit ion_attribute.h" | 5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condit ion_attribute.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| (...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 225 return std::find(content_types_.begin(), content_types_.end(), | 225 return std::find(content_types_.begin(), content_types_.end(), |
| 226 mime_type) == content_types_.end(); | 226 mime_type) == content_types_.end(); |
| 227 } | 227 } |
| 228 } | 228 } |
| 229 | 229 |
| 230 WebRequestConditionAttribute::Type | 230 WebRequestConditionAttribute::Type |
| 231 WebRequestConditionAttributeContentType::GetType() const { | 231 WebRequestConditionAttributeContentType::GetType() const { |
| 232 return CONDITION_CONTENT_TYPE; | 232 return CONDITION_CONTENT_TYPE; |
| 233 } | 233 } |
| 234 | 234 |
| 235 // | 235 // Manages a set of tests to be applied to name-value pairs representing |
| 236 // WebRequestConditionAttributeResponseHeaders | 236 // headers. This is a helper class to header-related condition attributes. |
| 237 // | 237 // It contains a set of test groups. A name-value pair satisfies the whole |
| 238 // set of test groups iff it passes at least one test group. | |
| 239 class TestManager { | |
| 240 public: | |
| 241 ~TestManager(); | |
| 238 | 242 |
| 239 WebRequestConditionAttributeResponseHeaders::StringMatchTest::StringMatchTest( | 243 // Creates an instance based on a list |tests| of test groups, encoded as |
| 240 const std::string& data, | 244 // dictionaries of the type declarativeWebRequest.HeaderFilter (see |
| 241 MatchType type) | 245 // declarative_web_request.json). |
| 242 : data_(data), | 246 static scoped_ptr<const TestManager> Create(const base::ListValue* tests); |
| 243 type_(type) {} | |
| 244 | 247 |
| 245 WebRequestConditionAttributeResponseHeaders::StringMatchTest::~StringMatchTest() | 248 bool TestNameValue(const std::string& name, const std::string& value) const; |
| 246 {} | |
| 247 | 249 |
| 248 WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::HeaderMatchTest( | 250 private: |
| 251 // Represents a single string-matching test. | |
| 252 class StringMatchTest { | |
| 253 public: | |
| 254 enum MatchType { kPrefix, kSuffix, kEquals, kContains }; | |
| 255 | |
| 256 StringMatchTest(const std::string& data, MatchType type); | |
| 257 ~StringMatchTest(); | |
| 258 | |
| 259 // Takes a string to be matched, as a StringValue in |content|, an | |
| 260 // indication |is_name_test| of whether the test will be used for matching | |
| 261 // against header values or names, and a |match_type|. Never returns NULL, | |
| 262 // except for memory failures. | |
| 263 static scoped_ptr<const StringMatchTest> Create(const Value* content, | |
| 264 bool is_name_test, | |
| 265 MatchType match_type); | |
| 266 | |
| 267 // Does |str| pass |*this| StringMatchTest? | |
| 268 bool Matches(const std::string& str) const; | |
| 269 | |
| 270 private: | |
| 271 const std::string data_; | |
| 272 const MatchType type_; | |
| 273 DISALLOW_COPY_AND_ASSIGN(StringMatchTest); | |
| 274 }; | |
| 275 | |
| 276 // Represents a test group -- a set of string matching tests to be applied to | |
| 277 // both the header name and value. | |
| 278 class HeaderMatchTest { | |
| 279 public: | |
| 280 // Takes ownership of the content of both |name| and |value|. | |
| 281 HeaderMatchTest(ScopedVector<const StringMatchTest>* name, | |
| 282 ScopedVector<const StringMatchTest>* value); | |
| 283 ~HeaderMatchTest(); | |
| 284 | |
| 285 // Gets the test group description in |tests| and creates the corresponding | |
| 286 // HeaderMatchTest. On failure returns NULL. | |
| 287 static scoped_ptr<const HeaderMatchTest> Create( | |
| 288 const base::DictionaryValue* tests); | |
| 289 | |
| 290 // Does the header |name|: |value| match all tests in this header test? | |
| 291 bool Matches(const std::string& name, const std::string& value) const; | |
| 292 | |
| 293 private: | |
| 294 // Tests to be passed by a header's name. | |
| 295 const ScopedVector<const StringMatchTest> name_; | |
| 296 // Tests to be passed by a header's value. | |
| 297 const ScopedVector<const StringMatchTest> value_; | |
| 298 DISALLOW_COPY_AND_ASSIGN(HeaderMatchTest); | |
| 299 }; | |
| 300 | |
| 301 explicit TestManager(ScopedVector<const HeaderMatchTest>* tests); | |
| 302 | |
| 303 const ScopedVector<const HeaderMatchTest> tests_; | |
| 304 | |
| 305 DISALLOW_COPY_AND_ASSIGN(TestManager); | |
| 306 }; | |
| 307 | |
| 308 // TestManager implementation | |
| 309 TestManager::~TestManager() {} | |
| 310 | |
| 311 // static | |
| 312 scoped_ptr<const TestManager> TestManager::Create( | |
| 313 const base::ListValue* tests) { | |
| 314 ScopedVector<const HeaderMatchTest> header_tests; | |
| 315 for (ListValue::const_iterator it = tests->begin(); | |
| 316 it != tests->end(); ++it) { | |
| 317 const DictionaryValue* tests = NULL; | |
| 318 if (!(*it)->GetAsDictionary(&tests)) | |
| 319 return scoped_ptr<const TestManager>(NULL); | |
| 320 | |
| 321 scoped_ptr<const HeaderMatchTest> header_test( | |
| 322 HeaderMatchTest::Create(tests)); | |
| 323 if (header_test.get() == NULL) | |
| 324 return scoped_ptr<const TestManager>(NULL); | |
| 325 header_tests.push_back(header_test.release()); | |
| 326 } | |
| 327 | |
| 328 return scoped_ptr<const TestManager>( | |
| 329 new TestManager(&header_tests)); | |
| 330 } | |
| 331 | |
| 332 bool TestManager::TestNameValue(const std::string& name, | |
| 333 const std::string& value) const { | |
| 334 for (size_t i = 0; i < tests_.size(); ++i) { | |
| 335 if (tests_[i]->Matches(name, value)) | |
| 336 return true; | |
| 337 } | |
| 338 return false; | |
| 339 } | |
| 340 | |
| 341 TestManager::TestManager(ScopedVector<const HeaderMatchTest>* tests) | |
| 342 : tests_(tests->Pass()) {} | |
| 343 | |
| 344 // TestManager::HeaderMatchTest implementation. | |
| 345 | |
| 346 TestManager::HeaderMatchTest::HeaderMatchTest( | |
| 249 ScopedVector<const StringMatchTest>* name, | 347 ScopedVector<const StringMatchTest>* name, |
| 250 ScopedVector<const StringMatchTest>* value) | 348 ScopedVector<const StringMatchTest>* value) |
| 251 : name_(name->Pass()), | 349 : name_(name->Pass()), |
| 252 value_(value->Pass()) {} | 350 value_(value->Pass()) {} |
| 253 | 351 |
| 254 WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::~HeaderMatchTest() | 352 TestManager::HeaderMatchTest::~HeaderMatchTest() {} |
| 255 {} | |
| 256 | |
| 257 WebRequestConditionAttributeResponseHeaders:: | |
| 258 WebRequestConditionAttributeResponseHeaders( | |
| 259 bool positive_test, ScopedVector<const HeaderMatchTest>* tests) | |
| 260 : tests_(tests->Pass()), | |
| 261 positive_test_(positive_test) {} | |
| 262 | |
| 263 WebRequestConditionAttributeResponseHeaders:: | |
| 264 ~WebRequestConditionAttributeResponseHeaders() {} | |
| 265 | 353 |
| 266 // static | 354 // static |
| 267 bool WebRequestConditionAttributeResponseHeaders::IsMatchingType( | 355 scoped_ptr<const TestManager::HeaderMatchTest> |
| 268 const std::string& instance_type) { | 356 TestManager::HeaderMatchTest::Create(const base::DictionaryValue* tests) { |
| 269 return instance_type == keys::kResponseHeadersKey || | |
| 270 instance_type == keys::kExcludeResponseHeadersKey; | |
| 271 } | |
| 272 | |
| 273 // static | |
| 274 scoped_ptr<WebRequestConditionAttribute> | |
| 275 WebRequestConditionAttributeResponseHeaders::Create( | |
| 276 const std::string& name, | |
| 277 const base::Value* value, | |
| 278 std::string* error) { | |
| 279 DCHECK(IsMatchingType(name)); | |
| 280 | |
| 281 const ListValue* value_as_list = NULL; | |
| 282 if (!value->GetAsList(&value_as_list)) { | |
| 283 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
| 284 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
| 285 } | |
| 286 | |
| 287 ScopedVector<const HeaderMatchTest> header_tests; | |
| 288 for (ListValue::const_iterator it = value_as_list->begin(); | |
| 289 it != value_as_list->end(); ++it) { | |
| 290 const DictionaryValue* tests = NULL; | |
| 291 if (!(*it)->GetAsDictionary(&tests)) { | |
| 292 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
| 293 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
| 294 } | |
| 295 | |
| 296 scoped_ptr<const HeaderMatchTest> header_test( | |
| 297 CreateHeaderMatchTest(tests, error)); | |
| 298 if (header_test.get() == NULL) | |
| 299 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
| 300 header_tests.push_back(header_test.release()); | |
| 301 } | |
| 302 | |
| 303 scoped_ptr<WebRequestConditionAttributeResponseHeaders> result; | |
| 304 result.reset(new WebRequestConditionAttributeResponseHeaders( | |
| 305 name == keys::kResponseHeadersKey, &header_tests)); | |
| 306 | |
| 307 return result.PassAs<WebRequestConditionAttribute>(); | |
| 308 } | |
| 309 | |
| 310 int WebRequestConditionAttributeResponseHeaders::GetStages() const { | |
| 311 return ON_HEADERS_RECEIVED; | |
| 312 } | |
| 313 | |
| 314 bool WebRequestConditionAttributeResponseHeaders::IsFulfilled( | |
| 315 const WebRequestRule::RequestData& request_data) const { | |
| 316 if (!(request_data.stage & GetStages())) | |
| 317 return false; | |
| 318 | |
| 319 const net::HttpResponseHeaders* headers = | |
| 320 request_data.original_response_headers; | |
| 321 if (headers == NULL) { | |
| 322 // Each header of an empty set satisfies (the negation of) everything; | |
| 323 // OTOH, there is no header to satisfy even the most permissive test. | |
| 324 return !positive_test_; | |
| 325 } | |
| 326 | |
| 327 // Has some header already passed some header test? | |
| 328 bool header_found = false; | |
| 329 | |
| 330 for (size_t i = 0; !header_found && i < tests_.size(); ++i) { | |
| 331 std::string name; | |
| 332 std::string value; | |
| 333 | |
| 334 void* iter = NULL; | |
| 335 while (!header_found && | |
| 336 headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 337 StringToLowerASCII(&name); // Header names are case-insensitive. | |
| 338 header_found |= tests_[i]->Matches(name, value); | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 return (positive_test_ ? header_found : !header_found); | |
| 343 } | |
| 344 | |
| 345 WebRequestConditionAttribute::Type | |
| 346 WebRequestConditionAttributeResponseHeaders::GetType() const { | |
| 347 return CONDITION_RESPONSE_HEADERS; | |
| 348 } | |
| 349 | |
| 350 bool WebRequestConditionAttributeResponseHeaders::StringMatchTest::Matches( | |
| 351 const std::string& str) const { | |
| 352 switch (type_) { | |
| 353 case kPrefix: | |
| 354 return StartsWithASCII(str, data_, true /*case_sensitive*/); | |
| 355 case kSuffix: | |
| 356 return EndsWith(str, data_, true /*case_sensitive*/); | |
| 357 case kEquals: | |
| 358 return data_ == str; | |
| 359 case kContains: | |
| 360 return str.find(data_) != std::string::npos; | |
| 361 } | |
| 362 // We never get past the "switch", but the compiler worries about no return. | |
| 363 NOTREACHED(); | |
| 364 return false; | |
| 365 } | |
| 366 | |
| 367 bool WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::Matches( | |
| 368 const std::string& name, | |
| 369 const std::string& value) const { | |
| 370 for (size_t i = 0; i < name_.size(); ++i) { | |
| 371 if (!name_[i]->Matches(name)) | |
| 372 return false; | |
| 373 } | |
| 374 | |
| 375 for (size_t i = 0; i < value_.size(); ++i) { | |
| 376 if (!value_[i]->Matches(value)) | |
| 377 return false; | |
| 378 } | |
| 379 | |
| 380 return true; | |
| 381 } | |
| 382 | |
| 383 | |
| 384 // static | |
| 385 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::HeaderMatchTest> | |
| 386 WebRequestConditionAttributeResponseHeaders::CreateHeaderMatchTest( | |
| 387 const DictionaryValue* tests, | |
| 388 std::string* error) { | |
| 389 ScopedVector<const StringMatchTest> name; | 357 ScopedVector<const StringMatchTest> name; |
| 390 ScopedVector<const StringMatchTest> value; | 358 ScopedVector<const StringMatchTest> value; |
| 391 | 359 |
| 392 for (DictionaryValue::key_iterator key = tests->begin_keys(); | 360 for (DictionaryValue::key_iterator key = tests->begin_keys(); |
| 393 key != tests->end_keys(); | 361 key != tests->end_keys(); |
| 394 ++key) { | 362 ++key) { |
| 395 bool is_name = false; // Is this test for header name? | 363 bool is_name = false; // Is this test for header name? |
| 396 MatchType match_type; | 364 StringMatchTest::MatchType match_type; |
| 397 if (*key == keys::kNamePrefixKey) { | 365 if (*key == keys::kNamePrefixKey) { |
| 398 is_name = true; | 366 is_name = true; |
| 399 match_type = kPrefix; | 367 match_type = StringMatchTest::kPrefix; |
| 400 } else if (*key == keys::kNameSuffixKey) { | 368 } else if (*key == keys::kNameSuffixKey) { |
| 401 is_name = true; | 369 is_name = true; |
| 402 match_type = kSuffix; | 370 match_type = StringMatchTest::kSuffix; |
| 403 } else if (*key == keys::kNameContainsKey) { | 371 } else if (*key == keys::kNameContainsKey) { |
| 404 is_name = true; | 372 is_name = true; |
| 405 match_type = kContains; | 373 match_type = StringMatchTest::kContains; |
| 406 } else if (*key == keys::kNameEqualsKey) { | 374 } else if (*key == keys::kNameEqualsKey) { |
| 407 is_name = true; | 375 is_name = true; |
| 408 match_type = kEquals; | 376 match_type = StringMatchTest::kEquals; |
| 409 } else if (*key == keys::kValuePrefixKey) { | 377 } else if (*key == keys::kValuePrefixKey) { |
| 410 match_type = kPrefix; | 378 match_type = StringMatchTest::kPrefix; |
| 411 } else if (*key == keys::kValueSuffixKey) { | 379 } else if (*key == keys::kValueSuffixKey) { |
| 412 match_type = kSuffix; | 380 match_type = StringMatchTest::kSuffix; |
| 413 } else if (*key == keys::kValueContainsKey) { | 381 } else if (*key == keys::kValueContainsKey) { |
| 414 match_type = kContains; | 382 match_type = StringMatchTest::kContains; |
| 415 } else if (*key == keys::kValueEqualsKey) { | 383 } else if (*key == keys::kValueEqualsKey) { |
| 416 match_type = kEquals; | 384 match_type = StringMatchTest::kEquals; |
| 417 } else { | 385 } else { |
| 418 NOTREACHED(); // JSON schema type checking should prevent this. | 386 NOTREACHED(); // JSON schema type checking should prevent this. |
| 419 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); | |
| 420 return scoped_ptr<const HeaderMatchTest>(NULL); | 387 return scoped_ptr<const HeaderMatchTest>(NULL); |
| 421 } | 388 } |
| 422 const Value* content = NULL; | 389 const Value* content = NULL; |
| 423 // This should not fire, we already checked that |key| is there. | 390 // This should not fire, we already checked that |key| is there. |
| 424 CHECK(tests->Get(*key, &content)); | 391 CHECK(tests->Get(*key, &content)); |
| 425 | 392 |
| 426 switch (content->GetType()) { | 393 switch (content->GetType()) { |
| 427 case Value::TYPE_LIST: { | 394 case Value::TYPE_LIST: { |
| 428 const ListValue* list = NULL; | 395 const ListValue* list = NULL; |
| 429 CHECK(content->GetAsList(&list)); | 396 CHECK(content->GetAsList(&list)); |
| 430 for (ListValue::const_iterator it = list->begin(); | 397 for (ListValue::const_iterator it = list->begin(); |
| 431 it != list->end(); ++it) { | 398 it != list->end(); ++it) { |
| 432 ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; | 399 ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; |
| 433 tests->push_back( | 400 tests->push_back( |
| 434 CreateStringMatchTest(*it, is_name, match_type).release()); | 401 StringMatchTest::Create(*it, is_name, match_type).release()); |
| 435 } | 402 } |
| 436 break; | 403 break; |
| 437 } | 404 } |
| 438 case Value::TYPE_STRING: { | 405 case Value::TYPE_STRING: { |
| 439 ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; | 406 ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; |
| 440 tests->push_back( | 407 tests->push_back( |
| 441 CreateStringMatchTest(content, is_name, match_type).release()); | 408 StringMatchTest::Create(content, is_name, match_type).release()); |
| 442 break; | 409 break; |
| 443 } | 410 } |
| 444 default: { | 411 default: { |
| 445 NOTREACHED(); // JSON schema type checking should prevent this. | 412 NOTREACHED(); // JSON schema type checking should prevent this. |
| 446 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); | |
| 447 return scoped_ptr<const HeaderMatchTest>(NULL); | 413 return scoped_ptr<const HeaderMatchTest>(NULL); |
| 448 } | 414 } |
| 449 } | 415 } |
| 450 } | 416 } |
| 451 | 417 |
| 452 return scoped_ptr<const HeaderMatchTest>(new HeaderMatchTest(&name, &value)); | 418 return scoped_ptr<const HeaderMatchTest>(new HeaderMatchTest(&name, &value)); |
| 453 } | 419 } |
| 454 | 420 |
| 421 bool TestManager::HeaderMatchTest::Matches( | |
| 422 const std::string& name, | |
| 423 const std::string& value) const { | |
| 424 for (size_t i = 0; i < name_.size(); ++i) { | |
| 425 if (!name_[i]->Matches(name)) | |
| 426 return false; | |
| 427 } | |
| 428 | |
| 429 for (size_t i = 0; i < value_.size(); ++i) { | |
| 430 if (!value_[i]->Matches(value)) | |
| 431 return false; | |
| 432 } | |
| 433 | |
| 434 return true; | |
| 435 } | |
| 436 | |
| 437 // TestManager::StringMatchTest implementation. | |
| 438 | |
| 439 TestManager::StringMatchTest::StringMatchTest(const std::string& data, | |
| 440 MatchType type) | |
| 441 : data_(data), | |
| 442 type_(type) {} | |
| 443 | |
| 444 TestManager::StringMatchTest::~StringMatchTest() {} | |
| 445 | |
| 455 // static | 446 // static |
| 456 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::StringMatchTest> | 447 scoped_ptr<const TestManager::StringMatchTest> |
| 457 WebRequestConditionAttributeResponseHeaders::CreateStringMatchTest( | 448 TestManager::StringMatchTest::Create(const Value* content, |
| 458 const Value* content, | 449 bool is_name_test, |
| 459 bool is_name_test, | 450 MatchType match_type) { |
| 460 MatchType match_type) { | |
| 461 std::string str; | 451 std::string str; |
| 462 | 452 |
| 463 CHECK(content->GetAsString(&str)); | 453 CHECK(content->GetAsString(&str)); |
| 464 if (is_name_test) // Header names are case-insensitive. | 454 if (is_name_test) // Header names are case-insensitive. |
| 465 StringToLowerASCII(&str); | 455 StringToLowerASCII(&str); |
| 466 | 456 |
| 467 return scoped_ptr<const StringMatchTest>( | 457 return scoped_ptr<const StringMatchTest>( |
| 468 new StringMatchTest(str, match_type)); | 458 new StringMatchTest(str, match_type)); |
| 469 } | 459 } |
| 470 | 460 |
| 461 bool TestManager::StringMatchTest::Matches( | |
| 462 const std::string& str) const { | |
| 463 switch (type_) { | |
| 464 case kPrefix: | |
| 465 return StartsWithASCII(str, data_, true /*case_sensitive*/); | |
| 466 case kSuffix: | |
| 467 return EndsWith(str, data_, true /*case_sensitive*/); | |
| 468 case kEquals: | |
| 469 return data_ == str; | |
| 470 case kContains: | |
| 471 return str.find(data_) != std::string::npos; | |
| 472 } | |
| 473 // 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.
| |
| 474 NOTREACHED(); | |
| 475 return false; | |
| 476 } | |
| 477 | |
| 478 // | |
| 479 // WebRequestConditionAttributeResponseHeaders | |
| 480 // | |
| 481 | |
| 482 WebRequestConditionAttributeResponseHeaders:: | |
| 483 WebRequestConditionAttributeResponseHeaders( | |
| 484 scoped_ptr<const TestManager>* test_manager, bool positive) | |
| 485 : test_manager_(test_manager->Pass()), | |
| 486 positive_(positive) {} | |
| 487 | |
| 488 WebRequestConditionAttributeResponseHeaders:: | |
| 489 ~WebRequestConditionAttributeResponseHeaders() {} | |
| 490 | |
| 491 // static | |
| 492 bool WebRequestConditionAttributeResponseHeaders::IsMatchingType( | |
| 493 const std::string& instance_type) { | |
| 494 return instance_type == keys::kResponseHeadersKey || | |
| 495 instance_type == keys::kExcludeResponseHeadersKey; | |
| 496 } | |
| 497 | |
| 498 // static | |
| 499 scoped_ptr<WebRequestConditionAttribute> | |
| 500 WebRequestConditionAttributeResponseHeaders::Create( | |
| 501 const std::string& name, | |
| 502 const base::Value* value, | |
| 503 std::string* error) { | |
| 504 DCHECK(IsMatchingType(name)); | |
| 505 | |
| 506 const ListValue* value_as_list = NULL; | |
| 507 if (!value->GetAsList(&value_as_list)) { | |
| 508 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
| 509 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
| 510 } | |
| 511 | |
| 512 scoped_ptr<const TestManager> test_manager( | |
| 513 TestManager::Create(value_as_list)); | |
| 514 if (test_manager.get() == NULL) { | |
| 515 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
| 516 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
| 517 } | |
| 518 | |
| 519 const bool positive = name == keys::kResponseHeadersKey; | |
| 520 return scoped_ptr<WebRequestConditionAttribute>( | |
| 521 new WebRequestConditionAttributeResponseHeaders(&test_manager, positive)); | |
| 522 } | |
| 523 | |
| 524 int WebRequestConditionAttributeResponseHeaders::GetStages() const { | |
| 525 return ON_HEADERS_RECEIVED; | |
| 526 } | |
| 527 | |
| 528 bool WebRequestConditionAttributeResponseHeaders::IsFulfilled( | |
| 529 const WebRequestRule::RequestData& request_data) const { | |
| 530 if (!(request_data.stage & GetStages())) | |
| 531 return false; | |
| 532 | |
| 533 const net::HttpResponseHeaders* headers = | |
| 534 request_data.original_response_headers; | |
| 535 if (headers == NULL) { | |
| 536 // Each header of an empty set satisfies (the negation of) everything; | |
| 537 // OTOH, there is no header to satisfy even the most permissive test. | |
| 538 return !positive_; | |
| 539 } | |
| 540 | |
| 541 bool passed = false; // Did some header pass TestNameValue? | |
| 542 std::string name; | |
| 543 std::string value; | |
| 544 void* iter = NULL; | |
| 545 while (!passed && headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 546 StringToLowerASCII(&name); // Header names are case-insensitive. | |
| 547 passed |= test_manager_->TestNameValue(name, value); | |
| 548 } | |
| 549 | |
| 550 return (positive_ ? passed : !passed); | |
| 551 } | |
| 552 | |
| 553 WebRequestConditionAttribute::Type | |
| 554 WebRequestConditionAttributeResponseHeaders::GetType() const { | |
| 555 return CONDITION_RESPONSE_HEADERS; | |
| 556 } | |
| 557 | |
| 471 } // namespace extensions | 558 } // namespace extensions |
| OLD | NEW |