root/chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Equals
  2. Create
  3. WebRequestConditionAttributeResourceType
  4. WebRequestConditionAttributeResourceType
  5. Create
  6. GetStages
  7. IsFulfilled
  8. GetType
  9. GetName
  10. Equals
  11. inclusive_
  12. WebRequestConditionAttributeContentType
  13. Create
  14. GetStages
  15. IsFulfilled
  16. GetType
  17. GetName
  18. Equals
  19. Create
  20. TestNameValue
  21. Create
  22. Matches
  23. case_sensitive_
  24. value_match_
  25. Create
  26. Matches
  27. positive_
  28. WebRequestConditionAttributeRequestHeaders
  29. PrepareHeaderMatcher
  30. Create
  31. GetStages
  32. IsFulfilled
  33. GetType
  34. GetName
  35. Equals
  36. positive_
  37. WebRequestConditionAttributeResponseHeaders
  38. Create
  39. GetStages
  40. IsFulfilled
  41. GetType
  42. GetName
  43. Equals
  44. WebRequestConditionAttributeThirdParty
  45. WebRequestConditionAttributeThirdParty
  46. Create
  47. GetStages
  48. IsFulfilled
  49. GetType
  50. GetName
  51. Equals
  52. WebRequestConditionAttributeStages
  53. WebRequestConditionAttributeStages
  54. ParseListOfStages
  55. Create
  56. GetStages
  57. IsFulfilled
  58. GetType
  59. GetName
  60. Equals

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h"

#include <algorithm>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/extensions/api/declarative/deduping_factory.h"
#include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h"
#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
#include "content/public/browser/resource_request_info.h"
#include "extensions/common/error_utils.h"
#include "net/base/net_errors.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/static_cookie_policy.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request.h"

using base::CaseInsensitiveCompareASCII;
using base::DictionaryValue;
using base::ListValue;
using base::StringValue;
using base::Value;

namespace helpers = extension_web_request_api_helpers;
namespace keys = extensions::declarative_webrequest_constants;

namespace extensions {

namespace {
// Error messages.
const char kInvalidValue[] = "Condition '*' has an invalid value";

struct WebRequestConditionAttributeFactory {
  DedupingFactory<WebRequestConditionAttribute> factory;

