Replace CaseFoldingHash with ASCIICaseInsensitiveHash
[WebKit-https.git] / Source / WebCore / platform / URL.cpp
1 /*
2  * Copyright (C) 2004, 2007, 2008, 2011, 2012, 2013, 2015 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Research In Motion Limited. 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
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "URL.h"
29
30 #include "DecodeEscapeSequences.h"
31 #include "MIMETypeRegistry.h"
32 #include "TextEncoding.h"
33 #include "UUID.h"
34 #include <stdio.h>
35 #include <unicode/uidna.h>
36 #include <wtf/HashMap.h>
37 #include <wtf/HexNumber.h>
38 #include <wtf/NeverDestroyed.h>
39 #include <wtf/StdLibExtras.h>
40 #include <wtf/text/CString.h>
41 #include <wtf/text/StringBuilder.h>
42 #include <wtf/text/StringHash.h>
43
44 // FIXME: This file makes too much use of the + operator on String.
45 // We either have to optimize that operator so it doesn't involve
46 // so many allocations, or change this to use StringBuffer instead.
47
48 using namespace WTF;
49
50 namespace WebCore {
51
52 typedef Vector<char, 512> CharBuffer;
53 typedef Vector<UChar, 512> UCharBuffer;
54
55 static const unsigned maximumValidPortNumber = 0xFFFE;
56 static const unsigned invalidPortNumber = 0xFFFF;
57
58 static inline bool isLetterMatchIgnoringCase(UChar character, char lowercaseLetter)
59 {
60     ASSERT(isASCIILower(lowercaseLetter));
61     return (character | 0x20) == lowercaseLetter;
62 }
63
64 static const char wsScheme[] = {'w', 's'};
65 static const char ftpScheme[] = {'f', 't', 'p'};
66 static const char ftpPort[] = {'2', '1'};
67 static const char wssScheme[] = {'w', 's', 's'};
68 static const char fileScheme[] = {'f', 'i', 'l', 'e'};
69 static const char httpScheme[] = {'h', 't', 't', 'p'};
70 static const char httpPort[] = {'8', '0'};
71 static const char httpsScheme[] = {'h', 't', 't', 'p', 's'};
72 static const char httpsPort[] = {'4', '4', '3'};
73 static const char gopherScheme[] = {'g', 'o', 'p', 'h', 'e', 'r'};
74 static const char gopherPort[] = {'7', '0'};
75
76 static inline bool isLetterMatchIgnoringCase(char character, char lowercaseLetter)
77 {
78     ASSERT(isASCIILower(lowercaseLetter));
79     return (character | 0x20) == lowercaseLetter;
80 }
81
82 enum URLCharacterClasses {
83     // alpha 
84     SchemeFirstChar = 1 << 0,
85
86     // ( alpha | digit | "+" | "-" | "." )
87     SchemeChar = 1 << 1,
88
89     // mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
90     // unreserved  = alphanum | mark
91     // ( unreserved | escaped | ";" | ":" | "&" | "=" | "+" | "$" | "," )
92     UserInfoChar = 1 << 2,
93
94     // alnum | "." | "-" | "%"
95     // The above is what the specification says, but we are lenient to
96     // match existing practice and also allow:
97     // "_"
98     HostnameChar = 1 << 3,
99
100     // hexdigit | ":" | "%"
101     IPv6Char = 1 << 4,
102
103     // "#" | "?" | "/" | nul
104     PathSegmentEndChar = 1 << 5,
105
106     // not allowed in path
107     BadChar = 1 << 6
108 };
109
110 static const unsigned char characterClassTable[256] = {
111     /* 0 nul */ PathSegmentEndChar,    /* 1 soh */ BadChar,
112     /* 2 stx */ BadChar,    /* 3 etx */ BadChar,
113     /* 4 eot */ BadChar,    /* 5 enq */ BadChar,    /* 6 ack */ BadChar,    /* 7 bel */ BadChar,
114     /* 8 bs */ BadChar,     /* 9 ht */ BadChar,     /* 10 nl */ BadChar,    /* 11 vt */ BadChar,
115     /* 12 np */ BadChar,    /* 13 cr */ BadChar,    /* 14 so */ BadChar,    /* 15 si */ BadChar,
116     /* 16 dle */ BadChar,   /* 17 dc1 */ BadChar,   /* 18 dc2 */ BadChar,   /* 19 dc3 */ BadChar,
117     /* 20 dc4 */ BadChar,   /* 21 nak */ BadChar,   /* 22 syn */ BadChar,   /* 23 etb */ BadChar,
118     /* 24 can */ BadChar,   /* 25 em */ BadChar,    /* 26 sub */ BadChar,   /* 27 esc */ BadChar,
119     /* 28 fs */ BadChar,    /* 29 gs */ BadChar,    /* 30 rs */ BadChar,    /* 31 us */ BadChar,
120     /* 32 sp */ BadChar,    /* 33  ! */ UserInfoChar,
121     /* 34  " */ BadChar,    /* 35  # */ PathSegmentEndChar | BadChar,
122     /* 36  $ */ UserInfoChar,    /* 37  % */ UserInfoChar | HostnameChar | IPv6Char | BadChar,
123     /* 38  & */ UserInfoChar,    /* 39  ' */ UserInfoChar,
124     /* 40  ( */ UserInfoChar,    /* 41  ) */ UserInfoChar,
125     /* 42  * */ UserInfoChar,    /* 43  + */ SchemeChar | UserInfoChar,
126     /* 44  , */ UserInfoChar,
127     /* 45  - */ SchemeChar | UserInfoChar | HostnameChar,
128     /* 46  . */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
129     /* 47  / */ PathSegmentEndChar,
130     /* 48  0 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
131     /* 49  1 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,    
132     /* 50  2 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
133     /* 51  3 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
134     /* 52  4 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
135     /* 53  5 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
136     /* 54  6 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
137     /* 55  7 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
138     /* 56  8 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
139     /* 57  9 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
140     /* 58  : */ UserInfoChar | IPv6Char,    /* 59  ; */ UserInfoChar,
141     /* 60  < */ BadChar,    /* 61  = */ UserInfoChar,
142     /* 62  > */ BadChar,    /* 63  ? */ PathSegmentEndChar | BadChar,
143     /* 64  @ */ 0,
144     /* 65  A */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,    
145     /* 66  B */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
146     /* 67  C */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
147     /* 68  D */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
148     /* 69  E */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
149     /* 70  F */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
150     /* 71  G */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
151     /* 72  H */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
152     /* 73  I */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
153     /* 74  J */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
154     /* 75  K */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
155     /* 76  L */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
156     /* 77  M */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
157     /* 78  N */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
158     /* 79  O */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
159     /* 80  P */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
160     /* 81  Q */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
161     /* 82  R */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
162     /* 83  S */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
163     /* 84  T */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
164     /* 85  U */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
165     /* 86  V */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
166     /* 87  W */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
167     /* 88  X */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
168     /* 89  Y */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
169     /* 90  Z */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
170     /* 91  [ */ 0,
171     /* 92  \ */ 0,    /* 93  ] */ 0,
172     /* 94  ^ */ 0,
173     /* 95  _ */ UserInfoChar | HostnameChar,
174     /* 96  ` */ 0,
175     /* 97  a */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
176     /* 98  b */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
177     /* 99  c */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
178     /* 100  d */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
179     /* 101  e */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
180     /* 102  f */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
181     /* 103  g */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
182     /* 104  h */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
183     /* 105  i */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
184     /* 106  j */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
185     /* 107  k */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
186     /* 108  l */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
187     /* 109  m */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
188     /* 110  n */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
189     /* 111  o */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
190     /* 112  p */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
191     /* 113  q */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
192     /* 114  r */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
193     /* 115  s */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
194     /* 116  t */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
195     /* 117  u */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
196     /* 118  v */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
197     /* 119  w */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
198     /* 120  x */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
199     /* 121  y */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
200     /* 122  z */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
201     /* 123  { */ 0,
202     /* 124  | */ 0,   /* 125  } */ 0,   /* 126  ~ */ UserInfoChar,   /* 127 del */ BadChar,
203     /* 128 */ BadChar, /* 129 */ BadChar, /* 130 */ BadChar, /* 131 */ BadChar,
204     /* 132 */ BadChar, /* 133 */ BadChar, /* 134 */ BadChar, /* 135 */ BadChar,
205     /* 136 */ BadChar, /* 137 */ BadChar, /* 138 */ BadChar, /* 139 */ BadChar,
206     /* 140 */ BadChar, /* 141 */ BadChar, /* 142 */ BadChar, /* 143 */ BadChar,
207     /* 144 */ BadChar, /* 145 */ BadChar, /* 146 */ BadChar, /* 147 */ BadChar,
208     /* 148 */ BadChar, /* 149 */ BadChar, /* 150 */ BadChar, /* 151 */ BadChar,
209     /* 152 */ BadChar, /* 153 */ BadChar, /* 154 */ BadChar, /* 155 */ BadChar,
210     /* 156 */ BadChar, /* 157 */ BadChar, /* 158 */ BadChar, /* 159 */ BadChar,
211     /* 160 */ BadChar, /* 161 */ BadChar, /* 162 */ BadChar, /* 163 */ BadChar,
212     /* 164 */ BadChar, /* 165 */ BadChar, /* 166 */ BadChar, /* 167 */ BadChar,
213     /* 168 */ BadChar, /* 169 */ BadChar, /* 170 */ BadChar, /* 171 */ BadChar,
214     /* 172 */ BadChar, /* 173 */ BadChar, /* 174 */ BadChar, /* 175 */ BadChar,
215     /* 176 */ BadChar, /* 177 */ BadChar, /* 178 */ BadChar, /* 179 */ BadChar,
216     /* 180 */ BadChar, /* 181 */ BadChar, /* 182 */ BadChar, /* 183 */ BadChar,
217     /* 184 */ BadChar, /* 185 */ BadChar, /* 186 */ BadChar, /* 187 */ BadChar,
218     /* 188 */ BadChar, /* 189 */ BadChar, /* 190 */ BadChar, /* 191 */ BadChar,
219     /* 192 */ BadChar, /* 193 */ BadChar, /* 194 */ BadChar, /* 195 */ BadChar,
220     /* 196 */ BadChar, /* 197 */ BadChar, /* 198 */ BadChar, /* 199 */ BadChar,
221     /* 200 */ BadChar, /* 201 */ BadChar, /* 202 */ BadChar, /* 203 */ BadChar,
222     /* 204 */ BadChar, /* 205 */ BadChar, /* 206 */ BadChar, /* 207 */ BadChar,
223     /* 208 */ BadChar, /* 209 */ BadChar, /* 210 */ BadChar, /* 211 */ BadChar,
224     /* 212 */ BadChar, /* 213 */ BadChar, /* 214 */ BadChar, /* 215 */ BadChar,
225     /* 216 */ BadChar, /* 217 */ BadChar, /* 218 */ BadChar, /* 219 */ BadChar,
226     /* 220 */ BadChar, /* 221 */ BadChar, /* 222 */ BadChar, /* 223 */ BadChar,
227     /* 224 */ BadChar, /* 225 */ BadChar, /* 226 */ BadChar, /* 227 */ BadChar,
228     /* 228 */ BadChar, /* 229 */ BadChar, /* 230 */ BadChar, /* 231 */ BadChar,
229     /* 232 */ BadChar, /* 233 */ BadChar, /* 234 */ BadChar, /* 235 */ BadChar,
230     /* 236 */ BadChar, /* 237 */ BadChar, /* 238 */ BadChar, /* 239 */ BadChar,
231     /* 240 */ BadChar, /* 241 */ BadChar, /* 242 */ BadChar, /* 243 */ BadChar,
232     /* 244 */ BadChar, /* 245 */ BadChar, /* 246 */ BadChar, /* 247 */ BadChar,
233     /* 248 */ BadChar, /* 249 */ BadChar, /* 250 */ BadChar, /* 251 */ BadChar,
234     /* 252 */ BadChar, /* 253 */ BadChar, /* 254 */ BadChar, /* 255 */ BadChar
235 };
236
237 enum PercentEncodeCharacterClass {
238     // Class names match the URL Standard; each class is a superset of the previous one.
239     PercentEncodeSimple = 255,
240     PercentEncodeDefault = 127,
241     PercentEncodePassword = 63,
242     PercentEncodeUsername = 31,
243 };
244
245 static const unsigned char percentEncodeClassTable[256] = {
246     /* 0 nul */ PercentEncodeSimple,    /* 1 soh */ PercentEncodeSimple,    /* 2 stx */ PercentEncodeSimple,    /* 3 etx */ PercentEncodeSimple,
247     /* 4 eot */ PercentEncodeSimple,    /* 5 enq */ PercentEncodeSimple,    /* 6 ack */ PercentEncodeSimple,    /* 7 bel */ PercentEncodeSimple,
248     /* 8 bs */ PercentEncodeSimple,     /* 9 ht */ PercentEncodeSimple,     /* 10 nl */ PercentEncodeSimple,    /* 11 vt */ PercentEncodeSimple,
249     /* 12 np */ PercentEncodeSimple,    /* 13 cr */ PercentEncodeSimple,    /* 14 so */ PercentEncodeSimple,    /* 15 si */ PercentEncodeSimple,
250     /* 16 dle */ PercentEncodeSimple,   /* 17 dc1 */ PercentEncodeSimple,   /* 18 dc2 */ PercentEncodeSimple,   /* 19 dc3 */ PercentEncodeSimple,
251     /* 20 dc4 */ PercentEncodeSimple,   /* 21 nak */ PercentEncodeSimple,   /* 22 syn */ PercentEncodeSimple,   /* 23 etb */ PercentEncodeSimple,
252     /* 24 can */ PercentEncodeSimple,   /* 25 em */ PercentEncodeSimple,    /* 26 sub */ PercentEncodeSimple,   /* 27 esc */ PercentEncodeSimple,
253     /* 28 fs */ PercentEncodeSimple,    /* 29 gs */ PercentEncodeSimple,    /* 30 rs */ PercentEncodeSimple,    /* 31 us */ PercentEncodeSimple,
254     /* 32 sp */ PercentEncodeDefault,
255     /* 33  ! */ 0,
256     /* 34  " */ PercentEncodeDefault,
257     /* 35  # */ PercentEncodeDefault,
258     /* 36  $ */ 0,
259     /* 37  % */ 0,
260     /* 38  & */ 0,
261     /* 39  ' */ 0,
262     /* 40  ( */ 0,
263     /* 41  ) */ 0,
264     /* 42  * */ 0,
265     /* 43  + */ 0,
266     /* 44  , */ 0,
267     /* 45  - */ 0,
268     /* 46  . */ 0,
269     /* 47  / */ PercentEncodePassword,
270     /* 48  0 */ 0,    /* 49  1 */ 0,    /* 50  2 */ 0,    /* 51  3 */ 0,
271     /* 52  4 */ 0,    /* 53  5 */ 0,    /* 54  6 */ 0,    /* 55  7 */ 0,
272     /* 56  8 */ 0,    /* 57  9 */ 0,
273     /* 58  : */ PercentEncodeUsername,
274     /* 59  ; */ 0,
275     /* 60  < */ PercentEncodeDefault,
276     /* 61  = */ 0,
277     /* 62  > */ PercentEncodeDefault,
278     /* 63  ? */ PercentEncodeDefault,
279     /* 64  @ */ PercentEncodePassword,
280     /* 65  A */ 0,    /* 66  B */ 0,    /* 67  C */ 0,    /* 68  D */ 0,
281     /* 69  E */ 0,    /* 70  F */ 0,    /* 71  G */ 0,    /* 72  H */ 0,
282     /* 73  I */ 0,    /* 74  J */ 0,    /* 75  K */ 0,    /* 76  L */ 0,
283     /* 77  M */ 0,    /* 78  N */ 0,    /* 79  O */ 0,    /* 80  P */ 0,
284     /* 81  Q */ 0,    /* 82  R */ 0,    /* 83  S */ 0,    /* 84  T */ 0,
285     /* 85  U */ 0,    /* 86  V */ 0,    /* 87  W */ 0,    /* 88  X */ 0,
286     /* 89  Y */ 0,    /* 90  Z */ 0,
287     /* 91  [ */ 0,
288     /* 92  \ */ PercentEncodePassword,
289     /* 93  ] */ 0,
290     /* 94  ^ */ 0,
291     /* 95  _ */ 0,
292     /* 96  ` */ PercentEncodeDefault,
293     /* 97  a */ 0,    /* 98  b */ 0,    /* 99  c */ 0,    /* 100  d */ 0,
294     /* 101  e */ 0,    /* 102  f */ 0,    /* 103  g */ 0,    /* 104  h */ 0,
295     /* 105  i */ 0,    /* 106  j */ 0,    /* 107  k */ 0,    /* 108  l */ 0,
296     /* 109  m */ 0,    /* 110  n */ 0,    /* 111  o */ 0,    /* 112  p */ 0,
297     /* 113  q */ 0,    /* 114  r */ 0,    /* 115  s */ 0,    /* 116  t */ 0,
298     /* 117  u */ 0,    /* 118  v */ 0,    /* 119  w */ 0,    /* 120  x */ 0,
299     /* 121  y */ 0,    /* 122  z */ 0,
300     /* 123  { */ 0,
301     /* 124  | */ 0,
302     /* 125  } */ 0,
303     /* 126  ~ */ 0,
304     /* 127 del */ PercentEncodeSimple,
305     /* 128 */ PercentEncodeSimple, /* 129 */ PercentEncodeSimple, /* 130 */ PercentEncodeSimple, /* 131 */ PercentEncodeSimple,
306     /* 132 */ PercentEncodeSimple, /* 133 */ PercentEncodeSimple, /* 134 */ PercentEncodeSimple, /* 135 */ PercentEncodeSimple,
307     /* 136 */ PercentEncodeSimple, /* 137 */ PercentEncodeSimple, /* 138 */ PercentEncodeSimple, /* 139 */ PercentEncodeSimple,
308     /* 140 */ PercentEncodeSimple, /* 141 */ PercentEncodeSimple, /* 142 */ PercentEncodeSimple, /* 143 */ PercentEncodeSimple,
309     /* 144 */ PercentEncodeSimple, /* 145 */ PercentEncodeSimple, /* 146 */ PercentEncodeSimple, /* 147 */ PercentEncodeSimple,
310     /* 148 */ PercentEncodeSimple, /* 149 */ PercentEncodeSimple, /* 150 */ PercentEncodeSimple, /* 151 */ PercentEncodeSimple,
311     /* 152 */ PercentEncodeSimple, /* 153 */ PercentEncodeSimple, /* 154 */ PercentEncodeSimple, /* 155 */ PercentEncodeSimple,
312     /* 156 */ PercentEncodeSimple, /* 157 */ PercentEncodeSimple, /* 158 */ PercentEncodeSimple, /* 159 */ PercentEncodeSimple,
313     /* 160 */ PercentEncodeSimple, /* 161 */ PercentEncodeSimple, /* 162 */ PercentEncodeSimple, /* 163 */ PercentEncodeSimple,
314     /* 164 */ PercentEncodeSimple, /* 165 */ PercentEncodeSimple, /* 166 */ PercentEncodeSimple, /* 167 */ PercentEncodeSimple,
315     /* 168 */ PercentEncodeSimple, /* 169 */ PercentEncodeSimple, /* 170 */ PercentEncodeSimple, /* 171 */ PercentEncodeSimple,
316     /* 172 */ PercentEncodeSimple, /* 173 */ PercentEncodeSimple, /* 174 */ PercentEncodeSimple, /* 175 */ PercentEncodeSimple,
317     /* 176 */ PercentEncodeSimple, /* 177 */ PercentEncodeSimple, /* 178 */ PercentEncodeSimple, /* 179 */ PercentEncodeSimple,
318     /* 180 */ PercentEncodeSimple, /* 181 */ PercentEncodeSimple, /* 182 */ PercentEncodeSimple, /* 183 */ PercentEncodeSimple,
319     /* 184 */ PercentEncodeSimple, /* 185 */ PercentEncodeSimple, /* 186 */ PercentEncodeSimple, /* 187 */ PercentEncodeSimple,
320     /* 188 */ PercentEncodeSimple, /* 189 */ PercentEncodeSimple, /* 190 */ PercentEncodeSimple, /* 191 */ PercentEncodeSimple,
321     /* 192 */ PercentEncodeSimple, /* 193 */ PercentEncodeSimple, /* 194 */ PercentEncodeSimple, /* 195 */ PercentEncodeSimple,
322     /* 196 */ PercentEncodeSimple, /* 197 */ PercentEncodeSimple, /* 198 */ PercentEncodeSimple, /* 199 */ PercentEncodeSimple,
323     /* 200 */ PercentEncodeSimple, /* 201 */ PercentEncodeSimple, /* 202 */ PercentEncodeSimple, /* 203 */ PercentEncodeSimple,
324     /* 204 */ PercentEncodeSimple, /* 205 */ PercentEncodeSimple, /* 206 */ PercentEncodeSimple, /* 207 */ PercentEncodeSimple,
325     /* 208 */ PercentEncodeSimple, /* 209 */ PercentEncodeSimple, /* 210 */ PercentEncodeSimple, /* 211 */ PercentEncodeSimple,
326     /* 212 */ PercentEncodeSimple, /* 213 */ PercentEncodeSimple, /* 214 */ PercentEncodeSimple, /* 215 */ PercentEncodeSimple,
327     /* 216 */ PercentEncodeSimple, /* 217 */ PercentEncodeSimple, /* 218 */ PercentEncodeSimple, /* 219 */ PercentEncodeSimple,
328     /* 220 */ PercentEncodeSimple, /* 221 */ PercentEncodeSimple, /* 222 */ PercentEncodeSimple, /* 223 */ PercentEncodeSimple,
329     /* 224 */ PercentEncodeSimple, /* 225 */ PercentEncodeSimple, /* 226 */ PercentEncodeSimple, /* 227 */ PercentEncodeSimple,
330     /* 228 */ PercentEncodeSimple, /* 229 */ PercentEncodeSimple, /* 230 */ PercentEncodeSimple, /* 231 */ PercentEncodeSimple,
331     /* 232 */ PercentEncodeSimple, /* 233 */ PercentEncodeSimple, /* 234 */ PercentEncodeSimple, /* 235 */ PercentEncodeSimple,
332     /* 236 */ PercentEncodeSimple, /* 237 */ PercentEncodeSimple, /* 238 */ PercentEncodeSimple, /* 239 */ PercentEncodeSimple,
333     /* 240 */ PercentEncodeSimple, /* 241 */ PercentEncodeSimple, /* 242 */ PercentEncodeSimple, /* 243 */ PercentEncodeSimple,
334     /* 244 */ PercentEncodeSimple, /* 245 */ PercentEncodeSimple, /* 246 */ PercentEncodeSimple, /* 247 */ PercentEncodeSimple,
335     /* 248 */ PercentEncodeSimple, /* 249 */ PercentEncodeSimple, /* 250 */ PercentEncodeSimple, /* 251 */ PercentEncodeSimple,
336     /* 252 */ PercentEncodeSimple, /* 253 */ PercentEncodeSimple, /* 254 */ PercentEncodeSimple, /* 255 */ PercentEncodeSimple
337 };
338
339 static int copyPathRemovingDots(char* dst, const char* src, int srcStart, int srcEnd);
340 static void encodeRelativeString(const String& rel, const TextEncoding&, CharBuffer& ouput);
341 static String substituteBackslashes(const String&);
342
343 static inline bool isSchemeFirstChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & SchemeFirstChar; }
344 static inline bool isSchemeFirstChar(UChar c) { return c <= 0xff && (characterClassTable[c] & SchemeFirstChar); }
345 static inline bool isSchemeChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & SchemeChar; }
346 static inline bool isSchemeChar(UChar c) { return c <= 0xff && (characterClassTable[c] & SchemeChar); }
347 static inline bool isUserInfoChar(unsigned char c) { return characterClassTable[c] & UserInfoChar; }
348 static inline bool isHostnameChar(unsigned char c) { return characterClassTable[c] & HostnameChar; }
349 static inline bool isIPv6Char(unsigned char c) { return characterClassTable[c] & IPv6Char; }
350 static inline bool isPathSegmentEndChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & PathSegmentEndChar; }
351 static inline bool isPathSegmentEndChar(UChar c) { return c <= 0xff && (characterClassTable[c] & PathSegmentEndChar); }
352 static inline bool isBadChar(unsigned char c) { return characterClassTable[c] & BadChar; }
353
354 static inline bool isSchemeCharacterMatchIgnoringCase(char character, char schemeCharacter)
355 {
356     ASSERT(isSchemeChar(character));
357     ASSERT(schemeCharacter & 0x20);
358     ASSERT(isASCIILower(schemeCharacter) || (!isASCIIUpper(schemeCharacter) && isSchemeChar(schemeCharacter)));
359     return (character | 0x20) == schemeCharacter;
360 }
361
362 String encodeWithURLEscapeSequences(const String& notEncodedString, PercentEncodeCharacterClass whatToEncode);
363
364 // Copies the source to the destination, assuming all the source characters are
365 // ASCII. The destination buffer must be large enough. Null characters are allowed
366 // in the source string, and no attempt is made to null-terminate the result.
367 static void copyASCII(const String& string, char* dest)
368 {
369     if (string.isEmpty())
370         return;
371
372     if (string.is8Bit())
373         memcpy(dest, string.characters8(), string.length());
374     else {
375         const UChar* src = string.characters16();
376         size_t length = string.length();
377         for (size_t i = 0; i < length; i++)
378             dest[i] = static_cast<char>(src[i]);
379     }
380 }
381
382 static void appendASCII(const String& base, const char* rel, size_t len, CharBuffer& buffer)
383 {
384     buffer.resize(base.length() + len + 1);
385     copyASCII(base, buffer.data());
386     memcpy(buffer.data() + base.length(), rel, len);
387     buffer[buffer.size() - 1] = '\0';
388 }
389
390 // FIXME: Move to WTFString.h eventually.
391 // Returns the index of the first index in string |s| of any of the characters
392 // in |toFind|. |toFind| should be a null-terminated string, all characters up
393 // to the null will be searched. Returns int if not found.
394 static int findFirstOf(StringView string, unsigned startPosition, const char* target)
395 {
396     unsigned length = string.length();
397     for (unsigned i = startPosition; i < length; ++i) {
398         for (unsigned j = 0; target[j]; ++j) {
399             if (string[i] == target[j])
400                 return i;
401         }
402     }
403     return -1;
404 }
405
406 static inline void checkEncodedString(const String& url)
407 {
408     ASSERT_UNUSED(url, url.containsOnlyASCII());
409     ASSERT_UNUSED(url, url.isEmpty() || isSchemeFirstChar(url[0]));
410 }
411
412 inline bool URL::protocolIs(const String& string, const char* protocol)
413 {
414     return WebCore::protocolIs(string, protocol);
415 }
416
417 void URL::invalidate()
418 {
419     m_isValid = false;
420     m_protocolIsInHTTPFamily = false;
421     m_schemeEnd = 0;
422     m_userStart = 0;
423     m_userEnd = 0;
424     m_passwordEnd = 0;
425     m_hostEnd = 0;
426     m_portEnd = 0;
427     m_pathEnd = 0;
428     m_pathAfterLastSlash = 0;
429     m_queryEnd = 0;
430     m_fragmentEnd = 0;
431 }
432
433 URL::URL(ParsedURLStringTag, const String& url)
434 {
435     parse(url);
436 #if OS(WINDOWS)
437     // FIXME(148598): Work around Windows local file handling bug in CFNetwork
438     ASSERT(isLocalFile() || url == m_string);
439 #else
440     ASSERT(url == m_string);
441 #endif
442 }
443
444 URL::URL(const URL& base, const String& relative)
445 {
446     init(base, relative, UTF8Encoding());
447 }
448
449 URL::URL(const URL& base, const String& relative, const TextEncoding& encoding)
450 {
451     // For UTF-{7,16,32}, we want to use UTF-8 for the query part as 
452     // we do when submitting a form. A form with GET method
453     // has its contents added to a URL as query params and it makes sense
454     // to be consistent.
455     init(base, relative, encoding.encodingForFormSubmission());
456 }
457
458 static bool shouldTrimFromURL(unsigned char c)
459 {
460     // Browsers ignore leading/trailing whitespace and control
461     // characters from URLs.  Note that c is an *unsigned* char here
462     // so this comparison should only catch control characters.
463     return c <= ' ';
464 }
465
466 void URL::init(const URL& base, const String& relative, const TextEncoding& encoding)
467 {
468     // Allow resolutions with a null or empty base URL, but not with any other invalid one.
469     // FIXME: Is this a good rule?
470     if (!base.m_isValid && !base.isEmpty()) {
471         m_string = relative;
472         invalidate();
473         return;
474     }
475
476     // For compatibility with Win IE, treat backslashes as if they were slashes,
477     // as long as we're not dealing with javascript: or data: URLs.
478     String rel = relative;
479     if (rel.contains('\\') && !(protocolIsJavaScript(rel) || protocolIs(rel, "data")))
480         rel = substituteBackslashes(rel);
481
482     bool allASCII = rel.containsOnlyASCII();
483     CharBuffer strBuffer;
484     char* str;
485     size_t len;
486     if (allASCII) {
487         len = rel.length();
488         strBuffer.resize(len + 1);
489         copyASCII(rel, strBuffer.data());
490         strBuffer[len] = 0;
491         str = strBuffer.data();
492     } else {
493         encodeRelativeString(rel, encoding, strBuffer);
494         str = strBuffer.data();
495         len = strlen(str);
496     }
497
498     // Get rid of leading whitespace and control characters.
499     while (len && shouldTrimFromURL(*str)) {
500         str++;
501         --len;
502     }
503
504     // Get rid of trailing whitespace and control characters.
505     while (len && shouldTrimFromURL(str[len - 1]))
506         str[--len] = '\0';
507
508     // According to the RFC, the reference should be interpreted as an
509     // absolute URI if possible, using the "leftmost, longest"
510     // algorithm. If the URI reference is absolute it will have a
511     // scheme, meaning that it will have a colon before the first
512     // non-scheme element.
513     bool absolute = false;
514     char* p = str;
515     if (isSchemeFirstChar(*p)) {
516         ++p;
517         while (isSchemeChar(*p)) {
518             ++p;
519         }
520         if (*p == ':') {
521             if (p[1] != '/' && equalIgnoringASCIICase(base.protocol(), StringView(reinterpret_cast<LChar*>(str), p - str)) && base.isHierarchical())
522                 str = p + 1;
523             else
524                 absolute = true;
525         }
526     }
527
528     CharBuffer parseBuffer;
529
530     if (absolute) {
531         parse(str, &relative);
532     } else {
533         // If the base is empty or opaque (e.g. data: or javascript:), then the URL is invalid
534         // unless the relative URL is a single fragment.
535         if (!base.isHierarchical()) {
536             if (str[0] == '#') {
537                 appendASCII(base.m_string.left(base.m_queryEnd), str, len, parseBuffer);
538                 parse(parseBuffer.data(), &relative);
539             } else {
540                 m_string = relative;
541                 invalidate();
542             }
543             return;
544         }
545
546         switch (str[0]) {
547         case '\0':
548             // The reference is empty, so this is a reference to the same document with any fragment identifier removed.
549             *this = base;
550             removeFragmentIdentifier();
551             break;
552         case '#': {
553             // must be fragment-only reference
554             appendASCII(base.m_string.left(base.m_queryEnd), str, len, parseBuffer);
555             parse(parseBuffer.data(), &relative);
556             break;
557         }
558         case '?': {
559             // query-only reference, special case needed for non-URL results
560             appendASCII(base.m_string.left(base.m_pathEnd), str, len, parseBuffer);
561             parse(parseBuffer.data(), &relative);
562             break;
563         }
564         case '/':
565             // must be net-path or absolute-path reference
566             if (str[1] == '/') {
567                 // net-path
568                 appendASCII(base.m_string.left(base.m_schemeEnd + 1), str, len, parseBuffer);
569                 parse(parseBuffer.data(), &relative);
570             } else {
571                 // abs-path
572                 appendASCII(base.m_string.left(base.m_portEnd), str, len, parseBuffer);
573                 parse(parseBuffer.data(), &relative);
574             }
575             break;
576         default:
577             {
578                 // must be relative-path reference
579
580                 // Base part plus relative part plus one possible slash added in between plus terminating \0 byte.
581                 const size_t bufferSize = base.m_pathEnd + 1 + len + 1;
582                 parseBuffer.resize(bufferSize);
583
584                 char* bufferPos = parseBuffer.data();
585                 char* bufferStart = bufferPos;
586
587                 // first copy everything before the path from the base
588                 CharBuffer baseStringBuffer(base.m_string.length());
589                 copyASCII(base.m_string, baseStringBuffer.data());
590                 const char* baseString = baseStringBuffer.data();
591                 const char* baseStringStart = baseString;
592                 const char* pathStart = baseStringStart + base.m_portEnd;
593                 while (baseStringStart < pathStart)
594                     *bufferPos++ = *baseStringStart++;
595                 char* bufferPathStart = bufferPos;
596
597                 // now copy the base path
598                 const char* baseStringEnd = baseString + base.m_pathEnd;
599
600                 // go back to the last slash
601                 while (baseStringEnd > baseStringStart && baseStringEnd[-1] != '/')
602                     baseStringEnd--;
603
604                 if (baseStringEnd == baseStringStart) {
605                     // no path in base, add a path separator if necessary
606                     if (base.m_schemeEnd + 1 != base.m_pathEnd && *str && *str != '?' && *str != '#')
607                         *bufferPos++ = '/';
608                 } else {
609                     bufferPos += copyPathRemovingDots(bufferPos, baseStringStart, 0, baseStringEnd - baseStringStart);
610                 }
611
612                 const char* relStringStart = str;
613                 const char* relStringPos = relStringStart;
614
615                 while (*relStringPos && *relStringPos != '?' && *relStringPos != '#') {
616                     if (relStringPos[0] == '.' && bufferPos[-1] == '/') {
617                         if (isPathSegmentEndChar(relStringPos[1])) {
618                             // skip over "." segment
619                             relStringPos += 1;
620                             if (relStringPos[0] == '/')
621                                 relStringPos++;
622                             continue;
623                         } else if (relStringPos[1] == '.' && isPathSegmentEndChar(relStringPos[2])) {
624                             // skip over ".." segment and rewind the last segment
625                             // the RFC leaves it up to the app to decide what to do with excess
626                             // ".." segments - we choose to drop them since some web content
627                             // relies on this.
628                             relStringPos += 2;
629                             if (relStringPos[0] == '/')
630                                 relStringPos++;
631                             if (bufferPos > bufferPathStart + 1)
632                                 bufferPos--;
633                             while (bufferPos > bufferPathStart + 1  && bufferPos[-1] != '/')
634                                 bufferPos--;
635                             continue;
636                         }
637                     }
638
639                     *bufferPos = *relStringPos;
640                     relStringPos++;
641                     bufferPos++;
642                 }
643
644                 // all done with the path work, now copy any remainder
645                 // of the relative reference; this will also add a null terminator
646                 strncpy(bufferPos, relStringPos, bufferSize - (bufferPos - bufferStart));
647
648                 parse(parseBuffer.data(), &relative);
649
650                 ASSERT(strlen(parseBuffer.data()) + 1 <= parseBuffer.size());
651                 break;
652             }
653         }
654     }
655 }
656
657 URL URL::isolatedCopy() const
658 {
659     URL result = *this;
660     result.m_string = result.m_string.isolatedCopy();
661     return result;
662 }
663
664 String URL::lastPathComponent() const
665 {
666     if (!hasPath())
667         return String();
668
669     unsigned end = m_pathEnd - 1;
670     if (m_string[end] == '/')
671         --end;
672
673     size_t start = m_string.reverseFind('/', end);
674     if (start < static_cast<unsigned>(m_portEnd))
675         return String();
676     ++start;
677
678     return m_string.substring(start, end - start + 1);
679 }
680
681 String URL::protocol() const
682 {
683     return m_string.left(m_schemeEnd);
684 }
685
686 String URL::host() const
687 {
688     int start = hostStart();
689     return m_string.substring(start, m_hostEnd - start);
690 }
691
692 unsigned short URL::port() const
693 {
694     // We return a port of 0 if there is no port specified. This can happen in two situations:
695     // 1) The URL contains no colon after the host name and before the path component of the URL.
696     // 2) The URL contains a colon but there's no port number before the path component of the URL begins.
697     if (m_hostEnd == m_portEnd || m_hostEnd == m_portEnd - 1)
698         return 0;
699
700     bool ok = false;
701     unsigned number;
702     if (m_string.is8Bit())
703         number = charactersToUIntStrict(m_string.characters8() + m_hostEnd + 1, m_portEnd - m_hostEnd - 1, &ok);
704     else
705         number = charactersToUIntStrict(m_string.characters16() + m_hostEnd + 1, m_portEnd - m_hostEnd - 1, &ok);
706     if (!ok || number > maximumValidPortNumber)
707         return invalidPortNumber;
708     return number;
709 }
710
711 String URL::user() const
712 {
713     return decodeURLEscapeSequences(m_string.substring(m_userStart, m_userEnd - m_userStart));
714 }
715
716 String URL::pass() const
717 {
718     if (m_passwordEnd == m_userEnd)
719         return String();
720
721     return decodeURLEscapeSequences(m_string.substring(m_userEnd + 1, m_passwordEnd - m_userEnd - 1));
722 }
723
724 String URL::encodedUser() const
725 {
726     return m_string.substring(m_userStart, m_userEnd - m_userStart);
727 }
728
729 String URL::encodedPass() const
730 {
731     if (m_passwordEnd == m_userEnd)
732         return String();
733
734     return m_string.substring(m_userEnd + 1, m_passwordEnd - m_userEnd - 1);
735 }
736
737 String URL::fragmentIdentifier() const
738 {
739     if (m_fragmentEnd == m_queryEnd)
740         return String();
741
742     return m_string.substring(m_queryEnd + 1, m_fragmentEnd - (m_queryEnd + 1));
743 }
744
745 bool URL::hasFragmentIdentifier() const
746 {
747     return m_fragmentEnd != m_queryEnd;
748 }
749
750 String URL::baseAsString() const
751 {
752     return m_string.left(m_pathAfterLastSlash);
753 }
754
755 #if !USE(CF)
756 String URL::fileSystemPath() const
757 {
758     if (!isValid() || !isLocalFile())
759         return String();
760
761     return decodeURLEscapeSequences(path());
762 }
763 #endif
764
765 #ifdef NDEBUG
766
767 static inline void assertProtocolIsGood(const char*)
768 {
769 }
770
771 #else
772
773 static void assertProtocolIsGood(const char* protocol)
774 {
775     const char* p = protocol;
776     while (*p) {
777         ASSERT(*p > ' ' && *p < 0x7F && !(*p >= 'A' && *p <= 'Z'));
778         ++p;
779     }
780 }
781
782 #endif
783
784 bool URL::protocolIs(const char* protocol) const
785 {
786     assertProtocolIsGood(protocol);
787
788     // JavaScript URLs are "valid" and should be executed even if URL decides they are invalid.
789     // The free function protocolIsJavaScript() should be used instead. 
790     ASSERT(!equalLettersIgnoringASCIICase(StringView(protocol), "javascript"));
791
792     if (!m_isValid)
793         return false;
794
795     // Do the comparison without making a new string object.
796     for (int i = 0; i < m_schemeEnd; ++i) {
797         if (!protocol[i] || !isSchemeCharacterMatchIgnoringCase(m_string[i], protocol[i]))
798             return false;
799     }
800     return !protocol[m_schemeEnd]; // We should have consumed all characters in the argument.
801 }
802
803 String URL::query() const
804 {
805     if (m_queryEnd == m_pathEnd)
806         return String();
807
808     return m_string.substring(m_pathEnd + 1, m_queryEnd - (m_pathEnd + 1)); 
809 }
810
811 String URL::path() const
812 {
813     return m_string.substring(m_portEnd, m_pathEnd - m_portEnd);
814 }
815
816 bool URL::setProtocol(const String& s)
817 {
818     // Firefox and IE remove everything after the first ':'.
819     size_t separatorPosition = s.find(':');
820     String newProtocol = s.substring(0, separatorPosition);
821
822     if (!isValidProtocol(newProtocol))
823         return false;
824
825     if (!m_isValid) {
826         parse(newProtocol + ':' + m_string);
827         return true;
828     }
829
830     parse(newProtocol + m_string.substring(m_schemeEnd));
831     return true;
832 }
833
834 void URL::setHost(const String& s)
835 {
836     if (!m_isValid)
837         return;
838
839     // FIXME: Non-ASCII characters must be encoded and escaped to match parse() expectations,
840     // and to avoid changing more than just the host.
841
842     bool slashSlashNeeded = m_userStart == m_schemeEnd + 1;
843
844     parse(m_string.left(hostStart()) + (slashSlashNeeded ? "//" : "") + s + m_string.substring(m_hostEnd));
845 }
846
847 void URL::removePort()
848 {
849     if (m_hostEnd == m_portEnd)
850         return;
851     parse(m_string.left(m_hostEnd) + m_string.substring(m_portEnd));
852 }
853
854 void URL::setPort(unsigned short i)
855 {
856     if (!m_isValid)
857         return;
858
859     bool colonNeeded = m_portEnd == m_hostEnd;
860     int portStart = (colonNeeded ? m_hostEnd : m_hostEnd + 1);
861
862     parse(m_string.left(portStart) + (colonNeeded ? ":" : "") + String::number(i) + m_string.substring(m_portEnd));
863 }
864
865 void URL::setHostAndPort(const String& hostAndPort)
866 {
867     if (!m_isValid)
868         return;
869
870     // FIXME: Non-ASCII characters must be encoded and escaped to match parse() expectations,
871     // and to avoid changing more than just host and port.
872
873     bool slashSlashNeeded = m_userStart == m_schemeEnd + 1;
874
875     parse(m_string.left(hostStart()) + (slashSlashNeeded ? "//" : "") + hostAndPort + m_string.substring(m_portEnd));
876 }
877
878 void URL::setUser(const String& user)
879 {
880     if (!m_isValid)
881         return;
882
883     // FIXME: Non-ASCII characters must be encoded and escaped to match parse() expectations,
884     // and to avoid changing more than just the user login.
885
886     int end = m_userEnd;
887     if (!user.isEmpty()) {
888         String u = encodeWithURLEscapeSequences(user, PercentEncodeUsername);
889         if (m_userStart == m_schemeEnd + 1)
890             u = "//" + u;
891         // Add '@' if we didn't have one before.
892         if (end == m_hostEnd || (end == m_passwordEnd && m_string[end] != '@'))
893             u.append('@');
894         parse(m_string.left(m_userStart) + u + m_string.substring(end));
895     } else {
896         // Remove '@' if we now have neither user nor password.
897         if (m_userEnd == m_passwordEnd && end != m_hostEnd && m_string[end] == '@')
898             end += 1;
899         // We don't want to parse in the extremely common case where we are not going to make a change.
900         if (m_userStart != end)
901             parse(m_string.left(m_userStart) + m_string.substring(end));
902     }
903 }
904
905 void URL::setPass(const String& password)
906 {
907     if (!m_isValid)
908         return;
909
910     int end = m_passwordEnd;
911     if (!password.isEmpty()) {
912         String p = ":" + encodeWithURLEscapeSequences(password, PercentEncodePassword) + "@";
913         if (m_userEnd == m_schemeEnd + 1)
914             p = "//" + p;
915         // Eat the existing '@' since we are going to add our own.
916         if (end != m_hostEnd && m_string[end] == '@')
917             end += 1;
918         parse(m_string.left(m_userEnd) + p + m_string.substring(end));
919     } else {
920         // Remove '@' if we now have neither user nor password.
921         if (m_userStart == m_userEnd && end != m_hostEnd && m_string[end] == '@')
922             end += 1;
923         // We don't want to parse in the extremely common case where we are not going to make a change.
924         if (m_userEnd != end)
925             parse(m_string.left(m_userEnd) + m_string.substring(end));
926     }
927 }
928
929 void URL::setFragmentIdentifier(const String& s)
930 {
931     if (!m_isValid)
932         return;
933
934     // FIXME: Non-ASCII characters must be encoded and escaped to match parse() expectations.
935     parse(m_string.left(m_queryEnd) + "#" + s);
936 }
937
938 void URL::removeFragmentIdentifier()
939 {
940     if (!m_isValid)
941         return;
942     parse(m_string.left(m_queryEnd));
943 }
944     
945 void URL::setQuery(const String& query)
946 {
947     if (!m_isValid)
948         return;
949
950     // FIXME: '#' and non-ASCII characters must be encoded and escaped.
951     // Usually, the query is encoded using document encoding, not UTF-8, but we don't have
952     // access to the document in this function.
953     if ((query.isEmpty() || query[0] != '?') && !query.isNull())
954         parse(m_string.left(m_pathEnd) + "?" + query + m_string.substring(m_queryEnd));
955     else
956         parse(m_string.left(m_pathEnd) + query + m_string.substring(m_queryEnd));
957
958 }
959
960 void URL::setPath(const String& s)
961 {
962     if (!m_isValid)
963         return;
964
965     // FIXME: encodeWithURLEscapeSequences does not correctly escape '#' and '?', so fragment and query parts
966     // may be inadvertently affected.
967     String path = s;
968     if (path.isEmpty() || path[0] != '/')
969         path = "/" + path;
970
971     parse(m_string.left(m_portEnd) + encodeWithURLEscapeSequences(path) + m_string.substring(m_pathEnd));
972 }
973
974 String decodeURLEscapeSequences(const String& string)
975 {
976     return decodeEscapeSequences<URLEscapeSequence>(string, UTF8Encoding());
977 }
978
979 String decodeURLEscapeSequences(const String& string, const TextEncoding& encoding)
980 {
981     return decodeEscapeSequences<URLEscapeSequence>(string, encoding);
982 }
983
984 // Caution: This function does not bounds check.
985 static void appendEscapedChar(char*& buffer, unsigned char c)
986 {
987     *buffer++ = '%';
988     placeByteAsHex(c, buffer);
989 }
990
991 static void appendEscapingBadChars(char*& buffer, const char* strStart, size_t length)
992 {
993     char* p = buffer;
994
995     const char* str = strStart;
996     const char* strEnd = strStart + length;
997     while (str < strEnd) {
998         unsigned char c = *str++;
999         if (isBadChar(c)) {
1000             if (c == '%' || c == '?')
1001                 *p++ = c;
1002             else if (c != 0x09 && c != 0x0a && c != 0x0d)
1003                 appendEscapedChar(p, c);
1004         } else
1005             *p++ = c;
1006     }
1007
1008     buffer = p;
1009 }
1010
1011 static void escapeAndAppendNonHierarchicalPart(char*& buffer, const char* strStart, size_t length)
1012 {
1013     char* p = buffer;
1014
1015     const char* str = strStart;
1016     const char* strEnd = strStart + length;
1017     while (str < strEnd) {
1018         unsigned char c = *str++;
1019         // Strip CR, LF and Tab from fragments, per:
1020         // https://bugs.webkit.org/show_bug.cgi?id=8770
1021         if (c == 0x09 || c == 0x0a || c == 0x0d)
1022             continue;
1023
1024         // Chrome and IE allow non-ascii characters in fragments, however doing
1025         // so would hit an ASSERT in checkEncodedString, so for now we don't.
1026         if (c < 0x20 || c >= 127) {
1027             appendEscapedChar(p, c);
1028             continue;
1029         }
1030         *p++ = c;
1031     }
1032
1033     buffer = p;
1034 }
1035
1036 // copy a path, accounting for "." and ".." segments
1037 static int copyPathRemovingDots(char* dst, const char* src, int srcStart, int srcEnd)
1038 {
1039     char* bufferPathStart = dst;
1040
1041     // empty path is a special case, and need not have a leading slash
1042     if (srcStart != srcEnd) {
1043         const char* baseStringStart = src + srcStart;
1044         const char* baseStringEnd = src + srcEnd;
1045         const char* baseStringPos = baseStringStart;
1046
1047         // this code is unprepared for paths that do not begin with a
1048         // slash and we should always have one in the source string
1049         ASSERT(baseStringPos[0] == '/');
1050
1051         // copy the leading slash into the destination
1052         *dst = *baseStringPos;
1053         baseStringPos++;
1054         dst++;
1055
1056         while (baseStringPos < baseStringEnd) {
1057             if (baseStringPos[0] == '.' && dst[-1] == '/') {
1058                 if (baseStringPos[1] == '/' || baseStringPos + 1 == baseStringEnd) {
1059                     // skip over "." segment
1060                     baseStringPos += 2;
1061                     continue;
1062                 } else if (baseStringPos[1] == '.' && (baseStringPos[2] == '/' ||
1063                                        baseStringPos + 2 == baseStringEnd)) {
1064                     // skip over ".." segment and rewind the last segment
1065                     // the RFC leaves it up to the app to decide what to do with excess
1066                     // ".." segments - we choose to drop them since some web content
1067                     // relies on this.
1068                     baseStringPos += 3;
1069                     if (dst > bufferPathStart + 1)
1070                         dst--;
1071                     while (dst > bufferPathStart && dst[-1] != '/')
1072                         dst--;
1073                     continue;
1074                 }
1075             }
1076
1077             *dst = *baseStringPos;
1078             baseStringPos++;
1079             dst++;
1080         }
1081     }
1082     *dst = '\0';
1083     return dst - bufferPathStart;
1084 }
1085
1086 static inline bool hasSlashDotOrDotDot(const char* str)
1087 {
1088     const unsigned char* p = reinterpret_cast<const unsigned char*>(str);
1089     if (!*p)
1090         return false;
1091     unsigned char pc = *p;
1092     while (unsigned char c = *++p) {
1093         if (c == '.' && (pc == '/' || pc == '.'))
1094             return true;
1095         pc = c;
1096     }
1097     return false;
1098 }
1099
1100 void URL::parse(const String& string)
1101 {
1102     checkEncodedString(string);
1103
1104     CharBuffer buffer(string.length() + 1);
1105     copyASCII(string, buffer.data());
1106     buffer[string.length()] = '\0';
1107     parse(buffer.data(), &string);
1108 }
1109
1110 #if PLATFORM(IOS)
1111 static bool shouldCanonicalizeScheme = true;
1112
1113 void enableURLSchemeCanonicalization(bool enableSchemeCanonicalization)
1114 {
1115     shouldCanonicalizeScheme = enableSchemeCanonicalization;
1116 }
1117 #endif
1118
1119 template<size_t length>
1120 static inline bool equal(const char* a, const char (&b)[length])
1121 {
1122 #if PLATFORM(IOS)
1123     if (!shouldCanonicalizeScheme) {
1124         for (size_t i = 0; i < length; ++i) {
1125             if (toASCIILower(a[i]) != b[i])
1126                 return false;
1127         }
1128         return true;
1129     }
1130 #endif
1131     for (size_t i = 0; i < length; ++i) {
1132         if (a[i] != b[i])
1133             return false;
1134     }
1135     return true;
1136 }
1137
1138 template<size_t lengthB>
1139 static inline bool equal(const char* stringA, size_t lengthA, const char (&stringB)[lengthB])
1140 {
1141     return lengthA == lengthB && equal(stringA, stringB);
1142 }
1143
1144 // List of default schemes is taken from google-url:
1145 // http://code.google.com/p/google-url/source/browse/trunk/src/url_canon_stdurl.cc#120
1146 static inline bool isDefaultPortForScheme(const char* port, size_t portLength, const char* scheme, size_t schemeLength)
1147 {
1148     // This switch is theoretically a performance optimization.  It came over when
1149     // the code was moved from google-url, but may be removed later.
1150     switch (schemeLength) {
1151     case 2:
1152         return equal(scheme, wsScheme) && equal(port, portLength, httpPort);
1153     case 3:
1154         if (equal(scheme, ftpScheme))
1155             return equal(port, portLength, ftpPort);
1156         if (equal(scheme, wssScheme))
1157             return equal(port, portLength, httpsPort);
1158         break;
1159     case 4:
1160         return equal(scheme, httpScheme) && equal(port, portLength, httpPort);
1161     case 5:
1162         return equal(scheme, httpsScheme) && equal(port, portLength, httpsPort);
1163     case 6:
1164         return equal(scheme, gopherScheme) && equal(port, portLength, gopherPort);
1165     }
1166     return false;
1167 }
1168
1169 static inline bool hostPortIsEmptyButCredentialsArePresent(int hostStart, int portEnd, char userinfoEndChar)
1170 {
1171     return userinfoEndChar == '@' && hostStart == portEnd;
1172 }
1173
1174 static bool isNonFileHierarchicalScheme(const char* scheme, size_t schemeLength)
1175 {
1176     switch (schemeLength) {
1177     case 2:
1178         return equal(scheme, wsScheme);
1179     case 3:
1180         return equal(scheme, ftpScheme) || equal(scheme, wssScheme);
1181     case 4:
1182         return equal(scheme, httpScheme);
1183     case 5:
1184         return equal(scheme, httpsScheme);
1185     case 6:
1186         return equal(scheme, gopherScheme);
1187     }
1188     return false;
1189 }
1190
1191 static bool isCanonicalHostnameLowercaseForScheme(const char* scheme, size_t schemeLength)
1192 {
1193     switch (schemeLength) {
1194     case 2:
1195         return equal(scheme, wsScheme);
1196     case 3:
1197         return equal(scheme, ftpScheme) || equal(scheme, wssScheme);
1198     case 4:
1199         return equal(scheme, httpScheme) || equal(scheme, fileScheme);
1200     case 5:
1201         return equal(scheme, httpsScheme);
1202     case 6:
1203         return equal(scheme, gopherScheme);
1204     }
1205     return false;
1206 }
1207
1208 void URL::parse(const char* url, const String* originalString)
1209 {
1210     if (!url || url[0] == '\0') {
1211         // valid URL must be non-empty
1212         m_string = originalString ? *originalString : url;
1213         invalidate();
1214         return;
1215     }
1216
1217     if (!isSchemeFirstChar(url[0])) {
1218         // scheme must start with an alphabetic character
1219         m_string = originalString ? *originalString : url;
1220         invalidate();
1221         return;
1222     }
1223
1224     int schemeEnd = 0;
1225     while (isSchemeChar(url[schemeEnd]))
1226         schemeEnd++;
1227
1228     if (url[schemeEnd] != ':') {
1229         m_string = originalString ? *originalString : url;
1230         invalidate();
1231         return;
1232     }
1233
1234     int userStart = schemeEnd + 1;
1235     int userEnd;
1236     int passwordStart;
1237     int passwordEnd;
1238     int hostStart;
1239     int hostEnd;
1240     int portStart;
1241     int portEnd;
1242
1243     bool hierarchical = url[schemeEnd + 1] == '/';
1244     bool hasSecondSlash = hierarchical && url[schemeEnd + 2] == '/';
1245
1246     bool isFile = schemeEnd == 4
1247         && isLetterMatchIgnoringCase(url[0], 'f')
1248         && isLetterMatchIgnoringCase(url[1], 'i')
1249         && isLetterMatchIgnoringCase(url[2], 'l')
1250         && isLetterMatchIgnoringCase(url[3], 'e');
1251
1252     m_protocolIsInHTTPFamily = isLetterMatchIgnoringCase(url[0], 'h')
1253         && isLetterMatchIgnoringCase(url[1], 't')
1254         && isLetterMatchIgnoringCase(url[2], 't')
1255         && isLetterMatchIgnoringCase(url[3], 'p')
1256         && (url[4] == ':' || (isLetterMatchIgnoringCase(url[4], 's') && url[5] == ':'));
1257
1258     if ((hierarchical && hasSecondSlash) || isNonFileHierarchicalScheme(url, schemeEnd)) {
1259         // The part after the scheme is either a net_path or an abs_path whose first path segment is empty.
1260         // Attempt to find an authority.
1261         // FIXME: Authority characters may be scanned twice, and it would be nice to be faster.
1262
1263         if (hierarchical) {
1264             userStart++;
1265             if (hasSecondSlash) {
1266                 userStart++;
1267                 if (isNonFileHierarchicalScheme(url, schemeEnd)) {
1268                     while (url[userStart] == '/')
1269                         userStart++;
1270                 }
1271             }
1272         }
1273
1274         userEnd = userStart;
1275
1276         int colonPos = 0;
1277         while (isUserInfoChar(url[userEnd])) {
1278             if (url[userEnd] == ':' && colonPos == 0)
1279                 colonPos = userEnd;
1280             userEnd++;
1281         }
1282
1283         if (url[userEnd] == '@') {
1284             // actual end of the userinfo, start on the host
1285             if (colonPos != 0) {
1286                 passwordEnd = userEnd;
1287                 userEnd = colonPos;
1288                 passwordStart = colonPos + 1;
1289             } else
1290                 passwordStart = passwordEnd = userEnd;
1291
1292             hostStart = passwordEnd + 1;
1293         } else if (url[userEnd] == '[' || isPathSegmentEndChar(url[userEnd])) {
1294             // hit the end of the authority, must have been no user
1295             // or looks like an IPv6 hostname
1296             // either way, try to parse it as a hostname
1297             userEnd = userStart;
1298             passwordStart = passwordEnd = userEnd;
1299             hostStart = userStart;
1300         } else {
1301             // invalid character
1302             m_string = originalString ? *originalString : url;
1303             invalidate();
1304             return;
1305         }
1306
1307         hostEnd = hostStart;
1308
1309         // IPV6 IP address
1310         if (url[hostEnd] == '[') {
1311             hostEnd++;
1312             while (isIPv6Char(url[hostEnd]))
1313                 hostEnd++;
1314             if (url[hostEnd] == ']')
1315                 hostEnd++;
1316             else {
1317                 // invalid character
1318                 m_string = originalString ? *originalString : url;
1319                 invalidate();
1320                 return;
1321             }
1322         } else {
1323             while (isHostnameChar(url[hostEnd]))
1324                 hostEnd++;
1325         }
1326         
1327         if (url[hostEnd] == ':') {
1328             portStart = portEnd = hostEnd + 1;
1329  
1330             // possible start of port
1331             portEnd = portStart;
1332             while (isASCIIDigit(url[portEnd]))
1333                 portEnd++;
1334         } else
1335             portStart = portEnd = hostEnd;
1336
1337         if (!isPathSegmentEndChar(url[portEnd])) {
1338             // invalid character
1339             m_string = originalString ? *originalString : url;
1340             invalidate();
1341             return;
1342         }
1343
1344         if (hostPortIsEmptyButCredentialsArePresent(hostStart, portEnd, url[passwordEnd])) {
1345             m_string = originalString ? *originalString : url;
1346             invalidate();
1347             return;
1348         }
1349
1350         if (userStart == portEnd && !m_protocolIsInHTTPFamily && !isFile) {
1351             // No authority found, which means that this is not a net_path, but rather an abs_path whose first two
1352             // path segments are empty. For file, http and https only, an empty authority is allowed.
1353             userStart -= 2;
1354             userEnd = userStart;
1355             passwordStart = userEnd;
1356             passwordEnd = passwordStart;
1357             hostStart = passwordEnd;
1358             hostEnd = hostStart;
1359             portStart = hostEnd;
1360             portEnd = hostEnd;
1361         }
1362     } else {
1363         // the part after the scheme must be an opaque_part or an abs_path
1364         userEnd = userStart;
1365         passwordStart = passwordEnd = userEnd;
1366         hostStart = hostEnd = passwordEnd;
1367         portStart = portEnd = hostEnd;
1368     }
1369
1370     int pathStart = portEnd;
1371     int pathEnd = pathStart;
1372     while (url[pathEnd] && url[pathEnd] != '?' && url[pathEnd] != '#')
1373         pathEnd++;
1374
1375     int queryStart = pathEnd;
1376     int queryEnd = queryStart;
1377     if (url[queryStart] == '?') {
1378         while (url[queryEnd] && url[queryEnd] != '#')
1379             queryEnd++;
1380     }
1381
1382     int fragmentStart = queryEnd;
1383     int fragmentEnd = fragmentStart;
1384     if (url[fragmentStart] == '#') {
1385         fragmentStart++;
1386         fragmentEnd = fragmentStart;
1387         while (url[fragmentEnd])
1388             fragmentEnd++;
1389     }
1390
1391     // assemble it all, remembering the real ranges
1392
1393     Vector<char, 4096> buffer(fragmentEnd * 3 + 1);
1394
1395     char *p = buffer.data();
1396     const char *strPtr = url;
1397
1398     // copy in the scheme
1399     const char *schemeEndPtr = url + schemeEnd;
1400 #if PLATFORM(IOS)
1401     if (shouldCanonicalizeScheme || m_protocolIsInHTTPFamily) {
1402         while (strPtr < schemeEndPtr)
1403             *p++ = toASCIILower(*strPtr++);
1404     } else {
1405         while (strPtr < schemeEndPtr)
1406             *p++ = *strPtr++;
1407     }
1408 #else
1409     while (strPtr < schemeEndPtr)
1410         *p++ = toASCIILower(*strPtr++);
1411 #endif
1412     m_schemeEnd = p - buffer.data();
1413
1414     bool hostIsLocalHost = portEnd - userStart == 9
1415         && isLetterMatchIgnoringCase(url[userStart], 'l')
1416         && isLetterMatchIgnoringCase(url[userStart+1], 'o')
1417         && isLetterMatchIgnoringCase(url[userStart+2], 'c')
1418         && isLetterMatchIgnoringCase(url[userStart+3], 'a')
1419         && isLetterMatchIgnoringCase(url[userStart+4], 'l')
1420         && isLetterMatchIgnoringCase(url[userStart+5], 'h')
1421         && isLetterMatchIgnoringCase(url[userStart+6], 'o')
1422         && isLetterMatchIgnoringCase(url[userStart+7], 's')
1423         && isLetterMatchIgnoringCase(url[userStart+8], 't');
1424
1425     // File URLs need a host part unless it is just file:// or file://localhost
1426     bool degenerateFilePath = pathStart == pathEnd && (hostStart == hostEnd || hostIsLocalHost);
1427
1428     // We drop empty credentials, but keep a colon in an empty host/port pair.
1429     // Removing hostname completely would change the structure of the URL on re-parsing.
1430     bool haveNonHostAuthorityPart = userStart != userEnd || passwordStart != passwordEnd || hostEnd != portEnd;
1431
1432     // add ":" after scheme
1433     *p++ = ':';
1434
1435     // if we have at least one authority part or a file URL - add "//" and authority
1436     if (isFile ? !degenerateFilePath : (haveNonHostAuthorityPart || hostStart != hostEnd)) {
1437         *p++ = '/';
1438         *p++ = '/';
1439
1440         m_userStart = p - buffer.data();
1441
1442         // copy in the user
1443         strPtr = url + userStart;
1444         const char* userEndPtr = url + userEnd;
1445         while (strPtr < userEndPtr) {
1446             char c = *strPtr++;
1447             ASSERT(isUserInfoChar(c));
1448             *p++ = c;
1449         }
1450         m_userEnd = p - buffer.data();
1451
1452         // copy in the password
1453         if (passwordEnd != passwordStart) {
1454             *p++ = ':';
1455             strPtr = url + passwordStart;
1456             const char* passwordEndPtr = url + passwordEnd;
1457             while (strPtr < passwordEndPtr) {
1458                 char c = *strPtr++;
1459                 ASSERT(isUserInfoChar(c));
1460                 *p++ = c;
1461             }
1462         }
1463         m_passwordEnd = p - buffer.data();
1464
1465         // If we had any user info, add "@"
1466         if (p - buffer.data() != m_userStart)
1467             *p++ = '@';
1468
1469         // copy in the host, except in the case of a file URL with authority="localhost"
1470         if (!(isFile && hostIsLocalHost && !haveNonHostAuthorityPart)) {
1471             strPtr = url + hostStart;
1472             const char* hostEndPtr = url + hostEnd;
1473             if (isCanonicalHostnameLowercaseForScheme(buffer.data(), m_schemeEnd)) {
1474                 while (strPtr < hostEndPtr) {
1475                     char c = toASCIILower(*strPtr++);
1476                     ASSERT(isHostnameChar(c) || c == '[' || c == ']' || c == ':');
1477                     *p++ = c;
1478                 }
1479             } else {
1480                 while (strPtr < hostEndPtr) {
1481                     char c = *strPtr++;
1482                     ASSERT(isHostnameChar(c) || c == '[' || c == ']' || c == ':');
1483                     *p++ = c;
1484                 }
1485             }
1486         }
1487         m_hostEnd = p - buffer.data();
1488
1489         // Copy in the port if the URL has one (and it's not default). Also, copy it if there was no hostname, so that there is still something in authority component.
1490         if (hostEnd != portStart) {
1491             const char* portStr = url + portStart;
1492             size_t portLength = portEnd - portStart;
1493             if ((portLength && !isDefaultPortForScheme(portStr, portLength, buffer.data(), m_schemeEnd))
1494                 || (hostStart == hostEnd && hostEnd != portStart)) {
1495                 *p++ = ':';
1496                 const char* portEndPtr = url + portEnd;
1497                 while (portStr < portEndPtr)
1498                     *p++ = *portStr++;
1499             }
1500         }
1501         m_portEnd = p - buffer.data();
1502     } else {
1503         if (isFile) {
1504             ASSERT(degenerateFilePath);
1505             *p++ = '/';
1506             *p++ = '/';
1507         }
1508         m_userStart = m_userEnd = m_passwordEnd = m_hostEnd = m_portEnd = p - buffer.data();
1509     }
1510
1511     // For canonicalization, ensure we have a '/' for no path.
1512     // Do this only for URL with protocol file, http or https.
1513     if ((m_protocolIsInHTTPFamily || isFile) && pathEnd == pathStart)
1514         *p++ = '/';
1515
1516     // add path, escaping bad characters
1517     if (!hierarchical)
1518         escapeAndAppendNonHierarchicalPart(p, url + pathStart, pathEnd - pathStart);
1519     else if (!hasSlashDotOrDotDot(url))
1520         appendEscapingBadChars(p, url + pathStart, pathEnd - pathStart);
1521     else {
1522         CharBuffer pathBuffer(pathEnd - pathStart + 1);
1523         size_t length = copyPathRemovingDots(pathBuffer.data(), url, pathStart, pathEnd);
1524         appendEscapingBadChars(p, pathBuffer.data(), length);
1525     }
1526
1527     m_pathEnd = p - buffer.data();
1528
1529     // Find the position after the last slash in the path, or
1530     // the position before the path if there are no slashes in it.
1531     int i;
1532     for (i = m_pathEnd; i > m_portEnd; --i) {
1533         if (buffer[i - 1] == '/')
1534             break;
1535     }
1536     m_pathAfterLastSlash = i;
1537
1538     // add query, escaping bad characters
1539     appendEscapingBadChars(p, url + queryStart, queryEnd - queryStart);
1540     m_queryEnd = p - buffer.data();
1541
1542     // add fragment, escaping bad characters
1543     if (fragmentEnd != queryEnd) {
1544         *p++ = '#';
1545         escapeAndAppendNonHierarchicalPart(p, url + fragmentStart, fragmentEnd - fragmentStart);
1546     }
1547     m_fragmentEnd = p - buffer.data();
1548
1549     ASSERT(p - buffer.data() <= static_cast<int>(buffer.size()));
1550     ASSERT(buffer.size() > 0);
1551
1552     // If we didn't end up actually changing the original string and
1553     // it was already in a String, reuse it to avoid extra allocation.
1554     if (originalString && equal(originalString->impl(), buffer.data(), m_fragmentEnd))
1555         m_string = *originalString;
1556     else
1557         m_string = String(buffer.data(), m_fragmentEnd);
1558
1559     m_isValid = true;
1560 }
1561
1562 bool equalIgnoringFragmentIdentifier(const URL& a, const URL& b)
1563 {
1564     if (a.m_queryEnd != b.m_queryEnd)
1565         return false;
1566     unsigned queryLength = a.m_queryEnd;
1567     for (unsigned i = 0; i < queryLength; ++i)
1568         if (a.string()[i] != b.string()[i])
1569             return false;
1570     return true;
1571 }
1572
1573 bool protocolHostAndPortAreEqual(const URL& a, const URL& b)
1574 {
1575     if (a.m_schemeEnd != b.m_schemeEnd)
1576         return false;
1577
1578     int hostStartA = a.hostStart();
1579     int hostLengthA = a.hostEnd() - hostStartA;
1580     int hostStartB = b.hostStart();
1581     int hostLengthB = b.hostEnd() - b.hostStart();
1582     if (hostLengthA != hostLengthB)
1583         return false;
1584
1585     // Check the scheme
1586     for (int i = 0; i < a.m_schemeEnd; ++i)
1587         if (a.string()[i] != b.string()[i])
1588             return false;
1589
1590     // And the host
1591     for (int i = 0; i < hostLengthA; ++i)
1592         if (a.string()[hostStartA + i] != b.string()[hostStartB + i])
1593             return false;
1594
1595     if (a.port() != b.port())
1596         return false;
1597
1598     return true;
1599 }
1600
1601 bool hostsAreEqual(const URL& a, const URL& b)
1602 {
1603     int hostStartA = a.hostStart();
1604     int hostLengthA = a.hostEnd() - hostStartA;
1605     int hostStartB = b.hostStart();
1606     int hostLengthB = b.hostEnd() - hostStartB;
1607     if (hostLengthA != hostLengthB)
1608         return false;
1609
1610     for (int i = 0; i < hostLengthA; ++i) {
1611         if (a.string()[hostStartA + i] != b.string()[hostStartB + i])
1612             return false;
1613     }
1614
1615     return true;
1616 }
1617
1618 String encodeWithURLEscapeSequences(const String& notEncodedString, PercentEncodeCharacterClass whatToEncode)
1619 {
1620     CString asUTF8 = notEncodedString.utf8();
1621
1622     CharBuffer buffer(asUTF8.length() * 3 + 1);
1623     char* p = buffer.data();
1624
1625     const char* str = asUTF8.data();
1626     const char* strEnd = str + asUTF8.length();
1627     while (str < strEnd) {
1628         unsigned char c = *str++;
1629         if (percentEncodeClassTable[c] >= whatToEncode)
1630             appendEscapedChar(p, c);
1631         else
1632             *p++ = c;
1633     }
1634
1635     ASSERT(p - buffer.data() <= static_cast<int>(buffer.size()));
1636
1637     return String(buffer.data(), p - buffer.data());
1638 }
1639
1640 String encodeWithURLEscapeSequences(const String& notEncodedString)
1641 {
1642     CString asUTF8 = notEncodedString.utf8();
1643
1644     CharBuffer buffer(asUTF8.length() * 3 + 1);
1645     char* p = buffer.data();
1646
1647     const char* str = asUTF8.data();
1648     const char* strEnd = str + asUTF8.length();
1649     while (str < strEnd) {
1650         unsigned char c = *str++;
1651         if (isBadChar(c))
1652             appendEscapedChar(p, c);
1653         else
1654             *p++ = c;
1655     }
1656
1657     ASSERT(p - buffer.data() <= static_cast<int>(buffer.size()));
1658
1659     return String(buffer.data(), p - buffer.data());
1660 }
1661
1662 static bool containsOnlyASCII(StringView string)
1663 {
1664     if (string.is8Bit())
1665         return charactersAreAllASCII(string.characters8(), string.length());
1666     return charactersAreAllASCII(string.characters16(), string.length());
1667 }
1668
1669 static bool protocolIs(StringView stringURL, const char* protocol)
1670 {
1671     assertProtocolIsGood(protocol);
1672     unsigned length = stringURL.length();
1673     for (unsigned i = 0; i < length; ++i) {
1674         if (!protocol[i])
1675             return stringURL[i] == ':';
1676         if (!isLetterMatchIgnoringCase(stringURL[i], protocol[i]))
1677             return false;
1678     }
1679     return false;
1680 }
1681
1682 // Appends the punycoded hostname identified by the given string and length to
1683 // the output buffer. The result will not be null terminated.
1684 static void appendEncodedHostname(UCharBuffer& buffer, StringView string)
1685 {
1686     // Needs to be big enough to hold an IDN-encoded name.
1687     // For host names bigger than this, we won't do IDN encoding, which is almost certainly OK.
1688     const unsigned hostnameBufferLength = 2048;
1689
1690     if (string.length() > hostnameBufferLength || containsOnlyASCII(string)) {
1691         append(buffer, string);
1692         return;
1693     }
1694
1695     UChar hostnameBuffer[hostnameBufferLength];
1696     UErrorCode error = U_ZERO_ERROR;
1697
1698 #if COMPILER(GCC_OR_CLANG)
1699 #pragma GCC diagnostic push
1700 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1701 #endif
1702     int32_t numCharactersConverted = uidna_IDNToASCII(string.upconvertedCharacters(), string.length(), hostnameBuffer,
1703         hostnameBufferLength, UIDNA_ALLOW_UNASSIGNED, 0, &error);
1704 #if COMPILER(GCC_OR_CLANG)
1705 #pragma GCC diagnostic pop
1706 #endif
1707
1708     if (error == U_ZERO_ERROR)
1709         buffer.append(hostnameBuffer, numCharactersConverted);
1710 }
1711
1712 static void findHostnamesInMailToURL(StringView string, Vector<std::pair<int, int>>& nameRanges)
1713 {
1714     // In a mailto: URL, host names come after a '@' character and end with a '>' or ',' or '?' or end of string character.
1715     // Skip quoted strings so that characters in them don't confuse us.
1716     // When we find a '?' character, we are past the part of the URL that contains host names.
1717
1718     nameRanges.clear();
1719
1720     int p = 0;
1721     while (1) {
1722         // Find start of host name or of quoted string.
1723         int hostnameOrStringStart = findFirstOf(string, p, "\"@?");
1724         if (hostnameOrStringStart == -1)
1725             return;
1726         UChar c = string[hostnameOrStringStart];
1727         p = hostnameOrStringStart + 1;
1728
1729         if (c == '?')
1730             return;
1731
1732         if (c == '@') {
1733             // Find end of host name.
1734             int hostnameStart = p;
1735             int hostnameEnd = findFirstOf(string, p, ">,?");
1736             bool done;
1737             if (hostnameEnd == -1) {
1738                 hostnameEnd = string.length();
1739                 done = true;
1740             } else {
1741                 p = hostnameEnd;
1742                 done = false;
1743             }
1744
1745             nameRanges.append(std::make_pair(hostnameStart, hostnameEnd));
1746
1747             if (done)
1748                 return;
1749         } else {
1750             // Skip quoted string.
1751             ASSERT(c == '"');
1752             while (1) {
1753                 int escapedCharacterOrStringEnd = findFirstOf(string, p, "\"\\");
1754                 if (escapedCharacterOrStringEnd == -1)
1755                     return;
1756
1757                 c = string[escapedCharacterOrStringEnd];
1758                 p = escapedCharacterOrStringEnd + 1;
1759
1760                 // If we are the end of the string, then break from the string loop back to the host name loop.
1761                 if (c == '"')
1762                     break;
1763
1764                 // Skip escaped character.
1765                 ASSERT(c == '\\');
1766                 if (p == static_cast<int>(string.length()))
1767                     return;
1768
1769                 ++p;
1770             }
1771         }
1772     }
1773 }
1774
1775 static bool findHostnameInHierarchicalURL(StringView string, int& startOffset, int& endOffset)
1776 {
1777     // Find the host name in a hierarchical URL.
1778     // It comes after a "://" sequence, with scheme characters preceding, and
1779     // this should be the first colon in the string.
1780     // It ends with the end of the string or a ":" or a path segment ending character.
1781     // If there is a "@" character, the host part is just the part after the "@".
1782     int separator = findFirstOf(string, 0, ":");
1783     if (separator == -1 || separator + 2 >= static_cast<int>(string.length()) || string[separator + 1] != '/' || string[separator + 2] != '/')
1784         return false;
1785
1786     // Check that all characters before the :// are valid scheme characters.
1787     if (!isSchemeFirstChar(string[0]))
1788         return false;
1789     for (int i = 1; i < separator; ++i) {
1790         if (!isSchemeChar(string[i]))
1791             return false;
1792     }
1793
1794     // Start after the separator.
1795     int authorityStart = separator + 3;
1796
1797     // Find terminating character.
1798     int hostnameEnd = string.length();
1799     for (int i = authorityStart; i < hostnameEnd; ++i) {
1800         UChar c = string[i];
1801         if (c == ':' || (isPathSegmentEndChar(c) && c != 0)) {
1802             hostnameEnd = i;
1803             break;
1804         }
1805     }
1806
1807     // Find "@" for the start of the host name.
1808     int userInfoTerminator = findFirstOf(string, authorityStart, "@");
1809     int hostnameStart;
1810     if (userInfoTerminator == -1 || userInfoTerminator > hostnameEnd)
1811         hostnameStart = authorityStart;
1812     else
1813         hostnameStart = userInfoTerminator + 1;
1814
1815     startOffset = hostnameStart;
1816     endOffset = hostnameEnd;
1817     return true;
1818 }
1819
1820 // Converts all hostnames found in the given input to punycode, preserving the
1821 // rest of the URL unchanged. The output will NOT be null-terminated.
1822 static void encodeHostnames(StringView string, UCharBuffer& buffer)
1823 {
1824     buffer.clear();
1825
1826     if (protocolIs(string, "mailto")) {
1827         Vector<std::pair<int, int>> hostnameRanges;
1828         findHostnamesInMailToURL(string, hostnameRanges);
1829         int n = hostnameRanges.size();
1830         int p = 0;
1831         for (int i = 0; i < n; ++i) {
1832             const std::pair<int, int>& r = hostnameRanges[i];
1833             append(buffer, string.substring(p, r.first - p));
1834             appendEncodedHostname(buffer, string.substring(r.first, r.second - r.first));
1835             p = r.second;
1836         }
1837         // This will copy either everything after the last hostname, or the
1838         // whole thing if there is no hostname.
1839         append(buffer, string.substring(p));
1840     } else {
1841         int hostStart, hostEnd;
1842         if (findHostnameInHierarchicalURL(string, hostStart, hostEnd)) {
1843             append(buffer, string.substring(0, hostStart)); // Before hostname.
1844             appendEncodedHostname(buffer, string.substring(hostStart, hostEnd - hostStart));
1845             append(buffer, string.substring(hostEnd)); // After hostname.
1846         } else {
1847             // No hostname to encode, return the input.
1848             append(buffer, string);
1849         }
1850     }
1851 }
1852
1853 static void encodeRelativeString(const String& rel, const TextEncoding& encoding, CharBuffer& output)
1854 {
1855     UCharBuffer s;
1856     encodeHostnames(rel, s);
1857
1858     TextEncoding pathEncoding(UTF8Encoding()); // Path is always encoded as UTF-8; other parts may depend on the scheme.
1859
1860     int pathEnd = -1;
1861     if (encoding != pathEncoding && encoding.isValid() && !protocolIs(rel, "mailto") && !protocolIs(rel, "data") && !protocolIsJavaScript(rel)) {
1862         // Find the first instance of either # or ?, keep pathEnd at -1 otherwise.
1863         pathEnd = findFirstOf(StringView(s.data(), s.size()), 0, "#?");
1864     }
1865
1866     if (pathEnd == -1) {
1867         CString decoded = pathEncoding.encode(StringView(s.data(), s.size()), URLEncodedEntitiesForUnencodables);
1868         output.resize(decoded.length());
1869         memcpy(output.data(), decoded.data(), decoded.length());
1870     } else {
1871         CString pathDecoded = pathEncoding.encode(StringView(s.data(), pathEnd), URLEncodedEntitiesForUnencodables);
1872         // Unencodable characters in URLs are represented by converting
1873         // them to XML entities and escaping non-alphanumeric characters.
1874         CString otherDecoded = encoding.encode(StringView(s.data() + pathEnd, s.size() - pathEnd), URLEncodedEntitiesForUnencodables);
1875
1876         output.resize(pathDecoded.length() + otherDecoded.length());
1877         memcpy(output.data(), pathDecoded.data(), pathDecoded.length());
1878         memcpy(output.data() + pathDecoded.length(), otherDecoded.data(), otherDecoded.length());
1879     }
1880     output.append('\0'); // null-terminate the output.
1881 }
1882
1883 static String substituteBackslashes(const String& string)
1884 {
1885     size_t questionPos = string.find('?');
1886     size_t hashPos = string.find('#');
1887     unsigned pathEnd;
1888
1889     if (hashPos != notFound && (questionPos == notFound || questionPos > hashPos))
1890         pathEnd = hashPos;
1891     else if (questionPos != notFound)
1892         pathEnd = questionPos;
1893     else
1894         pathEnd = string.length();
1895
1896     return string.left(pathEnd).replace('\\','/') + string.substring(pathEnd);
1897 }
1898
1899 bool URL::isHierarchical() const
1900 {
1901     if (!m_isValid)
1902         return false;
1903     ASSERT(m_string[m_schemeEnd] == ':');
1904     return m_string[m_schemeEnd + 1] == '/';
1905 }
1906
1907 void URL::copyToBuffer(Vector<char, 512>& buffer) const
1908 {
1909     // FIXME: This throws away the high bytes of all the characters in the string!
1910     // That's fine for a valid URL, which is all ASCII, but not for invalid URLs.
1911     buffer.resize(m_string.length());
1912     copyASCII(m_string, buffer.data());
1913 }
1914
1915 bool protocolIs(const String& url, const char* protocol)
1916 {
1917     // Do the comparison without making a new string object.
1918     assertProtocolIsGood(protocol);
1919     for (int i = 0; ; ++i) {
1920         if (!protocol[i])
1921             return url[i] == ':';
1922         if (!isLetterMatchIgnoringCase(url[i], protocol[i]))
1923             return false;
1924     }
1925 }
1926
1927 bool isValidProtocol(const String& protocol)
1928 {
1929     // RFC3986: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
1930     if (protocol.isEmpty())
1931         return false;
1932     if (!isSchemeFirstChar(protocol[0]))
1933         return false;
1934     unsigned protocolLength = protocol.length();
1935     for (unsigned i = 1; i < protocolLength; i++) {
1936         if (!isSchemeChar(protocol[i]))
1937             return false;
1938     }
1939     return true;
1940 }
1941
1942 #ifndef NDEBUG
1943 void URL::print() const
1944 {
1945     printf("%s\n", m_string.utf8().data());
1946 }
1947 #endif
1948
1949 String URL::strippedForUseAsReferrer() const
1950 {
1951     URL referrer(*this);
1952     referrer.setUser(String());
1953     referrer.setPass(String());
1954     referrer.removeFragmentIdentifier();
1955     return referrer.string();
1956 }
1957
1958 bool URL::isLocalFile() const
1959 {
1960     // Including feed here might be a bad idea since drag and drop uses this check
1961     // and including feed would allow feeds to potentially let someone's blog
1962     // read the contents of the clipboard on a drag, even without a drop.
1963     // Likewise with using the FrameLoader::shouldTreatURLAsLocal() function.
1964     return protocolIs("file");
1965 }
1966
1967 bool protocolIsJavaScript(const String& url)
1968 {
1969     return protocolIs(url, "javascript");
1970 }
1971
1972 bool protocolIsInHTTPFamily(const String& url)
1973 {
1974     // Do the comparison without making a new string object.
1975     return isLetterMatchIgnoringCase(url[0], 'h')
1976         && isLetterMatchIgnoringCase(url[1], 't')
1977         && isLetterMatchIgnoringCase(url[2], 't')
1978         && isLetterMatchIgnoringCase(url[3], 'p')
1979         && (url[4] == ':' || (isLetterMatchIgnoringCase(url[4], 's') && url[5] == ':'));
1980 }
1981
1982 const URL& blankURL()
1983 {
1984     static NeverDestroyed<URL> staticBlankURL(ParsedURLString, "about:blank");
1985     return staticBlankURL;
1986 }
1987
1988 bool URL::isBlankURL() const
1989 {
1990     return protocolIs("about");
1991 }
1992
1993 typedef HashMap<String, unsigned short, ASCIICaseInsensitiveHash> DefaultPortsMap;
1994 static const DefaultPortsMap& defaultPortsMap()
1995 {
1996     static NeverDestroyed<const DefaultPortsMap> defaultPortsMap(DefaultPortsMap({
1997         { "http", 80 },
1998         { "https", 443 },
1999         { "ftp", 21 },
2000         { "ftps", 990 }
2001     }));
2002     return defaultPortsMap.get();
2003 }
2004 unsigned short defaultPortForProtocol(const String& protocol)
2005 {
2006     return defaultPortsMap().get(protocol);
2007 }
2008
2009 bool isDefaultPortForProtocol(unsigned short port, const String& protocol)
2010 {
2011     if (protocol.isEmpty())
2012         return false;
2013
2014     return defaultPortForProtocol(protocol) == port;
2015 }
2016
2017 bool portAllowed(const URL& url)
2018 {
2019     unsigned short port = url.port();
2020
2021     // Since most URLs don't have a port, return early for the "no port" case.
2022     if (!port)
2023         return true;
2024
2025     // This blocked port list matches the port blocking that Mozilla implements.
2026     // See http://www.mozilla.org/projects/netlib/PortBanning.html for more information.
2027     static const unsigned short blockedPortList[] = {
2028         1,    // tcpmux
2029         7,    // echo
2030         9,    // discard
2031         11,   // systat
2032         13,   // daytime
2033         15,   // netstat
2034         17,   // qotd
2035         19,   // chargen
2036         20,   // FTP-data
2037         21,   // FTP-control
2038         22,   // SSH
2039         23,   // telnet
2040         25,   // SMTP
2041         37,   // time
2042         42,   // name
2043         43,   // nicname
2044         53,   // domain
2045         77,   // priv-rjs
2046         79,   // finger
2047         87,   // ttylink
2048         95,   // supdup
2049         101,  // hostriame
2050         102,  // iso-tsap
2051         103,  // gppitnp
2052         104,  // acr-nema
2053         109,  // POP2
2054         110,  // POP3
2055         111,  // sunrpc
2056         113,  // auth
2057         115,  // SFTP
2058         117,  // uucp-path
2059         119,  // nntp
2060         123,  // NTP
2061         135,  // loc-srv / epmap
2062         139,  // netbios
2063         143,  // IMAP2
2064         179,  // BGP
2065         389,  // LDAP
2066         465,  // SMTP+SSL
2067         512,  // print / exec
2068         513,  // login
2069         514,  // shell
2070         515,  // printer
2071         526,  // tempo
2072         530,  // courier
2073         531,  // Chat
2074         532,  // netnews
2075         540,  // UUCP
2076         556,  // remotefs
2077         563,  // NNTP+SSL
2078         587,  // ESMTP
2079         601,  // syslog-conn
2080         636,  // LDAP+SSL
2081         993,  // IMAP+SSL
2082         995,  // POP3+SSL
2083         2049, // NFS
2084         3659, // apple-sasl / PasswordServer [Apple addition]
2085         4045, // lockd
2086         6000, // X11
2087         6665, // Alternate IRC [Apple addition]
2088         6666, // Alternate IRC [Apple addition]
2089         6667, // Standard IRC [Apple addition]
2090         6668, // Alternate IRC [Apple addition]
2091         6669, // Alternate IRC [Apple addition]
2092         invalidPortNumber, // Used to block all invalid port numbers
2093     };
2094     const unsigned short* const blockedPortListEnd = blockedPortList + WTF_ARRAY_LENGTH(blockedPortList);
2095
2096 #ifndef NDEBUG
2097     // The port list must be sorted for binary_search to work.
2098     static bool checkedPortList = false;
2099     if (!checkedPortList) {
2100         for (const unsigned short* p = blockedPortList; p != blockedPortListEnd - 1; ++p)
2101             ASSERT(*p < *(p + 1));
2102         checkedPortList = true;
2103     }
2104 #endif
2105
2106     // If the port is not in the blocked port list, allow it.
2107     if (!std::binary_search(blockedPortList, blockedPortListEnd, port))
2108         return true;
2109
2110     // Allow ports 21 and 22 for FTP URLs, as Mozilla does.
2111     if ((port == 21 || port == 22) && url.protocolIs("ftp"))
2112         return true;
2113
2114     // Allow any port number in a file URL, since the port number is ignored.
2115     if (url.protocolIs("file"))
2116         return true;
2117
2118     return false;
2119 }
2120
2121 String mimeTypeFromDataURL(const String& url)
2122 {
2123     ASSERT(protocolIs(url, "data"));
2124     size_t index = url.find(';');
2125     if (index == notFound)
2126         index = url.find(',');
2127     if (index != notFound) {
2128         if (index > 5)
2129             return url.substring(5, index - 5).lower();
2130         return "text/plain"; // Data URLs with no MIME type are considered text/plain.
2131     }
2132     return "";
2133 }
2134
2135 String mimeTypeFromURL(const URL& url)
2136 {
2137     String decodedPath = decodeURLEscapeSequences(url.path());
2138     String extension = decodedPath.substring(decodedPath.reverseFind('.') + 1);
2139
2140     // We don't use MIMETypeRegistry::getMIMETypeForPath() because it returns "application/octet-stream" upon failure
2141     return MIMETypeRegistry::getMIMETypeForExtension(extension);
2142 }
2143
2144 bool URL::isSafeToSendToAnotherThread() const
2145 {
2146     return m_string.isSafeToSendToAnotherThread();
2147 }
2148
2149 String URL::stringCenterEllipsizedToLength(unsigned length) const
2150 {
2151     if (string().length() <= length)
2152         return string();
2153
2154     return string().left(length / 2 - 1) + "..." + string().right(length / 2 - 2);
2155 }
2156
2157 URL URL::fakeURLWithRelativePart(const String& relativePart)
2158 {
2159     return URL(URL(), "webkit-fake-url://" + createCanonicalUUIDString() + '/' + relativePart);
2160 }
2161
2162 URL URL::fileURLWithFileSystemPath(const String& filePath)
2163 {
2164     return URL(URL(), "file:///" + filePath);
2165 }
2166
2167 }