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
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h"
6
7#include <algorithm>
8
9#include "base/lazy_instance.h"
10#include "base/logging.h"
11#include "base/strings/string_util.h"
12#include "base/strings/stringprintf.h"
13#include "base/values.h"
14#include "chrome/browser/extensions/api/declarative/deduping_factory.h"
15#include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
16#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h"
17#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
18#include "chrome/browser/extensions/api/web_request/web_request_api_helpers.h"
19#include "content/public/browser/resource_request_info.h"
20#include "extensions/common/error_utils.h"
21#include "net/base/net_errors.h"
22#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
23#include "net/base/static_cookie_policy.h"
24#include "net/http/http_request_headers.h"
25#include "net/http/http_util.h"
26#include "net/url_request/url_request.h"
27
28using base::CaseInsensitiveCompareASCII;
29using base::DictionaryValue;
30using base::ListValue;
31using base::StringValue;
32using base::Value;
33
34namespace helpers = extension_web_request_api_helpers;
35namespace keys = extensions::declarative_webrequest_constants;
36
37namespace extensions {
38
39namespace {
40// Error messages.
41const char kInvalidValue[] = "Condition '*' has an invalid value";
42
43struct WebRequestConditionAttributeFactory {
44  DedupingFactory<WebRequestConditionAttribute> factory;
45
46  WebRequestConditionAttributeFactory() : factory(5) {
47    factory.RegisterFactoryMethod(
48        keys::kResourceTypeKey,
49        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
50        &WebRequestConditionAttributeResourceType::Create);
51
52    factory.RegisterFactoryMethod(
53        keys::kContentTypeKey,
54        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
55        &WebRequestConditionAttributeContentType::Create);
56    factory.RegisterFactoryMethod(
57        keys::kExcludeContentTypeKey,
58        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
59        &WebRequestConditionAttributeContentType::Create);
60
61    factory.RegisterFactoryMethod(
62        keys::kRequestHeadersKey,
63        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
64        &WebRequestConditionAttributeRequestHeaders::Create);
65    factory.RegisterFactoryMethod(
66        keys::kExcludeRequestHeadersKey,
67        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
68        &WebRequestConditionAttributeRequestHeaders::Create);
69
70    factory.RegisterFactoryMethod(
71        keys::kResponseHeadersKey,
72        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
73        &WebRequestConditionAttributeResponseHeaders::Create);
74    factory.RegisterFactoryMethod(
75        keys::kExcludeResponseHeadersKey,
76        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
77        &WebRequestConditionAttributeResponseHeaders::Create);
78
79    factory.RegisterFactoryMethod(
80        keys::kThirdPartyKey,
81        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
82        &WebRequestConditionAttributeThirdParty::Create);
83
84    factory.RegisterFactoryMethod(
85        keys::kStagesKey,
86        DedupingFactory<WebRequestConditionAttribute>::IS_PARAMETERIZED,
87        &WebRequestConditionAttributeStages::Create);
88  }
89};
90
91base::LazyInstance<WebRequestConditionAttributeFactory>::Leaky
92    g_web_request_condition_attribute_factory = LAZY_INSTANCE_INITIALIZER;
93
94}  // namespace
95
96//
97// WebRequestConditionAttribute
98//
99
100WebRequestConditionAttribute::WebRequestConditionAttribute() {}
101
102WebRequestConditionAttribute::~WebRequestConditionAttribute() {}
103
104bool WebRequestConditionAttribute::Equals(
105    const WebRequestConditionAttribute* other) const {
106  return GetType() == other->GetType();
107}
108
109// static
110scoped_refptr<const WebRequestConditionAttribute>
111WebRequestConditionAttribute::Create(
112    const std::string& name,
113    const base::Value* value,
114    std::string* error) {
115  CHECK(value != NULL && error != NULL);
116  bool bad_message = false;
117  return g_web_request_condition_attribute_factory.Get().factory.Instantiate(
118      name, value, error, &bad_message);
119}
120
121//
122// WebRequestConditionAttributeResourceType
123//
124
125WebRequestConditionAttributeResourceType::
126WebRequestConditionAttributeResourceType(
127    const std::vector<ResourceType::Type>& types)
128    : types_(types) {}
129
130WebRequestConditionAttributeResourceType::
131~WebRequestConditionAttributeResourceType() {}
132
133// static
134scoped_refptr<const WebRequestConditionAttribute>
135WebRequestConditionAttributeResourceType::Create(
136    const std::string& instance_type,
137    const base::Value* value,
138    std::string* error,
139    bool* bad_message) {
140  DCHECK(instance_type == keys::kResourceTypeKey);
141  const ListValue* value_as_list = NULL;
142  if (!value->GetAsList(&value_as_list)) {
143    *error = ErrorUtils::FormatErrorMessage(kInvalidValue,
144                                            keys::kResourceTypeKey);
145    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
146  }
147
148  size_t number_types = value_as_list->GetSize();
149
150  std::vector<ResourceType::Type> passed_types;
151  passed_types.reserve(number_types);
152  for (size_t i = 0; i < number_types; ++i) {
153    std::string resource_type_string;
154    ResourceType::Type type = ResourceType::LAST_TYPE;
155    if (!value_as_list->GetString(i, &resource_type_string) ||
156        !helpers::ParseResourceType(resource_type_string, &type)) {
157      *error = ErrorUtils::FormatErrorMessage(kInvalidValue,
158                                              keys::kResourceTypeKey);
159      return scoped_refptr<const WebRequestConditionAttribute>(NULL);
160    }
161    passed_types.push_back(type);
162  }
163
164  return scoped_refptr<const WebRequestConditionAttribute>(
165      new WebRequestConditionAttributeResourceType(passed_types));
166}
167
168int WebRequestConditionAttributeResourceType::GetStages() const {
169  return ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | ON_SEND_HEADERS |
170      ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED | ON_BEFORE_REDIRECT |
171      ON_RESPONSE_STARTED | ON_COMPLETED | ON_ERROR;
172}
173
174bool WebRequestConditionAttributeResourceType::IsFulfilled(
175    const WebRequestData& request_data) const {
176  if (!(request_data.stage & GetStages()))
177    return false;
178  const content::ResourceRequestInfo* info =
179      content::ResourceRequestInfo::ForRequest(request_data.request);
180  if (!info)
181    return false;
182  return std::find(types_.begin(), types_.end(), info->GetResourceType()) !=
183      types_.end();
184}
185
186WebRequestConditionAttribute::Type
187WebRequestConditionAttributeResourceType::GetType() const {
188  return CONDITION_RESOURCE_TYPE;
189}
190
191std::string WebRequestConditionAttributeResourceType::GetName() const {
192  return keys::kResourceTypeKey;
193}
194
195bool WebRequestConditionAttributeResourceType::Equals(
196    const WebRequestConditionAttribute* other) const {
197  if (!WebRequestConditionAttribute::Equals(other))
198    return false;
199  const WebRequestConditionAttributeResourceType* casted_other =
200      static_cast<const WebRequestConditionAttributeResourceType*>(other);
201  return types_ == casted_other->types_;
202}
203
204//
205// WebRequestConditionAttributeContentType
206//
207
208WebRequestConditionAttributeContentType::
209WebRequestConditionAttributeContentType(
210    const std::vector<std::string>& content_types,
211    bool inclusive)
212    : content_types_(content_types),
213      inclusive_(inclusive) {}
214
215WebRequestConditionAttributeContentType::
216~WebRequestConditionAttributeContentType() {}
217
218// static
219scoped_refptr<const WebRequestConditionAttribute>
220WebRequestConditionAttributeContentType::Create(
221      const std::string& name,
222      const base::Value* value,
223      std::string* error,
224      bool* bad_message) {
225  DCHECK(name == keys::kContentTypeKey || name == keys::kExcludeContentTypeKey);
226
227  const ListValue* value_as_list = NULL;
228  if (!value->GetAsList(&value_as_list)) {
229    *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name);
230    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
231  }
232  std::vector<std::string> content_types;
233  for (ListValue::const_iterator it = value_as_list->begin();
234       it != value_as_list->end(); ++it) {
235    std::string content_type;
236    if (!(*it)->GetAsString(&content_type)) {
237      *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name);
238      return scoped_refptr<const WebRequestConditionAttribute>(NULL);
239    }
240    content_types.push_back(content_type);
241  }
242
243  return scoped_refptr<const WebRequestConditionAttribute>(
244      new WebRequestConditionAttributeContentType(
245          content_types, name == keys::kContentTypeKey));
246}
247
248int WebRequestConditionAttributeContentType::GetStages() const {
249  return ON_HEADERS_RECEIVED;
250}
251
252bool WebRequestConditionAttributeContentType::IsFulfilled(
253    const WebRequestData& request_data) const {
254  if (!(request_data.stage & GetStages()))
255    return false;
256  std::string content_type;
257  request_data.original_response_headers->GetNormalizedHeader(
258      net::HttpRequestHeaders::kContentType, &content_type);
259  std::string mime_type;
260  std::string charset;
261  bool had_charset = false;
262  net::HttpUtil::ParseContentType(
263      content_type, &mime_type, &charset, &had_charset, NULL);
264
265  if (inclusive_) {
266    return std::find(content_types_.begin(), content_types_.end(),
267                     mime_type) != content_types_.end();
268  } else {
269    return std::find(content_types_.begin(), content_types_.end(),
270                     mime_type) == content_types_.end();
271  }
272}
273
274WebRequestConditionAttribute::Type
275WebRequestConditionAttributeContentType::GetType() const {
276  return CONDITION_CONTENT_TYPE;
277}
278
279std::string WebRequestConditionAttributeContentType::GetName() const {
280  return (inclusive_ ? keys::kContentTypeKey : keys::kExcludeContentTypeKey);
281}
282
283bool WebRequestConditionAttributeContentType::Equals(
284    const WebRequestConditionAttribute* other) const {
285  if (!WebRequestConditionAttribute::Equals(other))
286    return false;
287  const WebRequestConditionAttributeContentType* casted_other =
288      static_cast<const WebRequestConditionAttributeContentType*>(other);
289  return content_types_ == casted_other->content_types_ &&
290         inclusive_ == casted_other->inclusive_;
291}
292
293// Manages a set of tests to be applied to name-value pairs representing
294// headers. This is a helper class to header-related condition attributes.
295// It contains a set of test groups. A name-value pair satisfies the whole
296// set of test groups iff it passes at least one test group.
297class HeaderMatcher {
298 public:
299  ~HeaderMatcher();
300
301  // Creates an instance based on a list |tests| of test groups, encoded as
302  // dictionaries of the type declarativeWebRequest.HeaderFilter (see
303  // declarative_web_request.json).
304  static scoped_ptr<const HeaderMatcher> Create(const base::ListValue* tests);
305
306  // Does |this| match the header "|name|: |value|"?
307  bool TestNameValue(const std::string& name, const std::string& value) const;
308
309 private:
310  // Represents a single string-matching test.
311  class StringMatchTest {
312   public:
313    enum MatchType { kPrefix, kSuffix, kEquals, kContains };
314
315    // |data| is the pattern to be matched in the position given by |type|.
316    // Note that |data| must point to a StringValue object.
317    static scoped_ptr<StringMatchTest> Create(const Value* data,
318                                              MatchType type,
319                                              bool case_sensitive);
320    ~StringMatchTest();
321
322    // Does |str| pass |this| StringMatchTest?
323    bool Matches(const std::string& str) const;
324
325   private:
326    StringMatchTest(const std::string& data,
327                    MatchType type,
328                    bool case_sensitive);
329
330    const std::string data_;
331    const MatchType type_;
332    const bool case_sensitive_;
333    DISALLOW_COPY_AND_ASSIGN(StringMatchTest);
334  };
335
336  // Represents a test group -- a set of string matching tests to be applied to
337  // both the header name and value.
338  class HeaderMatchTest {
339   public:
340    ~HeaderMatchTest();
341
342    // Gets the test group description in |tests| and creates the corresponding
343    // HeaderMatchTest. On failure returns NULL.
344    static scoped_ptr<const HeaderMatchTest> Create(
345        const base::DictionaryValue* tests);
346
347    // Does the header "|name|: |value|" match all tests in |this|?
348    bool Matches(const std::string& name, const std::string& value) const;
349
350   private:
351    // Takes ownership of the content of both |name_match| and |value_match|.
352    HeaderMatchTest(ScopedVector<const StringMatchTest>* name_match,
353                    ScopedVector<const StringMatchTest>* value_match);
354
355    // Tests to be passed by a header's name.
356    const ScopedVector<const StringMatchTest> name_match_;
357    // Tests to be passed by a header's value.
358    const ScopedVector<const StringMatchTest> value_match_;
359    DISALLOW_COPY_AND_ASSIGN(HeaderMatchTest);
360  };
361
362  explicit HeaderMatcher(ScopedVector<const HeaderMatchTest>* tests);
363
364  const ScopedVector<const HeaderMatchTest> tests_;
365
366  DISALLOW_COPY_AND_ASSIGN(HeaderMatcher);
367};
368
369// HeaderMatcher implementation.
370
371HeaderMatcher::~HeaderMatcher() {}
372
373// static
374scoped_ptr<const HeaderMatcher> HeaderMatcher::Create(
375    const base::ListValue* tests) {
376  ScopedVector<const HeaderMatchTest> header_tests;
377  for (ListValue::const_iterator it = tests->begin();
378       it != tests->end(); ++it) {
379    const DictionaryValue* tests = NULL;
380    if (!(*it)->GetAsDictionary(&tests))
381      return scoped_ptr<const HeaderMatcher>();
382
383    scoped_ptr<const HeaderMatchTest> header_test(
384        HeaderMatchTest::Create(tests));
385    if (header_test.get() == NULL)
386      return scoped_ptr<const HeaderMatcher>();
387    header_tests.push_back(header_test.release());
388  }
389
390  return scoped_ptr<const HeaderMatcher>(new HeaderMatcher(&header_tests));
391}
392
393bool HeaderMatcher::TestNameValue(const std::string& name,
394                                  const std::string& value) const {
395  for (size_t i = 0; i < tests_.size(); ++i) {
396    if (tests_[i]->Matches(name, value))
397      return true;
398  }
399  return false;
400}
401
402HeaderMatcher::HeaderMatcher(ScopedVector<const HeaderMatchTest>* tests)
403  : tests_(tests->Pass()) {}
404
405// HeaderMatcher::StringMatchTest implementation.
406
407// static
408scoped_ptr<HeaderMatcher::StringMatchTest>
409HeaderMatcher::StringMatchTest::Create(const Value* data,
410                                       MatchType type,
411                                       bool case_sensitive) {
412  std::string str;
413  CHECK(data->GetAsString(&str));
414  return scoped_ptr<StringMatchTest>(
415      new StringMatchTest(str, type, case_sensitive));
416}
417
418HeaderMatcher::StringMatchTest::~StringMatchTest() {}
419
420bool HeaderMatcher::StringMatchTest::Matches(
421    const std::string& str) const {
422  switch (type_) {
423    case kPrefix:
424      return StartsWithASCII(str, data_, case_sensitive_);
425    case kSuffix:
426      return EndsWith(str, data_, case_sensitive_);
427    case kEquals:
428      return str.size() == data_.size() &&
429             StartsWithASCII(str, data_, case_sensitive_);
430    case kContains:
431      if (!case_sensitive_) {
432        return std::search(str.begin(), str.end(), data_.begin(), data_.end(),
433                           CaseInsensitiveCompareASCII<char>()) != str.end();
434      } else {
435        return str.find(data_) != std::string::npos;
436      }
437  }
438  // We never get past the "switch", but the compiler worries about no return.
439  NOTREACHED();
440  return false;
441}
442
443HeaderMatcher::StringMatchTest::StringMatchTest(const std::string& data,
444                                                MatchType type,
445                                                bool case_sensitive)
446    : data_(data),
447      type_(type),
448      case_sensitive_(case_sensitive) {}
449
450// HeaderMatcher::HeaderMatchTest implementation.
451
452HeaderMatcher::HeaderMatchTest::HeaderMatchTest(
453    ScopedVector<const StringMatchTest>* name_match,
454    ScopedVector<const StringMatchTest>* value_match)
455    : name_match_(name_match->Pass()),
456      value_match_(value_match->Pass()) {}
457
458HeaderMatcher::HeaderMatchTest::~HeaderMatchTest() {}
459
460// static
461scoped_ptr<const HeaderMatcher::HeaderMatchTest>
462HeaderMatcher::HeaderMatchTest::Create(const base::DictionaryValue* tests) {
463  ScopedVector<const StringMatchTest> name_match;
464  ScopedVector<const StringMatchTest> value_match;
465
466  for (DictionaryValue::Iterator it(*tests); !it.IsAtEnd(); it.Advance()) {
467    bool is_name = false;  // Is this test for header name?
468    StringMatchTest::MatchType match_type;
469    if (it.key() == keys::kNamePrefixKey) {
470      is_name = true;
471      match_type = StringMatchTest::kPrefix;
472    } else if (it.key() == keys::kNameSuffixKey) {
473      is_name = true;
474      match_type = StringMatchTest::kSuffix;
475    } else if (it.key() == keys::kNameContainsKey) {
476      is_name = true;
477      match_type = StringMatchTest::kContains;
478    } else if (it.key() == keys::kNameEqualsKey) {
479      is_name = true;
480      match_type = StringMatchTest::kEquals;
481    } else if (it.key() == keys::kValuePrefixKey) {
482      match_type = StringMatchTest::kPrefix;
483    } else if (it.key() == keys::kValueSuffixKey) {
484      match_type = StringMatchTest::kSuffix;
485    } else if (it.key() == keys::kValueContainsKey) {
486      match_type = StringMatchTest::kContains;
487    } else if (it.key() == keys::kValueEqualsKey) {
488      match_type = StringMatchTest::kEquals;
489    } else {
490      NOTREACHED();  // JSON schema type checking should prevent this.
491      return scoped_ptr<const HeaderMatchTest>();
492    }
493    const Value* content = &it.value();
494
495    ScopedVector<const StringMatchTest>* tests =
496        is_name ? &name_match : &value_match;
497    switch (content->GetType()) {
498      case Value::TYPE_LIST: {
499        const ListValue* list = NULL;
500        CHECK(content->GetAsList(&list));
501        for (ListValue::const_iterator it = list->begin();
502             it != list->end(); ++it) {
503          tests->push_back(
504              StringMatchTest::Create(*it, match_type, !is_name).release());
505        }
506        break;
507      }
508      case Value::TYPE_STRING: {
509        tests->push_back(
510            StringMatchTest::Create(content, match_type, !is_name).release());
511        break;
512      }
513      default: {
514        NOTREACHED();  // JSON schema type checking should prevent this.
515        return scoped_ptr<const HeaderMatchTest>();
516      }
517    }
518  }
519
520  return scoped_ptr<const HeaderMatchTest>(
521      new HeaderMatchTest(&name_match, &value_match));
522}
523
524bool HeaderMatcher::HeaderMatchTest::Matches(const std::string& name,
525                                             const std::string& value) const {
526  for (size_t i = 0; i < name_match_.size(); ++i) {
527    if (!name_match_[i]->Matches(name))
528      return false;
529  }
530
531  for (size_t i = 0; i < value_match_.size(); ++i) {
532    if (!value_match_[i]->Matches(value))
533      return false;
534  }
535
536  return true;
537}
538
539//
540// WebRequestConditionAttributeRequestHeaders
541//
542
543WebRequestConditionAttributeRequestHeaders::
544WebRequestConditionAttributeRequestHeaders(
545    scoped_ptr<const HeaderMatcher> header_matcher,
546    bool positive)
547    : header_matcher_(header_matcher.Pass()),
548      positive_(positive) {}
549
550WebRequestConditionAttributeRequestHeaders::
551~WebRequestConditionAttributeRequestHeaders() {}
552
553namespace {
554
555scoped_ptr<const HeaderMatcher> PrepareHeaderMatcher(
556    const std::string& name,
557    const base::Value* value,
558    std::string* error) {
559  const ListValue* value_as_list = NULL;
560  if (!value->GetAsList(&value_as_list)) {
561    *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name);
562    return scoped_ptr<const HeaderMatcher>();
563  }
564
565  scoped_ptr<const HeaderMatcher> header_matcher(
566      HeaderMatcher::Create(value_as_list));
567  if (header_matcher.get() == NULL)
568    *error = ErrorUtils::FormatErrorMessage(kInvalidValue, name);
569  return header_matcher.Pass();
570}
571
572}  // namespace
573
574// static
575scoped_refptr<const WebRequestConditionAttribute>
576WebRequestConditionAttributeRequestHeaders::Create(
577    const std::string& name,
578    const base::Value* value,
579    std::string* error,
580    bool* bad_message) {
581  DCHECK(name == keys::kRequestHeadersKey ||
582         name == keys::kExcludeRequestHeadersKey);
583
584  scoped_ptr<const HeaderMatcher> header_matcher(
585      PrepareHeaderMatcher(name, value, error));
586  if (header_matcher.get() == NULL)
587    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
588
589  return scoped_refptr<const WebRequestConditionAttribute>(
590      new WebRequestConditionAttributeRequestHeaders(
591          header_matcher.Pass(), name == keys::kRequestHeadersKey));
592}
593
594int WebRequestConditionAttributeRequestHeaders::GetStages() const {
595  // Currently we only allow matching against headers in the before-send-headers
596  // stage. The headers are accessible in other stages as well, but before
597  // allowing to match against them in further stages, we should consider
598  // caching the match result.
599  return ON_BEFORE_SEND_HEADERS;
600}
601
602bool WebRequestConditionAttributeRequestHeaders::IsFulfilled(
603    const WebRequestData& request_data) const {
604  if (!(request_data.stage & GetStages()))
605    return false;
606
607  const net::HttpRequestHeaders& headers =
608      request_data.request->extra_request_headers();
609
610  bool passed = false;  // Did some header pass TestNameValue?
611  net::HttpRequestHeaders::Iterator it(headers);
612  while (!passed && it.GetNext())
613    passed |= header_matcher_->TestNameValue(it.name(), it.value());
614
615  return (positive_ ? passed : !passed);
616}
617
618WebRequestConditionAttribute::Type
619WebRequestConditionAttributeRequestHeaders::GetType() const {
620  return CONDITION_REQUEST_HEADERS;
621}
622
623std::string WebRequestConditionAttributeRequestHeaders::GetName() const {
624  return (positive_ ? keys::kRequestHeadersKey
625                    : keys::kExcludeRequestHeadersKey);
626}
627
628bool WebRequestConditionAttributeRequestHeaders::Equals(
629    const WebRequestConditionAttribute* other) const {
630  // Comparing headers is too heavy, so we skip it entirely.
631  return false;
632}
633
634//
635// WebRequestConditionAttributeResponseHeaders
636//
637
638WebRequestConditionAttributeResponseHeaders::
639WebRequestConditionAttributeResponseHeaders(
640    scoped_ptr<const HeaderMatcher> header_matcher,
641    bool positive)
642    : header_matcher_(header_matcher.Pass()),
643      positive_(positive) {}
644
645WebRequestConditionAttributeResponseHeaders::
646~WebRequestConditionAttributeResponseHeaders() {}
647
648// static
649scoped_refptr<const WebRequestConditionAttribute>
650WebRequestConditionAttributeResponseHeaders::Create(
651    const std::string& name,
652    const base::Value* value,
653    std::string* error,
654    bool* bad_message) {
655  DCHECK(name == keys::kResponseHeadersKey ||
656         name == keys::kExcludeResponseHeadersKey);
657
658  scoped_ptr<const HeaderMatcher> header_matcher(
659      PrepareHeaderMatcher(name, value, error));
660  if (header_matcher.get() == NULL)
661    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
662
663  return scoped_refptr<const WebRequestConditionAttribute>(
664      new WebRequestConditionAttributeResponseHeaders(
665          header_matcher.Pass(), name == keys::kResponseHeadersKey));
666}
667
668int WebRequestConditionAttributeResponseHeaders::GetStages() const {
669  return ON_HEADERS_RECEIVED;
670}
671
672bool WebRequestConditionAttributeResponseHeaders::IsFulfilled(
673    const WebRequestData& request_data) const {
674  if (!(request_data.stage & GetStages()))
675    return false;
676
677  const net::HttpResponseHeaders* headers =
678      request_data.original_response_headers;
679  if (headers == NULL) {
680    // Each header of an empty set satisfies (the negation of) everything;
681    // OTOH, there is no header to satisfy even the most permissive test.
682    return !positive_;
683  }
684
685  bool passed = false;  // Did some header pass TestNameValue?
686  std::string name;
687  std::string value;
688  void* iter = NULL;
689  while (!passed && headers->EnumerateHeaderLines(&iter, &name, &value)) {
690    passed |= header_matcher_->TestNameValue(name, value);
691  }
692
693  return (positive_ ? passed : !passed);
694}
695
696WebRequestConditionAttribute::Type
697WebRequestConditionAttributeResponseHeaders::GetType() const {
698  return CONDITION_RESPONSE_HEADERS;
699}
700
701std::string WebRequestConditionAttributeResponseHeaders::GetName() const {
702  return (positive_ ? keys::kResponseHeadersKey
703                    : keys::kExcludeResponseHeadersKey);
704}
705
706bool WebRequestConditionAttributeResponseHeaders::Equals(
707    const WebRequestConditionAttribute* other) const {
708  return false;
709}
710
711//
712// WebRequestConditionAttributeThirdParty
713//
714
715WebRequestConditionAttributeThirdParty::
716WebRequestConditionAttributeThirdParty(bool match_third_party)
717    : match_third_party_(match_third_party) {}
718
719WebRequestConditionAttributeThirdParty::
720~WebRequestConditionAttributeThirdParty() {}
721
722// static
723scoped_refptr<const WebRequestConditionAttribute>
724WebRequestConditionAttributeThirdParty::Create(
725    const std::string& name,
726    const base::Value* value,
727    std::string* error,
728    bool* bad_message) {
729  DCHECK(name == keys::kThirdPartyKey);
730
731  bool third_party = false;  // Dummy value, gets overwritten.
732  if (!value->GetAsBoolean(&third_party)) {
733    *error = ErrorUtils::FormatErrorMessage(kInvalidValue,
734                                                     keys::kThirdPartyKey);
735    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
736  }
737
738  return scoped_refptr<const WebRequestConditionAttribute>(
739      new WebRequestConditionAttributeThirdParty(third_party));
740}
741
742int WebRequestConditionAttributeThirdParty::GetStages() const {
743  return ON_BEFORE_REQUEST | ON_BEFORE_SEND_HEADERS | ON_SEND_HEADERS |
744      ON_HEADERS_RECEIVED | ON_AUTH_REQUIRED | ON_BEFORE_REDIRECT |
745      ON_RESPONSE_STARTED | ON_COMPLETED | ON_ERROR;
746}
747
748bool WebRequestConditionAttributeThirdParty::IsFulfilled(
749    const WebRequestData& request_data) const {
750  if (!(request_data.stage & GetStages()))
751    return false;
752
753  // Request is "1st party" if it gets cookies under 3rd party-blocking policy.
754  const net::StaticCookiePolicy block_third_party_policy(
755      net::StaticCookiePolicy::BLOCK_ALL_THIRD_PARTY_COOKIES);
756  const int can_get_cookies = block_third_party_policy.CanGetCookies(
757          request_data.request->url(),
758          request_data.request->first_party_for_cookies());
759  const bool is_first_party = (can_get_cookies == net::OK);
760
761  return match_third_party_ ? !is_first_party : is_first_party;
762}
763
764WebRequestConditionAttribute::Type
765WebRequestConditionAttributeThirdParty::GetType() const {
766  return CONDITION_THIRD_PARTY;
767}
768
769std::string WebRequestConditionAttributeThirdParty::GetName() const {
770  return keys::kThirdPartyKey;
771}
772
773bool WebRequestConditionAttributeThirdParty::Equals(
774    const WebRequestConditionAttribute* other) const {
775  if (!WebRequestConditionAttribute::Equals(other))
776    return false;
777  const WebRequestConditionAttributeThirdParty* casted_other =
778      static_cast<const WebRequestConditionAttributeThirdParty*>(other);
779  return match_third_party_ == casted_other->match_third_party_;
780}
781
782//
783// WebRequestConditionAttributeStages
784//
785
786WebRequestConditionAttributeStages::
787WebRequestConditionAttributeStages(int allowed_stages)
788    : allowed_stages_(allowed_stages) {}
789
790WebRequestConditionAttributeStages::
791~WebRequestConditionAttributeStages() {}
792
793namespace {
794
795// Reads strings stored in |value|, which is expected to be a ListValue, and
796// sets corresponding bits (see RequestStage) in |out_stages|. Returns true on
797// success, false otherwise.
798bool ParseListOfStages(const Value& value, int* out_stages) {
799  const ListValue* list = NULL;
800  if (!value.GetAsList(&list))
801    return false;
802
803  int stages = 0;
804  std::string stage_name;
805  for (ListValue::const_iterator it = list->begin(); it != list->end(); ++it) {
806    if (!((*it)->GetAsString(&stage_name)))
807      return false;
808    if (stage_name == keys::kOnBeforeRequestEnum) {
809      stages |= ON_BEFORE_REQUEST;
810    } else if (stage_name == keys::kOnBeforeSendHeadersEnum) {
811      stages |= ON_BEFORE_SEND_HEADERS;
812    } else if (stage_name == keys::kOnHeadersReceivedEnum) {
813      stages |= ON_HEADERS_RECEIVED;
814    } else if (stage_name == keys::kOnAuthRequiredEnum) {
815      stages |= ON_AUTH_REQUIRED;
816    } else {
817      NOTREACHED();  // JSON schema checks prevent getting here.
818      return false;
819    }
820  }
821
822  *out_stages = stages;
823  return true;
824}
825
826}  // namespace
827
828// static
829scoped_refptr<const WebRequestConditionAttribute>
830WebRequestConditionAttributeStages::Create(const std::string& name,
831                                           const Value* value,
832                                           std::string* error,
833                                           bool* bad_message) {
834  DCHECK(name == keys::kStagesKey);
835
836  int allowed_stages = 0;
837  if (!ParseListOfStages(*value, &allowed_stages)) {
838    *error = ErrorUtils::FormatErrorMessage(kInvalidValue,
839                                                     keys::kStagesKey);
840    return scoped_refptr<const WebRequestConditionAttribute>(NULL);
841  }
842
843  return scoped_refptr<const WebRequestConditionAttribute>(
844      new WebRequestConditionAttributeStages(allowed_stages));
845}
846
847int WebRequestConditionAttributeStages::GetStages() const {
848  return allowed_stages_;
849}
850
851bool WebRequestConditionAttributeStages::IsFulfilled(
852    const WebRequestData& request_data) const {
853  // Note: removing '!=' triggers warning C4800 on the VS compiler.
854  return (request_data.stage & GetStages()) != 0;
855}
856
857WebRequestConditionAttribute::Type
858WebRequestConditionAttributeStages::GetType() const {
859  return CONDITION_STAGES;
860}
861
862std::string WebRequestConditionAttributeStages::GetName() const {
863  return keys::kStagesKey;
864}
865
866bool WebRequestConditionAttributeStages::Equals(
867    const WebRequestConditionAttribute* other) const {
868  if (!WebRequestConditionAttribute::Equals(other))
869    return false;
870  const WebRequestConditionAttributeStages* casted_other =
871      static_cast<const WebRequestConditionAttributeStages*>(other);
872  return allowed_stages_ == casted_other->allowed_stages_;
873}
874
875}  // namespace extensions
876