  WebRequestConditionAttributeFactory() : factory(5) {
    factory.RegisterFactoryMethod(
        keys::kResourceTypeKey,
        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
        &WebRequestConditionAttributeResourceType::Create);

    factory.RegisterFactoryMethod(
        keys::kContentTypeKey,
        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
        &WebRequestConditionAttributeContentType::Create);
    factory.RegisterFactoryMethod(
        keys::kExcludeContentTypeKey,
        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
        &WebRequestConditionAttributeContentType::Create);

    factory.RegisterFactoryMethod(
        keys::kRequestHeadersKey,
        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
        &WebRequestConditionAttributeRequestHeaders::Create);
    factory.RegisterFactoryMethod(
        keys::kExcludeRequestHeadersKey,
        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
        &WebRequestConditionAttributeRequestHeaders::Create);

    factory.RegisterFactoryMethod(
        keys::kResponseHeadersKey,
        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
        &WebRequestConditionAttributeResponseHeaders::Create);
    factory.RegisterFactoryMethod(
        keys::kExcludeResponseHeadersKey,
        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
        &WebRequestConditionAttributeResponseHeaders::Create);

    factory.RegisterFactoryMethod(
        keys::kThirdPartyKey,
        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
        &WebRequestConditionAttributeThirdParty::Create);

    factory.RegisterFactoryMethod(
        keys::kStagesKey,
        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
        &WebRequestConditionAttributeStages::Create);
  }
};

base::LazyInstance<WebRequestConditionAttributeFactory>::Leaky
    g_web_request_condition_attribute_factory = LAZY_INSTANCE_INITIALIZER;

}  // namespace

//
// WebRequestConditionAttribute
//

WebRequestConditionAttribute::WebRequestConditionAttribute() {}

WebRequestConditionAttribute::~WebRequestConditionAttribute() {}

bool WebRequestConditionAttribute::Equals(
    const WebRequestConditionAttribute* other) const {
  return GetType() == other->GetType();
}

// static
scoped_refptr<const WebRequestConditionAttribute>
WebRequestConditionAttribute::Create(
    const std::string& name,
    const base::Value* value,
    std::string* error) {
  CHECK(value != NULL && error != NULL);
  bool bad_message = false;
  return g_web_request_condition_attribute_factory.Get().factory.Instantiate(
      name, value, error, &bad_message);
}

//
// WebRequestConditionAttributeResourceType
//

WebRequestConditionAttributeResourceType::
WebRequestConditionAttributeResourceType(
    const std::vector<ResourceType::Type>& types)
    : types_(types) {}

WebRequestConditionAttributeResourceType::
~WebRequestConditionAttributeResourceType() {}

// static
scoped_refptr<const WebRequestConditionAttribute>
WebRequestConditionAttributeResourceType::Create(
    const std::string& instance_type,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  DCHECK(instance_type == keys::kResourceTypeKey);
  const base::ListValue* value_as_list = NULL;
  if (!value->GetAsList(&value_as_list)) {
    *error = ErrorUtils::FormatErrorMessage(kInvalidValue,
                                            keys::kResourceTypeKey);
    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
  }

  size_t number_types = value_as_list->GetSize();

  std::vector<ResourceType::Type> passed_types;
  passed_types.reserve(number_types);
  for (size_t i = 0; i < number_types; ++i) {
    std::string resource_type_string;
    ResourceType::Type type = ResourceType::LAST_TYPE;
    if (!value_as_list->GetString(i, &resource_type_string) ||
        !helpers::ParseResourceType(resource_type_string, &type)) {
      *error = ErrorUtils::FormatErrorMessage(kInvalidValue,
                                              keys::kResourceTypeKey);
      return scoped_refptr<const WebRequestConditionAttribute>(NULL);
    }
    passed_types.push_back(type);
  }

  return scoped_refptr<const WebRequestConditionAttribute>(
      new WebRequestConditionAttributeResourceType(passed_types));
}

int WebRequestConditionAttributeResourceType::GetStages() const {
  return ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | ON_SEND_HEADERS |
      ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED | ON_BEFORE_REDIRECT |
      ON_RESPONSE_STARTED | ON_COMPLETED | ON_ERROR;
}

bool WebRequestConditionAttributeResourceType::IsFulfilled(
    const WebRequestData& request_data) const {
  if (!(request_data.stage & GetStages()))
    return false;
  const content::ResourceRequestInfo* info =
      content::ResourceRequestInfo::ForRequest(request_data.request);
  if (!info)
    return false;
  return std::find(types_.begin(), types_.end(), info->GetResourceType()) !=
      types_.end();
}

WebRequestConditionAttribute::Type
WebRequestConditionAttributeResourceType::GetType() const {
  return CONDITION_RESOURCE_TYPE;
}

std::string WebRequestConditionAttributeResourceType::GetName() const {
  return keys::kResourceTypeKey;
}

bool WebRequestConditionAttributeResourceType::Equals(
    const WebRequestConditionAttribute* other) const {
  if (!WebRequestConditionAttribute::Equals(other))
    return false;
  const WebRequestConditionAttributeResourceType* casted_other =
      static_cast<const WebRequestConditionAttributeResourceType*>(other);
  return types_ == casted_other->types_;
}

//
// WebRequestConditionAttributeContentType
//

WebRequestConditionAttributeContentType::
WebRequestConditionAttributeContentType(
    const std::vector<std::string>& content_types,
    bool inclusive)
    : content_types_(content_types),
      inclusive_(inclusive) {}

WebRequestConditionAttributeContentType::
~WebRequestConditionAttributeContentType() {}

// static
scoped_refptr<const WebRequestConditionAttribute>
WebRequestConditionAttributeContentType::Create(
      const std::string& name,
      const base::Value* value,
      std::string* error,
      bool* bad_message) {
  DCHECK(name == keys::kContentTypeKey || name == keys::kExcludeContentTypeKey);

  const base::ListValue* value_as_list = NULL;
  if (!value->GetAsList(&value_as_list)) {
    *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name);
    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
  }
  std::vector<std::string> content_types;
  for (base::ListValue::const_iterator it = value_as_list->begin();
       it != value_as_list->end(); ++it) {
    std::string content_type;
    if (!(*it)->GetAsString(&content_type)) {
      *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name);
      return scoped_refptr<const WebRequestConditionAttribute>(NULL);
    }
    content_types.push_back(content_type);
  }

  return scoped_refptr<const WebRequestConditionAttribute>(
      new WebRequestConditionAttributeContentType(
          content_types, name == keys::kContentTypeKey));
}

