Move URL from WebCore to WTF
[WebKit-https.git] / Source / WebCore / html / URLUtils.h
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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 AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #pragma once
27
28 #include "SecurityOrigin.h"
29
30 namespace WebCore {
31
32 template <typename T>
33 class URLUtils {
34 public:
35     URL href() const { return static_cast<const T*>(this)->href(); }
36     void setHref(const String& url) { static_cast<T*>(this)->setHref(url); }
37
38     String toString() const;
39     String toJSON() const;
40
41     String origin() const;
42
43     String protocol() const;
44     void setProtocol(const String&);
45
46     String username() const;
47     void setUsername(const String&);
48
49     String password() const;
50     void setPassword(const String&);
51
52     String host() const;
53     void setHost(const String&);
54
55     String hostname() const;
56     void setHostname(const String&);
57
58     String port() const;
59     void setPort(const String&);
60
61     String pathname() const;
62     void setPathname(const String&);
63
64     String search() const;
65     void setSearch(const String&);
66
67     String hash() const;
68     void setHash(const String&);
69 };
70
71 template <typename T>
72 String URLUtils<T>::toString() const
73 {
74     return href().string();
75 }
76
77 template <typename T>
78 String URLUtils<T>::toJSON() const
79 {
80     return href().string();
81 }
82
83 template <typename T>
84 String URLUtils<T>::origin() const
85 {
86     RefPtr<SecurityOrigin> origin = SecurityOrigin::create(href());
87     return origin->toString();
88 }
89
90 template <typename T>
91 String URLUtils<T>::protocol() const
92 {
93     return makeString(href().protocol(), ':');
94 }
95
96 template <typename T>
97 void URLUtils<T>::setProtocol(const String& value)
98 {
99     URL url = href();
100     url.setProtocol(value);
101     setHref(url.string());
102 }
103
104 template <typename T>
105 String URLUtils<T>::username() const
106 {
107     return href().encodedUser();
108 }
109
110 template <typename T>
111 void URLUtils<T>::setUsername(const String& user)
112 {
113     URL url = href();
114     if (url.cannotBeABaseURL())
115         return;
116     url.setUser(user);
117     setHref(url);
118 }
119
120 template <typename T>
121 String URLUtils<T>::password() const
122 {
123     return href().encodedPass();
124 }
125
126 template <typename T>
127 void URLUtils<T>::setPassword(const String& pass)
128 {
129     URL url = href();
130     if (url.cannotBeABaseURL())
131         return;
132     url.setPass(pass);
133     setHref(url);
134 }
135
136 template <typename T>
137 String URLUtils<T>::host() const
138 {
139     return href().hostAndPort();
140 }
141
142 // This function does not allow leading spaces before the port number.
143 static inline unsigned parsePortFromStringPosition(const String& value, unsigned portStart, unsigned& portEnd)
144 {
145     portEnd = portStart;
146     while (isASCIIDigit(value[portEnd]))
147         ++portEnd;
148     return value.substring(portStart, portEnd - portStart).toUInt();
149 }
150
151 template <typename T>
152 void URLUtils<T>::setHost(const String& value)
153 {
154     if (value.isEmpty())
155         return;
156     URL url = href();
157     if (url.cannotBeABaseURL())
158         return;
159     if (!url.canSetHostOrPort())
160         return;
161
162     size_t separator = value.find(':');
163     if (!separator)
164         return;
165
166     if (separator == notFound)
167         url.setHostAndPort(value);
168     else {
169         unsigned portEnd;
170         unsigned port = parsePortFromStringPosition(value, separator + 1, portEnd);
171         if (!port) {
172             // http://dev.w3.org/html5/spec/infrastructure.html#url-decomposition-idl-attributes
173             // specifically goes against RFC 3986 (p3.2) and
174             // requires setting the port to "0" if it is set to empty string.
175             url.setHostAndPort(value.substring(0, separator + 1) + '0');
176         } else {
177             if (WTF::isDefaultPortForProtocol(port, url.protocol()))
178                 url.setHostAndPort(value.substring(0, separator));
179             else
180                 url.setHostAndPort(value.substring(0, portEnd));
181         }
182     }
183     setHref(url.string());
184 }
185
186 template <typename T>
187 String URLUtils<T>::hostname() const
188 {
189     return href().host().toString();
190 }
191
192 template <typename T>
193 void URLUtils<T>::setHostname(const String& value)
194 {
195     // Before setting new value:
196     // Remove all leading U+002F SOLIDUS ("/") characters.
197     unsigned i = 0;
198     unsigned hostLength = value.length();
199     while (value[i] == '/')
200         i++;
201
202     if (i == hostLength)
203         return;
204
205     URL url = href();
206     if (url.cannotBeABaseURL())
207         return;
208     if (!url.canSetHostOrPort())
209         return;
210
211     url.setHost(value.substring(i));
212     setHref(url.string());
213 }
214
215 template <typename T>
216 String URLUtils<T>::port() const
217 {
218     if (href().port())
219         return String::number(href().port().value());
220
221     return emptyString();
222 }
223
224 template <typename T>
225 void URLUtils<T>::setPort(const String& value)
226 {
227     URL url = href();
228     if (url.cannotBeABaseURL() || url.protocolIs("file"))
229         return;
230     if (!url.canSetHostOrPort())
231         return;
232
233     // http://dev.w3.org/html5/spec/infrastructure.html#url-decomposition-idl-attributes
234     // specifically goes against RFC 3986 (p3.2) and
235     // requires setting the port to "0" if it is set to empty string.
236     // FIXME: http://url.spec.whatwg.org/ doesn't appear to require this; test what browsers do
237     unsigned port = value.toUInt();
238     if (WTF::isDefaultPortForProtocol(port, url.protocol()))
239         url.removePort();
240     else
241         url.setPort(port);
242
243     setHref(url.string());
244 }
245
246 template <typename T>
247 String URLUtils<T>::pathname() const
248 {
249     return href().path();
250 }
251
252 template <typename T>
253 void URLUtils<T>::setPathname(const String& value)
254 {
255     URL url = href();
256     if (url.cannotBeABaseURL())
257         return;
258     if (!url.canSetPathname())
259         return;
260
261     if (value[0U] == '/')
262         url.setPath(value);
263     else
264         url.setPath("/" + value);
265
266     setHref(url.string());
267 }
268
269 template <typename T>
270 String URLUtils<T>::search() const
271 {
272     String query = href().query();
273     return query.isEmpty() ? emptyString() : "?" + query;
274 }
275
276 template <typename T>
277 void URLUtils<T>::setSearch(const String& value)
278 {
279     URL url = href();
280     if (value.isEmpty()) {
281         // If the given value is the empty string, set url's query to null.
282         url.setQuery({ });
283     } else {
284         String newSearch = (value[0U] == '?') ? value.substring(1) : value;
285         // Make sure that '#' in the query does not leak to the hash.
286         url.setQuery(newSearch.replaceWithLiteral('#', "%23"));
287     }
288
289     setHref(url.string());
290 }
291
292 template <typename T>
293 String URLUtils<T>::hash() const
294 {
295     String fragmentIdentifier = href().fragmentIdentifier();
296     if (fragmentIdentifier.isEmpty())
297         return emptyString();
298     return AtomicString(String("#" + fragmentIdentifier));
299 }
300
301 template <typename T>
302 void URLUtils<T>::setHash(const String& value)
303 {
304     URL url = href();
305     String newFragment = value[0U] == '#' ? value.substring(1) : value;
306     if (newFragment.isEmpty())
307         url.removeFragmentIdentifier();
308     else
309         url.setFragmentIdentifier(newFragment);
310     setHref(url.string());
311 }
312
313 } // namespace WebCore