blob: 91e65b42367716f7abc3f561ce8528e9a608e4e6 [file] [log] [blame]
/* 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)