OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/common/extensions/url_pattern.h" | 5 #include "chrome/common/extensions/url_pattern.h" |
6 | 6 |
| 7 #include "base/string_number_conversions.h" |
7 #include "base/string_piece.h" | 8 #include "base/string_piece.h" |
8 #include "base/string_split.h" | 9 #include "base/string_split.h" |
9 #include "base/string_util.h" | 10 #include "base/string_util.h" |
10 #include "chrome/common/url_constants.h" | 11 #include "chrome/common/url_constants.h" |
11 #include "googleurl/src/gurl.h" | 12 #include "googleurl/src/gurl.h" |
12 #include "googleurl/src/url_util.h" | 13 #include "googleurl/src/url_util.h" |
13 | 14 |
14 const char URLPattern::kAllUrlsPattern[] = "<all_urls>"; | 15 const char URLPattern::kAllUrlsPattern[] = "<all_urls>"; |
15 | 16 |
16 namespace { | 17 namespace { |
(...skipping 24 matching lines...) Expand all Loading... |
41 | 42 |
42 const char* kParseSuccess = "Success."; | 43 const char* kParseSuccess = "Success."; |
43 const char* kParseErrorMissingSchemeSeparator = "Missing scheme separator."; | 44 const char* kParseErrorMissingSchemeSeparator = "Missing scheme separator."; |
44 const char* kParseErrorInvalidScheme = "Invalid scheme."; | 45 const char* kParseErrorInvalidScheme = "Invalid scheme."; |
45 const char* kParseErrorWrongSchemeType = "Wrong scheme type."; | 46 const char* kParseErrorWrongSchemeType = "Wrong scheme type."; |
46 const char* kParseErrorEmptyHost = "Host can not be empty."; | 47 const char* kParseErrorEmptyHost = "Host can not be empty."; |
47 const char* kParseErrorInvalidHostWildcard = "Invalid host wildcard."; | 48 const char* kParseErrorInvalidHostWildcard = "Invalid host wildcard."; |
48 const char* kParseErrorEmptyPath = "Empty path."; | 49 const char* kParseErrorEmptyPath = "Empty path."; |
49 const char* kParseErrorHasColon = | 50 const char* kParseErrorHasColon = |
50 "Ports are not supported in URL patterns. ':' may not be used in a host."; | 51 "Ports are not supported in URL patterns. ':' may not be used in a host."; |
| 52 const char* kParseErrorInvalidPort = |
| 53 "Invalid port."; |
51 | 54 |
52 // Message explaining each URLPattern::ParseResult. | 55 // Message explaining each URLPattern::ParseResult. |
53 const char* kParseResultMessages[] = { | 56 const char* kParseResultMessages[] = { |
54 kParseSuccess, | 57 kParseSuccess, |
55 kParseErrorMissingSchemeSeparator, | 58 kParseErrorMissingSchemeSeparator, |
56 kParseErrorInvalidScheme, | 59 kParseErrorInvalidScheme, |
57 kParseErrorWrongSchemeType, | 60 kParseErrorWrongSchemeType, |
58 kParseErrorEmptyHost, | 61 kParseErrorEmptyHost, |
59 kParseErrorInvalidHostWildcard, | 62 kParseErrorInvalidHostWildcard, |
60 kParseErrorEmptyPath, | 63 kParseErrorEmptyPath, |
61 kParseErrorHasColon | 64 kParseErrorHasColon, |
| 65 kParseErrorInvalidPort, |
62 }; | 66 }; |
63 | 67 |
64 COMPILE_ASSERT(URLPattern::NUM_PARSE_RESULTS == arraysize(kParseResultMessages), | 68 COMPILE_ASSERT(URLPattern::NUM_PARSE_RESULTS == arraysize(kParseResultMessages), |
65 must_add_message_for_each_parse_result); | 69 must_add_message_for_each_parse_result); |
66 | 70 |
67 const char kPathSeparator[] = "/"; | 71 const char kPathSeparator[] = "/"; |
68 | 72 |
69 bool IsStandardScheme(const std::string& scheme) { | 73 bool IsStandardScheme(const std::string& scheme) { |
70 // "*" gets the same treatment as a standard scheme. | 74 // "*" gets the same treatment as a standard scheme. |
71 if (scheme == "*") | 75 if (scheme == "*") |
72 return true; | 76 return true; |
73 | 77 |
74 return url_util::IsStandard(scheme.c_str(), | 78 return url_util::IsStandard(scheme.c_str(), |
75 url_parse::Component(0, static_cast<int>(scheme.length()))); | 79 url_parse::Component(0, static_cast<int>(scheme.length()))); |
76 } | 80 } |
77 | 81 |
| 82 bool IsValidPortForScheme(const std::string scheme, const std::string& port) { |
| 83 if (port == "*") |
| 84 return true; |
| 85 |
| 86 // Only accept non-wildcard ports if the scheme uses ports. |
| 87 if (url_canon::DefaultPortForScheme(scheme.c_str(), scheme.length()) == |
| 88 url_parse::PORT_UNSPECIFIED) { |
| 89 return false; |
| 90 } |
| 91 |
| 92 int parsed_port = url_parse::PORT_UNSPECIFIED; |
| 93 if (!base::StringToInt(port, &parsed_port)) |
| 94 return false; |
| 95 return (parsed_port >= 0) && (parsed_port < 65536); |
| 96 } |
| 97 |
78 } // namespace | 98 } // namespace |
79 | 99 |
80 URLPattern::URLPattern() | 100 URLPattern::URLPattern() |
81 : valid_schemes_(SCHEME_NONE), | 101 : valid_schemes_(SCHEME_NONE), |
82 match_all_urls_(false), | 102 match_all_urls_(false), |
83 match_subdomains_(false) {} | 103 match_subdomains_(false), |
| 104 port_("*") {} |
84 | 105 |
85 URLPattern::URLPattern(int valid_schemes) | 106 URLPattern::URLPattern(int valid_schemes) |
86 : valid_schemes_(valid_schemes), match_all_urls_(false), | 107 : valid_schemes_(valid_schemes), |
87 match_subdomains_(false) {} | 108 match_all_urls_(false), |
| 109 match_subdomains_(false), |
| 110 port_("*") {} |
88 | 111 |
89 URLPattern::URLPattern(int valid_schemes, const std::string& pattern) | 112 URLPattern::URLPattern(int valid_schemes, const std::string& pattern) |
90 : valid_schemes_(valid_schemes), match_all_urls_(false), | 113 : valid_schemes_(valid_schemes), |
91 match_subdomains_(false) { | 114 match_all_urls_(false), |
| 115 match_subdomains_(false), |
| 116 port_("*") { |
92 | 117 |
93 // Strict error checking is used, because this constructor is only | 118 // Strict error checking is used, because this constructor is only |
94 // appropriate when we know |pattern| is valid. | 119 // appropriate when we know |pattern| is valid. |
95 if (PARSE_SUCCESS != Parse(pattern, PARSE_STRICT)) | 120 if (PARSE_SUCCESS != Parse(pattern, ERROR_ON_PORTS)) |
96 NOTREACHED() << "URLPattern is invalid: " << pattern; | 121 NOTREACHED() << "URLPattern is invalid: " << pattern; |
97 } | 122 } |
98 | 123 |
99 URLPattern::~URLPattern() { | 124 URLPattern::~URLPattern() { |
100 } | 125 } |
101 | 126 |
102 URLPattern::ParseResult URLPattern::Parse(const std::string& pattern, | 127 URLPattern::ParseResult URLPattern::Parse(const std::string& pattern, |
103 ParseOption strictness) { | 128 ParseOption strictness) { |
104 CHECK(strictness == PARSE_LENIENT || | |
105 strictness == PARSE_STRICT); | |
106 | |
107 // Special case pattern to match every valid URL. | 129 // Special case pattern to match every valid URL. |
108 if (pattern == kAllUrlsPattern) { | 130 if (pattern == kAllUrlsPattern) { |
109 match_all_urls_ = true; | 131 match_all_urls_ = true; |
110 match_subdomains_ = true; | 132 match_subdomains_ = true; |
111 scheme_ = "*"; | 133 scheme_ = "*"; |
112 host_.clear(); | 134 host_.clear(); |
113 SetPath("/*"); | 135 SetPath("/*"); |
114 return PARSE_SUCCESS; | 136 return PARSE_SUCCESS; |
115 } | 137 } |
116 | 138 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 // The first component can optionally be '*' to match all subdomains. | 195 // The first component can optionally be '*' to match all subdomains. |
174 std::vector<std::string> host_components; | 196 std::vector<std::string> host_components; |
175 base::SplitString(host_, '.', &host_components); | 197 base::SplitString(host_, '.', &host_components); |
176 if (host_components[0] == "*") { | 198 if (host_components[0] == "*") { |
177 match_subdomains_ = true; | 199 match_subdomains_ = true; |
178 host_components.erase(host_components.begin(), | 200 host_components.erase(host_components.begin(), |
179 host_components.begin() + 1); | 201 host_components.begin() + 1); |
180 } | 202 } |
181 host_ = JoinString(host_components, '.'); | 203 host_ = JoinString(host_components, '.'); |
182 | 204 |
183 // No other '*' can occur in the host, though. This isn't necessary, but is | |
184 // done as a convenience to developers who might otherwise be confused and | |
185 // think '*' works as a glob in the host. | |
186 if (host_.find('*') != std::string::npos) | |
187 return PARSE_ERROR_INVALID_HOST_WILDCARD; | |
188 | |
189 path_start_pos = host_end_pos; | 205 path_start_pos = host_end_pos; |
190 } | 206 } |
191 | 207 |
192 SetPath(pattern.substr(path_start_pos)); | 208 SetPath(pattern.substr(path_start_pos)); |
193 | 209 |
194 if (strictness == PARSE_STRICT && host_.find(':') != std::string::npos) | 210 size_t port_pos = host_.find(':'); |
195 return PARSE_ERROR_HAS_COLON; | 211 if (port_pos != std::string::npos) { |
| 212 switch (strictness) { |
| 213 case USE_PORTS: { |
| 214 if (!SetPort(host_.substr(port_pos + 1))) |
| 215 return PARSE_ERROR_INVALID_PORT; |
| 216 host_ = host_.substr(0, port_pos); |
| 217 break; |
| 218 } |
| 219 case ERROR_ON_PORTS: { |
| 220 return PARSE_ERROR_HAS_COLON; |
| 221 } |
| 222 case IGNORE_PORTS: { |
| 223 // Do nothing, i.e. leave the colon in the host. |
| 224 } |
| 225 } |
| 226 } |
| 227 |
| 228 // No other '*' can occur in the host, though. This isn't necessary, but is |
| 229 // done as a convenience to developers who might otherwise be confused and |
| 230 // think '*' works as a glob in the host. |
| 231 if (host_.find('*') != std::string::npos) |
| 232 return PARSE_ERROR_INVALID_HOST_WILDCARD; |
196 | 233 |
197 return PARSE_SUCCESS; | 234 return PARSE_SUCCESS; |
198 } | 235 } |
199 | 236 |
200 bool URLPattern::SetScheme(const std::string& scheme) { | 237 bool URLPattern::SetScheme(const std::string& scheme) { |
201 scheme_ = scheme; | 238 scheme_ = scheme; |
202 if (scheme_ == "*") { | 239 if (scheme_ == "*") { |
203 valid_schemes_ &= (SCHEME_HTTP | SCHEME_HTTPS); | 240 valid_schemes_ &= (SCHEME_HTTP | SCHEME_HTTPS); |
204 } else if (!IsValidScheme(scheme_)) { | 241 } else if (!IsValidScheme(scheme_)) { |
205 return false; | 242 return false; |
(...skipping 13 matching lines...) Expand all Loading... |
219 return false; | 256 return false; |
220 } | 257 } |
221 | 258 |
222 void URLPattern::SetPath(const std::string& path) { | 259 void URLPattern::SetPath(const std::string& path) { |
223 path_ = path; | 260 path_ = path; |
224 path_escaped_ = path_; | 261 path_escaped_ = path_; |
225 ReplaceSubstringsAfterOffset(&path_escaped_, 0, "\\", "\\\\"); | 262 ReplaceSubstringsAfterOffset(&path_escaped_, 0, "\\", "\\\\"); |
226 ReplaceSubstringsAfterOffset(&path_escaped_, 0, "?", "\\?"); | 263 ReplaceSubstringsAfterOffset(&path_escaped_, 0, "?", "\\?"); |
227 } | 264 } |
228 | 265 |
| 266 bool URLPattern::SetPort(const std::string& port) { |
| 267 if (IsValidPortForScheme(scheme_, port)) { |
| 268 port_ = port; |
| 269 return true; |
| 270 } |
| 271 return false; |
| 272 } |
| 273 |
229 bool URLPattern::MatchesURL(const GURL &test) const { | 274 bool URLPattern::MatchesURL(const GURL &test) const { |
230 if (!MatchesScheme(test.scheme())) | 275 if (!MatchesScheme(test.scheme())) |
231 return false; | 276 return false; |
232 | 277 |
233 if (match_all_urls_) | 278 if (match_all_urls_) |
234 return true; | 279 return true; |
235 | 280 |
236 // Ignore hostname if scheme is file://. | 281 // Ignore hostname if scheme is file://. |
237 if (scheme_ != chrome::kFileScheme && !MatchesHost(test)) | 282 if (scheme_ != chrome::kFileScheme && !MatchesHost(test)) |
238 return false; | 283 return false; |
239 | 284 |
240 if (!MatchesPath(test.PathForRequest())) | 285 if (!MatchesPath(test.PathForRequest())) |
241 return false; | 286 return false; |
242 | 287 |
| 288 if (!MatchesPort(test.EffectiveIntPort())) |
| 289 return false; |
| 290 |
243 return true; | 291 return true; |
244 } | 292 } |
245 | 293 |
246 bool URLPattern::MatchesScheme(const std::string& test) const { | 294 bool URLPattern::MatchesScheme(const std::string& test) const { |
247 if (!IsValidScheme(test)) | 295 if (!IsValidScheme(test)) |
248 return false; | 296 return false; |
249 | 297 |
250 return scheme_ == "*" || test == scheme_; | 298 return scheme_ == "*" || test == scheme_; |
251 } | 299 } |
252 | 300 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
289 return test.host()[test.host().length() - host_.length() - 1] == '.'; | 337 return test.host()[test.host().length() - host_.length() - 1] == '.'; |
290 } | 338 } |
291 | 339 |
292 bool URLPattern::MatchesPath(const std::string& test) const { | 340 bool URLPattern::MatchesPath(const std::string& test) const { |
293 if (!MatchPattern(test, path_escaped_)) | 341 if (!MatchPattern(test, path_escaped_)) |
294 return false; | 342 return false; |
295 | 343 |
296 return true; | 344 return true; |
297 } | 345 } |
298 | 346 |
| 347 bool URLPattern::MatchesPort(int port) const { |
| 348 if (port == url_parse::PORT_INVALID) |
| 349 return false; |
| 350 |
| 351 return port_ == "*" || port_ == base::IntToString(port); |
| 352 } |
| 353 |
299 std::string URLPattern::GetAsString() const { | 354 std::string URLPattern::GetAsString() const { |
300 if (match_all_urls_) | 355 if (match_all_urls_) |
301 return kAllUrlsPattern; | 356 return kAllUrlsPattern; |
302 | 357 |
303 bool standard_scheme = IsStandardScheme(scheme_); | 358 bool standard_scheme = IsStandardScheme(scheme_); |
304 | 359 |
305 std::string spec = scheme_ + | 360 std::string spec = scheme_ + |
306 (standard_scheme ? chrome::kStandardSchemeSeparator : ":"); | 361 (standard_scheme ? chrome::kStandardSchemeSeparator : ":"); |
307 | 362 |
308 if (scheme_ != chrome::kFileScheme && standard_scheme) { | 363 if (scheme_ != chrome::kFileScheme && standard_scheme) { |
309 if (match_subdomains_) { | 364 if (match_subdomains_) { |
310 spec += "*"; | 365 spec += "*"; |
311 if (!host_.empty()) | 366 if (!host_.empty()) |
312 spec += "."; | 367 spec += "."; |
313 } | 368 } |
314 | 369 |
315 if (!host_.empty()) | 370 if (!host_.empty()) |
316 spec += host_; | 371 spec += host_; |
| 372 |
| 373 if (port_ != "*") { |
| 374 spec += ":"; |
| 375 spec += port_; |
| 376 } |
317 } | 377 } |
318 | 378 |
319 if (!path_.empty()) | 379 if (!path_.empty()) |
320 spec += path_; | 380 spec += path_; |
321 | 381 |
322 return spec; | 382 return spec; |
323 } | 383 } |
324 | 384 |
325 bool URLPattern::OverlapsWith(const URLPattern& other) const { | 385 bool URLPattern::OverlapsWith(const URLPattern& other) const { |
326 if (!MatchesAnyScheme(other.GetExplicitSchemes()) && | 386 if (!MatchesAnyScheme(other.GetExplicitSchemes()) && |
327 !other.MatchesAnyScheme(GetExplicitSchemes())) { | 387 !other.MatchesAnyScheme(GetExplicitSchemes())) { |
328 return false; | 388 return false; |
329 } | 389 } |
330 | 390 |
331 if (!MatchesHost(other.host()) && !other.MatchesHost(host_)) | 391 if (!MatchesHost(other.host()) && !other.MatchesHost(host_)) |
332 return false; | 392 return false; |
333 | 393 |
| 394 if (port_ != "*" && other.port() != "*" && port_ != other.port()) |
| 395 return false; |
| 396 |
334 // We currently only use OverlapsWith() for the patterns inside | 397 // We currently only use OverlapsWith() for the patterns inside |
335 // URLPatternSet. In those cases, we know that the path will have only a | 398 // URLPatternSet. In those cases, we know that the path will have only a |
336 // single wildcard at the end. This makes figuring out overlap much easier. It | 399 // single wildcard at the end. This makes figuring out overlap much easier. It |
337 // seems like there is probably a computer-sciency way to solve the general | 400 // seems like there is probably a computer-sciency way to solve the general |
338 // case, but we don't need that yet. | 401 // case, but we don't need that yet. |
339 DCHECK(path_.find('*') == path_.size() - 1); | 402 DCHECK(path_.find('*') == path_.size() - 1); |
340 DCHECK(other.path().find('*') == other.path().size() - 1); | 403 DCHECK(other.path().find('*') == other.path().size() - 1); |
341 | 404 |
342 if (!MatchesPath(other.path().substr(0, other.path().size() - 1)) && | 405 if (!MatchesPath(other.path().substr(0, other.path().size() - 1)) && |
343 !other.MatchesPath(path_.substr(0, path_.size() - 1))) | 406 !other.MatchesPath(path_.substr(0, path_.size() - 1))) |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
387 } | 450 } |
388 | 451 |
389 return result; | 452 return result; |
390 } | 453 } |
391 | 454 |
392 // static | 455 // static |
393 const char* URLPattern::GetParseResultString( | 456 const char* URLPattern::GetParseResultString( |
394 URLPattern::ParseResult parse_result) { | 457 URLPattern::ParseResult parse_result) { |
395 return kParseResultMessages[parse_result]; | 458 return kParseResultMessages[parse_result]; |
396 } | 459 } |
OLD | NEW |