int WebRequestConditionAttributeContentType::GetStages() const {
  return ON_HEADERS_RECEIVED;
}

bool WebRequestConditionAttributeContentType::IsFulfilled(
    const WebRequestData& request_data) const {
  if (!(request_data.stage & GetStages()))
    return false;
  std::string content_type;
  request_data.original_response_headers->GetNormalizedHeader(
      net::HttpRequestHeaders::kContentType, &content_type);
  std::string mime_type;
  std::string charset;
  bool had_charset = false;
  net::HttpUtil::ParseContentType(
      content_type, &mime_type, &charset, &had_charset, NULL);

  if (inclusive_) {
    return std::find(content_types_.begin(), content_types_.end(),
                     mime_type) != content_types_.end();
  } else {
    return std::find(content_types_.begin(), content_types_.end(),
                     mime_type) == content_types_.end();
  }
}

WebRequestConditionAttribute::Type
WebRequestConditionAttributeContentType::GetType() const {
  return CONDITION_CONTENT_TYPE;
}

std::string WebRequestConditionAttributeContentType::GetName() const {
  return (inclusive_ ? keys::kContentTypeKey : keys::kExcludeContentTypeKey);
}

bool WebRequestConditionAttributeContentType::Equals(
    const WebRequestConditionAttribute* other) const {
  if (!WebRequestConditionAttribute::Equals(other))
    return false;
  const WebRequestConditionAttributeContentType* casted_other =
      static_cast<const WebRequestConditionAttributeContentType*>(other);
  return content_types_ == casted_other->content_types_ &&
         inclusive_ == casted_other->inclusive_;
}

// Manages a set of tests to be applied to name-value pairs representing
// headers. This is a helper class to header-related condition attributes.
// It contains a set of test groups. A name-value pair satisfies the whole
// set of test groups iff it passes at least one test group.
class HeaderMatcher {
 public:
  ~HeaderMatcher();

  // Creates an instance based on a list |tests| of test groups, encoded as
  // dictionaries of the type declarativeWebRequest.HeaderFilter (see
  // declarative_web_request.json).
  static scoped_ptr<const HeaderMatcher> Create(const base::ListValue* tests);

  // Does |this| match the header "|name|: |value|"?
  bool TestNameValue(const std::string& name, const std::string& value) const;

 private:
  // Represents a single string-matching test.
  class StringMatchTest {
   public:
    enum MatchType { kPrefix, kSuffix, kEquals, kContains };

    // |data| is the pattern to be matched in the position given by |type|.
    // Note that |data| must point to a StringValue object.
    static scoped_ptr<StringMatchTest> Create(const base::Value* data,
                                              MatchType type,
                                              bool case_sensitive);
    ~StringMatchTest();

    // Does |str| pass |this| StringMatchTest?
    bool Matches(const std::string& str) const;

   private:
    StringMatchTest(const std::string& data,
                    MatchType type,
                    bool case_sensitive);

    const std::string data_;
    const MatchType type_;
    const bool case_sensitive_;
    DISALLOW_COPY_AND_ASSIGN(StringMatchTest);
  };

  // Represents a test group -- a set of string matching tests to be applied to
  // both the header name and value.
  class HeaderMatchTest {
   public:
    ~HeaderMatchTest();

    // Gets the test group description in |tests| and creates the corresponding
    // HeaderMatchTest. On failure returns NULL.
    static scoped_ptr<const HeaderMatchTest> Create(
        const base::DictionaryValue* tests);

    // Does the header "|name|: |value|" match all tests in |this|?
    bool Matches(const std::string& name, const std::string& value) const;

   private:
    // Takes ownership of the content of both |name_match| and |value_match|.
    HeaderMatchTest(ScopedVector<const StringMatchTest>* name_match,
                    ScopedVector<const StringMatchTest>* value_match);

    // Tests to be passed by a header's name.
    const ScopedVector<const StringMatchTest> name_match_;
    // Tests to be passed by a header's value.
    const ScopedVector<const StringMatchTest> value_match_;
    DISALLOW_COPY_AND_ASSIGN(HeaderMatchTest);
  };

  explicit HeaderMatcher(ScopedVector<const HeaderMatchTest>* tests);

  const ScopedVector<const HeaderMatchTest> tests_;

  DISALLOW_COPY_AND_ASSIGN(HeaderMatcher);
};

