OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 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 "url/url_util.h" | 5 #include "url/url_util.h" |
6 | 6 |
7 #include <string.h> | 7 #include <string.h> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/debug/leak_annotations.h" | 10 #include "base/debug/leak_annotations.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
13 #include "url/url_canon_internal.h" | 13 #include "url/url_canon_internal.h" |
14 #include "url/url_file.h" | 14 #include "url/url_file.h" |
15 #include "url/url_util_internal.h" | 15 #include "url/url_util_internal.h" |
16 | 16 |
17 namespace url { | 17 namespace url { |
18 | 18 |
19 namespace { | 19 namespace { |
20 | 20 |
21 const int kNumStandardURLSchemes = 8; | 21 const int kNumStandardURLSchemes = 8; |
22 const char* kStandardURLSchemes[kNumStandardURLSchemes] = { | 22 const SchemeWithType kStandardURLSchemes[kNumStandardURLSchemes] = { |
23 kHttpScheme, | 23 {kHttpScheme, SCHEME_WITH_PORT}, |
24 kHttpsScheme, | 24 {kHttpsScheme, SCHEME_WITH_PORT}, |
25 kFileScheme, // Yes, file URLs can have a hostname! | 25 // Yes, file URLs can have a hostname, so file URLs should be handled as |
26 kFtpScheme, | 26 // "standard". File URLs never have a port as specified by the SchemeType |
27 kGopherScheme, | 27 // field. |
28 kWsScheme, // WebSocket. | 28 {kFileScheme, SCHEME_WITHOUT_PORT}, |
29 kWssScheme, // WebSocket secure. | 29 {kFtpScheme, SCHEME_WITH_PORT}, |
30 kFileSystemScheme, | 30 {kGopherScheme, SCHEME_WITH_PORT}, |
| 31 {kWsScheme, SCHEME_WITH_PORT}, // WebSocket. |
| 32 {kWssScheme, SCHEME_WITH_PORT}, // WebSocket secure. |
| 33 {kFileSystemScheme, SCHEME_WITHOUT_AUTHORITY}, |
31 }; | 34 }; |
32 | 35 |
33 // List of the currently installed standard schemes. This list is lazily | 36 // List of the currently installed standard schemes. This list is lazily |
34 // initialized by InitStandardSchemes and is leaked on shutdown to prevent | 37 // initialized by InitStandardSchemes and is leaked on shutdown to prevent |
35 // any destructors from being called that will slow us down or cause problems. | 38 // any destructors from being called that will slow us down or cause problems. |
36 std::vector<const char*>* standard_schemes = NULL; | 39 std::vector<SchemeWithType>* standard_schemes = NULL; |
37 | 40 |
38 // See the LockStandardSchemes declaration in the header. | 41 // See the LockStandardSchemes declaration in the header. |
39 bool standard_schemes_locked = false; | 42 bool standard_schemes_locked = false; |
40 | 43 |
41 // This template converts a given character type to the corresponding | 44 // This template converts a given character type to the corresponding |
42 // StringPiece type. | 45 // StringPiece type. |
43 template<typename CHAR> struct CharToStringPiece { | 46 template<typename CHAR> struct CharToStringPiece { |
44 }; | 47 }; |
45 template<> struct CharToStringPiece<char> { | 48 template<> struct CharToStringPiece<char> { |
46 typedef base::StringPiece Piece; | 49 typedef base::StringPiece Piece; |
47 }; | 50 }; |
48 template<> struct CharToStringPiece<base::char16> { | 51 template<> struct CharToStringPiece<base::char16> { |
49 typedef base::StringPiece16 Piece; | 52 typedef base::StringPiece16 Piece; |
50 }; | 53 }; |
51 | 54 |
52 // Ensures that the standard_schemes list is initialized, does nothing if it | 55 // Ensures that the standard_schemes list is initialized, does nothing if it |
53 // already has values. | 56 // already has values. |
54 void InitStandardSchemes() { | 57 void InitStandardSchemes() { |
55 if (standard_schemes) | 58 if (standard_schemes) |
56 return; | 59 return; |
57 standard_schemes = new std::vector<const char*>; | 60 standard_schemes = new std::vector<SchemeWithType>; |
58 for (int i = 0; i < kNumStandardURLSchemes; i++) | 61 for (int i = 0; i < kNumStandardURLSchemes; i++) |
59 standard_schemes->push_back(kStandardURLSchemes[i]); | 62 standard_schemes->push_back(kStandardURLSchemes[i]); |
60 } | 63 } |
61 | 64 |
62 // Given a string and a range inside the string, compares it to the given | 65 // Given a string and a range inside the string, compares it to the given |
63 // lower-case |compare_to| buffer. | 66 // lower-case |compare_to| buffer. |
64 template<typename CHAR> | 67 template<typename CHAR> |
65 inline bool DoCompareSchemeComponent(const CHAR* spec, | 68 inline bool DoCompareSchemeComponent(const CHAR* spec, |
66 const Component& component, | 69 const Component& component, |
67 const char* compare_to) { | 70 const char* compare_to) { |
68 if (!component.is_nonempty()) | 71 if (!component.is_nonempty()) |
69 return compare_to[0] == 0; // When component is empty, match empty scheme. | 72 return compare_to[0] == 0; // When component is empty, match empty scheme. |
70 return base::LowerCaseEqualsASCII( | 73 return base::LowerCaseEqualsASCII( |
71 typename CharToStringPiece<CHAR>::Piece( | 74 typename CharToStringPiece<CHAR>::Piece( |
72 &spec[component.begin], component.len), | 75 &spec[component.begin], component.len), |
73 compare_to); | 76 compare_to); |
74 } | 77 } |
75 | 78 |
76 // Returns true if the given scheme identified by |scheme| within |spec| is one | 79 // Returns true and sets |type| to the SchemeType of the given scheme |
77 // of the registered "standard" schemes. | 80 // identified by |scheme| within |spec| if the scheme is one of the registered |
| 81 // "standard" schemes. |
78 template<typename CHAR> | 82 template<typename CHAR> |
79 bool DoIsStandard(const CHAR* spec, const Component& scheme) { | 83 bool DoIsStandard(const CHAR* spec, |
| 84 const Component& scheme, |
| 85 SchemeType* type) { |
80 if (!scheme.is_nonempty()) | 86 if (!scheme.is_nonempty()) |
81 return false; // Empty or invalid schemes are non-standard. | 87 return false; // Empty or invalid schemes are non-standard. |
82 | 88 |
83 InitStandardSchemes(); | 89 InitStandardSchemes(); |
84 for (size_t i = 0; i < standard_schemes->size(); i++) { | 90 for (size_t i = 0; i < standard_schemes->size(); i++) { |
85 if (base::LowerCaseEqualsASCII( | 91 if (base::LowerCaseEqualsASCII( |
86 typename CharToStringPiece<CHAR>::Piece( | 92 typename CharToStringPiece<CHAR>::Piece( |
87 &spec[scheme.begin], scheme.len), | 93 &spec[scheme.begin], scheme.len), |
88 standard_schemes->at(i))) | 94 standard_schemes->at(i).scheme)) { |
| 95 *type = standard_schemes->at(i).type; |
89 return true; | 96 return true; |
| 97 } |
90 } | 98 } |
91 return false; | 99 return false; |
92 } | 100 } |
93 | 101 |
94 template<typename CHAR> | 102 template<typename CHAR> |
95 bool DoFindAndCompareScheme(const CHAR* str, | 103 bool DoFindAndCompareScheme(const CHAR* str, |
96 int str_len, | 104 int str_len, |
97 const char* compare, | 105 const char* compare, |
98 Component* found_scheme) { | 106 Component* found_scheme) { |
99 // Before extracting scheme, canonicalize the URL to remove any whitespace. | 107 // Before extracting scheme, canonicalize the URL to remove any whitespace. |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
149 } | 157 } |
150 #endif | 158 #endif |
151 | 159 |
152 Component scheme; | 160 Component scheme; |
153 if (!ExtractScheme(spec, spec_len, &scheme)) | 161 if (!ExtractScheme(spec, spec_len, &scheme)) |
154 return false; | 162 return false; |
155 | 163 |
156 // This is the parsed version of the input URL, we have to canonicalize it | 164 // This is the parsed version of the input URL, we have to canonicalize it |
157 // before storing it in our object. | 165 // before storing it in our object. |
158 bool success; | 166 bool success; |
| 167 SchemeType unused_scheme_type = SCHEME_WITH_PORT; |
159 if (DoCompareSchemeComponent(spec, scheme, url::kFileScheme)) { | 168 if (DoCompareSchemeComponent(spec, scheme, url::kFileScheme)) { |
160 // File URLs are special. | 169 // File URLs are special. |
161 ParseFileURL(spec, spec_len, &parsed_input); | 170 ParseFileURL(spec, spec_len, &parsed_input); |
162 success = CanonicalizeFileURL(spec, spec_len, parsed_input, | 171 success = CanonicalizeFileURL(spec, spec_len, parsed_input, |
163 charset_converter, output, output_parsed); | 172 charset_converter, output, output_parsed); |
164 } else if (DoCompareSchemeComponent(spec, scheme, url::kFileSystemScheme)) { | 173 } else if (DoCompareSchemeComponent(spec, scheme, url::kFileSystemScheme)) { |
165 // Filesystem URLs are special. | 174 // Filesystem URLs are special. |
166 ParseFileSystemURL(spec, spec_len, &parsed_input); | 175 ParseFileSystemURL(spec, spec_len, &parsed_input); |
167 success = CanonicalizeFileSystemURL(spec, spec_len, parsed_input, | 176 success = CanonicalizeFileSystemURL(spec, spec_len, parsed_input, |
168 charset_converter, output, | 177 charset_converter, output, |
169 output_parsed); | 178 output_parsed); |
170 | 179 |
171 } else if (DoIsStandard(spec, scheme)) { | 180 } else if (DoIsStandard(spec, scheme, &unused_scheme_type)) { |
172 // All "normal" URLs. | 181 // All "normal" URLs. |
173 ParseStandardURL(spec, spec_len, &parsed_input); | 182 ParseStandardURL(spec, spec_len, &parsed_input); |
174 success = CanonicalizeStandardURL(spec, spec_len, parsed_input, | 183 success = CanonicalizeStandardURL(spec, spec_len, parsed_input, |
175 charset_converter, output, output_parsed); | 184 charset_converter, output, output_parsed); |
176 | 185 |
177 } else if (DoCompareSchemeComponent(spec, scheme, url::kMailToScheme)) { | 186 } else if (DoCompareSchemeComponent(spec, scheme, url::kMailToScheme)) { |
178 // Mailto URLs are treated like standard URLs, with only a scheme, path, | 187 // Mailto URLs are treated like standard URLs, with only a scheme, path, |
179 // and query. | 188 // and query. |
180 ParseMailtoURL(spec, spec_len, &parsed_input); | 189 ParseMailtoURL(spec, spec_len, &parsed_input); |
181 success = CanonicalizeMailtoURL(spec, spec_len, parsed_input, output, | 190 success = CanonicalizeMailtoURL(spec, spec_len, parsed_input, output, |
(...skipping 28 matching lines...) Expand all Loading... |
210 bool base_is_hierarchical = false; | 219 bool base_is_hierarchical = false; |
211 if (base_spec && | 220 if (base_spec && |
212 base_parsed.scheme.is_nonempty()) { | 221 base_parsed.scheme.is_nonempty()) { |
213 int after_scheme = base_parsed.scheme.end() + 1; // Skip past the colon. | 222 int after_scheme = base_parsed.scheme.end() + 1; // Skip past the colon. |
214 int num_slashes = CountConsecutiveSlashes(base_spec, after_scheme, | 223 int num_slashes = CountConsecutiveSlashes(base_spec, after_scheme, |
215 base_spec_len); | 224 base_spec_len); |
216 base_is_authority_based = num_slashes > 1; | 225 base_is_authority_based = num_slashes > 1; |
217 base_is_hierarchical = num_slashes > 0; | 226 base_is_hierarchical = num_slashes > 0; |
218 } | 227 } |
219 | 228 |
| 229 SchemeType unused_scheme_type = SCHEME_WITH_PORT; |
220 bool standard_base_scheme = | 230 bool standard_base_scheme = |
221 base_parsed.scheme.is_nonempty() && | 231 base_parsed.scheme.is_nonempty() && |
222 DoIsStandard(base_spec, base_parsed.scheme); | 232 DoIsStandard(base_spec, base_parsed.scheme, &unused_scheme_type); |
223 | 233 |
224 bool is_relative; | 234 bool is_relative; |
225 Component relative_component; | 235 Component relative_component; |
226 if (!IsRelativeURL(base_spec, base_parsed, relative, relative_length, | 236 if (!IsRelativeURL(base_spec, base_parsed, relative, relative_length, |
227 (base_is_hierarchical || standard_base_scheme), | 237 (base_is_hierarchical || standard_base_scheme), |
228 &is_relative, &relative_component)) { | 238 &is_relative, &relative_component)) { |
229 // Error resolving. | 239 // Error resolving. |
230 return false; | 240 return false; |
231 } | 241 } |
232 | 242 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 // If we get here, then we know the scheme doesn't need to be replaced, so can | 343 // If we get here, then we know the scheme doesn't need to be replaced, so can |
334 // just key off the scheme in the spec to know how to do the replacements. | 344 // just key off the scheme in the spec to know how to do the replacements. |
335 if (DoCompareSchemeComponent(spec, parsed.scheme, url::kFileScheme)) { | 345 if (DoCompareSchemeComponent(spec, parsed.scheme, url::kFileScheme)) { |
336 return ReplaceFileURL(spec, parsed, replacements, charset_converter, output, | 346 return ReplaceFileURL(spec, parsed, replacements, charset_converter, output, |
337 out_parsed); | 347 out_parsed); |
338 } | 348 } |
339 if (DoCompareSchemeComponent(spec, parsed.scheme, url::kFileSystemScheme)) { | 349 if (DoCompareSchemeComponent(spec, parsed.scheme, url::kFileSystemScheme)) { |
340 return ReplaceFileSystemURL(spec, parsed, replacements, charset_converter, | 350 return ReplaceFileSystemURL(spec, parsed, replacements, charset_converter, |
341 output, out_parsed); | 351 output, out_parsed); |
342 } | 352 } |
343 if (DoIsStandard(spec, parsed.scheme)) { | 353 SchemeType unused_scheme_type = SCHEME_WITH_PORT; |
| 354 if (DoIsStandard(spec, parsed.scheme, &unused_scheme_type)) { |
344 return ReplaceStandardURL(spec, parsed, replacements, charset_converter, | 355 return ReplaceStandardURL(spec, parsed, replacements, charset_converter, |
345 output, out_parsed); | 356 output, out_parsed); |
346 } | 357 } |
347 if (DoCompareSchemeComponent(spec, parsed.scheme, url::kMailToScheme)) { | 358 if (DoCompareSchemeComponent(spec, parsed.scheme, url::kMailToScheme)) { |
348 return ReplaceMailtoURL(spec, parsed, replacements, output, out_parsed); | 359 return ReplaceMailtoURL(spec, parsed, replacements, output, out_parsed); |
349 } | 360 } |
350 | 361 |
351 // Default is a path URL. | 362 // Default is a path URL. |
352 return ReplacePathURL(spec, parsed, replacements, output, out_parsed); | 363 return ReplacePathURL(spec, parsed, replacements, output, out_parsed); |
353 } | 364 } |
354 | 365 |
355 } // namespace | 366 } // namespace |
356 | 367 |
357 void Initialize() { | 368 void Initialize() { |
358 InitStandardSchemes(); | 369 InitStandardSchemes(); |
359 } | 370 } |
360 | 371 |
361 void Shutdown() { | 372 void Shutdown() { |
362 if (standard_schemes) { | 373 if (standard_schemes) { |
363 delete standard_schemes; | 374 delete standard_schemes; |
364 standard_schemes = NULL; | 375 standard_schemes = NULL; |
365 } | 376 } |
366 } | 377 } |
367 | 378 |
368 void AddStandardScheme(const char* new_scheme) { | 379 void AddStandardScheme(const char* new_scheme, |
| 380 SchemeType type) { |
369 // If this assert triggers, it means you've called AddStandardScheme after | 381 // If this assert triggers, it means you've called AddStandardScheme after |
370 // LockStandardSchemes have been called (see the header file for | 382 // LockStandardSchemes have been called (see the header file for |
371 // LockStandardSchemes for more). | 383 // LockStandardSchemes for more). |
372 // | 384 // |
373 // This normally means you're trying to set up a new standard scheme too late | 385 // This normally means you're trying to set up a new standard scheme too late |
374 // in your application's init process. Locate where your app does this | 386 // in your application's init process. Locate where your app does this |
375 // initialization and calls LockStandardSchemes, and add your new standard | 387 // initialization and calls LockStandardSchemes, and add your new standard |
376 // scheme there. | 388 // scheme there. |
377 DCHECK(!standard_schemes_locked) << | 389 DCHECK(!standard_schemes_locked) << |
378 "Trying to add a standard scheme after the list has been locked."; | 390 "Trying to add a standard scheme after the list has been locked."; |
379 | 391 |
380 size_t scheme_len = strlen(new_scheme); | 392 size_t scheme_len = strlen(new_scheme); |
381 if (scheme_len == 0) | 393 if (scheme_len == 0) |
382 return; | 394 return; |
383 | 395 |
384 // Duplicate the scheme into a new buffer and add it to the list of standard | 396 // Duplicate the scheme into a new buffer and add it to the list of standard |
385 // schemes. This pointer will be leaked on shutdown. | 397 // schemes. This pointer will be leaked on shutdown. |
386 char* dup_scheme = new char[scheme_len + 1]; | 398 char* dup_scheme = new char[scheme_len + 1]; |
387 ANNOTATE_LEAKING_OBJECT_PTR(dup_scheme); | 399 ANNOTATE_LEAKING_OBJECT_PTR(dup_scheme); |
388 memcpy(dup_scheme, new_scheme, scheme_len + 1); | 400 memcpy(dup_scheme, new_scheme, scheme_len + 1); |
389 | 401 |
390 InitStandardSchemes(); | 402 InitStandardSchemes(); |
391 standard_schemes->push_back(dup_scheme); | 403 SchemeWithType scheme_with_type; |
| 404 scheme_with_type.scheme = dup_scheme; |
| 405 scheme_with_type.type = type; |
| 406 standard_schemes->push_back(scheme_with_type); |
392 } | 407 } |
393 | 408 |
394 void LockStandardSchemes() { | 409 void LockStandardSchemes() { |
395 standard_schemes_locked = true; | 410 standard_schemes_locked = true; |
396 } | 411 } |
397 | 412 |
398 bool IsStandard(const char* spec, const Component& scheme) { | 413 bool IsStandard(const char* spec, const Component& scheme) { |
399 return DoIsStandard(spec, scheme); | 414 SchemeType unused_scheme_type; |
| 415 return DoIsStandard(spec, scheme, &unused_scheme_type); |
| 416 } |
| 417 |
| 418 bool GetStandardSchemeType(const char* spec, |
| 419 const Component& scheme, |
| 420 SchemeType* type) { |
| 421 return DoIsStandard(spec, scheme, type); |
400 } | 422 } |
401 | 423 |
402 bool IsStandard(const base::char16* spec, const Component& scheme) { | 424 bool IsStandard(const base::char16* spec, const Component& scheme) { |
403 return DoIsStandard(spec, scheme); | 425 SchemeType unused_scheme_type; |
| 426 return DoIsStandard(spec, scheme, &unused_scheme_type); |
404 } | 427 } |
405 | 428 |
406 bool FindAndCompareScheme(const char* str, | 429 bool FindAndCompareScheme(const char* str, |
407 int str_len, | 430 int str_len, |
408 const char* compare, | 431 const char* compare, |
409 Component* found_scheme) { | 432 Component* found_scheme) { |
410 return DoFindAndCompareScheme(str, str_len, compare, found_scheme); | 433 return DoFindAndCompareScheme(str, str_len, compare, found_scheme); |
411 } | 434 } |
412 | 435 |
413 bool FindAndCompareScheme(const base::char16* str, | 436 bool FindAndCompareScheme(const base::char16* str, |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
551 return DoCompareSchemeComponent(spec, component, compare_to); | 574 return DoCompareSchemeComponent(spec, component, compare_to); |
552 } | 575 } |
553 | 576 |
554 bool CompareSchemeComponent(const base::char16* spec, | 577 bool CompareSchemeComponent(const base::char16* spec, |
555 const Component& component, | 578 const Component& component, |
556 const char* compare_to) { | 579 const char* compare_to) { |
557 return DoCompareSchemeComponent(spec, component, compare_to); | 580 return DoCompareSchemeComponent(spec, component, compare_to); |
558 } | 581 } |
559 | 582 |
560 } // namespace url | 583 } // namespace url |
OLD | NEW |