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.
5 #include "chrome/browser/extensions/api/declarative_webrequest/webrequest_condition.h"
8 #include "base/lazy_instance.h"
9 #include "base/logging.h"
10 #include "base/stringprintf.h"
11 #include "base/values.h"
12 #include "chrome/browser/extensions/api/declarative_webrequest/request_stages.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 "net/url_request/url_request.h"
18 static extensions::URLMatcherConditionSet::ID g_next_id
= 0;
20 // TODO(battre): improve error messaging to give more meaningful messages
21 // to the extension developer.
23 const char kExpectedDictionary
[] = "A condition has to be a dictionary.";
24 const char kConditionWithoutInstanceType
[] = "A condition had no instanceType";
25 const char kExpectedOtherConditionType
[] = "Expected a condition of type "
26 "experimental.webRequest.RequestMatcher";
27 const char kUnknownConditionAttribute
[] = "UnKnown condition attribute '%s'";
28 const char kConditionExpectedString
[] =
29 "Condition '%s' expected a string value";
31 // String literals from the JavaScript API:
32 const char kHostContainsKey
[] = "host_contains";
33 const char kHostEqualsKey
[] = "host_equals";
34 const char kHostPrefixKey
[] = "host_prefix";
35 const char kHostSuffixKey
[] = "host_suffix";
36 const char kHostSuffixPathPrefixKey
[] = "host_suffix_path_prefix";
37 const char kPathContainsKey
[] = "path_contains";
38 const char kPathEqualsKey
[] = "path_equals";
39 const char kPathPrefixKey
[] = "path_prefix";
40 const char kPathSuffixKey
[] = "path_suffix";
41 const char kQueryContainsKey
[] = "query_contains";
42 const char kQueryEqualsKey
[] = "query_equals";
43 const char kQueryPrefixKey
[] = "query_prefix";
44 const char kQuerySuffixKey
[] = "query_suffix";
45 const char kURLContainsKey
[] = "url_contains";
46 const char kURLEqualsKey
[] = "url_equals";
47 const char kURLPrefixKey
[] = "url_prefix";
48 const char kURLSuffixKey
[] = "url_suffix";
50 // Registry for all factory methods of extensions::URLMatcherConditionFactory
51 // that allows translating string literals from the extension API into
52 // the corresponding factory method to be called.
53 class URLMatcherConditionFactoryMethods
{
55 URLMatcherConditionFactoryMethods() {
56 typedef extensions::URLMatcherConditionFactory F
;
57 factory_methods_
[kHostContainsKey
] = &F::CreateHostContainsCondition
;
58 factory_methods_
[kHostEqualsKey
] = &F::CreateHostEqualsCondition
;
59 factory_methods_
[kHostPrefixKey
] = &F::CreateHostPrefixCondition
;
60 factory_methods_
[kHostSuffixKey
] = &F::CreateHostSuffixCondition
;
61 factory_methods_
[kPathContainsKey
] = &F::CreatePathContainsCondition
;
62 factory_methods_
[kPathEqualsKey
] = &F::CreatePathEqualsCondition
;
63 factory_methods_
[kPathPrefixKey
] = &F::CreatePathPrefixCondition
;
64 factory_methods_
[kPathSuffixKey
] = &F::CreatePathSuffixCondition
;
65 factory_methods_
[kQueryContainsKey
] = &F::CreateQueryContainsCondition
;
66 factory_methods_
[kQueryEqualsKey
] = &F::CreateQueryEqualsCondition
;
67 factory_methods_
[kQueryPrefixKey
] = &F::CreateQueryPrefixCondition
;
68 factory_methods_
[kQuerySuffixKey
] = &F::CreateQuerySuffixCondition
;
69 factory_methods_
[kURLContainsKey
] = &F::CreateURLContainsCondition
;
70 factory_methods_
[kURLEqualsKey
] = &F::CreateURLEqualsCondition
;
71 factory_methods_
[kURLPrefixKey
] = &F::CreateURLPrefixCondition
;
72 factory_methods_
[kURLSuffixKey
] = &F::CreateURLSuffixCondition
;
75 // Returns whether a factory method for the specified |pattern_type| (e.g.
76 // "host_suffix") is known.
77 bool Contains(const std::string
& pattern_type
) const {
78 return factory_methods_
.find(pattern_type
) != factory_methods_
.end();
81 // Creates a URLMatcherCondition instance from |url_matcher_condition_factory|
82 // of the given |pattern_type| (e.g. "host_suffix") for the given
83 // |pattern_value| (e.g. "example.com").
84 // The |pattern_type| needs to be known to this class (see Contains()) or
85 // a CHECK is triggered.
86 extensions::URLMatcherCondition
Call(
87 extensions::URLMatcherConditionFactory
* url_matcher_condition_factory
,
88 const std::string
& pattern_type
,
89 const std::string
& pattern_value
) const {
90 FactoryMethods::const_iterator i
= factory_methods_
.find(pattern_type
);
91 CHECK(i
!= factory_methods_
.end());
92 const FactoryMethod
& method
= i
->second
;
93 return (url_matcher_condition_factory
->*method
)(pattern_value
);
97 typedef extensions::URLMatcherCondition
98 (extensions::URLMatcherConditionFactory::* FactoryMethod
)
99 (const std::string
& prefix
);
100 typedef std::map
<std::string
, FactoryMethod
> FactoryMethods
;
102 FactoryMethods factory_methods_
;
104 DISALLOW_COPY_AND_ASSIGN(URLMatcherConditionFactoryMethods
);
107 static base::LazyInstance
<URLMatcherConditionFactoryMethods
>
108 g_url_matcher_condition_factory_methods
= LAZY_INSTANCE_INITIALIZER
;
112 namespace extensions
{
114 namespace keys
= declarative_webrequest_constants
;
117 // WebRequestCondition
120 WebRequestCondition::WebRequestCondition(
121 const URLMatcherConditionSet
& url_matcher_conditions
,
122 const WebRequestConditionAttributes
& condition_attributes
)
123 : url_matcher_conditions_(url_matcher_conditions
),
124 condition_attributes_(condition_attributes
),
125 applicable_request_stages_(~0) {
126 for (WebRequestConditionAttributes::const_iterator i
=
127 condition_attributes_
.begin(); i
!= condition_attributes_
.end(); ++i
) {
128 applicable_request_stages_
&= (*i
)->GetStages();
132 WebRequestCondition::~WebRequestCondition() {}
134 bool WebRequestCondition::IsFulfilled(net::URLRequest
* request
,
135 RequestStages request_stage
) const {
136 // All condition attributes must be fulfilled for a fulfilled condition.
137 if (!(request_stage
& applicable_request_stages_
)) {
138 // A condition that cannot be evaluated is considered as violated.
142 for (WebRequestConditionAttributes::const_iterator i
=
143 condition_attributes_
.begin(); i
!= condition_attributes_
.end(); ++i
) {
144 if (!(*i
)->IsFulfilled(request
, request_stage
))
151 scoped_ptr
<WebRequestCondition
> WebRequestCondition::Create(
152 URLMatcherConditionFactory
* url_matcher_condition_factory
,
153 const base::Value
& condition
,
154 std::string
* error
) {
155 const base::DictionaryValue
* condition_dict
= NULL
;
156 if (!condition
.GetAsDictionary(&condition_dict
)) {
157 *error
= kExpectedDictionary
;
158 return scoped_ptr
<WebRequestCondition
>(NULL
);
161 // Verify that we are dealing with a Condition whose type we understand.
162 std::string instance_type
;
163 if (!condition_dict
->GetString(keys::kInstanceTypeKey
, &instance_type
)) {
164 *error
= kConditionWithoutInstanceType
;
165 return scoped_ptr
<WebRequestCondition
>(NULL
);
167 if (instance_type
!= keys::kRequestMatcherType
) {
168 *error
= kExpectedOtherConditionType
;
169 return scoped_ptr
<WebRequestCondition
>(NULL
);
172 WebRequestConditionAttributes attributes
;
173 URLMatcherConditionSet::Conditions url_matcher_conditions
;
175 for (base::DictionaryValue::Iterator
iter(*condition_dict
);
176 iter
.HasNext(); iter
.Advance()) {
177 const std::string
& condition_attribute_name
= iter
.key();
178 const Value
& condition_attribute_value
= iter
.value();
179 if (condition_attribute_name
== keys::kInstanceTypeKey
) {
181 } else if (IsURLMatcherConditionAttribute(condition_attribute_name
)) {
182 URLMatcherCondition url_matcher_condition
=
183 CreateURLMatcherCondition(
184 url_matcher_condition_factory
,
185 condition_attribute_name
,
186 &condition_attribute_value
,
189 return scoped_ptr
<WebRequestCondition
>(NULL
);
190 url_matcher_conditions
.insert(url_matcher_condition
);
191 } else if (WebRequestConditionAttribute::IsKnownType(
192 condition_attribute_name
)) {
193 scoped_ptr
<WebRequestConditionAttribute
> attribute
=
194 WebRequestConditionAttribute::Create(
195 condition_attribute_name
,
196 &condition_attribute_value
,
199 return scoped_ptr
<WebRequestCondition
>(NULL
);
200 attributes
.push_back(make_linked_ptr(attribute
.release()));
202 *error
= base::StringPrintf(kUnknownConditionAttribute
,
203 condition_attribute_name
.c_str());
204 return scoped_ptr
<WebRequestCondition
>(NULL
);
208 // As the URL is the preliminary matching criterion that triggers the tests
209 // for the remaining condition attributes, we insert an empty URL match if
210 // no other url match conditions were specified. Such an empty URL is always
212 if (url_matcher_conditions
.empty()) {
213 url_matcher_conditions
.insert(
214 url_matcher_condition_factory
->CreateHostPrefixCondition(""));
217 URLMatcherConditionSet
url_matcher_condition_set(++g_next_id
,
218 url_matcher_conditions
);
219 return scoped_ptr
<WebRequestCondition
>(
220 new WebRequestCondition(url_matcher_condition_set
, attributes
));
224 bool WebRequestCondition::IsURLMatcherConditionAttribute(
225 const std::string
& condition_attribute_name
) {
226 return g_url_matcher_condition_factory_methods
.Get().Contains(
227 condition_attribute_name
);
231 URLMatcherCondition
WebRequestCondition::CreateURLMatcherCondition(
232 URLMatcherConditionFactory
* url_matcher_condition_factory
,
233 const std::string
& condition_attribute_name
,
234 const base::Value
* value
,
235 std::string
* error
) {
236 std::string str_value
;
237 if (!value
->GetAsString(&str_value
)) {
238 *error
= base::StringPrintf(kConditionExpectedString
,
239 condition_attribute_name
.c_str());
240 return URLMatcherCondition();
242 return g_url_matcher_condition_factory_methods
.Get().Call(
243 url_matcher_condition_factory
, condition_attribute_name
, str_value
);
248 // WebRequestConditionSet
251 WebRequestConditionSet::WebRequestConditionSet(
252 const std::vector
<linked_ptr
<WebRequestCondition
> >& conditions
)
253 : conditions_(conditions
) {
254 for (Conditions::iterator i
= conditions_
.begin(); i
!= conditions_
.end();
256 URLMatcherConditionSet::ID trigger_id
=
257 (*i
)->url_matcher_condition_set_id();
258 match_triggers_
[trigger_id
] = i
->get();
262 WebRequestConditionSet::~WebRequestConditionSet() {}
264 bool WebRequestConditionSet::IsFulfilled(
265 URLMatcherConditionSet::ID url_match
,
266 net::URLRequest
* request
,
267 RequestStages request_stage
) const {
268 MatchTriggers::const_iterator trigger
= match_triggers_
.find(url_match
);
269 DCHECK(trigger
!= match_triggers_
.end());
270 DCHECK_EQ(url_match
, trigger
->second
->url_matcher_condition_set_id());
271 return trigger
->second
->IsFulfilled(request
, request_stage
);
274 void WebRequestConditionSet::GetURLMatcherConditionSets(
275 std::vector
<URLMatcherConditionSet
>* condition_sets
) const {
276 for (Conditions::const_iterator i
= conditions_
.begin();
277 i
!= conditions_
.end(); ++i
) {
278 condition_sets
->push_back((*i
)->url_matcher_condition_set());
283 scoped_ptr
<WebRequestConditionSet
> WebRequestConditionSet::Create(
284 URLMatcherConditionFactory
* url_matcher_condition_factory
,
285 const AnyVector
& conditions
,
286 std::string
* error
) {
287 std::vector
<linked_ptr
<WebRequestCondition
> > result
;
289 for (AnyVector::const_iterator i
= conditions
.begin();
290 i
!= conditions
.end(); ++i
) {
292 scoped_ptr
<WebRequestCondition
> condition
=
293 WebRequestCondition::Create(url_matcher_condition_factory
,
294 (*i
)->value(), error
);
296 return scoped_ptr
<WebRequestConditionSet
>(NULL
);
297 result
.push_back(make_linked_ptr(condition
.release()));
300 return scoped_ptr
<WebRequestConditionSet
>(new WebRequestConditionSet(result
));
303 } // namespace extensions