1 // Copyright 2013 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 "components/url_matcher/url_matcher_factory.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/values.h"
14 #include "components/url_matcher/url_matcher_constants.h"
15 #include "components/url_matcher/url_matcher_helpers.h"
16 #include "third_party/re2/re2/re2.h"
18 namespace url_matcher
{
20 namespace helpers
= url_matcher_helpers
;
21 namespace keys
= url_matcher_constants
;
26 const char kInvalidPortRanges
[] = "Invalid port ranges in UrlFilter.";
27 const char kVectorOfStringsExpected
[] =
28 "UrlFilter attribute '%s' expected a vector of strings as parameter.";
29 const char kUnknownURLFilterAttribute
[] =
30 "Unknown attribute '%s' in UrlFilter.";
31 const char kAttributeExpectedString
[] =
32 "UrlFilter attribute '%s' expected a string value.";
33 const char kUnparseableRegexString
[] =
34 "Could not parse regular expression '%s': %s";
35 const char kLowerCaseExpected
[] = "%s values need to be in lower case.";
37 // Registry for all factory methods of URLMatcherConditionFactory
38 // that allows translating string literals from the extension API into
39 // the corresponding factory method to be called.
40 class URLMatcherConditionFactoryMethods
{
42 URLMatcherConditionFactoryMethods() {
43 typedef URLMatcherConditionFactory F
;
44 factory_methods_
[keys::kHostContainsKey
] = &F::CreateHostContainsCondition
;
45 factory_methods_
[keys::kHostEqualsKey
] = &F::CreateHostEqualsCondition
;
46 factory_methods_
[keys::kHostPrefixKey
] = &F::CreateHostPrefixCondition
;
47 factory_methods_
[keys::kHostSuffixKey
] = &F::CreateHostSuffixCondition
;
48 factory_methods_
[keys::kOriginAndPathMatchesKey
] =
49 &F::CreateOriginAndPathMatchesCondition
;
50 factory_methods_
[keys::kPathContainsKey
] = &F::CreatePathContainsCondition
;
51 factory_methods_
[keys::kPathEqualsKey
] = &F::CreatePathEqualsCondition
;
52 factory_methods_
[keys::kPathPrefixKey
] = &F::CreatePathPrefixCondition
;
53 factory_methods_
[keys::kPathSuffixKey
] = &F::CreatePathSuffixCondition
;
54 factory_methods_
[keys::kQueryContainsKey
] =
55 &F::CreateQueryContainsCondition
;
56 factory_methods_
[keys::kQueryEqualsKey
] = &F::CreateQueryEqualsCondition
;
57 factory_methods_
[keys::kQueryPrefixKey
] = &F::CreateQueryPrefixCondition
;
58 factory_methods_
[keys::kQuerySuffixKey
] = &F::CreateQuerySuffixCondition
;
59 factory_methods_
[keys::kURLContainsKey
] = &F::CreateURLContainsCondition
;
60 factory_methods_
[keys::kURLEqualsKey
] = &F::CreateURLEqualsCondition
;
61 factory_methods_
[keys::kURLPrefixKey
] = &F::CreateURLPrefixCondition
;
62 factory_methods_
[keys::kURLSuffixKey
] = &F::CreateURLSuffixCondition
;
63 factory_methods_
[keys::kURLMatchesKey
] = &F::CreateURLMatchesCondition
;
66 // Returns whether a factory method for the specified |pattern_type| (e.g.
67 // "host_suffix") is known.
68 bool Contains(const std::string
& pattern_type
) const {
69 return factory_methods_
.find(pattern_type
) != factory_methods_
.end();
72 // Creates a URLMatcherCondition instance from |url_matcher_condition_factory|
73 // of the given |pattern_type| (e.g. "host_suffix") for the given
74 // |pattern_value| (e.g. "example.com").
75 // The |pattern_type| needs to be known to this class (see Contains()) or
76 // a CHECK is triggered.
77 URLMatcherCondition
Call(
78 URLMatcherConditionFactory
* url_matcher_condition_factory
,
79 const std::string
& pattern_type
,
80 const std::string
& pattern_value
) const {
81 FactoryMethods::const_iterator i
= factory_methods_
.find(pattern_type
);
82 CHECK(i
!= factory_methods_
.end());
83 const FactoryMethod
& method
= i
->second
;
84 return (url_matcher_condition_factory
->*method
)(pattern_value
);
88 typedef URLMatcherCondition
89 (URLMatcherConditionFactory::* FactoryMethod
)
90 (const std::string
& prefix
);
91 typedef std::map
<std::string
, FactoryMethod
> FactoryMethods
;
93 FactoryMethods factory_methods_
;
95 DISALLOW_COPY_AND_ASSIGN(URLMatcherConditionFactoryMethods
);
98 static base::LazyInstance
<URLMatcherConditionFactoryMethods
>
99 g_url_matcher_condition_factory_methods
= LAZY_INSTANCE_INITIALIZER
;
104 scoped_refptr
<URLMatcherConditionSet
>
105 URLMatcherFactory::CreateFromURLFilterDictionary(
106 URLMatcherConditionFactory
* url_matcher_condition_factory
,
107 const base::DictionaryValue
* url_filter_dict
,
108 URLMatcherConditionSet::ID id
,
109 std::string
* error
) {
110 scoped_ptr
<URLMatcherSchemeFilter
> url_matcher_schema_filter
;
111 scoped_ptr
<URLMatcherPortFilter
> url_matcher_port_filter
;
112 URLMatcherConditionSet::Conditions url_matcher_conditions
;
114 for (base::DictionaryValue::Iterator
iter(*url_filter_dict
);
115 !iter
.IsAtEnd(); iter
.Advance()) {
116 const std::string
& condition_attribute_name
= iter
.key();
117 const base::Value
& condition_attribute_value
= iter
.value();
118 if (IsURLMatcherConditionAttribute(condition_attribute_name
)) {
119 // Handle {host, path, ...}{Prefix, Suffix, Contains, Equals}.
120 URLMatcherCondition url_matcher_condition
=
121 CreateURLMatcherCondition(
122 url_matcher_condition_factory
,
123 condition_attribute_name
,
124 &condition_attribute_value
,
127 return scoped_refptr
<URLMatcherConditionSet
>(NULL
);
128 url_matcher_conditions
.insert(url_matcher_condition
);
129 } else if (condition_attribute_name
== keys::kSchemesKey
) {
131 url_matcher_schema_filter
= CreateURLMatcherScheme(
132 &condition_attribute_value
, error
);
134 return scoped_refptr
<URLMatcherConditionSet
>(NULL
);
135 } else if (condition_attribute_name
== keys::kPortsKey
) {
137 url_matcher_port_filter
= CreateURLMatcherPorts(
138 &condition_attribute_value
, error
);
140 return scoped_refptr
<URLMatcherConditionSet
>(NULL
);
142 // Handle unknown attributes.
143 *error
= base::StringPrintf(kUnknownURLFilterAttribute
,
144 condition_attribute_name
.c_str());
145 return scoped_refptr
<URLMatcherConditionSet
>(NULL
);
149 // As the URL is the preliminary matching criterion that triggers the tests
150 // for the remaining condition attributes, we insert an empty URL match if
151 // no other url match conditions were specified. Such an empty URL is always
153 if (url_matcher_conditions
.empty()) {
154 url_matcher_conditions
.insert(
155 url_matcher_condition_factory
->CreateHostPrefixCondition(
159 scoped_refptr
<URLMatcherConditionSet
> url_matcher_condition_set(
160 new URLMatcherConditionSet(id
, url_matcher_conditions
,
161 url_matcher_schema_filter
.Pass(), url_matcher_port_filter
.Pass()));
162 return url_matcher_condition_set
;
166 bool URLMatcherFactory::IsURLMatcherConditionAttribute(
167 const std::string
& condition_attribute_name
) {
168 return g_url_matcher_condition_factory_methods
.Get().Contains(
169 condition_attribute_name
);
174 // Returns true if some alphabetic characters in this string are upper case.
175 bool ContainsUpperCase(const std::string
& str
) {
176 return std::find_if(str
.begin(), str
.end(), ::isupper
) != str
.end();
182 URLMatcherCondition
URLMatcherFactory::CreateURLMatcherCondition(
183 URLMatcherConditionFactory
* url_matcher_condition_factory
,
184 const std::string
& condition_attribute_name
,
185 const base::Value
* value
,
186 std::string
* error
) {
187 std::string str_value
;
188 if (!value
->GetAsString(&str_value
)) {
189 *error
= base::StringPrintf(kAttributeExpectedString
,
190 condition_attribute_name
.c_str());
191 return URLMatcherCondition();
193 if (condition_attribute_name
== keys::kHostContainsKey
||
194 condition_attribute_name
== keys::kHostPrefixKey
||
195 condition_attribute_name
== keys::kHostSuffixKey
||
196 condition_attribute_name
== keys::kHostEqualsKey
) {
197 if (ContainsUpperCase(str_value
)) {
198 *error
= base::StringPrintf(kLowerCaseExpected
, "Host");
199 return URLMatcherCondition();
203 // Test regular expressions for validity.
204 if (condition_attribute_name
== keys::kURLMatchesKey
||
205 condition_attribute_name
== keys::kOriginAndPathMatchesKey
) {
206 re2::RE2
regex(str_value
);
208 *error
= base::StringPrintf(
209 kUnparseableRegexString
, str_value
.c_str(), regex
.error().c_str());
210 return URLMatcherCondition();
213 return g_url_matcher_condition_factory_methods
.Get().Call(
214 url_matcher_condition_factory
, condition_attribute_name
, str_value
);
218 scoped_ptr
<URLMatcherSchemeFilter
> URLMatcherFactory::CreateURLMatcherScheme(
219 const base::Value
* value
,
220 std::string
* error
) {
221 std::vector
<std::string
> schemas
;
222 if (!helpers::GetAsStringVector(value
, &schemas
)) {
223 *error
= base::StringPrintf(kVectorOfStringsExpected
, keys::kSchemesKey
);
224 return scoped_ptr
<URLMatcherSchemeFilter
>();
226 for (std::vector
<std::string
>::const_iterator it
= schemas
.begin();
227 it
!= schemas
.end(); ++it
) {
228 if (ContainsUpperCase(*it
)) {
229 *error
= base::StringPrintf(kLowerCaseExpected
, "Scheme");
230 return scoped_ptr
<URLMatcherSchemeFilter
>();
233 return scoped_ptr
<URLMatcherSchemeFilter
>(
234 new URLMatcherSchemeFilter(schemas
));
238 scoped_ptr
<URLMatcherPortFilter
> URLMatcherFactory::CreateURLMatcherPorts(
239 const base::Value
* value
,
240 std::string
* error
) {
241 std::vector
<URLMatcherPortFilter::Range
> ranges
;
242 const base::ListValue
* value_list
= NULL
;
243 if (!value
->GetAsList(&value_list
)) {
244 *error
= kInvalidPortRanges
;
245 return scoped_ptr
<URLMatcherPortFilter
>();
248 for (base::ListValue::const_iterator i
= value_list
->begin();
249 i
!= value_list
->end(); ++i
) {
250 base::Value
* entry
= *i
;
252 base::ListValue
* range
= NULL
;
253 if (entry
->GetAsInteger(&port
)) {
254 ranges
.push_back(URLMatcherPortFilter::CreateRange(port
));
255 } else if (entry
->GetAsList(&range
)) {
256 int from
= 0, to
= 0;
257 if (range
->GetSize() != 2u ||
258 !range
->GetInteger(0, &from
) ||
259 !range
->GetInteger(1, &to
)) {
260 *error
= kInvalidPortRanges
;
261 return scoped_ptr
<URLMatcherPortFilter
>();
263 ranges
.push_back(URLMatcherPortFilter::CreateRange(from
, to
));
265 *error
= kInvalidPortRanges
;
266 return scoped_ptr
<URLMatcherPortFilter
>();
270 return scoped_ptr
<URLMatcherPortFilter
>(new URLMatcherPortFilter(ranges
));
273 } // namespace url_matcher