• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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