Add ports 6679 and 6697 (IRC SSL) to port blacklist
[WebKit-https.git] / Source / WebCore / platform / URL.cpp
1 /*
2  * Copyright (C) 2004, 2007-2008, 2011-2013, 2015-2016 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Research In Motion Limited. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "URL.h"
29
30 #include "DecodeEscapeSequences.h"
31 #include "MIMETypeRegistry.h"
32 #include "TextEncoding.h"
33 #include "URLParser.h"
34 #include <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/UUID.h>
41 #include <wtf/text/CString.h>
42 #include <wtf/text/StringBuilder.h>
43 #include <wtf/text/StringHash.h>
44 #include <wtf/text/TextStream.h>
45
46 // FIXME: This file makes too much use of the + operator on String.
47 // We either have to optimize that operator so it doesn't involve
48 // so many allocations, or change this to use StringBuffer instead.
49
50 using namespace WTF;
51
52 namespace WebCore {
53
54 typedef Vector<char, 512> CharBuffer;
55 typedef Vector<UChar, 512> UCharBuffer;
56
57 static const unsigned invalidPortNumber = 0xFFFF;
58
59 enum URLCharacterClasses {
60     // alpha 
61     SchemeFirstChar = 1 << 0,
62
63     // ( alpha | digit | "+" | "-" | "." )
64     SchemeChar = 1 << 1,
65
66     // mark        = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
67     // unreserved  = alphanum | mark
68     // ( unreserved | escaped | ";" | ":" | "&" | "=" | "+" | "$" | "," )
69     UserInfoChar = 1 << 2,
70
71     // alnum | "." | "-" | "%"
72     // The above is what the specification says, but we are lenient to
73     // match existing practice and also allow:
74     // "_"
75     HostnameChar = 1 << 3,
76
77     // hexdigit | ":" | "%"
78     IPv6Char = 1 << 4,
79
80     // "#" | "?" | "/" | nul
81     PathSegmentEndChar = 1 << 5,
82
83     // not allowed in path
84     BadChar = 1 << 6,
85
86     // "\t" | "\n" | "\r"
87     TabNewline = 1 << 7
88 };
89
90 static const unsigned char characterClassTable[256] = {
91     /* 0 nul */ PathSegmentEndChar,    /* 1 soh */ BadChar,
92     /* 2 stx */ BadChar,    /* 3 etx */ BadChar,
93     /* 4 eot */ BadChar,    /* 5 enq */ BadChar,    /* 6 ack */ BadChar,    /* 7 bel */ BadChar,
94     /* 8 bs */ BadChar,     /* 9 ht */ BadChar | TabNewline,                /* 10 nl */ BadChar | TabNewline,
95     /* 11 vt */ BadChar,    /* 12 np */ BadChar,    /* 13 cr */ BadChar | TabNewline,
96     /* 14 so */ BadChar,    /* 15 si */ BadChar,
97     /* 16 dle */ BadChar,   /* 17 dc1 */ BadChar,   /* 18 dc2 */ BadChar,   /* 19 dc3 */ BadChar,
98     /* 20 dc4 */ BadChar,   /* 21 nak */ BadChar,   /* 22 syn */ BadChar,   /* 23 etb */ BadChar,
99     /* 24 can */ BadChar,   /* 25 em */ BadChar,    /* 26 sub */ BadChar,   /* 27 esc */ BadChar,
100     /* 28 fs */ BadChar,    /* 29 gs */ BadChar,    /* 30 rs */ BadChar,    /* 31 us */ BadChar,
101     /* 32 sp */ BadChar,    /* 33  ! */ UserInfoChar,
102     /* 34  " */ BadChar,    /* 35  # */ PathSegmentEndChar | BadChar,
103     /* 36  $ */ UserInfoChar,    /* 37  % */ UserInfoChar | HostnameChar | IPv6Char | BadChar,
104     /* 38  & */ UserInfoChar,    /* 39  ' */ UserInfoChar,
105     /* 40  ( */ UserInfoChar,    /* 41  ) */ UserInfoChar,
106     /* 42  * */ UserInfoChar,    /* 43  + */ SchemeChar | UserInfoChar,
107     /* 44  , */ UserInfoChar,
108     /* 45  - */ SchemeChar | UserInfoChar | HostnameChar,
109     /* 46  . */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
110     /* 47  / */ PathSegmentEndChar,
111     /* 48  0 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
112     /* 49  1 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,    
113     /* 50  2 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
114     /* 51  3 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
115     /* 52  4 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
116     /* 53  5 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
117     /* 54  6 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
118     /* 55  7 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
119     /* 56  8 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
120     /* 57  9 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
121     /* 58  : */ UserInfoChar | IPv6Char,    /* 59  ; */ UserInfoChar,
122     /* 60  < */ BadChar,    /* 61  = */ UserInfoChar,
123     /* 62  > */ BadChar,    /* 63  ? */ PathSegmentEndChar | BadChar,
124     /* 64  @ */ 0,
125     /* 65  A */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,    
126     /* 66  B */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
127     /* 67  C */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
128     /* 68  D */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
129     /* 69  E */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
130     /* 70  F */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
131     /* 71  G */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
132     /* 72  H */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
133     /* 73  I */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
134     /* 74  J */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
135     /* 75  K */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
136     /* 76  L */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
137     /* 77  M */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
138     /* 78  N */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
139     /* 79  O */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
140     /* 80  P */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
141     /* 81  Q */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
142     /* 82  R */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
143     /* 83  S */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
144     /* 84  T */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
145     /* 85  U */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
146     /* 86  V */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
147     /* 87  W */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
148     /* 88  X */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
149     /* 89  Y */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
150     /* 90  Z */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
151     /* 91  [ */ 0,
152     /* 92  \ */ 0,    /* 93  ] */ 0,
153     /* 94  ^ */ 0,
154     /* 95  _ */ UserInfoChar | HostnameChar,
155     /* 96  ` */ 0,
156     /* 97  a */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
157     /* 98  b */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
158     /* 99  c */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
159     /* 100  d */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
160     /* 101  e */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char,
161     /* 102  f */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, 
162     /* 103  g */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
163     /* 104  h */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
164     /* 105  i */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
165     /* 106  j */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
166     /* 107  k */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
167     /* 108  l */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
168     /* 109  m */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
169     /* 110  n */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
170     /* 111  o */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
171     /* 112  p */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
172     /* 113  q */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
173     /* 114  r */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
174     /* 115  s */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
175     /* 116  t */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
176     /* 117  u */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
177     /* 118  v */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
178     /* 119  w */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
179     /* 120  x */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
180     /* 121  y */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar,
181     /* 122  z */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, 
182     /* 123  { */ 0,
183     /* 124  | */ 0,   /* 125  } */ 0,   /* 126  ~ */ UserInfoChar,   /* 127 del */ BadChar,
184     /* 128 */ BadChar, /* 129 */ BadChar, /* 130 */ BadChar, /* 131 */ BadChar,
185     /* 132 */ BadChar, /* 133 */ BadChar, /* 134 */ BadChar, /* 135 */ BadChar,
186     /* 136 */ BadChar, /* 137 */ BadChar, /* 138 */ BadChar, /* 139 */ BadChar,
187     /* 140 */ BadChar, /* 141 */ BadChar, /* 142 */ BadChar, /* 143 */ BadChar,
188     /* 144 */ BadChar, /* 145 */ BadChar, /* 146 */ BadChar, /* 147 */ BadChar,
189     /* 148 */ BadChar, /* 149 */ BadChar, /* 150 */ BadChar, /* 151 */ BadChar,
190     /* 152 */ BadChar, /* 153 */ BadChar, /* 154 */ BadChar, /* 155 */ BadChar,
191     /* 156 */ BadChar, /* 157 */ BadChar, /* 158 */ BadChar, /* 159 */ BadChar,
192     /* 160 */ BadChar, /* 161 */ BadChar, /* 162 */ BadChar, /* 163 */ BadChar,
193     /* 164 */ BadChar, /* 165 */ BadChar, /* 166 */ BadChar, /* 167 */ BadChar,
194     /* 168 */ BadChar, /* 169 */ BadChar, /* 170 */ BadChar, /* 171 */ BadChar,
195     /* 172 */ BadChar, /* 173 */ BadChar, /* 174 */ BadChar, /* 175 */ BadChar,
196     /* 176 */ BadChar, /* 177 */ BadChar, /* 178 */ BadChar, /* 179 */ BadChar,
197     /* 180 */ BadChar, /* 181 */ BadChar, /* 182 */ BadChar, /* 183 */ BadChar,
198     /* 184 */ BadChar, /* 185 */ BadChar, /* 186 */ BadChar, /* 187 */ BadChar,
199     /* 188 */ BadChar, /* 189 */ BadChar, /* 190 */ BadChar, /* 191 */ BadChar,
200     /* 192 */ BadChar, /* 193 */ BadChar, /* 194 */ BadChar, /* 195 */ BadChar,
201     /* 196 */ BadChar, /* 197 */ BadChar, /* 198 */ BadChar, /* 199 */ BadChar,
202     /* 200 */ BadChar, /* 201 */ BadChar, /* 202 */ BadChar, /* 203 */ BadChar,
203     /* 204 */ BadChar, /* 205 */ BadChar, /* 206 */ BadChar, /* 207 */ BadChar,
204     /* 208 */ BadChar, /* 209 */ BadChar, /* 210 */ BadChar, /* 211 */ BadChar,
205     /* 212 */ BadChar, /* 213 */ BadChar, /* 214 */ BadChar, /* 215 */ BadChar,
206     /* 216 */ BadChar, /* 217 */ BadChar, /* 218 */ BadChar, /* 219 */ BadChar,
207     /* 220 */ BadChar, /* 221 */ BadChar, /* 222 */ BadChar, /* 223 */ BadChar,
208     /* 224 */ BadChar, /* 225 */ BadChar, /* 226 */ BadChar, /* 227 */ BadChar,
209     /* 228 */ BadChar, /* 229 */ BadChar, /* 230 */ BadChar, /* 231 */ BadChar,
210     /* 232 */ BadChar, /* 233 */ BadChar, /* 234 */ BadChar, /* 235 */ BadChar,
211     /* 236 */ BadChar, /* 237 */ BadChar, /* 238 */ BadChar, /* 239 */ BadChar,
212     /* 240 */ BadChar, /* 241 */ BadChar, /* 242 */ BadChar, /* 243 */ BadChar,
213     /* 244 */ BadChar, /* 245 */ BadChar, /* 246 */ BadChar, /* 247 */ BadChar,
214     /* 248 */ BadChar, /* 249 */ BadChar, /* 250 */ BadChar, /* 251 */ BadChar,
215     /* 252 */ BadChar, /* 253 */ BadChar, /* 254 */ BadChar, /* 255 */ BadChar
216 };
217
218 enum PercentEncodeCharacterClass {
219     // Class names match the URL Standard; each class is a superset of the previous one.
220     PercentEncodeSimple = 255,
221     PercentEncodeDefault = 127,
222     PercentEncodePassword = 63,
223     PercentEncodeUsername = 31,
224 };
225
226 static const unsigned char percentEncodeClassTable[256] = {
227     /* 0 nul */ PercentEncodeSimple,    /* 1 soh */ PercentEncodeSimple,    /* 2 stx */ PercentEncodeSimple,    /* 3 etx */ PercentEncodeSimple,
228     /* 4 eot */ PercentEncodeSimple,    /* 5 enq */ PercentEncodeSimple,    /* 6 ack */ PercentEncodeSimple,    /* 7 bel */ PercentEncodeSimple,
229     /* 8 bs */ PercentEncodeSimple,     /* 9 ht */ PercentEncodeSimple,     /* 10 nl */ PercentEncodeSimple,    /* 11 vt */ PercentEncodeSimple,
230     /* 12 np */ PercentEncodeSimple,    /* 13 cr */ PercentEncodeSimple,    /* 14 so */ PercentEncodeSimple,    /* 15 si */ PercentEncodeSimple,
231     /* 16 dle */ PercentEncodeSimple,   /* 17 dc1 */ PercentEncodeSimple,   /* 18 dc2 */ PercentEncodeSimple,   /* 19 dc3 */ PercentEncodeSimple,
232     /* 20 dc4 */ PercentEncodeSimple,   /* 21 nak */ PercentEncodeSimple,   /* 22 syn */ PercentEncodeSimple,   /* 23 etb */ PercentEncodeSimple,
233     /* 24 can */ PercentEncodeSimple,   /* 25 em */ PercentEncodeSimple,    /* 26 sub */ PercentEncodeSimple,   /* 27 esc */ PercentEncodeSimple,
234     /* 28 fs */ PercentEncodeSimple,    /* 29 gs */ PercentEncodeSimple,    /* 30 rs */ PercentEncodeSimple,    /* 31 us */ PercentEncodeSimple,
235     /* 32 sp */ PercentEncodeDefault,
236     /* 33  ! */ 0,
237     /* 34  " */ PercentEncodeDefault,
238     /* 35  # */ PercentEncodeDefault,
239     /* 36  $ */ 0,
240     /* 37  % */ 0,
241     /* 38  & */ 0,
242     /* 39  ' */ 0,
243     /* 40  ( */ 0,
244     /* 41  ) */ 0,
245     /* 42  * */ 0,
246     /* 43  + */ 0,
247     /* 44  , */ 0,
248     /* 45  - */ 0,
249     /* 46  . */ 0,
250     /* 47  / */ PercentEncodePassword,
251     /* 48  0 */ 0,    /* 49  1 */ 0,    /* 50  2 */ 0,    /* 51  3 */ 0,
252     /* 52  4 */ 0,    /* 53  5 */ 0,    /* 54  6 */ 0,    /* 55  7 */ 0,
253     /* 56  8 */ 0,    /* 57  9 */ 0,
254     /* 58  : */ PercentEncodeUsername,
255     /* 59  ; */ 0,
256     /* 60  < */ PercentEncodeDefault,
257     /* 61  = */ 0,
258     /* 62  > */ PercentEncodeDefault,
259     /* 63  ? */ PercentEncodeDefault,
260     /* 64  @ */ PercentEncodePassword,
261     /* 65  A */ 0,    /* 66  B */ 0,    /* 67  C */ 0,    /* 68  D */ 0,
262     /* 69  E */ 0,    /* 70  F */ 0,    /* 71  G */ 0,    /* 72  H */ 0,
263     /* 73  I */ 0,    /* 74  J */ 0,    /* 75  K */ 0,    /* 76  L */ 0,
264     /* 77  M */ 0,    /* 78  N */ 0,    /* 79  O */ 0,    /* 80  P */ 0,
265     /* 81  Q */ 0,    /* 82  R */ 0,    /* 83  S */ 0,    /* 84  T */ 0,
266     /* 85  U */ 0,    /* 86  V */ 0,    /* 87  W */ 0,    /* 88  X */ 0,
267     /* 89  Y */ 0,    /* 90  Z */ 0,
268     /* 91  [ */ 0,
269     /* 92  \ */ PercentEncodePassword,
270     /* 93  ] */ 0,
271     /* 94  ^ */ 0,
272     /* 95  _ */ 0,
273     /* 96  ` */ PercentEncodeDefault,
274     /* 97  a */ 0,    /* 98  b */ 0,    /* 99  c */ 0,    /* 100  d */ 0,
275     /* 101  e */ 0,    /* 102  f */ 0,    /* 103  g */ 0,    /* 104  h */ 0,
276     /* 105  i */ 0,    /* 106  j */ 0,    /* 107  k */ 0,    /* 108  l */ 0,
277     /* 109  m */ 0,    /* 110  n */ 0,    /* 111  o */ 0,    /* 112  p */ 0,
278     /* 113  q */ 0,    /* 114  r */ 0,    /* 115  s */ 0,    /* 116  t */ 0,
279     /* 117  u */ 0,    /* 118  v */ 0,    /* 119  w */ 0,    /* 120  x */ 0,
280     /* 121  y */ 0,    /* 122  z */ 0,
281     /* 123  { */ 0,
282     /* 124  | */ 0,
283     /* 125  } */ 0,
284     /* 126  ~ */ 0,
285     /* 127 del */ PercentEncodeSimple,
286     /* 128 */ PercentEncodeSimple, /* 129 */ PercentEncodeSimple, /* 130 */ PercentEncodeSimple, /* 131 */ PercentEncodeSimple,
287     /* 132 */ PercentEncodeSimple, /* 133 */ PercentEncodeSimple, /* 134 */ PercentEncodeSimple, /* 135 */ PercentEncodeSimple,
288     /* 136 */ PercentEncodeSimple, /* 137 */ PercentEncodeSimple, /* 138 */ PercentEncodeSimple, /* 139 */ PercentEncodeSimple,
289     /* 140 */ PercentEncodeSimple, /* 141 */ PercentEncodeSimple, /* 142 */ PercentEncodeSimple, /* 143 */ PercentEncodeSimple,
290     /* 144 */ PercentEncodeSimple, /* 145 */ PercentEncodeSimple, /* 146 */ PercentEncodeSimple, /* 147 */ PercentEncodeSimple,
291     /* 148 */ PercentEncodeSimple, /* 149 */ PercentEncodeSimple, /* 150 */ PercentEncodeSimple, /* 151 */ PercentEncodeSimple,
292     /* 152 */ PercentEncodeSimple, /* 153 */ PercentEncodeSimple, /* 154 */ PercentEncodeSimple, /* 155 */ PercentEncodeSimple,
293     /* 156 */ PercentEncodeSimple, /* 157 */ PercentEncodeSimple, /* 158 */ PercentEncodeSimple, /* 159 */ PercentEncodeSimple,
294     /* 160 */ PercentEncodeSimple, /* 161 */ PercentEncodeSimple, /* 162 */ PercentEncodeSimple, /* 163 */ PercentEncodeSimple,
295     /* 164 */ PercentEncodeSimple, /* 165 */ PercentEncodeSimple, /* 166 */ PercentEncodeSimple, /* 167 */ PercentEncodeSimple,
296     /* 168 */ PercentEncodeSimple, /* 169 */ PercentEncodeSimple, /* 170 */ PercentEncodeSimple, /* 171 */ PercentEncodeSimple,
297     /* 172 */ PercentEncodeSimple, /* 173 */ PercentEncodeSimple, /* 174 */ PercentEncodeSimple, /* 175 */ PercentEncodeSimple,
298     /* 176 */ PercentEncodeSimple, /* 177 */ PercentEncodeSimple, /* 178 */ PercentEncodeSimple, /* 179 */ PercentEncodeSimple,
299     /* 180 */ PercentEncodeSimple, /* 181 */ PercentEncodeSimple, /* 182 */ PercentEncodeSimple, /* 183 */ PercentEncodeSimple,
300     /* 184 */ PercentEncodeSimple, /* 185 */ PercentEncodeSimple, /* 186 */ PercentEncodeSimple, /* 187 */ PercentEncodeSimple,
301     /* 188 */ PercentEncodeSimple, /* 189 */ PercentEncodeSimple, /* 190 */ PercentEncodeSimple, /* 191 */ PercentEncodeSimple,
302     /* 192 */ PercentEncodeSimple, /* 193 */ PercentEncodeSimple, /* 194 */ PercentEncodeSimple, /* 195 */ PercentEncodeSimple,
303     /* 196 */ PercentEncodeSimple, /* 197 */ PercentEncodeSimple, /* 198 */ PercentEncodeSimple, /* 199 */ PercentEncodeSimple,
304     /* 200 */ PercentEncodeSimple, /* 201 */ PercentEncodeSimple, /* 202 */ PercentEncodeSimple, /* 203 */ PercentEncodeSimple,
305     /* 204 */ PercentEncodeSimple, /* 205 */ PercentEncodeSimple, /* 206 */ PercentEncodeSimple, /* 207 */ PercentEncodeSimple,
306     /* 208 */ PercentEncodeSimple, /* 209 */ PercentEncodeSimple, /* 210 */ PercentEncodeSimple, /* 211 */ PercentEncodeSimple,
307     /* 212 */ PercentEncodeSimple, /* 213 */ PercentEncodeSimple, /* 214 */ PercentEncodeSimple, /* 215 */ PercentEncodeSimple,
308     /* 216 */ PercentEncodeSimple, /* 217 */ PercentEncodeSimple, /* 218 */ PercentEncodeSimple, /* 219 */ PercentEncodeSimple,
309     /* 220 */ PercentEncodeSimple, /* 221 */ PercentEncodeSimple, /* 222 */ PercentEncodeSimple, /* 223 */ PercentEncodeSimple,
310     /* 224 */ PercentEncodeSimple, /* 225 */ PercentEncodeSimple, /* 226 */ PercentEncodeSimple, /* 227 */ PercentEncodeSimple,
311     /* 228 */ PercentEncodeSimple, /* 229 */ PercentEncodeSimple, /* 230 */ PercentEncodeSimple, /* 231 */ PercentEncodeSimple,
312     /* 232 */ PercentEncodeSimple, /* 233 */ PercentEncodeSimple, /* 234 */ PercentEncodeSimple, /* 235 */ PercentEncodeSimple,
313     /* 236 */ PercentEncodeSimple, /* 237 */ PercentEncodeSimple, /* 238 */ PercentEncodeSimple, /* 239 */ PercentEncodeSimple,
314     /* 240 */ PercentEncodeSimple, /* 241 */ PercentEncodeSimple, /* 242 */ PercentEncodeSimple, /* 243 */ PercentEncodeSimple,
315     /* 244 */ PercentEncodeSimple, /* 245 */ PercentEncodeSimple, /* 246 */ PercentEncodeSimple, /* 247 */ PercentEncodeSimple,
316     /* 248 */ PercentEncodeSimple, /* 249 */ PercentEncodeSimple, /* 250 */ PercentEncodeSimple, /* 251 */ PercentEncodeSimple,
317     /* 252 */ PercentEncodeSimple, /* 253 */ PercentEncodeSimple, /* 254 */ PercentEncodeSimple, /* 255 */ PercentEncodeSimple
318 };
319
320 static inline bool isSchemeFirstChar(UChar c) { return c <= 0xff && (characterClassTable[c] & SchemeFirstChar); }
321 static inline bool isSchemeChar(UChar c) { return c <= 0xff && (characterClassTable[c] & SchemeChar); }
322 static inline bool isBadChar(unsigned char c) { return characterClassTable[c] & BadChar; }
323 static inline bool isTabNewline(UChar c) { return c <= 0xff && (characterClassTable[c] & TabNewline); }
324
325 String encodeWithURLEscapeSequences(const String& notEncodedString, PercentEncodeCharacterClass whatToEncode);
326
327 // Copies the source to the destination, assuming all the source characters are
328 // ASCII. The destination buffer must be large enough. Null characters are allowed
329 // in the source string, and no attempt is made to null-terminate the result.
330 static void copyASCII(const String& string, char* dest)
331 {
332     if (string.isEmpty())
333         return;
334
335     if (string.is8Bit())
336         memcpy(dest, string.characters8(), string.length());
337     else {
338         const UChar* src = string.characters16();
339         size_t length = string.length();
340         for (size_t i = 0; i < length; i++)
341             dest[i] = static_cast<char>(src[i]);
342     }
343 }
344
345 void URL::invalidate()
346 {
347     m_isValid = false;
348     m_protocolIsInHTTPFamily = false;
349     m_cannotBeABaseURL = false;
350     m_schemeEnd = 0;
351     m_userStart = 0;
352     m_userEnd = 0;
353     m_passwordEnd = 0;
354     m_hostEnd = 0;
355     m_portEnd = 0;
356     m_pathEnd = 0;
357     m_pathAfterLastSlash = 0;
358     m_queryEnd = 0;
359 }
360
361 URL::URL(ParsedURLStringTag, const String& url)
362 {
363     URLParser parser(url);
364     *this = parser.result();
365
366 #if OS(WINDOWS)
367     // FIXME(148598): Work around Windows local file handling bug in CFNetwork
368     ASSERT(isLocalFile() || url == m_string);
369 #else
370     ASSERT(url == m_string);
371 #endif
372 }
373
374 URL::URL(const URL& base, const String& relative)
375 {
376     URLParser parser(relative, base);
377     *this = parser.result();
378 }
379
380 URL::URL(const URL& base, const String& relative, const TextEncoding& encoding)
381 {
382     // For UTF-{7,16,32}, we want to use UTF-8 for the query part as
383     // we do when submitting a form. A form with GET method
384     // has its contents added to a URL as query params and it makes sense
385     // to be consistent.
386     URLParser parser(relative, base, encoding.encodingForFormSubmission());
387     *this = parser.result();
388 }
389
390 static bool shouldTrimFromURL(UChar c)
391 {
392     // Browsers ignore leading/trailing whitespace and control
393     // characters from URLs.  Note that c is an *unsigned* char here
394     // so this comparison should only catch control characters.
395     return c <= ' ';
396 }
397
398 URL URL::isolatedCopy() const
399 {
400     URL result = *this;
401     result.m_string = result.m_string.isolatedCopy();
402     return result;
403 }
404
405 String URL::lastPathComponent() const
406 {
407     if (!hasPath())
408         return String();
409
410     unsigned end = m_pathEnd - 1;
411     if (m_string[end] == '/')
412         --end;
413
414     size_t start = m_string.reverseFind('/', end);
415     if (start < static_cast<unsigned>(m_portEnd))
416         return String();
417     ++start;
418
419     return m_string.substring(start, end - start + 1);
420 }
421
422 StringView URL::protocol() const
423 {
424     return StringView(m_string).substring(0, m_schemeEnd);
425 }
426
427 String URL::host() const
428 {
429     unsigned start = hostStart();
430     return m_string.substring(start, m_hostEnd - start);
431 }
432
433 std::optional<uint16_t> URL::port() const
434 {
435     if (!m_portEnd || m_hostEnd >= m_portEnd - 1)
436         return std::nullopt;
437
438     bool ok = false;
439     unsigned number;
440     if (m_string.is8Bit())
441         number = charactersToUIntStrict(m_string.characters8() + m_hostEnd + 1, m_portEnd - m_hostEnd - 1, &ok);
442     else
443         number = charactersToUIntStrict(m_string.characters16() + m_hostEnd + 1, m_portEnd - m_hostEnd - 1, &ok);
444     if (!ok || number > std::numeric_limits<uint16_t>::max())
445         return std::nullopt;
446     return number;
447 }
448
449 String URL::hostAndPort() const
450 {
451     if (auto port = this->port())
452         return host() + ':' + String::number(port.value());
453     return host();
454 }
455
456 String URL::protocolHostAndPort() const
457 {
458     String result = m_string.substring(0, m_portEnd);
459
460     if (m_passwordEnd - m_userStart > 0) {
461         const int allowForTrailingAtSign = 1;
462         result.remove(m_userStart, m_passwordEnd - m_userStart + allowForTrailingAtSign);
463     }
464
465     return result;
466 }
467
468 String URL::user() const
469 {
470     return decodeURLEscapeSequences(m_string.substring(m_userStart, m_userEnd - m_userStart));
471 }
472
473 String URL::pass() const
474 {
475     if (m_passwordEnd == m_userEnd)
476         return String();
477
478     return decodeURLEscapeSequences(m_string.substring(m_userEnd + 1, m_passwordEnd - m_userEnd - 1));
479 }
480
481 String URL::encodedUser() const
482 {
483     return m_string.substring(m_userStart, m_userEnd - m_userStart);
484 }
485
486 String URL::encodedPass() const
487 {
488     if (m_passwordEnd == m_userEnd)
489         return String();
490
491     return m_string.substring(m_userEnd + 1, m_passwordEnd - m_userEnd - 1);
492 }
493
494 String URL::fragmentIdentifier() const
495 {
496     if (!m_isValid || m_queryEnd == m_string.length())
497         return String();
498
499     return m_string.substring(m_queryEnd + 1);
500 }
501
502 bool URL::hasFragmentIdentifier() const
503 {
504     return m_isValid && m_string.length() != m_queryEnd;
505 }
506
507 String URL::baseAsString() const
508 {
509     return m_string.left(m_pathAfterLastSlash);
510 }
511
512 #if !USE(CF)
513
514 String URL::fileSystemPath() const
515 {
516     if (!isValid() || !isLocalFile())
517         return String();
518
519     return decodeURLEscapeSequences(path());
520 }
521
522 #endif
523
524 #ifdef NDEBUG
525
526 static inline void assertProtocolIsGood(StringView)
527 {
528 }
529
530 #else
531
532 static void assertProtocolIsGood(StringView protocol)
533 {
534     // FIXME: We probably don't need this function any more.
535     // The isASCIIAlphaCaselessEqual function asserts that passed-in characters
536     // are ones it can handle; the older code did not and relied on these checks.
537     for (auto character : protocol.codeUnits()) {
538         ASSERT(isASCII(character));
539         ASSERT(character > ' ');
540         ASSERT(!isASCIIUpper(character));
541         ASSERT(toASCIILowerUnchecked(character) == character);
542     }
543 }
544
545 #endif
546
547 static Lock& defaultPortForProtocolMapForTestingLock()
548 {
549     static NeverDestroyed<Lock> lock;
550     return lock;
551 }
552
553 using DefaultPortForProtocolMapForTesting = HashMap<String, uint16_t>;
554 static DefaultPortForProtocolMapForTesting*& defaultPortForProtocolMapForTesting()
555 {
556     static DefaultPortForProtocolMapForTesting* defaultPortForProtocolMap;
557     return defaultPortForProtocolMap;
558 }
559
560 static DefaultPortForProtocolMapForTesting& ensureDefaultPortForProtocolMapForTesting()
561 {
562     DefaultPortForProtocolMapForTesting*& defaultPortForProtocolMap = defaultPortForProtocolMapForTesting();
563     if (!defaultPortForProtocolMap)
564         defaultPortForProtocolMap = new DefaultPortForProtocolMapForTesting;
565     return *defaultPortForProtocolMap;
566 }
567
568 void registerDefaultPortForProtocolForTesting(uint16_t port, const String& protocol)
569 {
570     LockHolder locker(defaultPortForProtocolMapForTestingLock());
571     ensureDefaultPortForProtocolMapForTesting().add(protocol, port);
572 }
573
574 void clearDefaultPortForProtocolMapForTesting()
575 {
576     LockHolder locker(defaultPortForProtocolMapForTestingLock());
577     if (auto* map = defaultPortForProtocolMapForTesting())
578         map->clear();
579 }
580
581 std::optional<uint16_t> defaultPortForProtocol(StringView protocol)
582 {
583     if (auto* overrideMap = defaultPortForProtocolMapForTesting()) {
584         LockHolder locker(defaultPortForProtocolMapForTestingLock());
585         ASSERT(overrideMap); // No need to null check again here since overrideMap cannot become null after being non-null.
586         auto iterator = overrideMap->find(protocol.toStringWithoutCopying());
587         if (iterator != overrideMap->end())
588             return iterator->value;
589     }
590     return URLParser::defaultPortForProtocol(protocol);
591 }
592
593 bool isDefaultPortForProtocol(uint16_t port, StringView protocol)
594 {
595     return defaultPortForProtocol(protocol) == port;
596 }
597
598 bool URL::protocolIs(const char* protocol) const
599 {
600     assertProtocolIsGood(StringView(reinterpret_cast<const LChar*>(protocol), strlen(protocol)));
601
602     // JavaScript URLs are "valid" and should be executed even if URL decides they are invalid.
603     // The free function protocolIsJavaScript() should be used instead. 
604     ASSERT(!equalLettersIgnoringASCIICase(StringView(protocol), "javascript"));
605
606     if (!m_isValid)
607         return false;
608
609     // Do the comparison without making a new string object.
610     for (unsigned i = 0; i < m_schemeEnd; ++i) {
611         if (!protocol[i] || !isASCIIAlphaCaselessEqual(m_string[i], protocol[i]))
612             return false;
613     }
614     return !protocol[m_schemeEnd]; // We should have consumed all characters in the argument.
615 }
616
617 bool URL::protocolIs(StringView protocol) const
618 {
619     assertProtocolIsGood(protocol);
620
621     if (!m_isValid)
622         return false;
623     
624     if (m_schemeEnd != protocol.length())
625         return false;
626
627     // Do the comparison without making a new string object.
628     for (unsigned i = 0; i < m_schemeEnd; ++i) {
629         if (!isASCIIAlphaCaselessEqual(m_string[i], protocol[i]))
630             return false;
631     }
632     return true;
633 }
634
635 String URL::query() const
636 {
637     if (m_queryEnd == m_pathEnd)
638         return String();
639
640     return m_string.substring(m_pathEnd + 1, m_queryEnd - (m_pathEnd + 1)); 
641 }
642
643 String URL::path() const
644 {
645     return m_string.substring(m_portEnd, m_pathEnd - m_portEnd);
646 }
647
648 bool URL::setProtocol(const String& s)
649 {
650     // Firefox and IE remove everything after the first ':'.
651     size_t separatorPosition = s.find(':');
652     String newProtocol = s.substring(0, separatorPosition);
653
654     if (!isValidProtocol(newProtocol))
655         return false;
656
657     if (!m_isValid) {
658         URLParser parser(makeString(newProtocol, ":", m_string));
659         *this = parser.result();
660         return true;
661     }
662
663     URLParser parser(makeString(newProtocol, m_string.substring(m_schemeEnd)));
664     *this = parser.result();
665     return true;
666 }
667
668 static bool containsOnlyASCII(StringView string)
669 {
670     if (string.is8Bit())
671         return charactersAreAllASCII(string.characters8(), string.length());
672     return charactersAreAllASCII(string.characters16(), string.length());
673 }
674     
675 // Appends the punycoded hostname identified by the given string and length to
676 // the output buffer. The result will not be null terminated.
677 // Return value of false means error in encoding.
678 static bool appendEncodedHostname(UCharBuffer& buffer, StringView string)
679 {
680     // Needs to be big enough to hold an IDN-encoded name.
681     // For host names bigger than this, we won't do IDN encoding, which is almost certainly OK.
682     const unsigned hostnameBufferLength = 2048;
683     
684     if (string.length() > hostnameBufferLength || containsOnlyASCII(string)) {
685         append(buffer, string);
686         return true;
687     }
688     
689     UChar hostnameBuffer[hostnameBufferLength];
690     UErrorCode error = U_ZERO_ERROR;
691     
692 #if COMPILER(GCC_OR_CLANG)
693 #pragma GCC diagnostic push
694 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
695 #endif
696     int32_t numCharactersConverted = uidna_IDNToASCII(string.upconvertedCharacters(), string.length(), hostnameBuffer,
697         hostnameBufferLength, UIDNA_ALLOW_UNASSIGNED, 0, &error);
698 #if COMPILER(GCC_OR_CLANG)
699 #pragma GCC diagnostic pop
700 #endif
701     
702     if (error == U_ZERO_ERROR) {
703         buffer.append(hostnameBuffer, numCharactersConverted);
704         return true;
705     }
706     return false;
707 }
708     
709 void URL::setHost(const String& s)
710 {
711     if (!m_isValid)
712         return;
713
714     auto colonIndex = s.find(':');
715     if (colonIndex != notFound)
716         return;
717
718     UCharBuffer encodedHostName;
719     if (!appendEncodedHostname(encodedHostName, s))
720         return;
721     
722     bool slashSlashNeeded = m_userStart == m_schemeEnd + 1;
723     
724     StringBuilder builder;
725     builder.append(m_string.left(hostStart()));
726     if (slashSlashNeeded)
727         builder.appendLiteral("//");
728     builder.append(StringView(encodedHostName.data(), encodedHostName.size()));
729     builder.append(m_string.substring(m_hostEnd));
730
731     URLParser parser(builder.toString());
732     *this = parser.result();
733 }
734
735 void URL::removePort()
736 {
737     if (m_hostEnd == m_portEnd)
738         return;
739     URLParser parser(m_string.left(m_hostEnd) + m_string.substring(m_portEnd));
740     *this = parser.result();
741 }
742
743 void URL::setPort(unsigned short i)
744 {
745     if (!m_isValid)
746         return;
747
748     bool colonNeeded = m_portEnd == m_hostEnd;
749     unsigned portStart = (colonNeeded ? m_hostEnd : m_hostEnd + 1);
750
751     URLParser parser(makeString(m_string.left(portStart), (colonNeeded ? ":" : ""), String::number(i), m_string.substring(m_portEnd)));
752     *this = parser.result();
753 }
754
755 void URL::setHostAndPort(const String& hostAndPort)
756 {
757     if (!m_isValid)
758         return;
759
760     StringView hostName(hostAndPort);
761     StringView port;
762     
763     auto colonIndex = hostName.find(':');
764     if (colonIndex != notFound) {
765         port = hostName.substring(colonIndex + 1);
766         bool ok;
767         int portInt = port.toIntStrict(ok);
768         if (!ok || portInt < 0)
769             return;
770         hostName = hostName.substring(0, colonIndex);
771     }
772
773     if (hostName.isEmpty())
774         return;
775
776     UCharBuffer encodedHostName;
777     if (!appendEncodedHostname(encodedHostName, hostName))
778         return;
779
780     bool slashSlashNeeded = m_userStart == m_schemeEnd + 1;
781
782     StringBuilder builder;
783     builder.append(m_string.left(hostStart()));
784     if (slashSlashNeeded)
785         builder.appendLiteral("//");
786     builder.append(StringView(encodedHostName.data(), encodedHostName.size()));
787     if (!port.isEmpty()) {
788         builder.appendLiteral(":");
789         builder.append(port);
790     }
791     builder.append(m_string.substring(m_portEnd));
792
793     URLParser parser(builder.toString());
794     *this = parser.result();
795 }
796
797 void URL::setUser(const String& user)
798 {
799     if (!m_isValid)
800         return;
801
802     // FIXME: Non-ASCII characters must be encoded and escaped to match parse() expectations,
803     // and to avoid changing more than just the user login.
804
805     unsigned end = m_userEnd;
806     if (!user.isEmpty()) {
807         String u = encodeWithURLEscapeSequences(user, PercentEncodeUsername);
808         if (m_userStart == m_schemeEnd + 1)
809             u = "//" + u;
810         // Add '@' if we didn't have one before.
811         if (end == m_hostEnd || (end == m_passwordEnd && m_string[end] != '@'))
812             u.append('@');
813         URLParser parser(makeString(m_string.left(m_userStart), u, m_string.substring(end)));
814         *this = parser.result();
815     } else {
816         // Remove '@' if we now have neither user nor password.
817         if (m_userEnd == m_passwordEnd && end != m_hostEnd && m_string[end] == '@')
818             end += 1;
819         // We don't want to parse in the extremely common case where we are not going to make a change.
820         if (m_userStart != end) {
821             URLParser parser(makeString(m_string.left(m_userStart), m_string.substring(end)));
822             *this = parser.result();
823         }
824     }
825 }
826
827 void URL::setPass(const String& password)
828 {
829     if (!m_isValid)
830         return;
831
832     unsigned end = m_passwordEnd;
833     if (!password.isEmpty()) {
834         String p = ":" + encodeWithURLEscapeSequences(password, PercentEncodePassword) + "@";
835         if (m_userEnd == m_schemeEnd + 1)
836             p = "//" + p;
837         // Eat the existing '@' since we are going to add our own.
838         if (end != m_hostEnd && m_string[end] == '@')
839             end += 1;
840         URLParser parser(makeString(m_string.left(m_userEnd), p, m_string.substring(end)));
841         *this = parser.result();
842     } else {
843         // Remove '@' if we now have neither user nor password.
844         if (m_userStart == m_userEnd && end != m_hostEnd && m_string[end] == '@')
845             end += 1;
846         // We don't want to parse in the extremely common case where we are not going to make a change.
847         if (m_userEnd != end) {
848             URLParser parser(makeString(m_string.left(m_userEnd), m_string.substring(end)));
849             *this = parser.result();
850         }
851     }
852 }
853
854 void URL::setFragmentIdentifier(StringView identifier)
855 {
856     if (!m_isValid)
857         return;
858
859     // FIXME: Optimize the case where the identifier already happens to be equal to what was passed?
860     // FIXME: Is it correct to do this without encoding and escaping non-ASCII characters?
861     *this = URLParser { makeString(StringView { m_string }.substring(0, m_queryEnd), '#', identifier) }.result();
862 }
863
864 void URL::removeFragmentIdentifier()
865 {
866     if (!m_isValid) {
867         ASSERT(!m_queryEnd);
868         return;
869     }
870     if (m_isValid && m_string.length() > m_queryEnd)
871         m_string = m_string.left(m_queryEnd);
872 }
873
874 void URL::removeQueryAndFragmentIdentifier()
875 {
876     if (!m_isValid)
877         return;
878
879     m_string = m_string.left(m_pathEnd);
880     m_queryEnd = m_pathEnd;
881 }
882
883 void URL::setQuery(const String& query)
884 {
885     if (!m_isValid)
886         return;
887
888     // FIXME: '#' and non-ASCII characters must be encoded and escaped.
889     // Usually, the query is encoded using document encoding, not UTF-8, but we don't have
890     // access to the document in this function.
891     // https://webkit.org/b/161176
892     if ((query.isEmpty() || query[0] != '?') && !query.isNull()) {
893         URLParser parser(makeString(m_string.left(m_pathEnd), "?", query, m_string.substring(m_queryEnd)));
894         *this = parser.result();
895     } else {
896         URLParser parser(makeString(m_string.left(m_pathEnd), query, m_string.substring(m_queryEnd)));
897         *this = parser.result();
898     }
899
900 }
901
902 void URL::setPath(const String& s)
903 {
904     if (!m_isValid)
905         return;
906
907     // FIXME: encodeWithURLEscapeSequences does not correctly escape '#' and '?', so fragment and query parts
908     // may be inadvertently affected.
909     String path = s;
910     if (path.isEmpty() || path[0] != '/')
911         path = "/" + path;
912
913     URLParser parser(makeString(m_string.left(m_portEnd), encodeWithURLEscapeSequences(path), m_string.substring(m_pathEnd)));
914     *this = parser.result();
915 }
916
917 String decodeURLEscapeSequences(const String& string)
918 {
919     return decodeEscapeSequences<URLEscapeSequence>(string, UTF8Encoding());
920 }
921
922 String decodeURLEscapeSequences(const String& string, const TextEncoding& encoding)
923 {
924     return decodeEscapeSequences<URLEscapeSequence>(string, encoding);
925 }
926
927 // Caution: This function does not bounds check.
928 static void appendEscapedChar(char*& buffer, unsigned char c)
929 {
930     *buffer++ = '%';
931     placeByteAsHex(c, buffer);
932 }
933
934 String URL::serialize(bool omitFragment) const
935 {
936     if (omitFragment)
937         return m_string.left(m_queryEnd);
938     return m_string;
939 }
940
941 #if PLATFORM(IOS)
942
943 static bool shouldCanonicalizeScheme = true;
944
945 void enableURLSchemeCanonicalization(bool enableSchemeCanonicalization)
946 {
947     shouldCanonicalizeScheme = enableSchemeCanonicalization;
948 }
949
950 #endif
951
952 template<size_t length>
953 static inline bool equal(const char* a, const char (&b)[length])
954 {
955 #if PLATFORM(IOS)
956     if (!shouldCanonicalizeScheme) {
957         for (size_t i = 0; i < length; ++i) {
958             if (toASCIILower(a[i]) != b[i])
959                 return false;
960         }
961         return true;
962     }
963 #endif
964     for (size_t i = 0; i < length; ++i) {
965         if (a[i] != b[i])
966             return false;
967     }
968     return true;
969 }
970
971 template<size_t lengthB>
972 static inline bool equal(const char* stringA, size_t lengthA, const char (&stringB)[lengthB])
973 {
974     return lengthA == lengthB && equal(stringA, stringB);
975 }
976
977 bool equalIgnoringFragmentIdentifier(const URL& a, const URL& b)
978 {
979     if (a.m_queryEnd != b.m_queryEnd)
980         return false;
981     unsigned queryLength = a.m_queryEnd;
982     for (unsigned i = 0; i < queryLength; ++i)
983         if (a.string()[i] != b.string()[i])
984             return false;
985     return true;
986 }
987
988 bool protocolHostAndPortAreEqual(const URL& a, const URL& b)
989 {
990     if (a.m_schemeEnd != b.m_schemeEnd)
991         return false;
992
993     unsigned hostStartA = a.hostStart();
994     unsigned hostLengthA = a.hostEnd() - hostStartA;
995     unsigned hostStartB = b.hostStart();
996     unsigned hostLengthB = b.hostEnd() - b.hostStart();
997     if (hostLengthA != hostLengthB)
998         return false;
999
1000     // Check the scheme
1001     for (unsigned i = 0; i < a.m_schemeEnd; ++i) {
1002         if (a.string()[i] != b.string()[i])
1003             return false;
1004     }
1005
1006     // And the host
1007     for (unsigned i = 0; i < hostLengthA; ++i) {
1008         if (a.string()[hostStartA + i] != b.string()[hostStartB + i])
1009             return false;
1010     }
1011
1012     if (a.port() != b.port())
1013         return false;
1014
1015     return true;
1016 }
1017
1018 bool hostsAreEqual(const URL& a, const URL& b)
1019 {
1020     unsigned hostStartA = a.hostStart();
1021     unsigned hostLengthA = a.hostEnd() - hostStartA;
1022     unsigned hostStartB = b.hostStart();
1023     unsigned hostLengthB = b.hostEnd() - hostStartB;
1024     if (hostLengthA != hostLengthB)
1025         return false;
1026
1027     for (unsigned i = 0; i < hostLengthA; ++i) {
1028         if (a.string()[hostStartA + i] != b.string()[hostStartB + i])
1029             return false;
1030     }
1031
1032     return true;
1033 }
1034
1035 String encodeWithURLEscapeSequences(const String& notEncodedString, PercentEncodeCharacterClass whatToEncode)
1036 {
1037     CString asUTF8 = notEncodedString.utf8();
1038
1039     CharBuffer buffer(asUTF8.length() * 3 + 1);
1040     char* p = buffer.data();
1041
1042     const char* str = asUTF8.data();
1043     const char* strEnd = str + asUTF8.length();
1044     while (str < strEnd) {
1045         unsigned char c = *str++;
1046         if (percentEncodeClassTable[c] >= whatToEncode)
1047             appendEscapedChar(p, c);
1048         else
1049             *p++ = c;
1050     }
1051
1052     ASSERT(p - buffer.data() <= static_cast<int>(buffer.size()));
1053
1054     return String(buffer.data(), p - buffer.data());
1055 }
1056
1057 String encodeWithURLEscapeSequences(const String& notEncodedString)
1058 {
1059     CString asUTF8 = notEncodedString.utf8();
1060
1061     CharBuffer buffer(asUTF8.length() * 3 + 1);
1062     char* p = buffer.data();
1063
1064     const char* str = asUTF8.data();
1065     const char* strEnd = str + asUTF8.length();
1066     while (str < strEnd) {
1067         unsigned char c = *str++;
1068         if (isBadChar(c))
1069             appendEscapedChar(p, c);
1070         else
1071             *p++ = c;
1072     }
1073
1074     ASSERT(p - buffer.data() <= static_cast<int>(buffer.size()));
1075
1076     return String(buffer.data(), p - buffer.data());
1077 }
1078
1079 bool URL::isHierarchical() const
1080 {
1081     if (!m_isValid)
1082         return false;
1083     ASSERT(m_string[m_schemeEnd] == ':');
1084     return m_string[m_schemeEnd + 1] == '/';
1085 }
1086
1087 void URL::copyToBuffer(Vector<char, 512>& buffer) const
1088 {
1089     // FIXME: This throws away the high bytes of all the characters in the string!
1090     // That's fine for a valid URL, which is all ASCII, but not for invalid URLs.
1091     buffer.resize(m_string.length());
1092     copyASCII(m_string, buffer.data());
1093 }
1094
1095 template<typename StringClass>
1096 bool protocolIsInternal(const StringClass& url, const char* protocol)
1097 {
1098     // Do the comparison without making a new string object.
1099     assertProtocolIsGood(StringView(reinterpret_cast<const LChar*>(protocol), strlen(protocol)));
1100     bool isLeading = true;
1101     for (unsigned i = 0, j = 0; url[i]; ++i) {
1102         // Skip leading whitespace and control characters.
1103         if (isLeading && shouldTrimFromURL(url[i]))
1104             continue;
1105         isLeading = false;
1106
1107         // Skip any tabs and newlines.
1108         if (isTabNewline(url[i]))
1109             continue;
1110
1111         if (!protocol[j])
1112             return url[i] == ':';
1113         if (!isASCIIAlphaCaselessEqual(url[i], protocol[j]))
1114             return false;
1115
1116         ++j;
1117     }
1118     
1119     return false;
1120 }
1121
1122 bool protocolIs(const String& url, const char* protocol)
1123 {
1124     return protocolIsInternal(url, protocol);
1125 }
1126
1127 inline bool URL::protocolIs(const String& string, const char* protocol)
1128 {
1129     return WebCore::protocolIsInternal(string, protocol);
1130 }
1131
1132 bool isValidProtocol(const String& protocol)
1133 {
1134     // RFC3986: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
1135     if (protocol.isEmpty())
1136         return false;
1137     if (!isSchemeFirstChar(protocol[0]))
1138         return false;
1139     unsigned protocolLength = protocol.length();
1140     for (unsigned i = 1; i < protocolLength; i++) {
1141         if (!isSchemeChar(protocol[i]))
1142             return false;
1143     }
1144     return true;
1145 }
1146
1147 #ifndef NDEBUG
1148
1149 void URL::print() const
1150 {
1151     printf("%s\n", m_string.utf8().data());
1152 }
1153
1154 #endif
1155
1156 String URL::strippedForUseAsReferrer() const
1157 {
1158     URL referrer(*this);
1159     referrer.setUser(String());
1160     referrer.setPass(String());
1161     referrer.removeFragmentIdentifier();
1162     return referrer.string();
1163 }
1164
1165 bool URL::isLocalFile() const
1166 {
1167     // Including feed here might be a bad idea since drag and drop uses this check
1168     // and including feed would allow feeds to potentially let someone's blog
1169     // read the contents of the clipboard on a drag, even without a drop.
1170     // Likewise with using the FrameLoader::shouldTreatURLAsLocal() function.
1171     return protocolIs("file");
1172 }
1173
1174 bool protocolIsJavaScript(const String& url)
1175 {
1176     return protocolIsInternal(url, "javascript");
1177 }
1178
1179 bool protocolIsJavaScript(StringView url)
1180 {
1181     return protocolIsInternal(url, "javascript");
1182 }
1183
1184 bool protocolIsInHTTPFamily(const String& url)
1185 {
1186     // Do the comparison without making a new string object.
1187     return isASCIIAlphaCaselessEqual(url[0], 'h')
1188         && isASCIIAlphaCaselessEqual(url[1], 't')
1189         && isASCIIAlphaCaselessEqual(url[2], 't')
1190         && isASCIIAlphaCaselessEqual(url[3], 'p')
1191         && (url[4] == ':' || (isASCIIAlphaCaselessEqual(url[4], 's') && url[5] == ':'));
1192 }
1193
1194 const URL& blankURL()
1195 {
1196     static NeverDestroyed<URL> staticBlankURL(ParsedURLString, "about:blank");
1197     return staticBlankURL;
1198 }
1199
1200 bool URL::isBlankURL() const
1201 {
1202     return protocolIs("about");
1203 }
1204
1205 bool portAllowed(const URL& url)
1206 {
1207     std::optional<uint16_t> port = url.port();
1208
1209     // Since most URLs don't have a port, return early for the "no port" case.
1210     if (!port)
1211         return true;
1212
1213     // This blocked port list matches the port blocking that Mozilla implements.
1214     // See http://www.mozilla.org/projects/netlib/PortBanning.html for more information.
1215     static const uint16_t blockedPortList[] = {
1216         1,    // tcpmux
1217         7,    // echo
1218         9,    // discard
1219         11,   // systat
1220         13,   // daytime
1221         15,   // netstat
1222         17,   // qotd
1223         19,   // chargen
1224         20,   // FTP-data
1225         21,   // FTP-control
1226         22,   // SSH
1227         23,   // telnet
1228         25,   // SMTP
1229         37,   // time
1230         42,   // name
1231         43,   // nicname
1232         53,   // domain
1233         77,   // priv-rjs
1234         79,   // finger
1235         87,   // ttylink
1236         95,   // supdup
1237         101,  // hostriame
1238         102,  // iso-tsap
1239         103,  // gppitnp
1240         104,  // acr-nema
1241         109,  // POP2
1242         110,  // POP3
1243         111,  // sunrpc
1244         113,  // auth
1245         115,  // SFTP
1246         117,  // uucp-path
1247         119,  // nntp
1248         123,  // NTP
1249         135,  // loc-srv / epmap
1250         139,  // netbios
1251         143,  // IMAP2
1252         179,  // BGP
1253         389,  // LDAP
1254         465,  // SMTP+SSL
1255         512,  // print / exec
1256         513,  // login
1257         514,  // shell
1258         515,  // printer
1259         526,  // tempo
1260         530,  // courier
1261         531,  // Chat
1262         532,  // netnews
1263         540,  // UUCP
1264         556,  // remotefs
1265         563,  // NNTP+SSL
1266         587,  // ESMTP
1267         601,  // syslog-conn
1268         636,  // LDAP+SSL
1269         993,  // IMAP+SSL
1270         995,  // POP3+SSL
1271         2049, // NFS
1272         3659, // apple-sasl / PasswordServer [Apple addition]
1273         4045, // lockd
1274         4190, // ManageSieve [Apple addition]
1275         6000, // X11
1276         6665, // Alternate IRC [Apple addition]
1277         6666, // Alternate IRC [Apple addition]
1278         6667, // Standard IRC [Apple addition]
1279         6668, // Alternate IRC [Apple addition]
1280         6669, // Alternate IRC [Apple addition]
1281         6679, // Alternate IRC SSL [Apple addition]
1282         6697, // IRC+SSL [Apple addition]
1283         invalidPortNumber, // Used to block all invalid port numbers
1284     };
1285
1286     // If the port is not in the blocked port list, allow it.
1287     ASSERT(std::is_sorted(std::begin(blockedPortList), std::end(blockedPortList)));
1288     if (!std::binary_search(std::begin(blockedPortList), std::end(blockedPortList), port.value()))
1289         return true;
1290
1291     // Allow ports 21 and 22 for FTP URLs, as Mozilla does.
1292     if ((port.value() == 21 || port.value() == 22) && url.protocolIs("ftp"))
1293         return true;
1294
1295     // Allow any port number in a file URL, since the port number is ignored.
1296     if (url.protocolIs("file"))
1297         return true;
1298
1299     return false;
1300 }
1301
1302 String mimeTypeFromDataURL(const String& url)
1303 {
1304     ASSERT(protocolIsInternal(url, "data"));
1305
1306     // FIXME: What's the right behavior when the URL has a comma first, but a semicolon later?
1307     // Currently this code will break at the semicolon in that case. Not sure that's correct.
1308     auto index = url.find(';', 5);
1309     if (index == notFound)
1310         index = url.find(',', 5);
1311     if (index == notFound) {
1312         // FIXME: There was an old comment here that made it sound like this should be returning text/plain.
1313         // But we have been returning empty string here for some time, so not changing its behavior at this time.
1314         return emptyString();
1315     }
1316     if (index == 5)
1317         return ASCIILiteral("text/plain");
1318     ASSERT(index >= 5);
1319     return url.substring(5, index - 5).convertToASCIILowercase();
1320 }
1321
1322 String mimeTypeFromURL(const URL& url)
1323 {
1324     String decodedPath = decodeURLEscapeSequences(url.path());
1325     String extension = decodedPath.substring(decodedPath.reverseFind('.') + 1);
1326
1327     // We don't use MIMETypeRegistry::getMIMETypeForPath() because it returns "application/octet-stream" upon failure
1328     return MIMETypeRegistry::getMIMETypeForExtension(extension);
1329 }
1330
1331 String URL::stringCenterEllipsizedToLength(unsigned length) const
1332 {
1333     if (string().length() <= length)
1334         return string();
1335
1336     return string().left(length / 2 - 1) + "..." + string().right(length / 2 - 2);
1337 }
1338
1339 URL URL::fakeURLWithRelativePart(const String& relativePart)
1340 {
1341     return URL(URL(), "webkit-fake-url://" + createCanonicalUUIDString() + '/' + relativePart);
1342 }
1343
1344 URL URL::fileURLWithFileSystemPath(const String& filePath)
1345 {
1346     return URL(URL(), "file:///" + filePath);
1347 }
1348
1349 TextStream& operator<<(TextStream& ts, const URL& url)
1350 {
1351     ts << url.string();
1352     return ts;
1353 }
1354
1355 } // namespace WebCore