// HeaderMatcher implementation.

HeaderMatcher::~HeaderMatcher() {}

// static
scoped_ptr<const HeaderMatcher> HeaderMatcher::Create(
    const base::ListValue* tests) {
  ScopedVector<const HeaderMatchTest> header_tests;
  for (base::ListValue::const_iterator it = tests->begin();
       it != tests->end(); ++it) {
    const base::DictionaryValue* tests = NULL;
    if (!(*it)->GetAsDictionary(&tests))
      return scoped_ptr<const HeaderMatcher>();

    scoped_ptr<const HeaderMatchTest> header_test(
        HeaderMatchTest::Create(tests));
    if (header_test.get() == NULL)
      return scoped_ptr<const HeaderMatcher>();
    header_tests.push_back(header_test.release());
  }

  return scoped_ptr<const HeaderMatcher>(new HeaderMatcher(&header_tests));
}

bool HeaderMatcher::TestNameValue(const std::string& name,
                                  const std::string& value) const {
  for (size_t i = 0; i < tests_.size(); ++i) {
    if (tests_[i]->Matches(name, value))
      return true;
  }
  return false;
}

HeaderMatcher::HeaderMatcher(ScopedVector<const HeaderMatchTest>* tests)
  : tests_(tests->Pass()) {}

// HeaderMatcher::StringMatchTest implementation.

// static
scoped_ptr<HeaderMatcher::StringMatchTest>
HeaderMatcher::StringMatchTest::Create(const base::Value* data,
                                       MatchType type,
                                       bool case_sensitive) {
  std::string str;
  CHECK(data->GetAsString(&str));
  return scoped_ptr<StringMatchTest>(
      new StringMatchTest(str, type, case_sensitive));
}

HeaderMatcher::StringMatchTest::~StringMatchTest() {}

bool HeaderMatcher::StringMatchTest::Matches(
    const std::string& str) const {
  switch (type_) {
    case kPrefix:
      return StartsWithASCII(str, data_, case_sensitive_);
    case kSuffix:
      return EndsWith(str, data_, case_sensitive_);
    case kEquals:
      return str.size() == data_.size() &&
             StartsWithASCII(str, data_, case_sensitive_);
    case kContains:
      if (!case_sensitive_) {
        return std::search(str.begin(), str.end(), data_.begin(), data_.end(),
                           CaseInsensitiveCompareASCII<char>()) != str.end();
      } else {
        return str.find(data_) != std::string::npos;
      }
  }
  // We never get past the "switch", but the compiler worries about no return.
  NOTREACHED();
  return false;
}

HeaderMatcher::StringMatchTest::StringMatchTest(const std::string& data,
                                                MatchType type,
                                                bool case_sensitive)
    : data_(data),
      type_(type),
      case_sensitive_(case_sensitive) {}

// HeaderMatcher::HeaderMatchTest implementation.

HeaderMatcher::HeaderMatchTest::HeaderMatchTest(
    ScopedVector<const StringMatchTest>* name_match,
    ScopedVector<const StringMatchTest>* value_match)
    : name_match_(name_match->Pass()),
      value_match_(value_match->Pass()) {}

HeaderMatcher::HeaderMatchTest::~HeaderMatchTest() {}

