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: 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. 68 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. 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); 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> 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 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, 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 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. 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 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 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 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 (base::ListValue::const_iterator i = value_list->begin(); 249 i != value_list->end(); ++i) { 250 base::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