1 // Copyright (c) 2012 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_fixer/url_fixer.h" 6 7 #include <algorithm> 8 9 #include "base/files/file_path.h" 10 #include "base/files/file_util.h" 11 #include "base/logging.h" 12 #if defined(OS_POSIX) 13 #include "base/path_service.h" 14 #endif 15 #include "base/strings/string_util.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "net/base/escape.h" 18 #include "net/base/filename_util.h" 19 #include "net/base/net_util.h" 20 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 21 #include "url/url_file.h" 22 #include "url/url_parse.h" 23 #include "url/url_util.h" 24 25 const char* url_fixer::home_directory_override = NULL; 26 27 namespace { 28 29 // Hardcode these constants to avoid dependences on //chrome and //content. 30 const char kChromeUIScheme[] = "chrome"; 31 const char kChromeUIDefaultHost[] = "version"; 32 const char kViewSourceScheme[] = "view-source"; 33 34 // TODO(estade): Remove these ugly, ugly functions. They are only used in 35 // SegmentURL. A url::Parsed object keeps track of a bunch of indices into 36 // a url string, and these need to be updated when the URL is converted from 37 // UTF8 to UTF16. Instead of this after-the-fact adjustment, we should parse it 38 // in the correct string format to begin with. 39 url::Component UTF8ComponentToUTF16Component( 40 const std::string& text_utf8, 41 const url::Component& component_utf8) { 42 if (component_utf8.len == -1) 43 return url::Component(); 44 45 std::string before_component_string = 46 text_utf8.substr(0, component_utf8.begin); 47 std::string component_string = 48 text_utf8.substr(component_utf8.begin, component_utf8.len); 49 base::string16 before_component_string_16 = 50 base::UTF8ToUTF16(before_component_string); 51 base::string16 component_string_16 = base::UTF8ToUTF16(component_string); 52 url::Component component_16(before_component_string_16.length(), 53 component_string_16.length()); 54 return component_16; 55 } 56 57 void UTF8PartsToUTF16Parts(const std::string& text_utf8, 58 const url::Parsed& parts_utf8, 59 url::Parsed* parts) { 60 if (base::IsStringASCII(text_utf8)) { 61 *parts = parts_utf8; 62 return; 63 } 64 65 parts->scheme = UTF8ComponentToUTF16Component(text_utf8, parts_utf8.scheme); 66 parts->username = 67 UTF8ComponentToUTF16Component(text_utf8, parts_utf8.username); 68 parts->password = 69 UTF8ComponentToUTF16Component(text_utf8, parts_utf8.password); 70 parts->host = UTF8ComponentToUTF16Component(text_utf8, parts_utf8.host); 71 parts->port = UTF8ComponentToUTF16Component(text_utf8, parts_utf8.port); 72 parts->path = UTF8ComponentToUTF16Component(text_utf8, parts_utf8.path); 73 parts->query = UTF8ComponentToUTF16Component(text_utf8, parts_utf8.query); 74 parts->ref = UTF8ComponentToUTF16Component(text_utf8, parts_utf8.ref); 75 } 76 77 base::TrimPositions TrimWhitespaceUTF8(const std::string& input, 78 base::TrimPositions positions, 79 std::string* output) { 80 // This implementation is not so fast since it converts the text encoding 81 // twice. Please feel free to file a bug if this function hurts the 82 // performance of Chrome. 83 DCHECK(base::IsStringUTF8(input)); 84 base::string16 input16 = base::UTF8ToUTF16(input); 85 base::string16 output16; 86 base::TrimPositions result = 87 base::TrimWhitespace(input16, positions, &output16); 88 *output = base::UTF16ToUTF8(output16); 89 return result; 90 } 91 92 // does some basic fixes for input that we want to test for file-ness 93 void PrepareStringForFileOps(const base::FilePath& text, 94 base::FilePath::StringType* output) { 95 #if defined(OS_WIN) 96 base::TrimWhitespace(text.value(), base::TRIM_ALL, output); 97 replace(output->begin(), output->end(), '/', '\\'); 98 #else 99 TrimWhitespaceUTF8(text.value(), base::TRIM_ALL, output); 100 #endif 101 } 102 103 // Tries to create a full path from |text|. If the result is valid and the 104 // file exists, returns true and sets |full_path| to the result. Otherwise, 105 // returns false and leaves |full_path| unchanged. 106 bool ValidPathForFile(const base::FilePath::StringType& text, 107 base::FilePath* full_path) { 108 base::FilePath file_path = base::MakeAbsoluteFilePath(base::FilePath(text)); 109 if (file_path.empty()) 110 return false; 111 112 if (!base::PathExists(file_path)) 113 return false; 114 115 *full_path = file_path; 116 return true; 117 } 118 119 #if defined(OS_POSIX) 120 // Given a path that starts with ~, return a path that starts with an 121 // expanded-out /user/foobar directory. 122 std::string FixupHomedir(const std::string& text) { 123 DCHECK(text.length() > 0 && text[0] == '~'); 124 125 if (text.length() == 1 || text[1] == '/') { 126 base::FilePath file_path; 127 if (url_fixer::home_directory_override) 128 file_path = base::FilePath(url_fixer::home_directory_override); 129 else 130 PathService::Get(base::DIR_HOME, &file_path); 131 132 // We'll probably break elsewhere if $HOME is undefined, but check here 133 // just in case. 134 if (file_path.value().empty()) 135 return text; 136 // Append requires to be a relative path, so we have to cut all preceeding 137 // '/' characters. 138 size_t i = 1; 139 while (i < text.length() && text[i] == '/') 140 ++i; 141 return file_path.Append(text.substr(i)).value(); 142 } 143 144 // Otherwise, this is a path like ~foobar/baz, where we must expand to 145 // user foobar's home directory. Officially, we should use getpwent(), 146 // but that is a nasty blocking call. 147 148 #if defined(OS_MACOSX) 149 static const char kHome[] = "/Users/"; 150 #else 151 static const char kHome[] = "/home/"; 152 #endif 153 return kHome + text.substr(1); 154 } 155 #endif 156 157 // Tries to create a file: URL from |text| if it looks like a filename, even if 158 // it doesn't resolve as a valid path or to an existing file. Returns a 159 // (possibly invalid) file: URL in |fixed_up_url| for input beginning 160 // with a drive specifier or "\\". Returns the unchanged input in other cases 161 // (including file: URLs: these don't look like filenames). 162 std::string FixupPath(const std::string& text) { 163 DCHECK(!text.empty()); 164 165 base::FilePath::StringType filename; 166 #if defined(OS_WIN) 167 base::FilePath input_path(base::UTF8ToWide(text)); 168 PrepareStringForFileOps(input_path, &filename); 169 170 // Fixup Windows-style drive letters, where "C:" gets rewritten to "C|". 171 if (filename.length() > 1 && filename[1] == '|') 172 filename[1] = ':'; 173 #elif defined(OS_POSIX) 174 base::FilePath input_path(text); 175 PrepareStringForFileOps(input_path, &filename); 176 if (filename.length() > 0 && filename[0] == '~') 177 filename = FixupHomedir(filename); 178 #endif 179 180 // Here, we know the input looks like a file. 181 GURL file_url = net::FilePathToFileURL(base::FilePath(filename)); 182 if (file_url.is_valid()) { 183 return base::UTF16ToUTF8(net::FormatUrl(file_url, 184 std::string(), 185 net::kFormatUrlOmitUsernamePassword, 186 net::UnescapeRule::NORMAL, 187 NULL, 188 NULL, 189 NULL)); 190 } 191 192 // Invalid file URL, just return the input. 193 return text; 194 } 195 196 // Checks |domain| to see if a valid TLD is already present. If not, appends 197 // |desired_tld| to the domain, and prepends "www." unless it's already present. 198 void AddDesiredTLD(const std::string& desired_tld, std::string* domain) { 199 if (desired_tld.empty() || domain->empty()) 200 return; 201 202 // Check the TLD. If the return value is positive, we already have a TLD, so 203 // abort. If the return value is std::string::npos, there's no valid host, 204 // but we can try to append a TLD anyway, since the host may become valid once 205 // the TLD is attached -- for example, "999999999999" is detected as a broken 206 // IP address and marked invalid, but attaching ".com" makes it legal. When 207 // the return value is 0, there's a valid host with no known TLD, so we can 208 // definitely append the user's TLD. We disallow unknown registries here so 209 // users can input "mail.yahoo" and hit ctrl-enter to get 210 // "www.mail.yahoo.com". 211 const size_t registry_length = 212 net::registry_controlled_domains::GetRegistryLength( 213 *domain, 214 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES, 215 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); 216 if ((registry_length != 0) && (registry_length != std::string::npos)) 217 return; 218 219 // Add the suffix at the end of the domain. 220 const size_t domain_length(domain->length()); 221 DCHECK_GT(domain_length, 0U); 222 DCHECK_NE(desired_tld[0], '.'); 223 if ((*domain)[domain_length - 1] != '.') 224 domain->push_back('.'); 225 domain->append(desired_tld); 226 227 // Now, if the domain begins with "www.", stop. 228 const std::string prefix("www."); 229 if (domain->compare(0, prefix.length(), prefix) != 0) { 230 // Otherwise, add www. to the beginning of the URL. 231 domain->insert(0, prefix); 232 } 233 } 234 235 inline void FixupUsername(const std::string& text, 236 const url::Component& part, 237 std::string* url) { 238 if (!part.is_valid()) 239 return; 240 241 // We don't fix up the username at the moment. 242 url->append(text, part.begin, part.len); 243 // Do not append the trailing '@' because we might need to include the user's 244 // password. FixupURL itself will append the '@' for us. 245 } 246 247 inline void FixupPassword(const std::string& text, 248 const url::Component& part, 249 std::string* url) { 250 if (!part.is_valid()) 251 return; 252 253 // We don't fix up the password at the moment. 254 url->append(":"); 255 url->append(text, part.begin, part.len); 256 } 257 258 void FixupHost(const std::string& text, 259 const url::Component& part, 260 bool has_scheme, 261 const std::string& desired_tld, 262 std::string* url) { 263 if (!part.is_valid()) 264 return; 265 266 // Make domain valid. 267 // Strip all leading dots and all but one trailing dot, unless the user only 268 // typed dots, in which case their input is totally invalid and we should just 269 // leave it unchanged. 270 std::string domain(text, part.begin, part.len); 271 const size_t first_nondot(domain.find_first_not_of('.')); 272 if (first_nondot != std::string::npos) { 273 domain.erase(0, first_nondot); 274 size_t last_nondot(domain.find_last_not_of('.')); 275 DCHECK(last_nondot != std::string::npos); 276 last_nondot += 2; // Point at second period in ending string 277 if (last_nondot < domain.length()) 278 domain.erase(last_nondot); 279 } 280 281 // Add any user-specified TLD, if applicable. 282 AddDesiredTLD(desired_tld, &domain); 283 284 url->append(domain); 285 } 286 287 void FixupPort(const std::string& text, 288 const url::Component& part, 289 std::string* url) { 290 if (!part.is_valid()) 291 return; 292 293 // We don't fix up the port at the moment. 294 url->append(":"); 295 url->append(text, part.begin, part.len); 296 } 297 298 inline void FixupPath(const std::string& text, 299 const url::Component& part, 300 std::string* url) { 301 if (!part.is_valid() || part.len == 0) { 302 // We should always have a path. 303 url->append("/"); 304 return; 305 } 306 307 // Append the path as is. 308 url->append(text, part.begin, part.len); 309 } 310 311 inline void FixupQuery(const std::string& text, 312 const url::Component& part, 313 std::string* url) { 314 if (!part.is_valid()) 315 return; 316 317 // We don't fix up the query at the moment. 318 url->append("?"); 319 url->append(text, part.begin, part.len); 320 } 321 322 inline void FixupRef(const std::string& text, 323 const url::Component& part, 324 std::string* url) { 325 if (!part.is_valid()) 326 return; 327 328 // We don't fix up the ref at the moment. 329 url->append("#"); 330 url->append(text, part.begin, part.len); 331 } 332 333 bool HasPort(const std::string& original_text, 334 const url::Component& scheme_component) { 335 // Find the range between the ":" and the "/". 336 size_t port_start = scheme_component.end() + 1; 337 size_t port_end = port_start; 338 while ((port_end < original_text.length()) && 339 !url::IsAuthorityTerminator(original_text[port_end])) 340 ++port_end; 341 if (port_end == port_start) 342 return false; 343 344 // Scan the range to see if it is entirely digits. 345 for (size_t i = port_start; i < port_end; ++i) { 346 if (!IsAsciiDigit(original_text[i])) 347 return false; 348 } 349 350 return true; 351 } 352 353 // Try to extract a valid scheme from the beginning of |text|. 354 // If successful, set |scheme_component| to the text range where the scheme 355 // was located, and fill |canon_scheme| with its canonicalized form. 356 // Otherwise, return false and leave the outputs in an indeterminate state. 357 bool GetValidScheme(const std::string& text, 358 url::Component* scheme_component, 359 std::string* canon_scheme) { 360 canon_scheme->clear(); 361 362 // Locate everything up to (but not including) the first ':' 363 if (!url::ExtractScheme( 364 text.data(), static_cast<int>(text.length()), scheme_component)) { 365 return false; 366 } 367 368 // Make sure the scheme contains only valid characters, and convert 369 // to lowercase. This also catches IPv6 literals like [::1], because 370 // brackets are not in the whitelist. 371 url::StdStringCanonOutput canon_scheme_output(canon_scheme); 372 url::Component canon_scheme_component; 373 if (!url::CanonicalizeScheme(text.data(), 374 *scheme_component, 375 &canon_scheme_output, 376 &canon_scheme_component)) { 377 return false; 378 } 379 380 // Strip the ':', and any trailing buffer space. 381 DCHECK_EQ(0, canon_scheme_component.begin); 382 canon_scheme->erase(canon_scheme_component.len); 383 384 // We need to fix up the segmentation for "www.example.com:/". For this 385 // case, we guess that schemes with a "." are not actually schemes. 386 if (canon_scheme->find('.') != std::string::npos) 387 return false; 388 389 // We need to fix up the segmentation for "www:123/". For this case, we 390 // will add an HTTP scheme later and make the URL parser happy. 391 // TODO(pkasting): Maybe we should try to use GURL's parser for this? 392 if (HasPort(text, *scheme_component)) 393 return false; 394 395 // Everything checks out. 396 return true; 397 } 398 399 // Performs the work for url_fixer::SegmentURL. |text| may be modified on 400 // output on success: a semicolon following a valid scheme is replaced with a 401 // colon. 402 std::string SegmentURLInternal(std::string* text, url::Parsed* parts) { 403 // Initialize the result. 404 *parts = url::Parsed(); 405 406 std::string trimmed; 407 TrimWhitespaceUTF8(*text, base::TRIM_ALL, &trimmed); 408 if (trimmed.empty()) 409 return std::string(); // Nothing to segment. 410 411 #if defined(OS_WIN) 412 int trimmed_length = static_cast<int>(trimmed.length()); 413 if (url::DoesBeginWindowsDriveSpec(trimmed.data(), 0, trimmed_length) || 414 url::DoesBeginUNCPath(trimmed.data(), 0, trimmed_length, true)) 415 return "file"; 416 #elif defined(OS_POSIX) 417 if (base::FilePath::IsSeparator(trimmed.data()[0]) || 418 trimmed.data()[0] == '~') 419 return "file"; 420 #endif 421 422 // Otherwise, we need to look at things carefully. 423 std::string scheme; 424 if (!GetValidScheme(*text, &parts->scheme, &scheme)) { 425 // Try again if there is a ';' in the text. If changing it to a ':' results 426 // in a scheme being found, continue processing with the modified text. 427 bool found_scheme = false; 428 size_t semicolon = text->find(';'); 429 if (semicolon != 0 && semicolon != std::string::npos) { 430 (*text)[semicolon] = ':'; 431 if (GetValidScheme(*text, &parts->scheme, &scheme)) 432 found_scheme = true; 433 else 434 (*text)[semicolon] = ';'; 435 } 436 if (!found_scheme) { 437 // Couldn't determine the scheme, so just pick one. 438 parts->scheme.reset(); 439 scheme = StartsWithASCII(*text, "ftp.", false) ? url::kFtpScheme 440 : url::kHttpScheme; 441 } 442 } 443 444 // Proceed with about and chrome schemes, but not file or nonstandard schemes. 445 if ((scheme != url::kAboutScheme) && (scheme != kChromeUIScheme) && 446 ((scheme == url::kFileScheme) || 447 !url::IsStandard( 448 scheme.c_str(), 449 url::Component(0, static_cast<int>(scheme.length()))))) { 450 return scheme; 451 } 452 453 if (scheme == url::kFileSystemScheme) { 454 // Have the GURL parser do the heavy lifting for us. 455 url::ParseFileSystemURL( 456 text->data(), static_cast<int>(text->length()), parts); 457 return scheme; 458 } 459 460 if (parts->scheme.is_valid()) { 461 // Have the GURL parser do the heavy lifting for us. 462 url::ParseStandardURL( 463 text->data(), static_cast<int>(text->length()), parts); 464 return scheme; 465 } 466 467 // We need to add a scheme in order for ParseStandardURL to be happy. 468 // Find the first non-whitespace character. 469 std::string::iterator first_nonwhite = text->begin(); 470 while ((first_nonwhite != text->end()) && IsWhitespace(*first_nonwhite)) 471 ++first_nonwhite; 472 473 // Construct the text to parse by inserting the scheme. 474 std::string inserted_text(scheme); 475 inserted_text.append(url::kStandardSchemeSeparator); 476 std::string text_to_parse(text->begin(), first_nonwhite); 477 text_to_parse.append(inserted_text); 478 text_to_parse.append(first_nonwhite, text->end()); 479 480 // Have the GURL parser do the heavy lifting for us. 481 url::ParseStandardURL( 482 text_to_parse.data(), static_cast<int>(text_to_parse.length()), parts); 483 484 // Offset the results of the parse to match the original text. 485 const int offset = -static_cast<int>(inserted_text.length()); 486 url_fixer::OffsetComponent(offset, &parts->scheme); 487 url_fixer::OffsetComponent(offset, &parts->username); 488 url_fixer::OffsetComponent(offset, &parts->password); 489 url_fixer::OffsetComponent(offset, &parts->host); 490 url_fixer::OffsetComponent(offset, &parts->port); 491 url_fixer::OffsetComponent(offset, &parts->path); 492 url_fixer::OffsetComponent(offset, &parts->query); 493 url_fixer::OffsetComponent(offset, &parts->ref); 494 495 return scheme; 496 } 497 498 } // namespace 499 500 std::string url_fixer::SegmentURL(const std::string& text, url::Parsed* parts) { 501 std::string mutable_text(text); 502 return SegmentURLInternal(&mutable_text, parts); 503 } 504 505 base::string16 url_fixer::SegmentURL(const base::string16& text, 506 url::Parsed* parts) { 507 std::string text_utf8 = base::UTF16ToUTF8(text); 508 url::Parsed parts_utf8; 509 std::string scheme_utf8 = SegmentURL(text_utf8, &parts_utf8); 510 UTF8PartsToUTF16Parts(text_utf8, parts_utf8, parts); 511 return base::UTF8ToUTF16(scheme_utf8); 512 } 513 514 GURL url_fixer::FixupURL(const std::string& text, 515 const std::string& desired_tld) { 516 std::string trimmed; 517 TrimWhitespaceUTF8(text, base::TRIM_ALL, &trimmed); 518 if (trimmed.empty()) 519 return GURL(); // Nothing here. 520 521 // Segment the URL. 522 url::Parsed parts; 523 std::string scheme(SegmentURLInternal(&trimmed, &parts)); 524 525 // For view-source: URLs, we strip "view-source:", do fixup, and stick it back 526 // on. This allows us to handle things like "view-source:google.com". 527 if (scheme == kViewSourceScheme) { 528 // Reject "view-source:view-source:..." to avoid deep recursion. 529 std::string view_source(kViewSourceScheme + std::string(":")); 530 if (!StartsWithASCII(text, view_source + view_source, false)) { 531 return GURL(kViewSourceScheme + std::string(":") + 532 FixupURL(trimmed.substr(scheme.length() + 1), desired_tld) 533 .possibly_invalid_spec()); 534 } 535 } 536 537 // We handle the file scheme separately. 538 if (scheme == url::kFileScheme) 539 return GURL(parts.scheme.is_valid() ? text : FixupPath(text)); 540 541 // We handle the filesystem scheme separately. 542 if (scheme == url::kFileSystemScheme) { 543 if (parts.inner_parsed() && parts.inner_parsed()->scheme.is_valid()) 544 return GURL(text); 545 return GURL(); 546 } 547 548 // Parse and rebuild about: and chrome: URLs, except about:blank. 549 bool chrome_url = 550 !LowerCaseEqualsASCII(trimmed, url::kAboutBlankURL) && 551 ((scheme == url::kAboutScheme) || (scheme == kChromeUIScheme)); 552 553 // For some schemes whose layouts we understand, we rebuild it. 554 if (chrome_url || 555 url::IsStandard(scheme.c_str(), 556 url::Component(0, static_cast<int>(scheme.length())))) { 557 // Replace the about: scheme with the chrome: scheme. 558 std::string url(chrome_url ? kChromeUIScheme : scheme); 559 url.append(url::kStandardSchemeSeparator); 560 561 // We need to check whether the |username| is valid because it is our 562 // responsibility to append the '@' to delineate the user information from 563 // the host portion of the URL. 564 if (parts.username.is_valid()) { 565 FixupUsername(trimmed, parts.username, &url); 566 FixupPassword(trimmed, parts.password, &url); 567 url.append("@"); 568 } 569 570 FixupHost(trimmed, parts.host, parts.scheme.is_valid(), desired_tld, &url); 571 if (chrome_url && !parts.host.is_valid()) 572 url.append(kChromeUIDefaultHost); 573 FixupPort(trimmed, parts.port, &url); 574 FixupPath(trimmed, parts.path, &url); 575 FixupQuery(trimmed, parts.query, &url); 576 FixupRef(trimmed, parts.ref, &url); 577 578 return GURL(url); 579 } 580 581 // In the worst-case, we insert a scheme if the URL lacks one. 582 if (!parts.scheme.is_valid()) { 583 std::string fixed_scheme(scheme); 584 fixed_scheme.append(url::kStandardSchemeSeparator); 585 trimmed.insert(0, fixed_scheme); 586 } 587 588 return GURL(trimmed); 589 } 590 591 // The rules are different here than for regular fixup, since we need to handle 592 // input like "hello.html" and know to look in the current directory. Regular 593 // fixup will look for cues that it is actually a file path before trying to 594 // figure out what file it is. If our logic doesn't work, we will fall back on 595 // regular fixup. 596 GURL url_fixer::FixupRelativeFile(const base::FilePath& base_dir, 597 const base::FilePath& text) { 598 base::FilePath old_cur_directory; 599 if (!base_dir.empty()) { 600 // Save the old current directory before we move to the new one. 601 base::GetCurrentDirectory(&old_cur_directory); 602 base::SetCurrentDirectory(base_dir); 603 } 604 605 // Allow funny input with extra whitespace and the wrong kind of slashes. 606 base::FilePath::StringType trimmed; 607 PrepareStringForFileOps(text, &trimmed); 608 609 bool is_file = true; 610 // Avoid recognizing definite non-file URLs as file paths. 611 GURL gurl(trimmed); 612 if (gurl.is_valid() && gurl.IsStandard()) 613 is_file = false; 614 base::FilePath full_path; 615 if (is_file && !ValidPathForFile(trimmed, &full_path)) { 616 // Not a path as entered, try unescaping it in case the user has 617 // escaped things. We need to go through 8-bit since the escaped values 618 // only represent 8-bit values. 619 #if defined(OS_WIN) 620 std::wstring unescaped = base::UTF8ToWide(net::UnescapeURLComponent( 621 base::WideToUTF8(trimmed), 622 net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS)); 623 #elif defined(OS_POSIX) 624 std::string unescaped = net::UnescapeURLComponent( 625 trimmed, 626 net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS); 627 #endif 628 629 if (!ValidPathForFile(unescaped, &full_path)) 630 is_file = false; 631 } 632 633 // Put back the current directory if we saved it. 634 if (!base_dir.empty()) 635 base::SetCurrentDirectory(old_cur_directory); 636 637 if (is_file) { 638 GURL file_url = net::FilePathToFileURL(full_path); 639 if (file_url.is_valid()) 640 return GURL( 641 base::UTF16ToUTF8(net::FormatUrl(file_url, 642 std::string(), 643 net::kFormatUrlOmitUsernamePassword, 644 net::UnescapeRule::NORMAL, 645 NULL, 646 NULL, 647 NULL))); 648 // Invalid files fall through to regular processing. 649 } 650 651 // Fall back on regular fixup for this input. 652 #if defined(OS_WIN) 653 std::string text_utf8 = base::WideToUTF8(text.value()); 654 #elif defined(OS_POSIX) 655 std::string text_utf8 = text.value(); 656 #endif 657 return FixupURL(text_utf8, std::string()); 658 } 659 660 void url_fixer::OffsetComponent(int offset, url::Component* part) { 661 DCHECK(part); 662 663 if (part->is_valid()) { 664 // Offset the location of this component. 665 part->begin += offset; 666 667 // This part might not have existed in the original text. 668 if (part->begin < 0) 669 part->reset(); 670 } 671 } 672 673 bool url_fixer::IsEquivalentScheme(const std::string& scheme1, 674 const std::string& scheme2) { 675 return scheme1 == scheme2 || 676 (scheme1 == url::kAboutScheme && scheme2 == kChromeUIScheme) || 677 (scheme1 == kChromeUIScheme && scheme2 == url::kAboutScheme); 678 } 679