WebCore/platform/KURLGoogle.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2004, 2007, 2008, 2009 Apple Inc. All rights reserved.
       
     3  * Copyright (C) 2008, 2009 Google Inc. All rights reserved.
       
     4  * 
       
     5  * Redistribution and use in source and binary forms, with or without
       
     6  * modification, are permitted provided that the following conditions are
       
     7  * met:
       
     8  * 
       
     9  *     * Redistributions of source code must retain the above copyright
       
    10  * notice, this list of conditions and the following disclaimer.
       
    11  *     * Redistributions in binary form must reproduce the above
       
    12  * copyright notice, this list of conditions and the following disclaimer
       
    13  * in the documentation and/or other materials provided with the
       
    14  * distribution.
       
    15  *     * Neither the name of Google Inc. nor the names of its
       
    16  * contributors may be used to endorse or promote products derived from
       
    17  * this software without specific prior written permission.
       
    18  * 
       
    19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
    20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
    21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
    22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
    23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
    24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
    25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
       
    26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    30  */
       
    31 
       
    32 #include "config.h"
       
    33 
       
    34 #if USE(GOOGLEURL)
       
    35 #include "KURL.h"
       
    36 
       
    37 #ifndef NDEBUG
       
    38 #include <stdio.h>
       
    39 #endif
       
    40 
       
    41 #include <algorithm>
       
    42 
       
    43 #include "StringHash.h"
       
    44 #include "NotImplemented.h"
       
    45 #include "TextEncoding.h"
       
    46 #include <wtf/HashMap.h>
       
    47 #include <wtf/Vector.h>
       
    48 #include <wtf/StdLibExtras.h>
       
    49 #include <wtf/text/CString.h>
       
    50 
       
    51 #include <googleurl/src/url_util.h>
       
    52 
       
    53 using WTF::isASCIILower;
       
    54 using WTF::toASCIILower;
       
    55 using std::binary_search;
       
    56 
       
    57 namespace WebCore {
       
    58 
       
    59 static const int maximumValidPortNumber = 0xFFFE;
       
    60 static const int invalidPortNumber = 0xFFFF;
       
    61 
       
    62 // Wraps WebCore's text encoding in a character set converter for the
       
    63 // canonicalizer.
       
    64 class KURLCharsetConverter : public url_canon::CharsetConverter {
       
    65 public:
       
    66     // The encoding parameter may be NULL, but in this case the object must not
       
    67     // be called.
       
    68     KURLCharsetConverter(const TextEncoding* encoding)
       
    69         : m_encoding(encoding)
       
    70     {
       
    71     }
       
    72 
       
    73     virtual void ConvertFromUTF16(const url_parse::UTF16Char* input, int inputLength,
       
    74                                   url_canon::CanonOutput* output)
       
    75     {
       
    76         CString encoded = m_encoding->encode(input, inputLength, URLEncodedEntitiesForUnencodables);
       
    77         output->Append(encoded.data(), static_cast<int>(encoded.length()));
       
    78     }
       
    79 
       
    80 private:
       
    81     const TextEncoding* m_encoding;
       
    82 };
       
    83 
       
    84 // Note that this function must be named differently than the one in KURL.cpp
       
    85 // since our unit tests evilly include both files, and their local definition
       
    86 // will be ambiguous.
       
    87 static inline void assertProtocolIsGood(const char* protocol)
       
    88 {
       
    89 #ifndef NDEBUG
       
    90     const char* p = protocol;
       
    91     while (*p) {
       
    92         ASSERT(*p > ' ' && *p < 0x7F && !(*p >= 'A' && *p <= 'Z'));
       
    93         ++p;
       
    94     }
       
    95 #endif
       
    96 }
       
    97 
       
    98 // Returns the characters for the given string, or a pointer to a static empty
       
    99 // string if the input string is NULL. This will always ensure we have a non-
       
   100 // NULL character pointer since ReplaceComponents has special meaning for NULL.
       
   101 static inline const url_parse::UTF16Char* CharactersOrEmpty(const String& str)
       
   102 {
       
   103     static const url_parse::UTF16Char zero = 0;
       
   104     return str.characters() ?
       
   105            reinterpret_cast<const url_parse::UTF16Char*>(str.characters()) :
       
   106            &zero;
       
   107 }
       
   108 
       
   109 static inline bool isUnicodeEncoding(const TextEncoding* encoding)
       
   110 {
       
   111     return encoding->encodingForFormSubmission() == UTF8Encoding();
       
   112 }
       
   113 
       
   114 static bool lowerCaseEqualsASCII(const char* begin, const char* end, const char* str)
       
   115 {
       
   116     while (begin != end && *str) {
       
   117         ASSERT(toASCIILower(*str) == *str);
       
   118         if (toASCIILower(*begin++) != *str++)
       
   119             return false;
       
   120     }
       
   121 
       
   122     // Both strings are equal (ignoring case) if and only if all of the characters were equal,
       
   123     // and the end of both has been reached.
       
   124     return begin == end && !*str;
       
   125 }
       
   126 
       
   127 static inline bool isSchemeFirstChar(char c)
       
   128 {
       
   129     return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
       
   130 }
       
   131 
       
   132 static inline bool isSchemeChar(char c)
       
   133 {
       
   134     return isSchemeFirstChar(c) || (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '*';
       
   135 }
       
   136 
       
   137 
       
   138 // KURLGooglePrivate -----------------------------------------------------------
       
   139 
       
   140 KURLGooglePrivate::KURLGooglePrivate()
       
   141     : m_isValid(false)
       
   142     , m_protocolInHTTPFamily(false)
       
   143     , m_utf8IsASCII(true)
       
   144     , m_stringIsValid(false)
       
   145 {
       
   146 }
       
   147 
       
   148 KURLGooglePrivate::KURLGooglePrivate(const url_parse::Parsed& parsed, bool isValid)
       
   149     : m_isValid(isValid)
       
   150     , m_protocolInHTTPFamily(false)
       
   151     , m_parsed(parsed)
       
   152     , m_utf8IsASCII(true)
       
   153     , m_stringIsValid(false)
       
   154 {
       
   155 }
       
   156 
       
   157 // Setters for the data. Using the ASCII version when you know the
       
   158 // data is ASCII will be slightly more efficient. The UTF-8 version
       
   159 // will always be correct if the caller is unsure.
       
   160 void KURLGooglePrivate::setUtf8(const CString& str)
       
   161 {
       
   162     const char* data = str.data();
       
   163     unsigned dataLength = str.length();
       
   164 
       
   165     // The m_utf8IsASCII must always be correct since the DeprecatedString
       
   166     // getter must create it with the proper constructor. This test can be
       
   167     // removed when DeprecatedString is gone, but it still might be a
       
   168     // performance win.
       
   169     m_utf8IsASCII = true;
       
   170     for (unsigned i = 0; i < dataLength; i++) {
       
   171         if (static_cast<unsigned char>(data[i]) >= 0x80) {
       
   172             m_utf8IsASCII = false;
       
   173             break;
       
   174         }
       
   175     }
       
   176 
       
   177     m_utf8 = str;
       
   178     m_stringIsValid = false;
       
   179     initProtocolInHTTPFamily();
       
   180 }
       
   181 
       
   182 void KURLGooglePrivate::setAscii(const CString& str)
       
   183 {
       
   184     m_utf8 = str;
       
   185     m_utf8IsASCII = true;
       
   186     m_stringIsValid = false;
       
   187     initProtocolInHTTPFamily();
       
   188 }
       
   189 
       
   190 void KURLGooglePrivate::init(const KURL& base,
       
   191                              const String& relative,
       
   192                              const TextEncoding* queryEncoding)
       
   193 {
       
   194     init(base, relative.characters(), relative.length(), queryEncoding);
       
   195 }
       
   196 
       
   197 // Note: code mostly duplicated below.
       
   198 void KURLGooglePrivate::init(const KURL& base, const char* rel, int relLength,
       
   199                              const TextEncoding* queryEncoding)
       
   200 {
       
   201     // As a performance optimization, we do not use the charset converter if
       
   202     // encoding is UTF-8 or other Unicode encodings. Note that this is
       
   203     // per HTML5 2.5.3 (resolving URL). The URL canonicalizer will be
       
   204     // more efficient with no charset converter object because it
       
   205     // can do UTF-8 internally with no extra copies.
       
   206 
       
   207     // We feel free to make the charset converter object every time since it's
       
   208     // just a wrapper around a reference.
       
   209     KURLCharsetConverter charsetConverterObject(queryEncoding);
       
   210     KURLCharsetConverter* charsetConverter =
       
   211         (!queryEncoding || isUnicodeEncoding(queryEncoding)) ? 0 :
       
   212         &charsetConverterObject;
       
   213 
       
   214     url_canon::RawCanonOutputT<char> output;
       
   215     const CString& baseStr = base.m_url.utf8String();
       
   216     m_isValid = url_util::ResolveRelative(baseStr.data(), baseStr.length(),
       
   217                                           base.m_url.m_parsed, rel, relLength,
       
   218                                           charsetConverter,
       
   219                                           &output, &m_parsed);
       
   220 
       
   221     // See FIXME in KURLGooglePrivate in the header. If canonicalization has not
       
   222     // changed the string, we can avoid an extra allocation by using assignment.
       
   223     //
       
   224     // When KURL encounters an error such that the URL is invalid and empty
       
   225     // (for example, resolving a relative URL on a non-hierarchical base), it
       
   226     // will produce an isNull URL, and calling setUtf8 will produce an empty
       
   227     // non-null URL. This is unlikely to affect anything, but we preserve this
       
   228     // just in case.
       
   229     if (m_isValid || output.length()) {
       
   230         // Without ref, the whole url is guaranteed to be ASCII-only.
       
   231         if (m_parsed.ref.is_nonempty())
       
   232             setUtf8(CString(output.data(), output.length()));
       
   233         else
       
   234             setAscii(CString(output.data(), output.length()));
       
   235     } else {
       
   236         // WebCore expects resolved URLs to be empty rather than NULL.
       
   237         setUtf8(CString("", 0));
       
   238     }
       
   239 }
       
   240 
       
   241 // Note: code mostly duplicated above. See FIXMEs and comments there.
       
   242 void KURLGooglePrivate::init(const KURL& base, const UChar* rel, int relLength,
       
   243                              const TextEncoding* queryEncoding)
       
   244 {
       
   245     KURLCharsetConverter charsetConverterObject(queryEncoding);
       
   246     KURLCharsetConverter* charsetConverter =
       
   247         (!queryEncoding || isUnicodeEncoding(queryEncoding)) ? 0 :
       
   248         &charsetConverterObject;
       
   249 
       
   250     url_canon::RawCanonOutputT<char> output;
       
   251     const CString& baseStr = base.m_url.utf8String();
       
   252     m_isValid = url_util::ResolveRelative(baseStr.data(), baseStr.length(),
       
   253                                           base.m_url.m_parsed, rel, relLength,
       
   254                                           charsetConverter,
       
   255                                           &output, &m_parsed);
       
   256 
       
   257 
       
   258     if (m_isValid || output.length()) {
       
   259         if (m_parsed.ref.is_nonempty())
       
   260             setUtf8(CString(output.data(), output.length()));
       
   261         else
       
   262             setAscii(CString(output.data(), output.length()));
       
   263     } else
       
   264         setUtf8(CString("", 0));
       
   265 }
       
   266 
       
   267 void KURLGooglePrivate::initProtocolInHTTPFamily()
       
   268 {
       
   269     if (!m_isValid) {
       
   270         m_protocolInHTTPFamily = false;
       
   271         return;
       
   272     }
       
   273 
       
   274     const char* scheme = m_utf8.data() + m_parsed.scheme.begin;
       
   275     if (m_parsed.scheme.len == 4)
       
   276         m_protocolInHTTPFamily = lowerCaseEqualsASCII(scheme, scheme + 4, "http");
       
   277     else if (m_parsed.scheme.len == 5)
       
   278         m_protocolInHTTPFamily = lowerCaseEqualsASCII(scheme, scheme + 5, "https");
       
   279     else
       
   280         m_protocolInHTTPFamily = false;
       
   281 }
       
   282 
       
   283 void KURLGooglePrivate::copyTo(KURLGooglePrivate* dest) const
       
   284 {
       
   285     dest->m_isValid = m_isValid;
       
   286     dest->m_protocolInHTTPFamily = m_protocolInHTTPFamily;
       
   287     dest->m_parsed = m_parsed;
       
   288 
       
   289     // Don't copy the 16-bit string since that will be regenerated as needed.
       
   290     dest->m_utf8 = CString(m_utf8.data(), m_utf8.length());
       
   291     dest->m_utf8IsASCII = m_utf8IsASCII;
       
   292     dest->m_stringIsValid = false;
       
   293 }
       
   294 
       
   295 String KURLGooglePrivate::componentString(const url_parse::Component& comp) const
       
   296 {
       
   297     if (!m_isValid || comp.len <= 0) {
       
   298         // KURL returns a NULL string if the URL is itself a NULL string, and an
       
   299         // empty string for other nonexistant entities.
       
   300         if (utf8String().isNull())
       
   301             return String();
       
   302         return String("", 0);
       
   303     }
       
   304     // begin and len are in terms of bytes which do not match
       
   305     // if string() is UTF-16 and input contains non-ASCII characters.
       
   306     // However, the only part in urlString that can contain non-ASCII
       
   307     // characters is 'ref' at the end of the string. In that case,
       
   308     // begin will always match the actual value and len (in terms of
       
   309     // byte) will be longer than what's needed by 'mid'. However, mid
       
   310     // truncates len to avoid go past the end of a string so that we can
       
   311     // get away withtout doing anything here.
       
   312     return string().substring(comp.begin, comp.len);
       
   313 }
       
   314 
       
   315 void KURLGooglePrivate::replaceComponents(const Replacements& replacements)
       
   316 {
       
   317     url_canon::RawCanonOutputT<char> output;
       
   318     url_parse::Parsed newParsed;
       
   319 
       
   320     m_isValid = url_util::ReplaceComponents(utf8String().data(),
       
   321                                             utf8String().length(), m_parsed, replacements, 0, &output, &newParsed);
       
   322 
       
   323     m_parsed = newParsed;
       
   324     if (m_parsed.ref.is_nonempty())
       
   325         setUtf8(CString(output.data(), output.length()));
       
   326     else
       
   327         setAscii(CString(output.data(), output.length()));
       
   328 }
       
   329 
       
   330 const String& KURLGooglePrivate::string() const
       
   331 {
       
   332     if (!m_stringIsValid) {
       
   333         // Must special case the NULL case, since constructing the
       
   334         // string like we do below will generate an empty rather than
       
   335         // a NULL string.
       
   336         if (m_utf8.isNull())
       
   337             m_string = String();
       
   338         else if (m_utf8IsASCII)
       
   339             m_string = String(m_utf8.data(), m_utf8.length());
       
   340         else
       
   341             m_string = String::fromUTF8(m_utf8.data(), m_utf8.length());
       
   342         m_stringIsValid = true;
       
   343     }
       
   344     return m_string;
       
   345 }
       
   346 
       
   347 // KURL ------------------------------------------------------------------------
       
   348 
       
   349 // Creates with NULL-terminated string input representing an absolute URL.
       
   350 // WebCore generally calls this only with hardcoded strings, so the input is
       
   351 // ASCII. We treat is as UTF-8 just in case.
       
   352 KURL::KURL(ParsedURLStringTag, const char *url)
       
   353 {
       
   354     // FIXME The Mac code checks for beginning with a slash and converting to a
       
   355     // file: URL. We will want to add this as well once we can compile on a
       
   356     // system like that.
       
   357     m_url.init(KURL(), url, strlen(url), 0);
       
   358 
       
   359     // The one-argument constructors should never generate a NULL string.
       
   360     // This is a funny quirk of KURL.cpp (probably a bug) which we preserve.
       
   361     if (m_url.utf8String().isNull())
       
   362         m_url.setAscii(CString("", 0));
       
   363 }
       
   364 
       
   365 // Initializes with a string representing an absolute URL. No encoding
       
   366 // information is specified. This generally happens when a KURL is converted
       
   367 // to a string and then converted back. In this case, the URL is already
       
   368 // canonical and in proper escaped form so needs no encoding. We treat it was
       
   369 // UTF-8 just in case.
       
   370 KURL::KURL(ParsedURLStringTag, const String& url)
       
   371 {
       
   372     if (!url.isNull())
       
   373         m_url.init(KURL(), url, 0);
       
   374     else {
       
   375         // WebCore expects us to preserve the nullness of strings when this
       
   376         // constructor is used. In all other cases, it expects a non-null
       
   377         // empty string, which is what init() will create.
       
   378         m_url.m_isValid = false;
       
   379         m_url.m_protocolInHTTPFamily = false;
       
   380     }
       
   381 }
       
   382 
       
   383 // Constructs a new URL given a base URL and a possibly relative input URL.
       
   384 // This assumes UTF-8 encoding.
       
   385 KURL::KURL(const KURL& base, const String& relative)
       
   386 {
       
   387     m_url.init(base, relative, 0);
       
   388 }
       
   389 
       
   390 // Constructs a new URL given a base URL and a possibly relative input URL.
       
   391 // Any query portion of the relative URL will be encoded in the given encoding.
       
   392 KURL::KURL(const KURL& base,
       
   393            const String& relative,
       
   394            const TextEncoding& encoding)
       
   395 {
       
   396     m_url.init(base, relative, &encoding.encodingForFormSubmission());
       
   397 }
       
   398 
       
   399 KURL::KURL(const CString& canonicalSpec,
       
   400            const url_parse::Parsed& parsed, bool isValid)
       
   401     : m_url(parsed, isValid)
       
   402 {
       
   403     // We know the reference fragment is the only part that can be UTF-8, so
       
   404     // we know it's ASCII when there is no ref.
       
   405     if (parsed.ref.is_nonempty())
       
   406         m_url.setUtf8(canonicalSpec);
       
   407     else
       
   408         m_url.setAscii(canonicalSpec);
       
   409 }
       
   410 
       
   411 #if PLATFORM(CF)
       
   412 KURL::KURL(CFURLRef)
       
   413 {
       
   414     notImplemented();
       
   415     invalidate();
       
   416 }
       
   417 
       
   418 CFURLRef KURL::createCFURL() const
       
   419 {
       
   420     notImplemented();
       
   421     return 0;
       
   422 }
       
   423 #endif
       
   424 
       
   425 KURL KURL::copy() const
       
   426 {
       
   427     KURL result = *this;
       
   428     m_url.copyTo(&result.m_url);
       
   429     return result;
       
   430 }
       
   431 
       
   432 bool KURL::isNull() const
       
   433 {
       
   434     return m_url.utf8String().isNull();
       
   435 }
       
   436 
       
   437 bool KURL::isEmpty() const
       
   438 {
       
   439     return !m_url.utf8String().length();
       
   440 }
       
   441 
       
   442 bool KURL::isValid() const
       
   443 {
       
   444     return m_url.m_isValid;
       
   445 }
       
   446 
       
   447 bool KURL::hasPort() const
       
   448 {
       
   449     return hostEnd() < pathStart();
       
   450 }
       
   451 
       
   452 bool KURL::protocolInHTTPFamily() const
       
   453 {
       
   454     return m_url.m_protocolInHTTPFamily;
       
   455 }
       
   456 
       
   457 bool KURL::hasPath() const
       
   458 {
       
   459     // Note that http://www.google.com/" has a path, the path is "/". This can
       
   460     // return false only for invalid or nonstandard URLs.
       
   461     return m_url.m_parsed.path.len >= 0;
       
   462 }
       
   463 
       
   464 // We handle "parameters" separated by a semicolon, while KURL.cpp does not,
       
   465 // which can lead to different results in some cases.
       
   466 String KURL::lastPathComponent() const
       
   467 {
       
   468     // When the output ends in a slash, WebCore has different expectations than
       
   469     // the GoogleURL library. For "/foo/bar/" the library will return the empty
       
   470     // string, but WebCore wants "bar".
       
   471     url_parse::Component path = m_url.m_parsed.path;
       
   472     if (path.len > 0 && m_url.utf8String().data()[path.end() - 1] == '/')
       
   473         path.len--;
       
   474 
       
   475     url_parse::Component file;
       
   476     url_parse::ExtractFileName(m_url.utf8String().data(), path, &file);
       
   477 
       
   478     // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns
       
   479     // a null string when the path is empty, which we duplicate here.
       
   480     if (!file.is_nonempty())
       
   481         return String();
       
   482     return m_url.componentString(file);
       
   483 }
       
   484 
       
   485 String KURL::protocol() const
       
   486 {
       
   487     return m_url.componentString(m_url.m_parsed.scheme);
       
   488 }
       
   489 
       
   490 String KURL::host() const
       
   491 {
       
   492     // Note: KURL.cpp unescapes here.
       
   493     return m_url.componentString(m_url.m_parsed.host);
       
   494 }
       
   495 
       
   496 // Returns 0 when there is no port.
       
   497 //
       
   498 // We treat URL's with out-of-range port numbers as invalid URLs, and they will
       
   499 // be rejected by the canonicalizer. KURL.cpp will allow them in parsing, but
       
   500 // return invalidPortNumber from this port() function, so we mirror that behavior here.
       
   501 unsigned short KURL::port() const
       
   502 {
       
   503     if (!m_url.m_isValid || m_url.m_parsed.port.len <= 0)
       
   504         return 0;
       
   505     int port = url_parse::ParsePort(m_url.utf8String().data(), m_url.m_parsed.port);
       
   506     ASSERT(port != url_parse::PORT_UNSPECIFIED); // Checked port.len <= 0 before.
       
   507 
       
   508     if (port == url_parse::PORT_INVALID || port > maximumValidPortNumber) // Mimic KURL::port()
       
   509         port = invalidPortNumber;
       
   510 
       
   511     return static_cast<unsigned short>(port);
       
   512 }
       
   513 
       
   514 // Returns the empty string if there is no password.
       
   515 String KURL::pass() const
       
   516 {
       
   517     // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns
       
   518     // a null string when the password is empty, which we duplicate here.
       
   519     if (!m_url.m_parsed.password.is_nonempty())
       
   520         return String();
       
   521 
       
   522     // Note: KURL.cpp unescapes here.
       
   523     return m_url.componentString(m_url.m_parsed.password);
       
   524 }
       
   525 
       
   526 // Returns the empty string if there is no username.
       
   527 String KURL::user() const
       
   528 {
       
   529     // Note: KURL.cpp unescapes here.
       
   530     return m_url.componentString(m_url.m_parsed.username);
       
   531 }
       
   532 
       
   533 String KURL::fragmentIdentifier() const
       
   534 {
       
   535     // Empty but present refs ("foo.com/bar#") should result in the empty
       
   536     // string, which m_url.componentString will produce. Nonexistant refs should be
       
   537     // the NULL string.
       
   538     if (!m_url.m_parsed.ref.is_valid())
       
   539         return String();
       
   540 
       
   541     // Note: KURL.cpp unescapes here.
       
   542     return m_url.componentString(m_url.m_parsed.ref);
       
   543 }
       
   544 
       
   545 bool KURL::hasFragmentIdentifier() const
       
   546 {
       
   547     // Note: KURL.cpp unescapes here.
       
   548     // FIXME determine if KURL.cpp agrees about an empty ref
       
   549     return m_url.m_parsed.ref.len >= 0;
       
   550 }
       
   551 
       
   552 String KURL::baseAsString() const
       
   553 {
       
   554     // FIXME: There is probably a more efficient way to do this?
       
   555     return string().left(pathAfterLastSlash());
       
   556 }
       
   557 
       
   558 String KURL::query() const
       
   559 {
       
   560     if (m_url.m_parsed.query.len >= 0)
       
   561         return m_url.componentString(m_url.m_parsed.query);
       
   562 
       
   563     // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns
       
   564     // an empty string when the query is empty rather than a null (not sure
       
   565     // which is right).
       
   566     // Returns a null if the query is not specified, instead of empty.
       
   567     if (m_url.m_parsed.query.is_valid())
       
   568         return String("", 0);
       
   569     return String();
       
   570 }
       
   571 
       
   572 String KURL::path() const
       
   573 {
       
   574     // Note: KURL.cpp unescapes here.
       
   575     return m_url.componentString(m_url.m_parsed.path);
       
   576 }
       
   577 
       
   578 bool KURL::setProtocol(const String& protocol)
       
   579 {
       
   580     // Firefox and IE remove everything after the first ':'.
       
   581     int separatorPosition = protocol.find(':');
       
   582     String newProtocol = protocol.substring(0, separatorPosition);
       
   583 
       
   584     // If KURL is given an invalid scheme, it returns failure without modifying
       
   585     // the URL at all. This is in contrast to most other setters which modify
       
   586     // the URL and set "m_isValid."
       
   587     url_canon::RawCanonOutputT<char> canonProtocol;
       
   588     url_parse::Component protocolComponent;
       
   589     if (!url_canon::CanonicalizeScheme(newProtocol.characters(),
       
   590                                        url_parse::Component(0, newProtocol.length()),
       
   591                                        &canonProtocol, &protocolComponent)
       
   592         || !protocolComponent.is_nonempty())
       
   593         return false;
       
   594 
       
   595     KURLGooglePrivate::Replacements replacements;
       
   596     replacements.SetScheme(CharactersOrEmpty(newProtocol),
       
   597                            url_parse::Component(0, newProtocol.length()));
       
   598     m_url.replaceComponents(replacements);
       
   599 
       
   600     // isValid could be false but we still return true here. This is because
       
   601     // WebCore or JS scripts can build up a URL by setting individual
       
   602     // components, and a JS exception is based on the return value of this
       
   603     // function. We want to throw the exception and stop the script only when
       
   604     // its trying to set a bad protocol, and not when it maybe just hasn't
       
   605     // finished building up its final scheme.
       
   606     return true;
       
   607 }
       
   608 
       
   609 void KURL::setHost(const String& host)
       
   610 {
       
   611     KURLGooglePrivate::Replacements replacements;
       
   612     replacements.SetHost(CharactersOrEmpty(host),
       
   613                          url_parse::Component(0, host.length()));
       
   614     m_url.replaceComponents(replacements);
       
   615 }
       
   616 
       
   617 void KURL::setHostAndPort(const String& s)
       
   618 {
       
   619     String host = s;
       
   620     String port;
       
   621     int hostEnd = s.find(":");
       
   622     if (hostEnd != -1) {
       
   623         host = s.left(hostEnd);
       
   624         port = s.substring(hostEnd + 1);
       
   625     }
       
   626 
       
   627     KURLGooglePrivate::Replacements replacements;
       
   628     // Host can't be removed, so we always set.
       
   629     replacements.SetHost(CharactersOrEmpty(host),
       
   630                          url_parse::Component(0, host.length()));
       
   631 
       
   632     if (port.isEmpty())  // Port may be removed, so we support clearing.
       
   633         replacements.ClearPort();
       
   634     else
       
   635         replacements.SetPort(CharactersOrEmpty(port), url_parse::Component(0, port.length()));
       
   636     m_url.replaceComponents(replacements);
       
   637 }
       
   638 
       
   639 void KURL::removePort()
       
   640 {
       
   641     if (hasPort()) {
       
   642         String urlWithoutPort = m_url.string().left(hostEnd()) + m_url.string().substring(pathStart());
       
   643         m_url.setUtf8(urlWithoutPort.utf8());
       
   644     }
       
   645 }
       
   646 
       
   647 void KURL::setPort(unsigned short i)
       
   648 {
       
   649     KURLGooglePrivate::Replacements replacements;
       
   650     String portStr;
       
   651     if (i) {
       
   652         portStr = String::number(i);
       
   653         replacements.SetPort(
       
   654             reinterpret_cast<const url_parse::UTF16Char*>(portStr.characters()),
       
   655             url_parse::Component(0, portStr.length()));
       
   656 
       
   657     } else {
       
   658         // Clear any existing port when it is set to 0.
       
   659         replacements.ClearPort();
       
   660     }
       
   661     m_url.replaceComponents(replacements);
       
   662 }
       
   663 
       
   664 void KURL::setUser(const String& user)
       
   665 {
       
   666     // This function is commonly called to clear the username, which we
       
   667     // normally don't have, so we optimize this case.
       
   668     if (user.isEmpty() && !m_url.m_parsed.username.is_valid())
       
   669         return;
       
   670 
       
   671     // The canonicalizer will clear any usernames that are empty, so we
       
   672     // don't have to explicitly call ClearUsername() here.
       
   673     KURLGooglePrivate::Replacements replacements;
       
   674     replacements.SetUsername(CharactersOrEmpty(user),
       
   675                              url_parse::Component(0, user.length()));
       
   676     m_url.replaceComponents(replacements);
       
   677 }
       
   678 
       
   679 void KURL::setPass(const String& pass)
       
   680 {
       
   681     // This function is commonly called to clear the password, which we
       
   682     // normally don't have, so we optimize this case.
       
   683     if (pass.isEmpty() && !m_url.m_parsed.password.is_valid())
       
   684         return;
       
   685 
       
   686     // The canonicalizer will clear any passwords that are empty, so we
       
   687     // don't have to explicitly call ClearUsername() here.
       
   688     KURLGooglePrivate::Replacements replacements;
       
   689     replacements.SetPassword(CharactersOrEmpty(pass),
       
   690                              url_parse::Component(0, pass.length()));
       
   691     m_url.replaceComponents(replacements);
       
   692 }
       
   693 
       
   694 void KURL::setFragmentIdentifier(const String& s)
       
   695 {
       
   696     // This function is commonly called to clear the ref, which we
       
   697     // normally don't have, so we optimize this case.
       
   698     if (s.isNull() && !m_url.m_parsed.ref.is_valid())
       
   699         return;
       
   700 
       
   701     KURLGooglePrivate::Replacements replacements;
       
   702     if (s.isNull())
       
   703         replacements.ClearRef();
       
   704     else
       
   705         replacements.SetRef(CharactersOrEmpty(s), url_parse::Component(0, s.length()));
       
   706     m_url.replaceComponents(replacements);
       
   707 }
       
   708 
       
   709 void KURL::removeFragmentIdentifier()
       
   710 {
       
   711     KURLGooglePrivate::Replacements replacements;
       
   712     replacements.ClearRef();
       
   713     m_url.replaceComponents(replacements);
       
   714 }
       
   715 
       
   716 void KURL::setQuery(const String& query)
       
   717 {
       
   718     KURLGooglePrivate::Replacements replacements;
       
   719     if (query.isNull()) {
       
   720         // KURL.cpp sets to NULL to clear any query.
       
   721         replacements.ClearQuery();
       
   722     } else if (query.length() > 0 && query[0] == '?') {
       
   723         // WebCore expects the query string to begin with a question mark, but
       
   724         // GoogleURL doesn't. So we trim off the question mark when setting.
       
   725         replacements.SetQuery(CharactersOrEmpty(query),
       
   726                               url_parse::Component(1, query.length() - 1));
       
   727     } else {
       
   728         // When set with the empty string or something that doesn't begin with
       
   729         // a question mark, KURL.cpp will add a question mark for you. The only
       
   730         // way this isn't compatible is if you call this function with an empty
       
   731         // string. KURL.cpp will leave a '?' with nothing following it in the
       
   732         // URL, whereas we'll clear it.
       
   733         // FIXME We should eliminate this difference.
       
   734         replacements.SetQuery(CharactersOrEmpty(query),
       
   735                               url_parse::Component(0, query.length()));
       
   736     }
       
   737     m_url.replaceComponents(replacements);
       
   738 }
       
   739 
       
   740 void KURL::setPath(const String& path)
       
   741 {
       
   742     // Empty paths will be canonicalized to "/", so we don't have to worry
       
   743     // about calling ClearPath().
       
   744     KURLGooglePrivate::Replacements replacements;
       
   745     replacements.SetPath(CharactersOrEmpty(path),
       
   746                          url_parse::Component(0, path.length()));
       
   747     m_url.replaceComponents(replacements);
       
   748 }
       
   749 
       
   750 // On Mac, this just seems to return the same URL, but with "/foo/bar" for
       
   751 // file: URLs instead of file:///foo/bar. We don't bother with any of this,
       
   752 // at least for now.
       
   753 String KURL::prettyURL() const
       
   754 {
       
   755     if (!m_url.m_isValid)
       
   756         return String();
       
   757     return m_url.string();
       
   758 }
       
   759 
       
   760 bool protocolIsJavaScript(const String& url)
       
   761 {
       
   762     return protocolIs(url, "javascript");
       
   763 }
       
   764 
       
   765 // We copied the KURL version here on Dec 4, 2009 while doing a WebKit
       
   766 // merge.
       
   767 //
       
   768 // FIXME Somehow share this with KURL? Like we'd theoretically merge with
       
   769 // decodeURLEscapeSequences below?
       
   770 bool isDefaultPortForProtocol(unsigned short port, const String& protocol)
       
   771 {
       
   772     if (protocol.isEmpty())
       
   773         return false;
       
   774 
       
   775     typedef HashMap<String, unsigned, CaseFoldingHash> DefaultPortsMap;
       
   776     DEFINE_STATIC_LOCAL(DefaultPortsMap, defaultPorts, ());
       
   777     if (defaultPorts.isEmpty()) {
       
   778         defaultPorts.set("http", 80);
       
   779         defaultPorts.set("https", 443);
       
   780         defaultPorts.set("ftp", 21);
       
   781         defaultPorts.set("ftps", 990);
       
   782     }
       
   783     return defaultPorts.get(protocol) == port;
       
   784 }
       
   785 
       
   786 // We copied the KURL version here on Dec 4, 2009 while doing a WebKit
       
   787 // merge.
       
   788 //
       
   789 // FIXME Somehow share this with KURL? Like we'd theoretically merge with
       
   790 // decodeURLEscapeSequences below?
       
   791 bool portAllowed(const KURL& url)
       
   792 {
       
   793     unsigned short port = url.port();
       
   794 
       
   795     // Since most URLs don't have a port, return early for the "no port" case.
       
   796     if (!port)
       
   797         return true;
       
   798 
       
   799     // This blocked port list matches the port blocking that Mozilla implements.
       
   800     // See http://www.mozilla.org/projects/netlib/PortBanning.html for more information.
       
   801     static const unsigned short blockedPortList[] = {
       
   802         1,    // tcpmux
       
   803         7,    // echo
       
   804         9,    // discard
       
   805         11,   // systat
       
   806         13,   // daytime
       
   807         15,   // netstat
       
   808         17,   // qotd
       
   809         19,   // chargen
       
   810         20,   // FTP-data
       
   811         21,   // FTP-control
       
   812         22,   // SSH
       
   813         23,   // telnet
       
   814         25,   // SMTP
       
   815         37,   // time
       
   816         42,   // name
       
   817         43,   // nicname
       
   818         53,   // domain
       
   819         77,   // priv-rjs
       
   820         79,   // finger
       
   821         87,   // ttylink
       
   822         95,   // supdup
       
   823         101,  // hostriame
       
   824         102,  // iso-tsap
       
   825         103,  // gppitnp
       
   826         104,  // acr-nema
       
   827         109,  // POP2
       
   828         110,  // POP3
       
   829         111,  // sunrpc
       
   830         113,  // auth
       
   831         115,  // SFTP
       
   832         117,  // uucp-path
       
   833         119,  // nntp
       
   834         123,  // NTP
       
   835         135,  // loc-srv / epmap
       
   836         139,  // netbios
       
   837         143,  // IMAP2
       
   838         179,  // BGP
       
   839         389,  // LDAP
       
   840         465,  // SMTP+SSL
       
   841         512,  // print / exec
       
   842         513,  // login
       
   843         514,  // shell
       
   844         515,  // printer
       
   845         526,  // tempo
       
   846         530,  // courier
       
   847         531,  // Chat
       
   848         532,  // netnews
       
   849         540,  // UUCP
       
   850         556,  // remotefs
       
   851         563,  // NNTP+SSL
       
   852         587,  // ESMTP
       
   853         601,  // syslog-conn
       
   854         636,  // LDAP+SSL
       
   855         993,  // IMAP+SSL
       
   856         995,  // POP3+SSL
       
   857         2049, // NFS
       
   858         3659, // apple-sasl / PasswordServer [Apple addition]
       
   859         4045, // lockd
       
   860         6000, // X11
       
   861         6665, // Alternate IRC [Apple addition]
       
   862         6666, // Alternate IRC [Apple addition]
       
   863         6667, // Standard IRC [Apple addition]
       
   864         6668, // Alternate IRC [Apple addition]
       
   865         6669, // Alternate IRC [Apple addition]
       
   866         invalidPortNumber, // Used to block all invalid port numbers
       
   867     };
       
   868     const unsigned short* const blockedPortListEnd = blockedPortList + sizeof(blockedPortList) / sizeof(blockedPortList[0]);
       
   869 
       
   870 #ifndef NDEBUG
       
   871     // The port list must be sorted for binary_search to work.
       
   872     static bool checkedPortList = false;
       
   873     if (!checkedPortList) {
       
   874         for (const unsigned short* p = blockedPortList; p != blockedPortListEnd - 1; ++p)
       
   875             ASSERT(*p < *(p + 1));
       
   876         checkedPortList = true;
       
   877     }
       
   878 #endif
       
   879 
       
   880     // If the port is not in the blocked port list, allow it.
       
   881     if (!binary_search(blockedPortList, blockedPortListEnd, port))
       
   882         return true;
       
   883 
       
   884     // Allow ports 21 and 22 for FTP URLs, as Mozilla does.
       
   885     if ((port == 21 || port == 22) && url.protocolIs("ftp"))
       
   886         return true;
       
   887 
       
   888     // Allow any port number in a file URL, since the port number is ignored.
       
   889     if (url.protocolIs("file"))
       
   890         return true;
       
   891 
       
   892     return false;
       
   893 }
       
   894 
       
   895 // We copied the KURL version here on Sept 12, 2008 while doing a WebKit
       
   896 // merge.
       
   897 // 
       
   898 // FIXME Somehow share this with KURL? Like we'd theoretically merge with
       
   899 // decodeURLEscapeSequences below?
       
   900 String mimeTypeFromDataURL(const String& url)
       
   901 {
       
   902     ASSERT(protocolIs(url, "data"));
       
   903     int index = url.find(';');
       
   904     if (index == -1)
       
   905         index = url.find(',');
       
   906     if (index != -1) {
       
   907         int len = index - 5;
       
   908         if (len > 0)
       
   909             return url.substring(5, len);
       
   910         return "text/plain"; // Data URLs with no MIME type are considered text/plain.
       
   911     }
       
   912     return "";
       
   913 }
       
   914 
       
   915 String decodeURLEscapeSequences(const String& str)
       
   916 {
       
   917     return decodeURLEscapeSequences(str, UTF8Encoding());
       
   918 }
       
   919 
       
   920 // In KURL.cpp's implementation, this is called by every component getter.
       
   921 // It will unescape every character, including NULL. This is scary, and may
       
   922 // cause security holes. We never call this function for components, and
       
   923 // just return the ASCII versions instead.
       
   924 //
       
   925 // This function is also used to decode javascript: URLs and as a general
       
   926 // purpose unescaping function.
       
   927 //
       
   928 // FIXME These should be merged to the KURL.cpp implementation.
       
   929 String decodeURLEscapeSequences(const String& str, const TextEncoding& encoding)
       
   930 {
       
   931     // FIXME We can probably use KURL.cpp's version of this function
       
   932     // without modification. However, I'm concerned about
       
   933     // https://bugs.webkit.org/show_bug.cgi?id=20559 so am keeping this old
       
   934     // custom code for now. Using their version will also fix the bug that
       
   935     // we ignore the encoding.
       
   936     //
       
   937     // FIXME b/1350291: This does not get called very often. We just convert
       
   938     // first to 8-bit UTF-8, then unescape, then back to 16-bit. This kind of
       
   939     // sucks, and we don't use the encoding properly, which will make some
       
   940     // obscure anchor navigations fail.
       
   941     CString cstr = str.utf8();
       
   942 
       
   943     const char* input = cstr.data();
       
   944     int inputLength = cstr.length();
       
   945 
       
   946     url_canon::RawCanonOutputT<url_parse::UTF16Char> unescaped;
       
   947 
       
   948     url_util::DecodeURLEscapeSequences(input, inputLength, &unescaped);
       
   949 
       
   950     return String(reinterpret_cast<UChar*>(unescaped.data()),
       
   951                   unescaped.length());
       
   952 }
       
   953 
       
   954 bool KURL::protocolIs(const char* protocol) const
       
   955 {
       
   956     assertProtocolIsGood(protocol);
       
   957 
       
   958     // JavaScript URLs are "valid" and should be executed even if KURL decides they are invalid.
       
   959     // The free function protocolIsJavaScript() should be used instead.
       
   960     // FIXME: Chromium code needs to be fixed for this assert to be enabled. ASSERT(strcmp(protocol, "javascript"));
       
   961 
       
   962     if (m_url.m_parsed.scheme.len <= 0)
       
   963         return !protocol;
       
   964     return lowerCaseEqualsASCII(
       
   965         m_url.utf8String().data() + m_url.m_parsed.scheme.begin,
       
   966         m_url.utf8String().data() + m_url.m_parsed.scheme.end(),
       
   967         protocol);
       
   968 }
       
   969 
       
   970 bool KURL::isLocalFile() const
       
   971 {
       
   972     return protocolIs("file");
       
   973 }
       
   974 
       
   975 // This is called to escape a URL string. It is only used externally when
       
   976 // constructing mailto: links to set the query section. Since our query setter
       
   977 // will automatically do the correct escaping, this function does not have to
       
   978 // do any work.
       
   979 //
       
   980 // There is a possibility that a future called may use this function in other
       
   981 // ways, and may expect to get a valid URL string. The dangerous thing we want
       
   982 // to protect against here is accidentally getting NULLs in a string that is
       
   983 // not supposed to have NULLs. Therefore, we escape NULLs here to prevent this.
       
   984 String encodeWithURLEscapeSequences(const String& notEncodedString)
       
   985 {
       
   986     CString utf8 = UTF8Encoding().encode(
       
   987         reinterpret_cast<const UChar*>(notEncodedString.characters()),
       
   988         notEncodedString.length(),
       
   989         URLEncodedEntitiesForUnencodables);
       
   990     const char* input = utf8.data();
       
   991     int inputLength = utf8.length();
       
   992 
       
   993     Vector<char, 2048> buffer;
       
   994     for (int i = 0; i < inputLength; i++) {
       
   995         if (!input[i])
       
   996             buffer.append("%00", 3);
       
   997         else
       
   998             buffer.append(input[i]);
       
   999     }
       
  1000     return String(buffer.data(), buffer.size());
       
  1001 }
       
  1002 
       
  1003 bool KURL::isHierarchical() const
       
  1004 {
       
  1005     if (!m_url.m_parsed.scheme.is_nonempty())
       
  1006         return false;
       
  1007     return url_util::IsStandard(
       
  1008         &m_url.utf8String().data()[m_url.m_parsed.scheme.begin],
       
  1009         m_url.m_parsed.scheme);
       
  1010 }
       
  1011 
       
  1012 #ifndef NDEBUG
       
  1013 void KURL::print() const
       
  1014 {
       
  1015     printf("%s\n", m_url.utf8String().data());
       
  1016 }
       
  1017 #endif
       
  1018 
       
  1019 void KURL::invalidate()
       
  1020 {
       
  1021     // This is only called from the constructor so resetting the (automatically
       
  1022     // initialized) string and parsed structure would be a waste of time.
       
  1023     m_url.m_isValid = false;
       
  1024     m_url.m_protocolInHTTPFamily = false;
       
  1025 }
       
  1026 
       
  1027 // Equal up to reference fragments, if any.
       
  1028 bool equalIgnoringFragmentIdentifier(const KURL& a, const KURL& b)
       
  1029 {
       
  1030     // Compute the length of each URL without its ref. Note that the reference
       
  1031     // begin (if it exists) points to the character *after* the '#', so we need
       
  1032     // to subtract one.
       
  1033     int aLength = a.m_url.utf8String().length();
       
  1034     if (a.m_url.m_parsed.ref.len >= 0)
       
  1035         aLength = a.m_url.m_parsed.ref.begin - 1;
       
  1036 
       
  1037     int bLength = b.m_url.utf8String().length();
       
  1038     if (b.m_url.m_parsed.ref.len >= 0)
       
  1039         bLength = b.m_url.m_parsed.ref.begin - 1;
       
  1040 
       
  1041     return aLength == bLength
       
  1042         && !strncmp(a.m_url.utf8String().data(), b.m_url.utf8String().data(), aLength);
       
  1043 }
       
  1044 
       
  1045 unsigned KURL::hostStart() const
       
  1046 {
       
  1047     return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::HOST, false);
       
  1048 }
       
  1049 
       
  1050 unsigned KURL::hostEnd() const
       
  1051 {
       
  1052     return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::PORT, true);
       
  1053 }
       
  1054 
       
  1055 unsigned KURL::pathStart() const
       
  1056 {
       
  1057     return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::PATH, false);
       
  1058 }
       
  1059 
       
  1060 unsigned KURL::pathEnd() const
       
  1061 {
       
  1062     return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::QUERY, true);
       
  1063 }
       
  1064 
       
  1065 unsigned KURL::pathAfterLastSlash() const
       
  1066 {
       
  1067     // When there's no path, ask for what would be the beginning of it.
       
  1068     if (!m_url.m_parsed.path.is_valid())
       
  1069         return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::PATH, false);
       
  1070 
       
  1071     url_parse::Component filename;
       
  1072     url_parse::ExtractFileName(m_url.utf8String().data(), m_url.m_parsed.path,
       
  1073                                &filename);
       
  1074     return filename.begin;
       
  1075 }
       
  1076 
       
  1077 const KURL& blankURL()
       
  1078 {
       
  1079     static KURL staticBlankURL(ParsedURLString, "about:blank");
       
  1080     return staticBlankURL;
       
  1081 }
       
  1082 
       
  1083 bool protocolIs(const String& url, const char* protocol)
       
  1084 {
       
  1085     // Do the comparison without making a new string object.
       
  1086     assertProtocolIsGood(protocol);
       
  1087 
       
  1088     // Check the scheme like GURL does.
       
  1089     return url_util::FindAndCompareScheme(url.characters(), url.length(), 
       
  1090         protocol, NULL); 
       
  1091 }
       
  1092 
       
  1093 inline bool KURL::protocolIs(const String& string, const char* protocol)
       
  1094 {
       
  1095     return WebCore::protocolIs(string, protocol);
       
  1096 }
       
  1097 
       
  1098 bool protocolHostAndPortAreEqual(const KURL& a, const KURL& b)
       
  1099 {
       
  1100     if (a.parsed().scheme.end() != b.parsed().scheme.end())
       
  1101         return false;
       
  1102 
       
  1103     int hostStartA = a.hostStart();
       
  1104     int hostLengthA = a.hostEnd() - hostStartA;
       
  1105     int hostStartB = b.hostStart();
       
  1106     int hostLengthB = b.hostEnd() - b.hostStart();
       
  1107     if (hostLengthA != hostLengthB)
       
  1108         return false;
       
  1109 
       
  1110     // Check the scheme
       
  1111     for (int i = 0; i < a.parsed().scheme.end(); ++i)
       
  1112         if (a.string()[i] != b.string()[i])
       
  1113             return false;
       
  1114     
       
  1115     // And the host
       
  1116     for (int i = 0; i < hostLengthA; ++i)
       
  1117         if (a.string()[hostStartA + i] != b.string()[hostStartB + i])
       
  1118             return false;
       
  1119     
       
  1120     if (a.port() != b.port())
       
  1121         return false;
       
  1122 
       
  1123     return true;
       
  1124 }
       
  1125 
       
  1126 } // namespace WebCore
       
  1127 
       
  1128 #endif // USE(GOOGLEURL)