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 HeaderMatcher { | |
| 240 public: | |
| 241 ~HeaderMatcher(); | |
| 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). |
| 246 static scoped_ptr<const HeaderMatcher> Create(const base::ListValue* tests); | |
| 247 | |
| 248 // Does |this| match the header "|name|: |value|"? | |
| 249 bool TestNameValue(const std::string& name, const std::string& value) const; | |
| 250 | |
| 251 private: | |
| 252 // Represents a single string-matching test. | |
| 253 class StringMatchTest { | |
| 254 public: | |
| 255 enum MatchType { kPrefix, kSuffix, kEquals, kContains }; | |
| 256 | |
| 257 StringMatchTest(const std::string& data, MatchType type); | |
| 258 ~StringMatchTest(); | |
| 259 | |
| 260 // Takes a string to be matched, as a StringValue in |content|, an | |
| 261 // indication |is_name_test| of whether the test will be used for matching | |
| 262 // against header values or names, and a |match_type|. Never returns NULL, | |
| 263 // except for memory failures. | |
| 264 static scoped_ptr<const StringMatchTest> Create(const Value* content, | |
| 265 bool is_name_test, | |
| 266 MatchType match_type); | |
|
battre
2012/09/03 08:46:47
You can do this in a separate CL, but I think we c
vabr (Chromium)
2012/09/03 10:12:36
I thought about this before, but decided against i
battre
2012/09/03 12:48:08
My thoughts were from the perspective of somebody
vabr (Chromium)
2012/09/03 14:21:29
I agree with both points. StringToLowerASCII is no
| |
| 267 | |
| 268 // Does |str| pass |this| StringMatchTest? | |
| 269 bool Matches(const std::string& str) const; | |
| 270 | |
| 271 private: | |
| 272 const std::string data_; | |
| 273 const MatchType type_; | |
| 274 DISALLOW_COPY_AND_ASSIGN(StringMatchTest); | |
| 275 }; | |
| 276 | |
| 277 // Represents a test group -- a set of string matching tests to be applied to | |
| 278 // both the header name and value. | |
| 279 class HeaderMatchTest { | |
| 280 public: | |
| 281 ~HeaderMatchTest(); | |
| 282 | |
| 283 // Gets the test group description in |tests| and creates the corresponding | |
| 284 // HeaderMatchTest. On failure returns NULL. | |
| 285 static scoped_ptr<const HeaderMatchTest> Create( | |
| 286 const base::DictionaryValue* tests); | |
| 287 | |
| 288 // Does the header "|name|: |value|" match all tests in |this|? | |
| 289 bool Matches(const std::string& name, const std::string& value) const; | |
| 290 | |
| 291 private: | |
| 292 // Takes ownership of the content of both |name_match| and |value_match|. | |
| 293 HeaderMatchTest(ScopedVector<const StringMatchTest>* name_match, | |
| 294 ScopedVector<const StringMatchTest>* value_match); | |
| 295 | |
| 296 // Tests to be passed by a header's name. | |
| 297 const ScopedVector<const StringMatchTest> name_match_; | |
| 298 // Tests to be passed by a header's value. | |
| 299 const ScopedVector<const StringMatchTest> value_match_; | |
| 300 DISALLOW_COPY_AND_ASSIGN(HeaderMatchTest); | |
| 301 }; | |
| 302 | |
| 303 explicit HeaderMatcher(ScopedVector<const HeaderMatchTest>* tests); | |
| 304 | |
| 305 const ScopedVector<const HeaderMatchTest> tests_; | |
| 306 | |
| 307 DISALLOW_COPY_AND_ASSIGN(HeaderMatcher); | |
| 308 }; | |
| 309 | |
| 310 // HeaderMatcher implementation. | |
| 311 | |
| 312 HeaderMatcher::~HeaderMatcher() {} | |
| 313 | |
| 314 // static | |
| 315 scoped_ptr<const HeaderMatcher> HeaderMatcher::Create( | |
| 316 const base::ListValue* tests) { | |
| 317 ScopedVector<const HeaderMatchTest> header_tests; | |
| 318 for (ListValue::const_iterator it = tests->begin(); | |
| 319 it != tests->end(); ++it) { | |
| 320 const DictionaryValue* tests = NULL; | |
| 321 if (!(*it)->GetAsDictionary(&tests)) | |
| 322 return scoped_ptr<const HeaderMatcher>(NULL); | |
| 323 | |
| 324 scoped_ptr<const HeaderMatchTest> header_test( | |
| 325 HeaderMatchTest::Create(tests)); | |
| 326 if (header_test.get() == NULL) | |
| 327 return scoped_ptr<const HeaderMatcher>(NULL); | |
| 328 header_tests.push_back(header_test.release()); | |
| 329 } | |
| 330 | |
| 331 return scoped_ptr<const HeaderMatcher>( | |
| 332 new HeaderMatcher(&header_tests)); | |
|
battre
2012/09/03 08:46:47
nit: single line
vabr (Chromium)
2012/09/03 10:12:36
Done.
| |
| 333 } | |
| 334 | |
| 335 bool HeaderMatcher::TestNameValue(const std::string& name, | |
| 336 const std::string& value) const { | |
| 337 for (size_t i = 0; i < tests_.size(); ++i) { | |
| 338 if (tests_[i]->Matches(name, value)) | |
| 339 return true; | |
| 340 } | |
| 341 return false; | |
| 342 } | |
| 343 | |
| 344 HeaderMatcher::HeaderMatcher(ScopedVector<const HeaderMatchTest>* tests) | |
| 345 : tests_(tests->Pass()) {} | |
| 346 | |
| 347 // HeaderMatcher::StringMatchTest implementation. | |
| 348 | |
| 349 HeaderMatcher::StringMatchTest::StringMatchTest(const std::string& data, | |
| 350 MatchType type) | |
| 242 : data_(data), | 351 : data_(data), |
| 243 type_(type) {} | 352 type_(type) {} |
| 244 | 353 |
| 245 WebRequestConditionAttributeResponseHeaders::StringMatchTest::~StringMatchTest() | 354 HeaderMatcher::StringMatchTest::~StringMatchTest() {} |
| 246 {} | |
| 247 | |
| 248 WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::HeaderMatchTest( | |
| 249 ScopedVector<const StringMatchTest>* name, | |
| 250 ScopedVector<const StringMatchTest>* value) | |
| 251 : name_(name->Pass()), | |
| 252 value_(value->Pass()) {} | |
| 253 | |
| 254 WebRequestConditionAttributeResponseHeaders::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 | 355 |
| 266 // static | 356 // static |
| 267 bool WebRequestConditionAttributeResponseHeaders::IsMatchingType( | 357 scoped_ptr<const HeaderMatcher::StringMatchTest> |
| 268 const std::string& instance_type) { | 358 HeaderMatcher::StringMatchTest::Create(const Value* content, |
| 269 return instance_type == keys::kResponseHeadersKey || | 359 bool is_name_test, |
| 270 instance_type == keys::kExcludeResponseHeadersKey; | 360 MatchType match_type) { |
| 361 std::string str; | |
| 362 | |
| 363 CHECK(content->GetAsString(&str)); | |
| 364 // Header names are case-insensitive, we match them in lowercase. | |
| 365 if (is_name_test) | |
| 366 StringToLowerASCII(&str); | |
| 367 | |
| 368 return scoped_ptr<const StringMatchTest>( | |
| 369 new StringMatchTest(str, match_type)); | |
| 271 } | 370 } |
| 272 | 371 |
| 273 // static | 372 bool HeaderMatcher::StringMatchTest::Matches( |
| 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 { | 373 const std::string& str) const { |
| 352 switch (type_) { | 374 switch (type_) { |
| 353 case kPrefix: | 375 case kPrefix: |
| 354 return StartsWithASCII(str, data_, true /*case_sensitive*/); | 376 return StartsWithASCII(str, data_, true /*case_sensitive*/); |
| 355 case kSuffix: | 377 case kSuffix: |
| 356 return EndsWith(str, data_, true /*case_sensitive*/); | 378 return EndsWith(str, data_, true /*case_sensitive*/); |
| 357 case kEquals: | 379 case kEquals: |
| 358 return data_ == str; | 380 return data_ == str; |
| 359 case kContains: | 381 case kContains: |
| 360 return str.find(data_) != std::string::npos; | 382 return str.find(data_) != std::string::npos; |
| 383 default: | |
| 384 // We never go here, all cases done above. Just to silence the compiler. | |
| 385 NOTREACHED(); | |
| 386 return false; | |
|
battre
2012/09/03 08:46:47
I disagree with this change. This makes the compil
vabr (Chromium)
2012/09/03 10:12:36
You're right, reverted.
I guess I need to learn to
| |
| 361 } | 387 } |
| 362 // We never get past the "switch", but the compiler worries about no return. | |
| 363 NOTREACHED(); | |
| 364 return false; | |
| 365 } | 388 } |
| 366 | 389 |
| 367 bool WebRequestConditionAttributeResponseHeaders::HeaderMatchTest::Matches( | 390 // HeaderMatcher::HeaderMatchTest implementation. |
| 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 | 391 |
| 375 for (size_t i = 0; i < value_.size(); ++i) { | 392 HeaderMatcher::HeaderMatchTest::HeaderMatchTest( |
| 376 if (!value_[i]->Matches(value)) | 393 ScopedVector<const StringMatchTest>* name_match, |
| 377 return false; | 394 ScopedVector<const StringMatchTest>* value_match) |
| 378 } | 395 : name_match_(name_match->Pass()), |
| 396 value_match_(value_match->Pass()) {} | |
| 379 | 397 |
| 380 return true; | 398 HeaderMatcher::HeaderMatchTest::~HeaderMatchTest() {} |
| 381 } | |
| 382 | |
| 383 | 399 |
| 384 // static | 400 // static |
| 385 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::HeaderMatchTest> | 401 scoped_ptr<const HeaderMatcher::HeaderMatchTest> |
| 386 WebRequestConditionAttributeResponseHeaders::CreateHeaderMatchTest( | 402 HeaderMatcher::HeaderMatchTest::Create(const base::DictionaryValue* tests) { |
| 387 const DictionaryValue* tests, | 403 ScopedVector<const StringMatchTest> name_match; |
| 388 std::string* error) { | 404 ScopedVector<const StringMatchTest> value_match; |
| 389 ScopedVector<const StringMatchTest> name; | |
| 390 ScopedVector<const StringMatchTest> value; | |
| 391 | 405 |
| 392 for (DictionaryValue::key_iterator key = tests->begin_keys(); | 406 for (DictionaryValue::key_iterator key = tests->begin_keys(); |
| 393 key != tests->end_keys(); | 407 key != tests->end_keys(); |
| 394 ++key) { | 408 ++key) { |
| 395 bool is_name = false; // Is this test for header name? | 409 bool is_name = false; // Is this test for header name? |
| 396 MatchType match_type; | 410 StringMatchTest::MatchType match_type; |
| 397 if (*key == keys::kNamePrefixKey) { | 411 if (*key == keys::kNamePrefixKey) { |
| 398 is_name = true; | 412 is_name = true; |
| 399 match_type = kPrefix; | 413 match_type = StringMatchTest::kPrefix; |
| 400 } else if (*key == keys::kNameSuffixKey) { | 414 } else if (*key == keys::kNameSuffixKey) { |
| 401 is_name = true; | 415 is_name = true; |
| 402 match_type = kSuffix; | 416 match_type = StringMatchTest::kSuffix; |
| 403 } else if (*key == keys::kNameContainsKey) { | 417 } else if (*key == keys::kNameContainsKey) { |
| 404 is_name = true; | 418 is_name = true; |
| 405 match_type = kContains; | 419 match_type = StringMatchTest::kContains; |
| 406 } else if (*key == keys::kNameEqualsKey) { | 420 } else if (*key == keys::kNameEqualsKey) { |
| 407 is_name = true; | 421 is_name = true; |
| 408 match_type = kEquals; | 422 match_type = StringMatchTest::kEquals; |
| 409 } else if (*key == keys::kValuePrefixKey) { | 423 } else if (*key == keys::kValuePrefixKey) { |
| 410 match_type = kPrefix; | 424 match_type = StringMatchTest::kPrefix; |
| 411 } else if (*key == keys::kValueSuffixKey) { | 425 } else if (*key == keys::kValueSuffixKey) { |
| 412 match_type = kSuffix; | 426 match_type = StringMatchTest::kSuffix; |
| 413 } else if (*key == keys::kValueContainsKey) { | 427 } else if (*key == keys::kValueContainsKey) { |
| 414 match_type = kContains; | 428 match_type = StringMatchTest::kContains; |
| 415 } else if (*key == keys::kValueEqualsKey) { | 429 } else if (*key == keys::kValueEqualsKey) { |
| 416 match_type = kEquals; | 430 match_type = StringMatchTest::kEquals; |
| 417 } else { | 431 } else { |
| 418 NOTREACHED(); // JSON schema type checking should prevent this. | 432 NOTREACHED(); // JSON schema type checking should prevent this. |
| 419 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); | |
|
battre
2012/09/03 08:46:47
why did you remove this?
vabr (Chromium)
2012/09/03 10:12:36
In all situations where creating a HeaderMatchTest
battre
2012/09/03 12:48:08
Thanks. Makes sense.
| |
| 420 return scoped_ptr<const HeaderMatchTest>(NULL); | 433 return scoped_ptr<const HeaderMatchTest>(NULL); |
| 421 } | 434 } |
| 422 const Value* content = NULL; | 435 const Value* content = NULL; |
| 423 // This should not fire, we already checked that |key| is there. | 436 // This should not fire, we already checked that |key| is there. |
| 424 CHECK(tests->Get(*key, &content)); | 437 CHECK(tests->Get(*key, &content)); |
| 425 | 438 |
| 426 switch (content->GetType()) { | 439 switch (content->GetType()) { |
| 427 case Value::TYPE_LIST: { | 440 case Value::TYPE_LIST: { |
| 428 const ListValue* list = NULL; | 441 const ListValue* list = NULL; |
| 429 CHECK(content->GetAsList(&list)); | 442 CHECK(content->GetAsList(&list)); |
| 430 for (ListValue::const_iterator it = list->begin(); | 443 for (ListValue::const_iterator it = list->begin(); |
| 431 it != list->end(); ++it) { | 444 it != list->end(); ++it) { |
| 432 ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; | 445 ScopedVector<const StringMatchTest>* tests = |
| 446 is_name ? &name_match : &value_match; | |
| 433 tests->push_back( | 447 tests->push_back( |
| 434 CreateStringMatchTest(*it, is_name, match_type).release()); | 448 StringMatchTest::Create(*it, is_name, match_type).release()); |
| 435 } | 449 } |
| 436 break; | 450 break; |
| 437 } | 451 } |
| 438 case Value::TYPE_STRING: { | 452 case Value::TYPE_STRING: { |
| 439 ScopedVector<const StringMatchTest>* tests = is_name ? &name : &value; | 453 ScopedVector<const StringMatchTest>* tests = |
| 454 is_name ? &name_match : &value_match; | |
| 440 tests->push_back( | 455 tests->push_back( |
| 441 CreateStringMatchTest(content, is_name, match_type).release()); | 456 StringMatchTest::Create(content, is_name, match_type).release()); |
| 442 break; | 457 break; |
| 443 } | 458 } |
| 444 default: { | 459 default: { |
| 445 NOTREACHED(); // JSON schema type checking should prevent this. | 460 NOTREACHED(); // JSON schema type checking should prevent this. |
| 446 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, *key); | |
|
battre
2012/09/03 08:46:47
why did you remove this?
vabr (Chromium)
2012/09/03 10:12:36
Please see above the explanation for HeaderMatchTe
| |
| 447 return scoped_ptr<const HeaderMatchTest>(NULL); | 461 return scoped_ptr<const HeaderMatchTest>(NULL); |
| 448 } | 462 } |
| 449 } | 463 } |
| 450 } | 464 } |
| 451 | 465 |
| 452 return scoped_ptr<const HeaderMatchTest>(new HeaderMatchTest(&name, &value)); | 466 return scoped_ptr<const HeaderMatchTest>( |
| 467 new HeaderMatchTest(&name_match, &value_match)); | |
| 468 } | |
| 469 | |
| 470 bool HeaderMatcher::HeaderMatchTest::Matches(const std::string& name, | |
| 471 const std::string& value) const { | |
| 472 for (size_t i = 0; i < name_match_.size(); ++i) { | |
| 473 if (!name_match_[i]->Matches(name)) | |
| 474 return false; | |
| 475 } | |
| 476 | |
| 477 for (size_t i = 0; i < value_match_.size(); ++i) { | |
| 478 if (!value_match_[i]->Matches(value)) | |
| 479 return false; | |
| 480 } | |
| 481 | |
| 482 return true; | |
| 483 } | |
| 484 | |
| 485 // | |
| 486 // WebRequestConditionAttributeResponseHeaders | |
| 487 // | |
| 488 | |
| 489 WebRequestConditionAttributeResponseHeaders:: | |
| 490 WebRequestConditionAttributeResponseHeaders( | |
| 491 scoped_ptr<const HeaderMatcher>* header_matcher, bool positive) | |
|
battre
2012/09/03 08:46:47
nit: bool positive goes into next line (either all
vabr (Chromium)
2012/09/03 10:12:36
Done.
| |
| 492 : header_matcher_(header_matcher->Pass()), | |
| 493 positive_(positive) {} | |
| 494 | |
| 495 WebRequestConditionAttributeResponseHeaders:: | |
| 496 ~WebRequestConditionAttributeResponseHeaders() {} | |
| 497 | |
| 498 // static | |
| 499 bool WebRequestConditionAttributeResponseHeaders::IsMatchingType( | |
| 500 const std::string& instance_type) { | |
| 501 return instance_type == keys::kResponseHeadersKey || | |
| 502 instance_type == keys::kExcludeResponseHeadersKey; | |
| 453 } | 503 } |
| 454 | 504 |
| 455 // static | 505 // static |
| 456 scoped_ptr<const WebRequestConditionAttributeResponseHeaders::StringMatchTest> | 506 scoped_ptr<WebRequestConditionAttribute> |
| 457 WebRequestConditionAttributeResponseHeaders::CreateStringMatchTest( | 507 WebRequestConditionAttributeResponseHeaders::Create( |
| 458 const Value* content, | 508 const std::string& name, |
| 459 bool is_name_test, | 509 const base::Value* value, |
| 460 MatchType match_type) { | 510 std::string* error) { |
| 461 std::string str; | 511 DCHECK(IsMatchingType(name)); |
| 462 | 512 |
| 463 CHECK(content->GetAsString(&str)); | 513 const ListValue* value_as_list = NULL; |
| 464 if (is_name_test) // Header names are case-insensitive. | 514 if (!value->GetAsList(&value_as_list)) { |
| 465 StringToLowerASCII(&str); | 515 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); |
| 516 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
| 517 } | |
| 466 | 518 |
| 467 return scoped_ptr<const StringMatchTest>( | 519 scoped_ptr<const HeaderMatcher> header_matcher( |
| 468 new StringMatchTest(str, match_type)); | 520 HeaderMatcher::Create(value_as_list)); |
| 521 if (header_matcher.get() == NULL) { | |
| 522 *error = ExtensionErrorUtils::FormatErrorMessage(kInvalidValue, name); | |
| 523 return scoped_ptr<WebRequestConditionAttribute>(NULL); | |
| 524 } | |
| 525 | |
| 526 const bool positive = name == keys::kResponseHeadersKey; | |
|
battre
2012/09/03 08:46:47
nit: no need for const
vabr (Chromium)
2012/09/03 10:12:36
I disagree. I want to document that |positive| is
battre
2012/09/03 12:48:08
I think the downside is the additional amount of c
| |
| 527 return scoped_ptr<WebRequestConditionAttribute>( | |
| 528 new WebRequestConditionAttributeResponseHeaders( | |
| 529 &header_matcher, positive)); | |
| 530 } | |
| 531 | |
| 532 int WebRequestConditionAttributeResponseHeaders::GetStages() const { | |
| 533 return ON_HEADERS_RECEIVED; | |
| 534 } | |
| 535 | |
| 536 bool WebRequestConditionAttributeResponseHeaders::IsFulfilled( | |
| 537 const WebRequestRule::RequestData& request_data) const { | |
| 538 if (!(request_data.stage & GetStages())) | |
| 539 return false; | |
| 540 | |
| 541 const net::HttpResponseHeaders* headers = | |
| 542 request_data.original_response_headers; | |
| 543 if (headers == NULL) { | |
| 544 // Each header of an empty set satisfies (the negation of) everything; | |
| 545 // OTOH, there is no header to satisfy even the most permissive test. | |
| 546 return !positive_; | |
| 547 } | |
| 548 | |
| 549 bool passed = false; // Did some header pass TestNameValue? | |
| 550 std::string name; | |
| 551 std::string value; | |
| 552 void* iter = NULL; | |
| 553 while (!passed && headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 554 StringToLowerASCII(&name); // Header names are case-insensitive. | |
| 555 passed |= header_matcher_->TestNameValue(name, value); | |
| 556 } | |
| 557 | |
| 558 return (positive_ ? passed : !passed); | |
| 559 } | |
| 560 | |
| 561 WebRequestConditionAttribute::Type | |
| 562 WebRequestConditionAttributeResponseHeaders::GetType() const { | |
| 563 return CONDITION_RESPONSE_HEADERS; | |
| 469 } | 564 } |
| 470 | 565 |
| 471 } // namespace extensions | 566 } // namespace extensions |
| OLD | NEW |