| /* Based on nsURLParsers.cc from Mozilla |
| * ------------------------------------- |
| * Copyright (C) 1998 Netscape Communications Corporation. |
| * Copyright 2012, Google Inc. All rights reserved. |
| * Copyright (C) 2012 Apple Inc. All rights reserved. |
| * The contents of this file are subject to the Mozilla Public License Version |
| * 1.1 (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * http://www.mozilla.org/MPL/ |
| * |
| * Software distributed under the License is distributed on an "AS IS" basis, |
| * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
| * for the specific language governing rights and limitations under the |
| * License. |
| * |
| * The Original Code is mozilla.org code. |
| * |
| * The Initial Developer of the Original Code is |
| * Netscape Communications Corporation. |
| * Portions created by the Initial Developer are Copyright (C) 1998 |
| * the Initial Developer. All Rights Reserved. |
| * |
| * Contributor(s): |
| * Darin Fisher (original author) |
| * |
| * Alternatively, the contents of this file may be used under the terms of |
| * either the GNU General Public License Version 2 or later (the "GPL"), or |
| * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
| * in which case the provisions of the GPL or the LGPL are applicable instead |
| * of those above. If you wish to allow use of your version of this file only |
| * under the terms of either the GPL or the LGPL, and not to allow others to |
| * use your version of this file under the terms of the MPL, indicate your |
| * decision by deleting the provisions above and replace them with the notice |
| * and other provisions required by the GPL or the LGPL. If you do not delete |
| * the provisions above, a recipient may use your version of this file under |
| * the terms of any one of the MPL, the GPL or the LGPL. |
| * |
| */ |
| |
| #include "config.h" |
| #include "URLParse.h" |
| |
| #include "URLParseInternal.h" |
| #include "URLUtil.h" |
| #include "URLUtilInternal.h" |
| #include <stdlib.h> |
| #include <wtf/ASCIICType.h> |
| |
| #if USE(WTFURL) |
| |
| namespace WTF { |
| |
| namespace URLParser { |
| |
| namespace { |
| |
| // Returns the offset of the next authority terminator in the input starting |
| // from startOffset. If no terminator is found, the return value will be equal |
| // to specLength. |
| template<typename CharacterType> |
| int findNextAuthorityTerminator(const CharacterType* spec, int startOffset, int specLength) |
| { |
| for (int i = startOffset; i < specLength; i++) { |
| if (IsAuthorityTerminator(spec[i])) |
| return i; |
| } |
| return specLength; // Not found. |
| } |
| |
| template<typename CharacterType> |
| void parseUserInfo(const CharacterType* spec, const URLComponent& user, URLComponent& username, URLComponent& password) |
| { |
| // Find the first colon in the user section, which separates the username and |
| // password. |
| int colonOffset = 0; |
| while (colonOffset < user.length() && spec[user.begin() + colonOffset] != ':') |
| ++colonOffset; |
| |
| if (colonOffset < user.length()) { |
| // Found separator: <username>:<password> |
| username = URLComponent(user.begin(), colonOffset); |
| password = URLComponent::fromRange(user.begin() + colonOffset + 1, user.end()); |
| } else { |
| // No separator, treat everything as the username |
| username = user; |
| password = URLComponent(); |
| } |
| } |
| |
| template<typename CharacterType> |
| void parseServerInfo(const CharacterType* spec, const URLComponent& serverInfo, URLComponent& hostname, URLComponent& port) |
| { |
| if (!serverInfo.length()) { |
| // No server info, host name is empty. |
| hostname = URLComponent(); |
| port = URLComponent(); |
| return; |
| } |
| |
| // If the host starts with a left-bracket, assume the entire host is an |
| // IPv6 literal. Otherwise, assume none of the host is an IPv6 literal. |
| // This assumption will be overridden if we find a right-bracket. |
| // |
| // Our IPv6 address canonicalization code requires both brackets to exist, |
| // but the ability to locate an incomplete address can still be useful. |
| int ipv6Terminator = spec[serverInfo.begin()] == '[' ? serverInfo.end() : -1; |
| int colon = -1; |
| |
| // Find the last right-bracket, and the last colon. |
| for (int i = serverInfo.begin(); i < serverInfo.end(); ++i) { |
| switch (spec[i]) { |
| case ']': |
| ipv6Terminator = i; |
| break; |
| case ':': |
| colon = i; |
| break; |
| } |
| } |
| |
| if (colon > ipv6Terminator) { |
| // Found a port number: <hostname>:<port> |
| hostname = URLComponent::fromRange(serverInfo.begin(), colon); |
| if (!hostname.length()) |
| hostname.reset(); |
| port = URLComponent::fromRange(colon + 1, serverInfo.end()); |
| } else { |
| // No port: <hostname> |
| hostname = serverInfo; |
| port = URLComponent(); |
| } |
| } |
| |
| // Given an already-identified auth section, breaks it into its consituent |
| // parts. The port number will be parsed and the resulting integer will be |
| // filled into the given *port variable, or -1 if there is no port number or it |
| // is invalid. |
| template<typename CharacterType> |
| void doParseAuthority(const CharacterType* spec, const URLComponent& authority, URLComponent& username, URLComponent& password, URLComponent& hostname, URLComponent& port) |
| { |
| ASSERT_WITH_MESSAGE(authority.isValid(), "We should always get an authority"); |
| if (!authority.length()) { |
| username = URLComponent(); |
| password = URLComponent(); |
| hostname = URLComponent(); |
| port = URLComponent(); |
| return; |
| } |
| |
| // Search backwards for @, which is the separator between the user info and |
| // the server info. |
| int i = authority.end() - 1; |
| while (i > authority.begin() && spec[i] != '@') |
| --i; |
| |
| if (spec[i] == '@') { |
| // Found user info: <user-info>@<server-info> |
| parseUserInfo(spec, URLComponent(authority.begin(), i - authority.begin()), username, password); |
| parseServerInfo(spec, URLComponent::fromRange(i + 1, authority.end()), hostname, port); |
| } else { |
| // No user info, everything is server info. |
| username = URLComponent(); |
| password = URLComponent(); |
| parseServerInfo(spec, authority, hostname, port); |
| } |
| } |
| |
| template<typename CharacterType> |
| void parsePath(const CharacterType* spec, const URLComponent& hierarchicalidentifiers, URLComponent& resourcePath, URLComponent& query, URLComponent& fragment) |
| { |
| // path = [/]<segment1>/<segment2>/<...>/<segmentN>;<param>?<query>#<ref> |
| |
| // Special case when there is no path. |
| if (hierarchicalidentifiers.length() == -1) { |
| resourcePath = URLComponent(); |
| query = URLComponent(); |
| fragment = URLComponent(); |
| return; |
| } |
| ASSERT_WITH_MESSAGE(hierarchicalidentifiers.length() > 0, "We should never have 0 length paths"); |
| |
| // Search for first occurrence of either ? or #. |
| int pathEnd = hierarchicalidentifiers.end(); |
| |
| int querySeparator = -1; // Index of the '?' |
| int fragmentSeparator = -1; // Index of the '#' |
| for (int i = hierarchicalidentifiers.begin(); i < pathEnd; ++i) { |
| switch (spec[i]) { |
| case '?': |
| // Only match the query string if it precedes the reference fragment |
| // and when we haven't found one already. |
| if (fragmentSeparator < 0 && querySeparator < 0) |
| querySeparator = i; |
| break; |
| case '#': |
| // Record the first # sign only. |
| if (fragmentSeparator < 0) |
| fragmentSeparator = i; |
| break; |
| } |
| } |
| |
| // Markers pointing to the character after each of these corresponding |
| // components. The code below words from the end back to the beginning, |
| // and will update these indices as it finds components that exist. |
| int resourcePathEnd = -1; |
| int queryEnd = -1; |
| |
| // Ref fragment: from the # to the end of the path. |
| if (fragmentSeparator >= 0) { |
| resourcePathEnd = queryEnd = fragmentSeparator; |
| fragment = URLComponent::fromRange(fragmentSeparator + 1, pathEnd); |
| } else { |
| resourcePathEnd = queryEnd = pathEnd; |
| fragment = URLComponent(); |
| } |
| |
| // Query fragment: everything from the ? to the next boundary (either the end |
| // of the path or the ref fragment). |
| if (querySeparator >= 0) { |
| resourcePathEnd = querySeparator; |
| query = URLComponent::fromRange(querySeparator + 1, queryEnd); |
| } else |
| query = URLComponent(); |
| |
| // File path: treat an empty file path as no file path. |
| if (resourcePathEnd != hierarchicalidentifiers.begin()) |
| resourcePath = URLComponent::fromRange(hierarchicalidentifiers.begin(), resourcePathEnd); |
| else |
| resourcePath = URLComponent(); |
| } |
| |
| template<typename CharacterType> |
| bool doExtractScheme(const CharacterType* url, int urlLength, URLComponent& scheme) |
| { |
| // Skip leading whitespace and control characters. |
| int begin = 0; |
| while (begin < urlLength && shouldTrimFromURL(url[begin])) |
| ++begin; |
| |
| if (begin == urlLength) |
| return false; // Input is empty or all whitespace. |
| |
| // Find the first colon character. |
| for (int i = begin; i < urlLength; ++i) { |
| if (url[i] == ':') { |
| scheme = URLComponent::fromRange(begin, i); |
| return true; |
| } |
| } |
| return false; // No colon found: no scheme |
| } |
| |
| // Fills in all members of the Parsed structure except for the scheme. |
| // |
| // |spec| is the full spec being parsed, of length |specLength|. |
| // |afterScheme| is the character immediately following the scheme (after the |
| // colon) where we'll begin parsing. |
| // |
| // Compatability data points. I list "host", "path" extracted: |
| // Input IE6 Firefox Us |
| // ----- -------------- -------------- -------------- |
| // http://foo.com/ "foo.com", "/" "foo.com", "/" "foo.com", "/" |
| // http:foo.com/ "foo.com", "/" "foo.com", "/" "foo.com", "/" |
| // http:/foo.com/ fail(*) "foo.com", "/" "foo.com", "/" |
| // http:\foo.com/ fail(*) "\foo.com", "/"(fail) "foo.com", "/" |
| // http:////foo.com/ "foo.com", "/" "foo.com", "/" "foo.com", "/" |
| // |
| // (*) Interestingly, although IE fails to load these URLs, its history |
| // canonicalizer handles them, meaning if you've been to the corresponding |
| // "http://foo.com/" link, it will be colored. |
| template <typename CharacterType> |
| void doParseAfterScheme(const CharacterType* spec, int specLength, int afterScheme, URLSegments& parsed) |
| { |
| int slashesCount = countConsecutiveSlashes(spec, afterScheme, specLength); |
| int afterSlashes = afterScheme + slashesCount; |
| |
| // First split into two main parts, the authority (username, password, host, |
| // and port) and the full path (path, query, and reference). |
| URLComponent authority; |
| URLComponent fullPath; |
| |
| // Found "//<some data>", looks like an authority section. Treat everything |
| // from there to the next slash (or end of spec) to be the authority. Note |
| // that we ignore the number of slashes and treat it as the authority. |
| int endAuth = findNextAuthorityTerminator(spec, afterSlashes, specLength); |
| authority = URLComponent::fromRange(afterSlashes, endAuth); |
| |
| if (endAuth == specLength) // No beginning of path found. |
| fullPath = URLComponent(); |
| else // Everything starting from the slash to the end is the path. |
| fullPath = URLComponent::fromRange(endAuth, specLength); |
| |
| // Now parse those two sub-parts. |
| doParseAuthority(spec, authority, parsed.username, parsed.password, parsed.host, parsed.port); |
| parsePath(spec, fullPath, parsed.path, parsed.query, parsed.fragment); |
| } |
| |
| // The main parsing function for standard URLs. Standard URLs have a scheme, |
| // host, path, etc. |
| template<typename CharacterType> |
| void doParseStandardURL(const CharacterType* spec, int specLength, URLSegments& parsed) |
| { |
| ASSERT(specLength >= 0); |
| |
| // Strip leading & trailing spaces and control characters. |
| int begin = 0; |
| trimURL(spec, begin, specLength); |
| |
| int afterScheme = -1; |
| if (doExtractScheme(spec, specLength, parsed.scheme)) |
| afterScheme = parsed.scheme.end() + 1; // Skip past the colon. |
| else { |
| // Say there's no scheme when there is no colon. We could also say that |
| // everything is the scheme. Both would produce an invalid URL, but this way |
| // seems less wrong in more cases. |
| parsed.scheme.reset(); |
| afterScheme = begin; |
| } |
| doParseAfterScheme(spec, specLength, afterScheme, parsed); |
| } |
| |
| template<typename CharacterType> |
| void doParseFileSystemURL(const CharacterType* spec, int specLength, URLSegments& parsed) |
| { |
| ASSERT(specLength >= 0); |
| |
| // Get the unused parts of the URL out of the way. |
| parsed.username.reset(); |
| parsed.password.reset(); |
| parsed.host.reset(); |
| parsed.port.reset(); |
| parsed.path.reset(); // May use this; reset for convenience. |
| parsed.fragment.reset(); // May use this; reset for convenience. |
| parsed.query.reset(); // May use this; reset for convenience. |
| parsed.clearInnerURLSegments(); // May use this; reset for convenience. |
| |
| // Strip leading & trailing spaces and control characters. |
| int begin = 0; |
| trimURL(spec, begin, specLength); |
| |
| // Handle empty specs or ones that contain only whitespace or control chars. |
| if (begin == specLength) { |
| parsed.scheme.reset(); |
| return; |
| } |
| |
| int innerStart = -1; |
| |
| // Extract the scheme. We also handle the case where there is no scheme. |
| if (doExtractScheme(&spec[begin], specLength - begin, parsed.scheme)) { |
| // Offset the results since we gave ExtractScheme a substring. |
| parsed.scheme.moveBy(begin); |
| |
| if (parsed.scheme.end() == specLength - 1) |
| return; |
| |
| innerStart = parsed.scheme.end() + 1; |
| } else { |
| // No scheme found; that's not valid for filesystem URLs. |
| parsed.scheme.reset(); |
| return; |
| } |
| |
| URLComponent innerScheme; |
| const CharacterType* innerSpec = &spec[innerStart]; |
| int innerSpecLength = specLength - innerStart; |
| |
| if (doExtractScheme(innerSpec, innerSpecLength, innerScheme)) { |
| // Offset the results since we gave ExtractScheme a substring. |
| innerScheme.moveBy(innerStart); |
| |
| if (innerScheme.end() == specLength - 1) |
| return; |
| } else { |
| // No scheme found; that's not valid for filesystem URLs. |
| // The best we can do is return "filesystem://". |
| return; |
| } |
| |
| URLSegments innerParsed; |
| if (URLUtilities::CompareSchemeComponent(spec, innerScheme, URLUtilities::kFileScheme)) { |
| // File URLs are special. |
| ParseFileURL(innerSpec, innerSpecLength, &innerParsed); |
| } else if (URLUtilities::CompareSchemeComponent(spec, innerScheme, URLUtilities::kFileSystemScheme)) { |
| // Filesystem URLs don't nest. |
| return; |
| } else if (URLUtilities::isStandard(spec, innerScheme)) { |
| // All "normal" URLs. |
| doParseStandardURL(innerSpec, innerSpecLength, innerParsed); |
| } else |
| return; |
| |
| // All members of innerParsed need to be offset by innerStart. |
| // If we had any scheme that supported nesting more than one level deep, |
| // we'd have to recurse into the innerParsed's innerParsed when |
| // adjusting by innerStart. |
| innerParsed.moveFromComponentBy(URLSegments::Scheme, innerStart); |
| |
| // Query and ref move from innerParsed to parsed. |
| parsed.query = innerParsed.query; |
| innerParsed.query.reset(); |
| parsed.fragment = innerParsed.fragment; |
| innerParsed.fragment.reset(); |
| |
| parsed.setInnerURLSegments(innerParsed); |
| if (!innerParsed.scheme.isValid() || !innerParsed.path.isValid() || innerParsed.innerURLSegments()) |
| return; |
| |
| // The path in innerParsed should start with a slash, then have a filesystem |
| // type followed by a slash. From the first slash up to but excluding the |
| // second should be what it keeps; the rest goes to parsed. If the path ends |
| // before the second slash, it's still pretty clear what the user meant, so |
| // we'll let that through. |
| if (!isURLSlash(spec[innerParsed.path.begin()])) |
| return; |
| |
| int innerPathEnd = innerParsed.path.begin() + 1; // skip the leading slash |
| while (innerPathEnd < specLength && !isURLSlash(spec[innerPathEnd])) |
| ++innerPathEnd; |
| parsed.path.setBegin(innerPathEnd); |
| int newInnerPathLength = innerPathEnd - innerParsed.path.begin(); |
| parsed.path.setLength(innerParsed.path.length() - newInnerPathLength); |
| innerParsed.path.setLength(newInnerPathLength); |
| parsed.setInnerURLSegments(innerParsed); |
| } |
| |
| // Initializes a path URL which is merely a scheme followed by a path. Examples |
| // include "about:foo" and "javascript:alert('bar');" |
| template<typename CharacterType> |
| void doParsePathURL(const CharacterType* spec, int specLength, URLSegments& parsed) |
| { |
| // Get the non-path and non-scheme parts of the URL out of the way, we never |
| // use them. |
| parsed.username.reset(); |
| parsed.password.reset(); |
| parsed.host.reset(); |
| parsed.port.reset(); |
| parsed.query.reset(); |
| parsed.fragment.reset(); |
| |
| // Strip leading & trailing spaces and control characters. |
| int begin = 0; |
| trimURL(spec, begin, specLength); |
| |
| // Handle empty specs or ones that contain only whitespace or control chars. |
| if (begin == specLength) { |
| parsed.scheme.reset(); |
| parsed.path.reset(); |
| return; |
| } |
| |
| // Extract the scheme, with the path being everything following. We also |
| // handle the case where there is no scheme. |
| if (ExtractScheme(&spec[begin], specLength - begin, &parsed.scheme)) { |
| // Offset the results since we gave ExtractScheme a substring. |
| parsed.scheme.moveBy(begin); |
| |
| // For compatability with the standard URL parser, we treat no path as |
| // -1, rather than having a length of 0 (we normally wouldn't care so |
| // much for these non-standard URLs). |
| if (parsed.scheme.end() == specLength - 1) |
| parsed.path.reset(); |
| else |
| parsed.path = URLComponent::fromRange(parsed.scheme.end() + 1, specLength); |
| } else { |
| // No scheme found, just path. |
| parsed.scheme.reset(); |
| parsed.path = URLComponent::fromRange(begin, specLength); |
| } |
| } |
| |
| template<typename CharacterType> |
| void doParseMailtoURL(const CharacterType* spec, int specLength, URLSegments& parsed) |
| { |
| ASSERT(specLength >= 0); |
| |
| // Get the non-path and non-scheme parts of the URL out of the way, we never |
| // use them. |
| parsed.username.reset(); |
| parsed.password.reset(); |
| parsed.host.reset(); |
| parsed.port.reset(); |
| parsed.fragment.reset(); |
| parsed.query.reset(); // May use this; reset for convenience. |
| |
| // Strip leading & trailing spaces and control characters. |
| int begin = 0; |
| trimURL(spec, begin, specLength); |
| |
| // Handle empty specs or ones that contain only whitespace or control chars. |
| if (begin == specLength) { |
| parsed.scheme.reset(); |
| parsed.path.reset(); |
| return; |
| } |
| |
| int pathBegin = -1; |
| int pathEnd = -1; |
| |
| // Extract the scheme, with the path being everything following. We also |
| // handle the case where there is no scheme. |
| if (ExtractScheme(&spec[begin], specLength - begin, &parsed.scheme)) { |
| // Offset the results since we gave ExtractScheme a substring. |
| parsed.scheme.moveBy(begin); |
| |
| if (parsed.scheme.end() != specLength - 1) { |
| pathBegin = parsed.scheme.end() + 1; |
| pathEnd = specLength; |
| } |
| } else { |
| // No scheme found, just path. |
| parsed.scheme.reset(); |
| pathBegin = begin; |
| pathEnd = specLength; |
| } |
| |
| // Split [pathBegin, pathEnd) into a path + query. |
| for (int i = pathBegin; i < pathEnd; ++i) { |
| if (spec[i] == '?') { |
| parsed.query = URLComponent::fromRange(i + 1, pathEnd); |
| pathEnd = i; |
| break; |
| } |
| } |
| |
| // For compatability with the standard URL parser, treat no path as |
| // -1, rather than having a length of 0 |
| if (pathBegin == pathEnd) |
| parsed.path.reset(); |
| else |
| parsed.path = URLComponent::fromRange(pathBegin, pathEnd); |
| } |
| |
| // Converts a port number in a string to an integer. We'd like to just call |
| // sscanf but our input is not null-terminated, which sscanf requires. Instead, |
| // we copy the digits to a small stack buffer (since we know the maximum number |
| // of digits in a valid port number) that we can null terminate. |
| template<typename CharacterType> |
| int doParsePort(const CharacterType* spec, const URLComponent& component) |
| { |
| // Easy success case when there is no port. |
| const int kMaxDigits = 5; |
| if (!component.isNonEmpty()) |
| return PORT_UNSPECIFIED; |
| |
| // Skip over any leading 0s. |
| URLComponent digitComponent(component.end(), 0); |
| for (int i = 0; i < component.length(); i++) { |
| if (spec[component.begin() + i] != '0') { |
| digitComponent = URLComponent::fromRange(component.begin() + i, component.end()); |
| break; |
| } |
| } |
| if (!digitComponent.length()) |
| return 0; // All digits were 0. |
| |
| // Verify we don't have too many digits (we'll be copying to our buffer so |
| // we need to double-check). |
| if (digitComponent.length() > kMaxDigits) |
| return PORT_INVALID; |
| |
| // Copy valid digits to the buffer. |
| char digits[kMaxDigits + 1]; // +1 for null terminator |
| for (int i = 0; i < digitComponent.length(); i++) { |
| CharacterType ch = spec[digitComponent.begin() + i]; |
| if (!isASCIIDigit(ch)) { |
| // Invalid port digit, fail. |
| return PORT_INVALID; |
| } |
| digits[i] = static_cast<char>(ch); |
| } |
| |
| // Null-terminate the string and convert to integer. Since we guarantee |
| // only digits, atoi's lack of error handling is OK. |
| digits[digitComponent.length()] = 0; |
| int port = atoi(digits); |
| if (port > 65535) |
| return PORT_INVALID; // Out of range. |
| return port; |
| } |
| |
| template<typename CharacterType> |
| void doExtractFileName(const CharacterType* spec, const URLComponent& path, URLComponent& fileName) |
| { |
| // Handle empty paths: they have no file names. |
| if (!path.isNonEmpty()) { |
| fileName = URLComponent(); |
| return; |
| } |
| |
| // Search backwards for a parameter, which is a normally unused field in a |
| // URL delimited by a semicolon. We parse the parameter as part of the |
| // path, but here, we don't want to count it. The last semicolon is the |
| // parameter. The path should start with a slash, so we don't need to check |
| // the first one. |
| int fileEnd = path.end(); |
| for (int i = path.end() - 1; i > path.begin(); --i) { |
| if (spec[i] == ';') { |
| fileEnd = i; |
| break; |
| } |
| } |
| |
| // Now search backwards from the filename end to the previous slash |
| // to find the beginning of the filename. |
| for (int i = fileEnd - 1; i >= path.begin(); --i) { |
| if (isURLSlash(spec[i])) { |
| // File name is everything following this character to the end |
| fileName = URLComponent::fromRange(i + 1, fileEnd); |
| return; |
| } |
| } |
| |
| // No slash found, this means the input was degenerate (generally paths |
| // will start with a slash). Let's call everything the file name. |
| fileName = URLComponent::fromRange(path.begin(), fileEnd); |
| return; |
| } |
| |
| template<typename CharacterType> |
| bool doExtractQueryKeyValue(const CharacterType* spec, URLComponent& query, URLComponent& key, URLComponent& value) |
| { |
| if (!query.isNonEmpty()) |
| return false; |
| |
| int start = query.begin(); |
| int current = start; |
| int end = query.end(); |
| |
| // We assume the beginning of the input is the beginning of the "key" and we |
| // skip to the end of it. |
| key.setBegin(current); |
| while (current < end && spec[current] != '&' && spec[current] != '=') |
| ++current; |
| key.setLength(current - key.begin()); |
| |
| // Skip the separator after the key (if any). |
| if (current < end && spec[current] == '=') |
| ++current; |
| |
| // Find the value part. |
| value.setBegin(current); |
| while (current < end && spec[current] != '&') |
| ++current; |
| value.setLength(current - value.begin()); |
| |
| // Finally skip the next separator if any |
| if (current < end && spec[current] == '&') |
| ++current; |
| |
| // Save the new query |
| query = URLComponent::fromRange(current, end); |
| return true; |
| } |
| |
| } // namespace |
| |
| bool ExtractScheme(const char* url, int urlLength, URLComponent* scheme) |
| { |
| return doExtractScheme(url, urlLength, *scheme); |
| } |
| |
| bool ExtractScheme(const UChar* url, int urlLength, URLComponent* scheme) |
| { |
| return doExtractScheme(url, urlLength, *scheme); |
| } |
| |
| // This handles everything that may be an authority terminator, including |
| // backslash. For special backslash handling see DoParseAfterScheme. |
| bool IsAuthorityTerminator(UChar character) |
| { |
| return isURLSlash(character) || character == '?' || character == '#'; |
| } |
| |
| void ExtractFileName(const char* url, const URLComponent& path, URLComponent* fileName) |
| { |
| doExtractFileName(url, path, *fileName); |
| } |
| |
| void ExtractFileName(const UChar* url, const URLComponent& path, URLComponent* fileName) |
| { |
| doExtractFileName(url, path, *fileName); |
| } |
| |
| bool ExtractQueryKeyValue(const char* url, URLComponent* query, URLComponent* key, URLComponent* value) |
| { |
| return doExtractQueryKeyValue(url, *query, *key, *value); |
| } |
| |
| bool ExtractQueryKeyValue(const UChar* url, URLComponent* query, URLComponent* key, URLComponent* value) |
| { |
| return doExtractQueryKeyValue(url, *query, *key, *value); |
| } |
| |
| void ParseAuthority(const char* spec, const URLComponent& auth, URLComponent* username, URLComponent* password, URLComponent* hostname, URLComponent* portNumber) |
| { |
| doParseAuthority(spec, auth, *username, *password, *hostname, *portNumber); |
| } |
| |
| void ParseAuthority(const UChar* spec, const URLComponent& auth, URLComponent* username, URLComponent* password, URLComponent* hostname, URLComponent* portNumber) |
| { |
| doParseAuthority(spec, auth, *username, *password, *hostname, *portNumber); |
| } |
| |
| int ParsePort(const char* url, const URLComponent& port) |
| { |
| return doParsePort(url, port); |
| } |
| |
| int ParsePort(const UChar* url, const URLComponent& port) |
| { |
| return doParsePort(url, port); |
| } |
| |
| void ParseStandardURL(const char* url, int urlLength, URLSegments* parsed) |
| { |
| doParseStandardURL(url, urlLength, *parsed); |
| } |
| |
| void ParseStandardURL(const UChar* url, int urlLength, URLSegments* parsed) |
| { |
| doParseStandardURL(url, urlLength, *parsed); |
| } |
| |
| void ParsePathURL(const char* url, int urlLength, URLSegments* parsed) |
| { |
| doParsePathURL(url, urlLength, *parsed); |
| } |
| |
| void ParsePathURL(const UChar* url, int urlLength, URLSegments* parsed) |
| { |
| doParsePathURL(url, urlLength, *parsed); |
| } |
| |
| void ParseFileSystemURL(const char* url, int urlLength, URLSegments* parsed) |
| { |
| doParseFileSystemURL(url, urlLength, *parsed); |
| } |
| |
| void ParseFileSystemURL(const UChar* url, int urlLength, URLSegments* parsed) |
| { |
| doParseFileSystemURL(url, urlLength, *parsed); |
| } |
| |
| void ParseMailtoURL(const char* url, int urlLength, URLSegments* parsed) |
| { |
| doParseMailtoURL(url, urlLength, *parsed); |
| } |
| |
| void ParseMailtoURL(const UChar* url, int urlLength, URLSegments* parsed) |
| { |
| doParseMailtoURL(url, urlLength, *parsed); |
| } |
| |
| void parsePathInternal(const char* spec, const URLComponent& path, URLComponent* filepath, URLComponent* query, URLComponent* fragment) |
| { |
| parsePath(spec, path, *filepath, *query, *fragment); |
| } |
| |
| void parsePathInternal(const UChar* spec, const URLComponent& path, URLComponent* filepath, URLComponent* query, URLComponent* fragment) |
| { |
| parsePath(spec, path, *filepath, *query, *fragment); |
| } |
| |
| void parseAfterScheme(const char* spec, int specLength, int afterScheme, URLSegments& parsed) |
| { |
| doParseAfterScheme(spec, specLength, afterScheme, parsed); |
| } |
| |
| void parseAfterScheme(const UChar* spec, int specLength, int afterScheme, URLSegments& parsed) |
| { |
| doParseAfterScheme(spec, specLength, afterScheme, parsed); |
| } |
| |
| } // namespace URLParser |
| |
| } // namespace WTF |
| |
| #endif // USE(WTFURL) |