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

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: Typo in a comment 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"
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698