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