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.
4
5 #include "components/url_matcher/url_matcher_factory.h"
6
7 #include <algorithm>
8 #include <cctype>
9
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"
17
18 namespace url_matcher {
19
20 namespace helpers = url_matcher_helpers;
21 namespace keys = url_matcher_constants;
22
23 namespace {
24
25 // Error messages:
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.";
36
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 {
41 public:
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;
64 }
65
66 // Returns whether a factory method for the specified |pattern_type| (e.g.
67 // "host_suffix") is known.
Contains(const std::string & pattern_type) const68 bool Contains(const std::string& pattern_type) const {
69 return factory_methods_.find(pattern_type) != factory_methods_.end();
70 }
71
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.
Call(URLMatcherConditionFactory * url_matcher_condition_factory,const std::string & pattern_type,const std::string & pattern_value) const77 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);
85 }
86
87 private:
88 typedef URLMatcherCondition
89 (URLMatcherConditionFactory::* FactoryMethod)
90 (const std::string& prefix);
91 typedef std::map<std::string, FactoryMethod> FactoryMethods;
92
93 FactoryMethods factory_methods_;
94
95 DISALLOW_COPY_AND_ASSIGN(URLMatcherConditionFactoryMethods);
96 };
97
98 static base::LazyInstance<URLMatcherConditionFactoryMethods>
99 g_url_matcher_condition_factory_methods = LAZY_INSTANCE_INITIALIZER;
100
101 } // namespace
102
103 // static
104 scoped_refptr<URLMatcherConditionSet>
CreateFromURLFilterDictionary(URLMatcherConditionFactory * url_matcher_condition_factory,const base::DictionaryValue * url_filter_dict,URLMatcherConditionSet::ID id,std::string * error)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;
113
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 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,
125 error);
126 if (!error->empty())
127 return scoped_refptr<URLMatcherConditionSet>(NULL);
128 url_matcher_conditions.insert(url_matcher_condition);
129 } else if (condition_attribute_name == keys::kSchemesKey) {
130 // Handle scheme.
131 url_matcher_schema_filter = CreateURLMatcherScheme(
132 &condition_attribute_value, error);
133 if (!error->empty())
134 return scoped_refptr<URLMatcherConditionSet>(NULL);
135 } else if (condition_attribute_name == keys::kPortsKey) {
136 // Handle ports.
137 url_matcher_port_filter = CreateURLMatcherPorts(
138 &condition_attribute_value, error);
139 if (!error->empty())
140 return scoped_refptr<URLMatcherConditionSet>(NULL);
141 } else {
142 // Handle unknown attributes.
143 *error = base::StringPrintf(kUnknownURLFilterAttribute,
144 condition_attribute_name.c_str());
145 return scoped_refptr<URLMatcherConditionSet>(NULL);
146 }
147 }
148
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
152 // matched.
153 if (url_matcher_conditions.empty()) {
154 url_matcher_conditions.insert(
155 url_matcher_condition_factory->CreateHostPrefixCondition(
156 std::string()));
157 }
158
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;
163 }
164
165 // static
IsURLMatcherConditionAttribute(const std::string & condition_attribute_name)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);
170 }
171
172 namespace {
173
174 // Returns true if some alphabetic characters in this string are upper case.
ContainsUpperCase(const std::string & str)175 bool ContainsUpperCase(const std::string& str) {
176 return std::find_if(str.begin(), str.end(), ::isupper) != str.end();
177 }
178
179 } // namespace
180
181 // static
CreateURLMatcherCondition(URLMatcherConditionFactory * url_matcher_condition_factory,const std::string & condition_attribute_name,const base::Value * value,std::string * error)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();
192 }
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();
200 }
201 }
202
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);
207 if (!regex.ok()) {
208 *error = base::StringPrintf(
209 kUnparseableRegexString, str_value.c_str(), regex.error().c_str());
210 return URLMatcherCondition();
211 }
212 }
213 return g_url_matcher_condition_factory_methods.Get().Call(
214 url_matcher_condition_factory, condition_attribute_name, str_value);
215 }
216
217 // static
CreateURLMatcherScheme(const base::Value * value,std::string * error)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>();
225 }
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>();
231 }
232 }
233 return scoped_ptr<URLMatcherSchemeFilter>(
234 new URLMatcherSchemeFilter(schemas));
235 }
236
237 // static
CreateURLMatcherPorts(const base::Value * value,std::string * error)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>();
246 }
247
248 for (ListValue::const_iterator i = value_list->begin();
249 i != value_list->end(); ++i) {
250 Value* entry = *i;
251 int port = 0;
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>();
262 }
263 ranges.push_back(URLMatcherPortFilter::CreateRange(from, to));
264 } else {
265 *error = kInvalidPortRanges;
266 return scoped_ptr<URLMatcherPortFilter>();
267 }
268 }
269
270 return scoped_ptr<URLMatcherPortFilter>(new URLMatcherPortFilter(ranges));
271 }
272
273 } // namespace url_matcher
274