Integrate most of GoogleURL in WTFURL
[WebKit-https.git] / Source / WTF / wtf / url / src / URLCanonICU.cpp
1 /*
2  * Copyright 2011 Google Inc. All rights reserved.
3  * Copyright 2012 Apple 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 // ICU integration functions.
33
34 #include "config.h"
35
36 #if USE(WTFURL)
37
38 #include "URLCanonInternal.h" // for _itoa_s
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unicode/ucnv.h>
42 #include <unicode/ucnv_cb.h>
43 #include <unicode/uidna.h>
44
45 namespace WTF {
46
47 namespace URLCanonicalizer {
48
49 namespace {
50
51 // Called when converting a character that can not be represented, this will
52 // append an escaped version of the numerical character reference for that code
53 // point. It is of the form "&#1234;" and we will escape the non-digits to
54 // "%26%231234%3B". Why? This is what Netscape did back in the olden days.
55 void appendURLEscapedChar(const void* /* context */,
56                           UConverterFromUnicodeArgs* fromArgs,
57                           const UChar* /* code_units */,
58                           int32_t /* length */,
59                           UChar32 codePoint,
60                           UConverterCallbackReason reason,
61                           UErrorCode* err)
62 {
63     if (reason == UCNV_UNASSIGNED) {
64         *err = U_ZERO_ERROR;
65
66         const static int prefixLength = 6;
67         const static char prefix[prefixLength + 1] = "%26%23"; // "&#" percent-escaped
68         ucnv_cbFromUWriteBytes(fromArgs, prefix, prefixLength, 0, err);
69
70         ASSERT(codePoint < 0x110000);
71         char number[8]; // Max Unicode code point is 7 digits.
72         _itoa_s(codePoint, number, 10);
73         int numberLength = static_cast<int>(strlen(number));
74         ucnv_cbFromUWriteBytes(fromArgs, number, numberLength, 0, err);
75
76         const static int postfixLength = 3;
77         const static char postfix[postfixLength + 1] = "%3B"; // ";" percent-escaped
78         ucnv_cbFromUWriteBytes(fromArgs, postfix, postfixLength, 0, err);
79     }
80 }
81
82 // A class for scoping the installation of the invalid character callback.
83 class AppendHandlerInstaller {
84 public:
85     // The owner of this object must ensure that the converter is alive for the
86     // duration of this object's lifetime.
87     AppendHandlerInstaller(UConverter* converter)
88         : m_converter(converter)
89     {
90         UErrorCode err = U_ZERO_ERROR;
91         ucnv_setFromUCallBack(m_converter, appendURLEscapedChar, 0, &m_oldCallback, &m_oldContext, &err);
92     }
93
94     ~AppendHandlerInstaller()
95     {
96         UErrorCode err = U_ZERO_ERROR;
97         ucnv_setFromUCallBack(m_converter, m_oldCallback, m_oldContext, 0, 0, &err);
98     }
99
100 private:
101     UConverter* m_converter;
102
103     UConverterFromUCallback m_oldCallback;
104     const void* m_oldContext;
105 };
106
107 } // namespace
108
109 // Converts the Unicode input representing a hostname to ASCII using IDN rules.
110 // The output must be ASCII, but is represented as wide characters.
111 //
112 // On success, the output will be filled with the ASCII host name and it will
113 // return true. Unlike most other canonicalization functions, this assumes that
114 // the output is empty. The beginning of the host will be at offset 0, and
115 // the length of the output will be set to the length of the new host name.
116 //
117 // On error, this will return false. The output in this case is undefined.
118 bool IDNToASCII(const UChar* src, int sourceLength, URLBuffer<UChar>& output)
119 {
120     ASSERT(!output.length()); // Output buffer is assumed empty.
121     while (true) {
122         // Use ALLOW_UNASSIGNED to be more tolerant of hostnames that violate
123         // the spec (which do exist). This does not present any risk and is a
124         // little more future proof.
125         UErrorCode err = U_ZERO_ERROR;
126         int numConverted = uidna_IDNToASCII(src, sourceLength, output.data(),
127                                              output.capacity(),
128                                              UIDNA_ALLOW_UNASSIGNED, 0, &err);
129         if (err == U_ZERO_ERROR) {
130             output.setLength(numConverted);
131             return true;
132         }
133         if (err != U_BUFFER_OVERFLOW_ERROR)
134             return false; // Unknown error, give up.
135
136         // Not enough room in our buffer, expand.
137         output.resize(output.capacity() * 2);
138     }
139 }
140
141 bool readUTFChar(const char* str, int* begin, int length, unsigned* codePointOut)
142 {
143     int codePoint; // Avoids warning when U8_NEXT writes -1 to it.
144     U8_NEXT(str, *begin, length, codePoint);
145     *codePointOut = static_cast<unsigned>(codePoint);
146
147     // The ICU macro above moves to the next char, we want to point to the last
148     // char consumed.
149     (*begin)--;
150
151     // Validate the decoded value.
152     if (U_IS_UNICODE_CHAR(codePoint))
153         return true;
154     *codePointOut = kUnicodeReplacementCharacter;
155     return false;
156 }
157
158 bool readUTFChar(const UChar* str, int* begin, int length, unsigned* codePoint)
159 {
160     if (U16_IS_SURROGATE(str[*begin])) {
161         if (!U16_IS_SURROGATE_LEAD(str[*begin]) || *begin + 1 >= length || !U16_IS_TRAIL(str[*begin + 1])) {
162             // Invalid surrogate pair.
163             *codePoint = kUnicodeReplacementCharacter;
164             return false;
165         }
166
167         // Valid surrogate pair.
168         *codePoint = U16_GET_SUPPLEMENTARY(str[*begin], str[*begin + 1]);
169         (*begin)++;
170     } else {
171         // Not a surrogate, just one 16-bit word.
172         *codePoint = str[*begin];
173     }
174
175     if (U_IS_UNICODE_CHAR(*codePoint))
176         return true;
177
178     // Invalid code point.
179     *codePoint = kUnicodeReplacementCharacter;
180     return false;
181 }
182
183 } // namespace URLCanonicalizer
184
185 } // namespace WTF
186
187 #endif // USE(WTFURL)