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.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/stl_util.h"
10#include "base/strings/stringprintf.h"
11#include "base/values.h"
12#include "chrome/browser/extensions/api/declarative_webrequest/request_stage.h"
13#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition_attribute.h"
14#include "chrome/browser/extensions/api/declarative_webrequest/webrequest_constants.h"
15#include "components/url_matcher/url_matcher_factory.h"
16#include "net/url_request/url_request.h"
17
18using url_matcher::URLMatcherConditionFactory;
19using url_matcher::URLMatcherConditionSet;
20using url_matcher::URLMatcherFactory;
21
22namespace keys = extensions::declarative_webrequest_constants;
23
24namespace {
25static URLMatcherConditionSet::ID g_next_id = 0;
26
27// TODO(battre): improve error messaging to give more meaningful messages
28// to the extension developer.
29// Error messages:
30const char kExpectedDictionary[] = "A condition has to be a dictionary.";
31const char kConditionWithoutInstanceType[] = "A condition had no instanceType";
32const char kExpectedOtherConditionType[] = "Expected a condition of type "
33    "declarativeWebRequest.RequestMatcher";
34const char kInvalidTypeOfParamter[] = "Attribute '%s' has an invalid type";
35const char kConditionCannotBeFulfilled[] = "A condition can never be "
36    "fulfilled because its attributes cannot all be tested at the "
37    "same time in the request life-cycle.";
38}  // namespace
39
40namespace extensions {
41
42namespace keys = declarative_webrequest_constants;
43
44//
45// WebRequestData
46//
47
48WebRequestData::WebRequestData(net::URLRequest* request, RequestStage stage)
49    : request(request),
50      stage(stage),
51      original_response_headers(NULL) {}
52
53WebRequestData::WebRequestData(
54    net::URLRequest* request,
55    RequestStage stage,
56    const net::HttpResponseHeaders* original_response_headers)
57    : request(request),
58      stage(stage),
59      original_response_headers(original_response_headers) {}
60
61WebRequestData::~WebRequestData() {}
62
63//
64// WebRequestDataWithMatchIds
65//
66
67WebRequestDataWithMatchIds::WebRequestDataWithMatchIds(
68    const WebRequestData* request_data)
69    : data(request_data) {}
70
71WebRequestDataWithMatchIds::~WebRequestDataWithMatchIds() {}
72
73//
74// WebRequestCondition
75//
76
77WebRequestCondition::WebRequestCondition(
78    scoped_refptr<URLMatcherConditionSet> url_matcher_conditions,
79    scoped_refptr<URLMatcherConditionSet> first_party_url_matcher_conditions,
80    const WebRequestConditionAttributes& condition_attributes)
81    : url_matcher_conditions_(url_matcher_conditions),
82      first_party_url_matcher_conditions_(first_party_url_matcher_conditions),
83      condition_attributes_(condition_attributes),
84      applicable_request_stages_(~0) {
85  for (WebRequestConditionAttributes::const_iterator i =
86       condition_attributes_.begin(); i != condition_attributes_.end(); ++i) {
87    applicable_request_stages_ &= (*i)->GetStages();
88  }
89}
90
91WebRequestCondition::~WebRequestCondition() {}
92
93bool WebRequestCondition::IsFulfilled(
94    const MatchData& request_data) const {
95  if (!(request_data.data->stage & applicable_request_stages_)) {
96    // A condition that cannot be evaluated is considered as violated.
97    return false;
98  }
99
100  // Check URL attributes if present.
101  if (url_matcher_conditions_.get() &&
102      !ContainsKey(request_data.url_match_ids, url_matcher_conditions_->id()))
103    return false;
104  if (first_party_url_matcher_conditions_.get() &&
105      !ContainsKey(request_data.first_party_url_match_ids,
106                   first_party_url_matcher_conditions_->id()))
107    return false;
108
109  // All condition attributes must be fulfilled for a fulfilled condition.
110  for (WebRequestConditionAttributes::const_iterator i =
111           condition_attributes_.begin();
112       i != condition_attributes_.end(); ++i) {
113    if (!(*i)->IsFulfilled(*(request_data.data)))
114      return false;
115  }
116  return true;
117}
118
119void WebRequestCondition::GetURLMatcherConditionSets(
120    URLMatcherConditionSet::Vector* condition_sets) const {
121  if (url_matcher_conditions_.get())
122    condition_sets->push_back(url_matcher_conditions_);
123  if (first_party_url_matcher_conditions_.get())
124    condition_sets->push_back(first_party_url_matcher_conditions_);
125}
126
127// static
128scoped_ptr<WebRequestCondition> WebRequestCondition::Create(
129    const Extension* extension,
130    URLMatcherConditionFactory* url_matcher_condition_factory,
131    const base::Value& condition,
132    std::string* error) {
133  const base::DictionaryValue* condition_dict = NULL;
134  if (!condition.GetAsDictionary(&condition_dict)) {
135    *error = kExpectedDictionary;
136    return scoped_ptr<WebRequestCondition>();
137  }
138
139  // Verify that we are dealing with a Condition whose type we understand.
140  std::string instance_type;
141  if (!condition_dict->GetString(keys::kInstanceTypeKey, &instance_type)) {
142    *error = kConditionWithoutInstanceType;
143    return scoped_ptr<WebRequestCondition>();
144  }
145  if (instance_type != keys::kRequestMatcherType) {
146    *error = kExpectedOtherConditionType;
147    return scoped_ptr<WebRequestCondition>();
148  }
149
150  WebRequestConditionAttributes attributes;
151  scoped_refptr<URLMatcherConditionSet> url_matcher_condition_set;
152  scoped_refptr<URLMatcherConditionSet> first_party_url_matcher_condition_set;
153
154  for (base::DictionaryValue::Iterator iter(*condition_dict);
155       !iter.IsAtEnd(); iter.Advance()) {
156    const std::string& condition_attribute_name = iter.key();
157    const Value& condition_attribute_value = iter.value();
158    const bool name_is_url = condition_attribute_name == keys::kUrlKey;
159    if (condition_attribute_name == keys::kInstanceTypeKey) {
160      // Skip this.
161    } else if (name_is_url ||
162               condition_attribute_name == keys::kFirstPartyForCookiesUrlKey) {
163      const base::DictionaryValue* dict = NULL;
164      if (!condition_attribute_value.GetAsDictionary(&dict)) {
165        *error = base::StringPrintf(kInvalidTypeOfParamter,
166                                    condition_attribute_name.c_str());
167      } else {
168        if (name_is_url) {
169          url_matcher_condition_set =
170              URLMatcherFactory::CreateFromURLFilterDictionary(
171                  url_matcher_condition_factory, dict, ++g_next_id, error);
172        } else {
173          first_party_url_matcher_condition_set =
174              URLMatcherFactory::CreateFromURLFilterDictionary(
175                  url_matcher_condition_factory, dict, ++g_next_id, error);
176        }
177      }
178    } else {
179      scoped_refptr<const WebRequestConditionAttribute> attribute =
180          WebRequestConditionAttribute::Create(
181              condition_attribute_name,
182              &condition_attribute_value,
183              error);
184      if (attribute.get())
185        attributes.push_back(attribute);
186    }
187    if (!error->empty())
188      return scoped_ptr<WebRequestCondition>();
189  }
190
191  scoped_ptr<WebRequestCondition> result(
192      new WebRequestCondition(url_matcher_condition_set,
193                              first_party_url_matcher_condition_set,
194                              attributes));
195
196  if (!result->stages()) {
197    *error = kConditionCannotBeFulfilled;
198    return scoped_ptr<WebRequestCondition>();
199  }
200
201  return result.Pass();
202}
203
204}  // namespace extensions
205