// static
scoped_ptr<const HeaderMatcher::HeaderMatchTest>
HeaderMatcher::HeaderMatchTest::Create(const base::DictionaryValue* tests) {
  ScopedVector<const StringMatchTest> name_match;
  ScopedVector<const StringMatchTest> value_match;

  for (base::DictionaryValue::Iterator it(*tests);
       !it.IsAtEnd(); it.Advance()) {
    bool is_name = false;  // Is this test for header name?
    StringMatchTest::MatchType match_type;
    if (it.key() == keys::kNamePrefixKey) {
      is_name = true;
      match_type = StringMatchTest::kPrefix;
    } else if (it.key() == keys::kNameSuffixKey) {
      is_name = true;
      match_type = StringMatchTest::kSuffix;
    } else if (it.key() == keys::kNameContainsKey) {
      is_name = true;
      match_type = StringMatchTest::kContains;
    } else if (it.key() == keys::kNameEqualsKey) {
      is_name = true;
      match_type = StringMatchTest::kEquals;
    } else if (it.key() == keys::kValuePrefixKey) {
      match_type = StringMatchTest::kPrefix;
    } else if (it.key() == keys::kValueSuffixKey) {
      match_type = StringMatchTest::kSuffix;
    } else if (it.key() == keys::kValueContainsKey) {
      match_type = StringMatchTest::kContains;
    } else if (it.key() == keys::kValueEqualsKey) {
      match_type = StringMatchTest::kEquals;
    } else {
      NOTREACHED();  // JSON schema type checking should prevent this.
      return scoped_ptr<const HeaderMatchTest>();
    }
    const base::Value* content = &it.value();

    ScopedVector<const StringMatchTest>* tests =
        is_name ? &name_match : &value_match;
    switch (content->GetType()) {
      case base::Value::TYPE_LIST: {
        const base::ListValue* list = NULL;
        CHECK(content->GetAsList(&list));
        for (base::ListValue::const_iterator it = list->begin();
             it != list->end(); ++it) {
          tests->push_back(
              StringMatchTest::Create(*it, match_type, !is_name).release());
        }
        break;
      }
      case base::Value::TYPE_STRING: {
        tests->push_back(
            StringMatchTest::Create(content, match_type, !is_name).release());
        break;
      }
      default: {
        NOTREACHED();  // JSON schema type checking should prevent this.
        return scoped_ptr<const HeaderMatchTest>();
      }
    }
  }

  return scoped_ptr<const HeaderMatchTest>(
      new HeaderMatchTest(&name_match, &value_match));
}

bool HeaderMatcher::HeaderMatchTest::Matches(const std::string& name,
                                             const std::string& value) const {
  for (size_t i = 0; i < name_match_.size(); ++i) {
    if (!name_match_[i]->Matches(name))
      return false;
  }

  for (size_t i = 0; i < value_match_.size(); ++i) {
    if (!value_match_[i]->Matches(value))
      return false;
  }

  return true;
}

//
// WebRequestConditionAttributeRequestHeaders
//

WebRequestConditionAttributeRequestHeaders::
WebRequestConditionAttributeRequestHeaders(
    scoped_ptr<const HeaderMatcher> header_matcher,
    bool positive)
    : header_matcher_(header_matcher.Pass()),
      positive_(positive) {}

WebRequestConditionAttributeRequestHeaders::
~WebRequestConditionAttributeRequestHeaders() {}

namespace {

scoped_ptr<const HeaderMatcher> PrepareHeaderMatcher(
    const std::string& name,
    const base::Value* value,
    std::string* error) {
  const base::ListValue* value_as_list = NULL;
  if (!value->GetAsList(&value_as_list)) {
    *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name);
    return scoped_ptr<const HeaderMatcher>();
  }

  scoped_ptr<const HeaderMatcher> header_matcher(
      HeaderMatcher::Create(value_as_list));
  if (header_matcher.get() == NULL)
    *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name);
  return header_matcher.Pass();
}

}  // namespace

// static
scoped_refptr<const WebRequestConditionAttribute>
WebRequestConditionAttributeRequestHeaders::Create(
    const std::string& name,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  DCHECK(name == keys::kRequestHeadersKey ||
         name == keys::kExcludeRequestHeadersKey);

  scoped_ptr<const HeaderMatcher> header_matcher(
      PrepareHeaderMatcher(name, value, error));
  if (header_matcher.get() == NULL)
    return scoped_refptr<const WebRequestConditionAttribute>(NULL);

  return scoped_refptr<const WebRequestConditionAttribute>(
      new WebRequestConditionAttributeRequestHeaders(
          header_matcher.Pass(), name == keys::kRequestHeadersKey));
}

int WebRequestConditionAttributeRequestHeaders::GetStages() const {
  // Currently we only allow matching against headers in the before-send-headers
  // stage. The headers are accessible in other stages as well, but before
  // allowing to match against them in further stages, we should consider
  // caching the match result.
  return ON_BEFORE_SEND_HEADERS;
}

bool WebRequestConditionAttributeRequestHeaders::IsFulfilled(
    const WebRequestData& request_data) const {
  if (!(request_data.stage & GetStages()))
    return false;

  const net::HttpRequestHeaders& headers =
      request_data.request->extra_request_headers();

  bool passed = false;  // Did some header pass TestNameValue?
  net::HttpRequestHeaders::Iterator it(headers);
  while (!passed && it.GetNext())
    passed |= header_matcher_->TestNameValue(it.name(), it.value());

  return (positive_ ? passed : !passed);
}

