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