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