WebRequestConditionAttribute::Type
WebRequestConditionAttributeRequestHeaders::GetType() const {
  return CONDITION_REQUEST_HEADERS;
}

std::string WebRequestConditionAttributeRequestHeaders::GetName() const {
  return (positive_ ? keys::kRequestHeadersKey
                    : keys::kExcludeRequestHeadersKey);
}

bool WebRequestConditionAttributeRequestHeaders::Equals(
    const WebRequestConditionAttribute* other) const {
  // Comparing headers is too heavy, so we skip it entirely.
  return false;
}

//
// WebRequestConditionAttributeResponseHeaders
//

WebRequestConditionAttributeResponseHeaders::
WebRequestConditionAttributeResponseHeaders(
    scoped_ptr<const HeaderMatcher> header_matcher,
    bool positive)
    : header_matcher_(header_matcher.Pass()),
      positive_(positive) {}

WebRequestConditionAttributeResponseHeaders::
~WebRequestConditionAttributeResponseHeaders() {}

// static
scoped_refptr<const WebRequestConditionAttribute>
WebRequestConditionAttributeResponseHeaders::Create(
    const std::string& name,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  DCHECK(name == keys::kResponseHeadersKey ||
         name == keys::kExcludeResponseHeadersKey);

  scoped_ptr<const HeaderMatcher> header_matcher(
      PrepareHeaderMatcher(name, value, error));
  if (header_matcher.get() == NULL)
    return scoped_refptr<const WebRequestConditionAttribute>(NULL);

  return scoped_refptr<const WebRequestConditionAttribute>(
      new WebRequestConditionAttributeResponseHeaders(
          header_matcher.Pass(), name == keys::kResponseHeadersKey));
}

int WebRequestConditionAttributeResponseHeaders::GetStages() const {
  return ON_HEADERS_RECEIVED;
}

bool WebRequestConditionAttributeResponseHeaders::IsFulfilled(
    const WebRequestData& request_data) const {
  if (!(request_data.stage & GetStages()))
    return false;

  const net::HttpResponseHeaders* headers =
      request_data.original_response_headers;
  if (headers == NULL) {
    // Each header of an empty set satisfies (the negation of) everything;
    // OTOH, there is no header to satisfy even the most permissive test.
    return !positive_;
  }

  bool passed = false;  // Did some header pass TestNameValue?
  std::string name;
  std::string value;
  void* iter = NULL;
  while (!passed && headers->EnumerateHeaderLines(&iter, &name, &value)) {
    passed |= header_matcher_->TestNameValue(name, value);
  }

  return (positive_ ? passed : !passed);
}

WebRequestConditionAttribute::Type
WebRequestConditionAttributeResponseHeaders::GetType() const {
  return CONDITION_RESPONSE_HEADERS;
}

std::string WebRequestConditionAttributeResponseHeaders::GetName() const {
  return (positive_ ? keys::kResponseHeadersKey
                    : keys::kExcludeResponseHeadersKey);
}

bool WebRequestConditionAttributeResponseHeaders::Equals(
    const WebRequestConditionAttribute* other) const {
  return false;
}

//
// WebRequestConditionAttributeThirdParty
//

WebRequestConditionAttributeThirdParty::
WebRequestConditionAttributeThirdParty(bool match_third_party)
    : match_third_party_(match_third_party) {}

WebRequestConditionAttributeThirdParty::
~WebRequestConditionAttributeThirdParty() {}

// static
scoped_refptr<const WebRequestConditionAttribute>
WebRequestConditionAttributeThirdParty::Create(
    const std::string& name,
    const base::Value* value,
    std::string* error,
    bool* bad_message) {
  DCHECK(name == keys::kThirdPartyKey);

  bool third_party = false;  // Dummy value, gets overwritten.
  if (!value->GetAsBoolean(&third_party)) {
    *error = ErrorUtils::FormatErrorMessage(kInvalidValue,
                                                     keys::kThirdPartyKey);
    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
  }

  return scoped_refptr<const WebRequestConditionAttribute>(
      new WebRequestConditionAttributeThirdParty(third_party));
}

int WebRequestConditionAttributeThirdParty::GetStages() const {
  return ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | ON_SEND_HEADERS |
      ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED | ON_BEFORE_REDIRECT |
      ON_RESPONSE_STARTED | ON_COMPLETED | ON_ERROR;
}

