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