OLD | NEW |
| (Empty) |
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/common/extensions/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/stringprintf.h" | |
13 #include "base/values.h" | |
14 #include "chrome/common/extensions/matcher/url_matcher_constants.h" | |
15 #include "chrome/common/extensions/matcher/url_matcher_helpers.h" | |
16 #include "extensions/common/error_utils.h" | |
17 #include "third_party/re2/re2/re2.h" | |
18 | |
19 namespace helpers = extensions::url_matcher_helpers; | |
20 namespace keys = extensions::url_matcher_constants; | |
21 | |
22 namespace { | |
23 // Error messages: | |
24 const char kInvalidPortRanges[] = "Invalid port ranges in UrlFilter."; | |
25 const char kVectorOfStringsExpected[] = | |
26 "UrlFilter attribute '*' expected a vector of strings as parameter."; | |
27 const char kUnknownURLFilterAttribute[] = | |
28 "Unknown attribute '*' in UrlFilter."; | |
29 const char kAttributeExpectedString[] = | |
30 "UrlFilter attribute '*' expected a string value."; | |
31 const char kUnparseableRegexString[] = | |
32 "Could not parse regular expression '*': *"; | |
33 const char kLowerCaseExpected[] = "* values need to be in lower case."; | |
34 | |
35 // Registry for all factory methods of extensions::URLMatcherConditionFactory | |
36 // that allows translating string literals from the extension API into | |
37 // the corresponding factory method to be called. | |
38 class URLMatcherConditionFactoryMethods { | |
39 public: | |
40 URLMatcherConditionFactoryMethods() { | |
41 typedef extensions::URLMatcherConditionFactory F; | |
42 factory_methods_[keys::kHostContainsKey] = &F::CreateHostContainsCondition; | |
43 factory_methods_[keys::kHostEqualsKey] = &F::CreateHostEqualsCondition; | |
44 factory_methods_[keys::kHostPrefixKey] = &F::CreateHostPrefixCondition; | |
45 factory_methods_[keys::kHostSuffixKey] = &F::CreateHostSuffixCondition; | |
46 factory_methods_[keys::kPathContainsKey] = &F::CreatePathContainsCondition; | |
47 factory_methods_[keys::kPathEqualsKey] = &F::CreatePathEqualsCondition; | |
48 factory_methods_[keys::kPathPrefixKey] = &F::CreatePathPrefixCondition; | |
49 factory_methods_[keys::kPathSuffixKey] = &F::CreatePathSuffixCondition; | |
50 factory_methods_[keys::kQueryContainsKey] = | |
51 &F::CreateQueryContainsCondition; | |
52 factory_methods_[keys::kQueryEqualsKey] = &F::CreateQueryEqualsCondition; | |
53 factory_methods_[keys::kQueryPrefixKey] = &F::CreateQueryPrefixCondition; | |
54 factory_methods_[keys::kQuerySuffixKey] = &F::CreateQuerySuffixCondition; | |
55 factory_methods_[keys::kURLContainsKey] = &F::CreateURLContainsCondition; | |
56 factory_methods_[keys::kURLEqualsKey] = &F::CreateURLEqualsCondition; | |
57 factory_methods_[keys::kURLPrefixKey] = &F::CreateURLPrefixCondition; | |
58 factory_methods_[keys::kURLSuffixKey] = &F::CreateURLSuffixCondition; | |
59 factory_methods_[keys::kURLMatchesKey] = &F::CreateURLMatchesCondition; | |
60 } | |
61 | |
62 // Returns whether a factory method for the specified |pattern_type| (e.g. | |
63 // "host_suffix") is known. | |
64 bool Contains(const std::string& pattern_type) const { | |
65 return factory_methods_.find(pattern_type) != factory_methods_.end(); | |
66 } | |
67 | |
68 // Creates a URLMatcherCondition instance from |url_matcher_condition_factory| | |
69 // of the given |pattern_type| (e.g. "host_suffix") for the given | |
70 // |pattern_value| (e.g. "example.com"). | |
71 // The |pattern_type| needs to be known to this class (see Contains()) or | |
72 // a CHECK is triggered. | |
73 extensions::URLMatcherCondition Call( | |
74 extensions::URLMatcherConditionFactory* url_matcher_condition_factory, | |
75 const std::string& pattern_type, | |
76 const std::string& pattern_value) const { | |
77 FactoryMethods::const_iterator i = factory_methods_.find(pattern_type); | |
78 CHECK(i != factory_methods_.end()); | |
79 const FactoryMethod& method = i->second; | |
80 return (url_matcher_condition_factory->*method)(pattern_value); | |
81 } | |
82 | |
83 private: | |
84 typedef extensions::URLMatcherCondition | |
85 (extensions::URLMatcherConditionFactory::* FactoryMethod) | |
86 (const std::string& prefix); | |
87 typedef std::map<std::string, FactoryMethod> FactoryMethods; | |
88 | |
89 FactoryMethods factory_methods_; | |
90 | |
91 DISALLOW_COPY_AND_ASSIGN(URLMatcherConditionFactoryMethods); | |
92 }; | |
93 | |
94 static base::LazyInstance<URLMatcherConditionFactoryMethods> | |
95 g_url_matcher_condition_factory_methods = LAZY_INSTANCE_INITIALIZER; | |
96 | |
97 } // namespace | |
98 | |
99 namespace extensions { | |
100 | |
101 // static | |
102 scoped_refptr<URLMatcherConditionSet> | |
103 URLMatcherFactory::CreateFromURLFilterDictionary( | |
104 URLMatcherConditionFactory* url_matcher_condition_factory, | |
105 const base::DictionaryValue* url_filter_dict, | |
106 URLMatcherConditionSet::ID id, | |
107 std::string* error) { | |
108 scoped_ptr<URLMatcherSchemeFilter> url_matcher_schema_filter; | |
109 scoped_ptr<URLMatcherPortFilter> url_matcher_port_filter; | |
110 URLMatcherConditionSet::Conditions url_matcher_conditions; | |
111 | |
112 for (base::DictionaryValue::Iterator iter(*url_filter_dict); | |
113 iter.HasNext(); iter.Advance()) { | |
114 const std::string& condition_attribute_name = iter.key(); | |
115 const Value& condition_attribute_value = iter.value(); | |
116 if (IsURLMatcherConditionAttribute(condition_attribute_name)) { | |
117 // Handle {host, path, ...}{Prefix, Suffix, Contains, Equals}. | |
118 URLMatcherCondition url_matcher_condition = | |
119 CreateURLMatcherCondition( | |
120 url_matcher_condition_factory, | |
121 condition_attribute_name, | |
122 &condition_attribute_value, | |
123 error); | |
124 if (!error->empty()) | |
125 return scoped_refptr<URLMatcherConditionSet>(NULL); | |
126 url_matcher_conditions.insert(url_matcher_condition); | |
127 } else if (condition_attribute_name == keys::kSchemesKey) { | |
128 // Handle scheme. | |
129 url_matcher_schema_filter = CreateURLMatcherScheme( | |
130 &condition_attribute_value, error); | |
131 if (!error->empty()) | |
132 return scoped_refptr<URLMatcherConditionSet>(NULL); | |
133 } else if (condition_attribute_name == keys::kPortsKey) { | |
134 // Handle ports. | |
135 url_matcher_port_filter = CreateURLMatcherPorts( | |
136 &condition_attribute_value, error); | |
137 if (!error->empty()) | |
138 return scoped_refptr<URLMatcherConditionSet>(NULL); | |
139 } else { | |
140 // Handle unknown attributes. | |
141 *error = ErrorUtils::FormatErrorMessage( | |
142 kUnknownURLFilterAttribute, | |
143 condition_attribute_name); | |
144 return scoped_refptr<URLMatcherConditionSet>(NULL); | |
145 } | |
146 } | |
147 | |
148 // As the URL is the preliminary matching criterion that triggers the tests | |
149 // for the remaining condition attributes, we insert an empty URL match if | |
150 // no other url match conditions were specified. Such an empty URL is always | |
151 // matched. | |
152 if (url_matcher_conditions.empty()) { | |
153 url_matcher_conditions.insert( | |
154 url_matcher_condition_factory->CreateHostPrefixCondition("")); | |
155 } | |
156 | |
157 scoped_refptr<URLMatcherConditionSet> url_matcher_condition_set( | |
158 new URLMatcherConditionSet(id, url_matcher_conditions, | |
159 url_matcher_schema_filter.Pass(), url_matcher_port_filter.Pass())); | |
160 return url_matcher_condition_set; | |
161 } | |
162 | |
163 // static | |
164 bool URLMatcherFactory::IsURLMatcherConditionAttribute( | |
165 const std::string& condition_attribute_name) { | |
166 return g_url_matcher_condition_factory_methods.Get().Contains( | |
167 condition_attribute_name); | |
168 } | |
169 | |
170 namespace { | |
171 | |
172 // Returns true if some alphabetic characters in this string are upper case. | |
173 bool ContainsUpperCase(const std::string& str) { | |
174 return std::find_if(str.begin(), str.end(), ::isupper) != str.end(); | |
175 } | |
176 | |
177 } // namespace | |
178 | |
179 // static | |
180 URLMatcherCondition URLMatcherFactory::CreateURLMatcherCondition( | |
181 URLMatcherConditionFactory* url_matcher_condition_factory, | |
182 const std::string& condition_attribute_name, | |
183 const base::Value* value, | |
184 std::string* error) { | |
185 std::string str_value; | |
186 if (!value->GetAsString(&str_value)) { | |
187 *error = ErrorUtils::FormatErrorMessage(kAttributeExpectedString, | |
188 condition_attribute_name); | |
189 return URLMatcherCondition(); | |
190 } | |
191 if (condition_attribute_name == keys::kHostContainsKey || | |
192 condition_attribute_name == keys::kHostPrefixKey || | |
193 condition_attribute_name == keys::kHostSuffixKey || | |
194 condition_attribute_name == keys::kHostEqualsKey) { | |
195 if (ContainsUpperCase(str_value)) { | |
196 *error = ErrorUtils::FormatErrorMessage(kLowerCaseExpected, | |
197 "Host"); | |
198 return URLMatcherCondition(); | |
199 } | |
200 } | |
201 | |
202 // Test regular expressions for validity. | |
203 if (condition_attribute_name == keys::kURLMatchesKey) { | |
204 re2::RE2 regex(str_value); | |
205 if (!regex.ok()) { | |
206 *error = ErrorUtils::FormatErrorMessage(kUnparseableRegexString, | |
207 str_value, regex.error()); | |
208 return URLMatcherCondition(); | |
209 } | |
210 } | |
211 return g_url_matcher_condition_factory_methods.Get().Call( | |
212 url_matcher_condition_factory, condition_attribute_name, str_value); | |
213 } | |
214 | |
215 // static | |
216 scoped_ptr<URLMatcherSchemeFilter> URLMatcherFactory::CreateURLMatcherScheme( | |
217 const base::Value* value, | |
218 std::string* error) { | |
219 std::vector<std::string> schemas; | |
220 if (!helpers::GetAsStringVector(value, &schemas)) { | |
221 *error = ErrorUtils::FormatErrorMessage(kVectorOfStringsExpected, | |
222 keys::kSchemesKey); | |
223 return scoped_ptr<URLMatcherSchemeFilter>(NULL); | |
224 } | |
225 for (std::vector<std::string>::const_iterator it = schemas.begin(); | |
226 it != schemas.end(); ++it) { | |
227 if (ContainsUpperCase(*it)) { | |
228 *error = ErrorUtils::FormatErrorMessage(kLowerCaseExpected, | |
229 "Scheme"); | |
230 return scoped_ptr<URLMatcherSchemeFilter>(NULL); | |
231 } | |
232 } | |
233 return scoped_ptr<URLMatcherSchemeFilter>( | |
234 new URLMatcherSchemeFilter(schemas)); | |
235 } | |
236 | |
237 // static | |
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>(NULL); | |
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>(NULL); | |
262 } | |
263 ranges.push_back(URLMatcherPortFilter::CreateRange(from, to)); | |
264 } else { | |
265 *error = kInvalidPortRanges; | |
266 return scoped_ptr<URLMatcherPortFilter>(NULL); | |
267 } | |
268 } | |
269 | |
270 return scoped_ptr<URLMatcherPortFilter>(new URLMatcherPortFilter(ranges)); | |
271 } | |
272 | |
273 } // namespace extensions | |
OLD | NEW |