diff options
author | Kristian Monsen <kristianm@google.com> | 2010-05-21 16:53:46 +0100 |
---|---|---|
committer | Kristian Monsen <kristianm@google.com> | 2010-05-25 10:24:15 +0100 |
commit | 6c2af9490927c3c5959b5cb07461b646f8b32f6c (patch) | |
tree | f7111b9b22befab472616c1d50ec94eb50f1ec8c /JavaScriptCore/wtf/url/src | |
parent | a149172322a9067c14e8b474a53e63649aa17cad (diff) | |
download | external_webkit-6c2af9490927c3c5959b5cb07461b646f8b32f6c.zip external_webkit-6c2af9490927c3c5959b5cb07461b646f8b32f6c.tar.gz external_webkit-6c2af9490927c3c5959b5cb07461b646f8b32f6c.tar.bz2 |
Merge WebKit at r59636: Initial merge by git
Change-Id: I59b289c4e6b18425f06ce41cc9d34c522515de91
Diffstat (limited to 'JavaScriptCore/wtf/url/src')
-rw-r--r-- | JavaScriptCore/wtf/url/src/RawURLBuffer.h | 71 | ||||
-rw-r--r-- | JavaScriptCore/wtf/url/src/URLBuffer.h | 136 | ||||
-rw-r--r-- | JavaScriptCore/wtf/url/src/URLComponent.h | 12 | ||||
-rw-r--r-- | JavaScriptCore/wtf/url/src/URLParser.h | 575 |
4 files changed, 791 insertions, 3 deletions
diff --git a/JavaScriptCore/wtf/url/src/RawURLBuffer.h b/JavaScriptCore/wtf/url/src/RawURLBuffer.h new file mode 100644 index 0000000..9bb2e8e --- /dev/null +++ b/JavaScriptCore/wtf/url/src/RawURLBuffer.h @@ -0,0 +1,71 @@ +// Copyright 2010, Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef RawURLBuffer_h +#define RawURLBuffer_h + +#include "URLBuffer.h" +#include <stdlib.h> + +namespace WTF { + +// Simple implementation of the URLBuffer using new[]. This class +// also supports a static buffer so if it is allocated on the stack, most +// URLs can be canonicalized with no heap allocations. +template<typename CHAR, int inlineCapacity = 1024> +class RawURLBuffer : public URLBuffer<CHAR> { +public: + RawURLBuffer() : URLBuffer<CHAR>() + { + this->m_buffer = m_inlineBuffer; + this->m_capacity = inlineCapacity; + } + + virtual ~RawURLBuffer() + { + if (this->m_buffer != m_inlineBuffer) + delete[] this->m_buffer; + } + + virtual void resize(int size) + { + CHAR* newBuffer = new CHAR[size]; + memcpy(newBuffer, this->m_buffer, sizeof(CHAR) * (this->m_length < size ? this->m_length : size)); + if (this->m_buffer != m_inlineBuffer) + delete[] this->m_buffer; + this->m_buffer = newBuffer; + this->m_capacity = size; + } + +protected: + CHAR m_inlineBuffer[inlineCapacity]; +}; + +} // namespace WTF + +#endif // RawURLBuffer_h diff --git a/JavaScriptCore/wtf/url/src/URLBuffer.h b/JavaScriptCore/wtf/url/src/URLBuffer.h new file mode 100644 index 0000000..e07402e --- /dev/null +++ b/JavaScriptCore/wtf/url/src/URLBuffer.h @@ -0,0 +1,136 @@ +// Copyright 2010, Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef URLBuffer_h +#define URLBuffer_h + +namespace WTF { + +// Base class for the canonicalizer output, this maintains a buffer and +// supports simple resizing and append operations on it. +// +// It is VERY IMPORTANT that no virtual function calls be made on the common +// code path. We only have two virtual function calls, the destructor and a +// resize function that is called when the existing buffer is not big enough. +// The derived class is then in charge of setting up our buffer which we will +// manage. +template<typename CHAR> +class URLBuffer { +public: + URLBuffer() : m_buffer(0), m_capacity(0), m_length(0) { } + virtual ~URLBuffer() { } + + // Implemented to resize the buffer. This function should update the buffer + // pointer to point to the new buffer, and any old data up to |m_length| in + // the buffer must be copied over. + // + // The new size must be larger than m_capacity. + virtual void resize(int) = 0; + + inline char at(int offset) const { return m_buffer[offset]; } + inline void set(int offset, CHAR ch) + { + // FIXME: Add ASSERT(offset < length()); + m_buffer[offset] = ch; + } + + // Returns the current capacity of the buffer. The length() is the number of + // characters that have been declared to be written, but the capacity() is + // the number that can be written without reallocation. If the caller must + // write many characters at once, it can make sure there is enough capacity, + // write the data, then use setLength() to declare the new length(). + int capacity() const { return m_capacity; } + int length() const { return m_length; } + + // The output will NOT be 0-terminated. Call length() to get the length. + const CHAR* data() const { return m_buffer; } + CHAR* data() { return m_buffer; } + + // Shortens the URL to the new length. Used for "backing up" when processing + // relative paths. This can also be used if an external function writes a lot + // of data to the buffer (when using the "Raw" version below) beyond the end, + // to declare the new length. + void setLength(int length) + { + // FIXME: Add ASSERT(length < capacity()); + m_length = length; + } + + // This is the most performance critical function, since it is called for + // every character. + void append(CHAR ch) + { + // In VC2005, putting this common case first speeds up execution + // dramatically because this branch is predicted as taken. + if (m_length < m_capacity) { + m_buffer[m_length] = ch; + ++m_length; + return; + } + + if (!grow(1)) + return; + + m_buffer[m_length] = ch; + ++m_length; + } + + void append(const CHAR* str, int strLength) + { + if (m_length + strLength > m_capacity) { + if (!grow(m_length + strLength - m_capacity)) + return; + } + for (int i = 0; i < strLength; i++) + m_buffer[m_length + i] = str[i]; + m_length += strLength; + } + +protected: + // Returns true if the buffer could be resized, false on OOM. + bool grow(int minimumAdditionalCapacity) + { + static const int minimumCapacity = 16; + int newCapacity = m_capacity ? m_capacity : minimumCapacity; + do { + if (newCapacity >= (1 << 30)) // Prevent overflow below. + return false; + newCapacity *= 2; + } while (newCapacity < m_capacity + minimumAdditionalCapacity); + resize(newCapacity); + return true; + } + + CHAR* m_buffer; + int m_capacity; + int m_length; // Used characters in the buffer. +}; + +} // namespace WTF + +#endif // URLBuffer_h diff --git a/JavaScriptCore/wtf/url/src/URLComponent.h b/JavaScriptCore/wtf/url/src/URLComponent.h index ca7e6f3..1b7976e 100644 --- a/JavaScriptCore/wtf/url/src/URLComponent.h +++ b/JavaScriptCore/wtf/url/src/URLComponent.h @@ -37,13 +37,19 @@ public: URLComponent() : m_begin(0), m_length(-1) { } URLComponent(int begin, int length) : m_begin(begin), m_length(length) { } + // Helper that returns a component created with the given begin and ending + // points. The ending point is non-inclusive. + static inline URLComponent fromRange(int begin, int end) + { + return URLComponent(begin, end - begin); + } + // Returns true if this component is valid, meaning the length is given. Even // valid components may be empty to record the fact that they exist. bool isValid() const { return m_length != -1; } - // Returns true if the given component is specified on false, the component - // is either empty or invalid. - bool isNonempty() const { return m_length > 0; } + bool isNonEmpty() const { return m_length > 0; } + bool isEmptyOrInvalid() const { return m_length <= 0; } void reset() { diff --git a/JavaScriptCore/wtf/url/src/URLParser.h b/JavaScriptCore/wtf/url/src/URLParser.h new file mode 100644 index 0000000..4d5ca51 --- /dev/null +++ b/JavaScriptCore/wtf/url/src/URLParser.h @@ -0,0 +1,575 @@ +/* Based on nsURLParsers.cc from Mozilla + * ------------------------------------- + * Copyright (C) 1998 Netscape Communications Corporation. + * + * Other contributors: + * Darin Fisher (original author) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#ifndef URLParser_h +#define URLParser_h + +#include "URLComponent.h" +#include "URLSegments.h" + +namespace WTF { + +template<typename CHAR> +class URLParser { +public: + enum SpecialPort { + UnspecifiedPort = -1, + InvalidPort = -2, + }; + + // This handles everything that may be an authority terminator, including + // backslash. For special backslash handling see parseAfterScheme. + static bool isPossibleAuthorityTerminator(CHAR ch) + { + return isURLSlash(ch) || ch == '?' || ch == '#' || ch == ';'; + } + + // Given an already-identified auth section, breaks it into its constituent + // 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. + static void parseAuthority(const CHAR* spec, const URLComponent& auth, URLComponent& username, URLComponent& password, URLComponent& host, URLComponent& port) + { + // FIXME: add ASSERT(auth.isValid()); // We should always get an authority. + if (!auth.length()) { + username.reset(); + password.reset(); + host.reset(); + port.reset(); + return; + } + + // Search backwards for @, which is the separator between the user info + // and the server info. RFC 3986 forbids @ from occuring in auth, but + // someone might include it in a password unescaped. + int i = auth.begin() + auth.length() - 1; + while (i > auth.begin() && spec[i] != '@') + --i; + + if (spec[i] == '@') { + // Found user info: <user-info>@<server-info> + parseUserInfo(spec, URLComponent(auth.begin(), i - auth.begin()), username, password); + parseServerInfo(spec, URLComponent::fromRange(i + 1, auth.begin() + auth.length()), host, port); + } else { + // No user info, everything is server info. + username.reset(); + password.reset(); + parseServerInfo(spec, auth, host, port); + } + } + + static bool extractScheme(const CHAR* spec, int specLength, URLComponent& scheme) + { + // Skip leading whitespace and control characters. + int begin = 0; + while (begin < specLength && shouldTrimFromURL(spec[begin])) + begin++; + if (begin == specLength) + return false; // Input is empty or all whitespace. + + // Find the first colon character. + for (int i = begin; i < specLength; i++) { + if (spec[i] == ':') { + scheme = URLComponent::fromRange(begin, i); + return true; + } + } + return false; // No colon found: no scheme + } + + // Fills in all members of the URLSegments structure (except for the + // scheme) for standard URLs. + // + // |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. + static void parseAfterScheme(const CHAR* spec, int specLength, int afterScheme, URLSegments& parsed) + { + int numberOfSlashes = consecutiveSlashes(spec, afterScheme, specLength); + int afterSlashes = afterScheme + numberOfSlashes; + + // 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 authEnd = nextAuthorityTerminator(spec, afterSlashes, specLength); + authority = URLComponent(afterSlashes, authEnd - afterSlashes); + + if (authEnd == specLength) // No beginning of path found. + fullPath = URLComponent(); + else // Everything starting from the slash to the end is the path. + fullPath = URLComponent(authEnd, specLength - authEnd); + + // Now parse those two sub-parts. + parseAuthority(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. + static void parseStandardURL(const CHAR* spec, int specLength, URLSegments& parsed) + { + // FIXME: add ASSERT(specLength >= 0); + + // Strip leading & trailing spaces and control characters. + int begin = 0; + trimURL(spec, begin, specLength); + + int afterScheme; + if (extractScheme(spec, specLength, parsed.scheme)) + afterScheme = parsed.scheme.end() + 1; // Skip past the colon. + else { + // Say there's no scheme when there is a 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; + } + parseAfterScheme(spec, specLength, afterScheme, parsed); + } + + static void parsePath(const CHAR* spec, const URLComponent& path, URLComponent& filepath, URLComponent& query, URLComponent& fragment) + { + // path = [/]<segment1>/<segment2>/<...>/<segmentN>;<param>?<query>#<fragment> + + // Special case when there is no path. + if (!path.isValid()) { + filepath.reset(); + query.reset(); + fragment.reset(); + return; + } + // FIXME: add ASSERT(path.length() > 0); // We should never have 0 length paths. + + // Search for first occurrence of either ? or #. + int pathEnd = path.begin() + path.length(); + + int querySeparator = -1; // Index of the '?' + int refSeparator = -1; // Index of the '#' + for (int i = path.begin(); i < pathEnd; i++) { + switch (spec[i]) { + case '?': + if (querySeparator < 0) + querySeparator = i; + break; + case '#': + refSeparator = i; + i = pathEnd; // Break out of the loop. + break; + default: + break; + } + } + + // Markers pointing to the character after each of these corresponding + // components. The code below works from the end back to the beginning, + // and will update these indices as it finds components that exist. + int fileEnd, queryEnd; + + // Fragment: from the # to the end of the path. + if (refSeparator >= 0) { + fileEnd = refSeparator; + queryEnd = refSeparator; + fragment = URLComponent::fromRange(refSeparator + 1, pathEnd); + } else { + fileEnd = pathEnd; + queryEnd = pathEnd; + fragment.reset(); + } + + // Query fragment: everything from the ? to the next boundary (either + // the end of the path or the fragment fragment). + if (querySeparator >= 0) { + fileEnd = querySeparator; + query = URLComponent::fromRange(querySeparator + 1, queryEnd); + } else + query.reset(); + + // File path: treat an empty file path as no file path. + if (fileEnd != path.begin()) + filepath = URLComponent::fromRange(path.begin(), fileEnd); + else + filepath.reset(); + } + + // Initializes a path URL which is merely a scheme followed by a path. + // Examples include "about:foo" and "javascript:alert('bar');" + static void parsePathURL(const CHAR* 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. + // FIXME: Perhaps this is unnecessary? + 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.setBegin(parsed.scheme.begin() + begin); + + // For compatibility 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); + } + } + + static void parseMailtoURL(const CHAR* spec, int specLength, URLSegments& parsed) + { + // FIXME: add 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.setBegin(parsed.scheme.begin() + 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 compatibility 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); + } + + static int parsePort(const CHAR* spec, const URLComponent& component) + { + // Easy success case when there is no port. + const int maxDigits = 5; + if (component.isEmptyOrInvalid()) + return UnspecifiedPort; + + URLComponent nonZeroDigits(component.end(), 0); + for (int i = 0; i < component.length(); ++i) { + if (spec[component.begin() + i] != '0') { + nonZeroDigits = URLComponent::fromRange(component.begin() + i, component.end()); + break; + } + } + if (!nonZeroDigits.length()) + return 0; // All digits were 0. + + if (nonZeroDigits.length() > maxDigits) + return InvalidPort; + + int port = 0; + for (int i = 0; i < nonZeroDigits.length(); ++i) { + CHAR ch = spec[nonZeroDigits.begin() + i]; + if (!isPortDigit(ch)) + return InvalidPort; + port *= 10; + port += static_cast<char>(ch) - '0'; + } + if (port > 65535) + return InvalidPort; + return port; + } + + static void extractFileName(const CHAR* spec, const URLComponent& path, URLComponent& fileName) + { + // Handle empty paths: they have no file names. + if (path.isEmptyOrInvalid()) { + fileName.reset(); + 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. + 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); + } + + static bool extractQueryKeyValue(const CHAR* spec, URLComponent& query, URLComponent& key, URLComponent& value) + { + if (query.isEmptyOrInvalid()) + 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; + } + +// FIXME: This should be protected or private. +public: + // We treat slashes and backslashes the same for IE compatibility. + static inline bool isURLSlash(CHAR ch) + { + return ch == '/' || ch == '\\'; + } + + // Returns true if we should trim this character from the URL because it is + // a space or a control character. + static inline bool shouldTrimFromURL(CHAR ch) + { + return ch <= ' '; + } + + // Given an already-initialized begin index and end index (the index after + // the last CHAR in spec), this shrinks the range to eliminate + // "should-be-trimmed" characters. + static inline void trimURL(const CHAR* spec, int& begin, int& end) + { + // Strip leading whitespace and control characters. + while (begin < end && shouldTrimFromURL(spec[begin])) + ++begin; + + // Strip trailing whitespace and control characters. We need the >i + // test for when the input string is all blanks; we don't want to back + // past the input. + while (end > begin && shouldTrimFromURL(spec[end - 1])) + --end; + } + + // Counts the number of consecutive slashes starting at the given offset + // in the given string of the given length. + static inline int consecutiveSlashes(const CHAR *string, int beginOffset, int stringLength) + { + int count = 0; + while (beginOffset + count < stringLength && isURLSlash(string[beginOffset + count])) + ++count; + return count; + } + +private: + // URLParser cannot be constructed. + URLParser(); + + // Returns true if the given character is a valid digit to use in a port. + static inline bool isPortDigit(CHAR ch) + { + return ch >= '0' && ch <= '9'; + } + + // 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. + static int nextAuthorityTerminator(const CHAR* spec, int startOffset, int specLength) + { + for (int i = startOffset; i < specLength; i++) { + if (isPossibleAuthorityTerminator(spec[i])) + return i; + } + return specLength; // Not found. + } + + static void parseUserInfo(const CHAR* 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.begin() + user.length()); + } else { + // No separator, treat everything as the username + username = user; + password = URLComponent(); + } + } + + static void parseServerInfo(const CHAR* spec, const URLComponent& serverInfo, URLComponent& host, URLComponent& port) + { + if (!serverInfo.length()) { + // No server info, host name is empty. + host.reset(); + port.reset(); + 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; + default: + break; + } + } + + if (colon > ipv6Terminator) { + // Found a port number: <hostname>:<port> + host = URLComponent::fromRange(serverInfo.begin(), colon); + if (!host.length()) + host.reset(); + port = URLComponent::fromRange(colon + 1, serverInfo.end()); + } else { + // No port: <hostname> + host = serverInfo; + port.reset(); + } + } +}; + +} // namespace WTF + +#endif // URLParser_h |