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

Side by Side Diff: chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc

Issue 10923002: Extracting header testing code from WebRequestConditionAttributeResponseHeaders. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Correction of a negated condition and more unit-tests Created 8 years, 3 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698