bool WebRequestConditionAttributeThirdParty::IsFulfilled(
    const WebRequestData& request_data) const {
  if (!(request_data.stage & GetStages()))
    return false;

  // Request is "1st party" if it gets cookies under 3rd party-blocking policy.
  const net::StaticCookiePolicy block_third_party_policy(
      net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES);
  const int can_get_cookies = block_third_party_policy.CanGetCookies(
          request_data.request->url(),
          request_data.request->first_party_for_cookies());
  const bool is_first_party = (can_get_cookies == net::OK);

  return match_third_party_ ? !is_first_party : is_first_party;
}

WebRequestConditionAttribute::Type
WebRequestConditionAttributeThirdParty::GetType() const {
  return CONDITION_THIRD_PARTY;
}

std::string WebRequestConditionAttributeThirdParty::GetName() const {
  return keys::kThirdPartyKey;
}

bool WebRequestConditionAttributeThirdParty::Equals(
    const WebRequestConditionAttribute* other) const {
  if (!WebRequestConditionAttribute::Equals(other))
    return false;
  const WebRequestConditionAttributeThirdParty* casted_other =
      static_cast<const WebRequestConditionAttributeThirdParty*>(other);
  return match_third_party_ == casted_other->match_third_party_;
}

//
// WebRequestConditionAttributeStages
//

WebRequestConditionAttributeStages::
WebRequestConditionAttributeStages(int allowed_stages)
    : allowed_stages_(allowed_stages) {}

WebRequestConditionAttributeStages::
~WebRequestConditionAttributeStages() {}

namespace {

// Reads strings stored in |value|, which is expected to be a ListValue, and
// sets corresponding bits (see RequestStage) in |out_stages|. Returns true on
// success, false otherwise.
bool ParseListOfStages(const base::Value& value, int* out_stages) {
  const base::ListValue* list = NULL;
  if (!value.GetAsList(&list))
    return false;

  int stages = 0;
  std::string stage_name;
  for (base::ListValue::const_iterator it = list->begin();
       it != list->end(); ++it) {
    if (!((*it)->GetAsString(&stage_name)))
      return false;
    if (stage_name == keys::kOnBeforeRequestEnum) {
      stages |= ON_BEFORE_REQUEST;
    } else if (stage_name == keys::kOnBeforeSendHeadersEnum) {
      stages |= ON_BEFORE_SEND_HEADERS;
    } else if (stage_name == keys::kOnHeadersReceivedEnum) {
      stages |= ON_HEADERS_RECEIVED;
    } else if (stage_name == keys::kOnAuthRequiredEnum) {
      stages |= ON_AUTH_REQUIRED;
    } else {
      NOTREACHED();  // JSON schema checks prevent getting here.
      return false;
    }
  }

  *out_stages = stages;
  return true;
}

}  // namespace

// static
scoped_refptr<const WebRequestConditionAttribute>
WebRequestConditionAttributeStages::Create(const std::string& name,
                                           const base::Value* value,
                                           std::string* error,
                                           bool* bad_message) {
  DCHECK(name == keys::kStagesKey);

  int allowed_stages = 0;
  if (!ParseListOfStages(*value, &allowed_stages)) {
    *error = ErrorUtils::FormatErrorMessage(kInvalidValue,
                                                     keys::kStagesKey);
    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
  }

  return scoped_refptr<const WebRequestConditionAttribute>(
      new WebRequestConditionAttributeStages(allowed_stages));
}

int WebRequestConditionAttributeStages::GetStages() const {
  return allowed_stages_;
}

bool WebRequestConditionAttributeStages::IsFulfilled(
    const WebRequestData& request_data) const {
  // Note: removing '!=' triggers warning C4800 on the VS compiler.
  return (request_data.stage & GetStages()) != 0;
}

WebRequestConditionAttribute::Type
WebRequestConditionAttributeStages::GetType() const {
  return CONDITION_STAGES;
}

std::string WebRequestConditionAttributeStages::GetName() const {
  return keys::kStagesKey;
}

bool WebRequestConditionAttributeStages::Equals(
    const WebRequestConditionAttribute* other) const {
  if (!WebRequestConditionAttribute::Equals(other))
    return false;
  const WebRequestConditionAttributeStages* casted_other =
      static_cast<const WebRequestConditionAttributeStages*>(other);
  return allowed_stages_ == casted_other->allowed_stages_;
}

}  // namespace extensions

/* [<][>][^][v][top][bottom][index][help] */