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