1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host;
16
17 import static org.htmlunit.BrowserVersionFeatures.JS_ANCHOR_HOSTNAME_IGNORE_BLANK;
18
19 import java.net.MalformedURLException;
20 import java.util.List;
21
22 import org.apache.commons.lang3.StringUtils;
23 import org.htmlunit.corejs.javascript.Context;
24 import org.htmlunit.corejs.javascript.Scriptable;
25 import org.htmlunit.javascript.HtmlUnitScriptable;
26 import org.htmlunit.javascript.JavaScriptEngine;
27 import org.htmlunit.javascript.configuration.JsxClass;
28 import org.htmlunit.javascript.configuration.JsxConstructor;
29 import org.htmlunit.javascript.configuration.JsxConstructorAlias;
30 import org.htmlunit.javascript.configuration.JsxFunction;
31 import org.htmlunit.javascript.configuration.JsxGetter;
32 import org.htmlunit.javascript.configuration.JsxSetter;
33 import org.htmlunit.javascript.configuration.JsxStaticFunction;
34 import org.htmlunit.javascript.host.file.Blob;
35 import org.htmlunit.javascript.host.file.File;
36 import org.htmlunit.util.NameValuePair;
37 import org.htmlunit.util.UrlUtils;
38
39
40
41
42
43
44
45
46
47 @JsxClass
48 public class URL extends HtmlUnitScriptable {
49
50 private java.net.URL url_;
51
52
53
54
55
56
57
58
59
60 @JsxConstructor
61 @JsxConstructorAlias(alias = "webkitURL")
62 public void jsConstructor(final String url, final Object base) {
63 String baseStr = null;
64 if (!JavaScriptEngine.isUndefined(base)) {
65 baseStr = JavaScriptEngine.toString(base);
66 }
67
68 try {
69 if (org.htmlunit.util.StringUtils.isBlank(baseStr)) {
70 url_ = UrlUtils.toUrlUnsafe(url);
71 }
72 else {
73 final java.net.URL baseUrl = UrlUtils.toUrlUnsafe(baseStr);
74 url_ = UrlUtils.toUrlUnsafe(UrlUtils.resolveUrl(baseUrl, url));
75 }
76 url_ = UrlUtils.removeRedundantPort(url_);
77 }
78 catch (final MalformedURLException e) {
79 throw JavaScriptEngine.typeError(e.toString());
80 }
81 }
82
83
84
85
86
87
88
89
90
91
92 @JsxStaticFunction
93 public static String createObjectURL(final Object fileOrBlob) {
94 if (fileOrBlob instanceof File file) {
95 return getWindow(file).getDocument().generateBlobUrl(file);
96 }
97
98 if (fileOrBlob instanceof Blob blob) {
99 return getWindow(blob).getDocument().generateBlobUrl(blob);
100 }
101
102 return null;
103 }
104
105
106
107
108
109 @JsxStaticFunction
110 public static void revokeObjectURL(final Scriptable objectURL) {
111 getWindow(objectURL).getDocument().revokeBlobUrl(Context.toString(objectURL));
112 }
113
114
115
116
117 @JsxGetter
118 public String getHash() {
119 if (url_ == null) {
120 return null;
121 }
122 final String ref = url_.getRef();
123 return ref == null ? "" : "#" + ref;
124 }
125
126
127
128
129
130 @JsxSetter
131 public void setHash(final String fragment) throws MalformedURLException {
132 if (url_ == null) {
133 return;
134 }
135 url_ = UrlUtils.getUrlWithNewRef(url_, org.htmlunit.util.StringUtils.isEmptyOrNull(fragment) ? null : fragment);
136 }
137
138
139
140
141
142 @JsxGetter
143 public String getHost() {
144 if (url_ == null) {
145 return null;
146 }
147 final int port = url_.getPort();
148 return url_.getHost() + (port > 0 ? ":" + port : "");
149 }
150
151
152
153
154
155 @JsxSetter
156 public void setHost(final String host) throws MalformedURLException {
157 if (url_ == null) {
158 return;
159 }
160
161 String newHost = StringUtils.substringBefore(host, ':');
162 if (org.htmlunit.util.StringUtils.isEmptyOrNull(newHost)) {
163 return;
164 }
165
166 try {
167 int ip = Integer.parseInt(newHost);
168 final StringBuilder ipString = new StringBuilder();
169 ipString.insert(0, ip % 256);
170 ipString.insert(0, '.');
171
172 ip = ip / 256;
173 ipString.insert(0, ip % 256);
174 ipString.insert(0, '.');
175
176 ip = ip / 256;
177 ipString.insert(0, ip % 256);
178 ipString.insert(0, '.');
179 ip = ip / 256;
180 ipString.insert(0, ip % 256);
181
182 newHost = ipString.toString();
183 }
184 catch (final Exception expected) {
185
186 }
187
188 url_ = UrlUtils.getUrlWithNewHost(url_, newHost);
189
190 final String newPort = StringUtils.substringAfter(host, ':');
191 if (org.htmlunit.util.StringUtils.isNotBlank(newHost)) {
192 try {
193 url_ = UrlUtils.getUrlWithNewHostAndPort(url_, newHost, Integer.parseInt(newPort));
194 }
195 catch (final Exception expected) {
196
197 }
198 }
199 else {
200 url_ = UrlUtils.getUrlWithNewHost(url_, newHost);
201 }
202
203 url_ = UrlUtils.removeRedundantPort(url_);
204 }
205
206
207
208
209
210 @JsxGetter
211 public String getHostname() {
212 if (url_ == null) {
213 return null;
214 }
215
216 return UrlUtils.encodeAnchor(url_.getHost());
217 }
218
219
220
221
222
223 @JsxSetter
224 public void setHostname(final String hostname) throws MalformedURLException {
225 if (getBrowserVersion().hasFeature(JS_ANCHOR_HOSTNAME_IGNORE_BLANK)) {
226 if (!org.htmlunit.util.StringUtils.isBlank(hostname)) {
227 url_ = UrlUtils.getUrlWithNewHost(url_, hostname);
228 }
229 }
230 else if (!org.htmlunit.util.StringUtils.isEmptyOrNull(hostname)) {
231 url_ = UrlUtils.getUrlWithNewHost(url_, hostname);
232 }
233 }
234
235
236
237
238 @JsxGetter
239 public String getHref() {
240 if (url_ == null) {
241 return null;
242 }
243
244 return jsToString();
245 }
246
247
248
249
250
251 @JsxSetter
252 public void setHref(final String href) throws MalformedURLException {
253 if (url_ == null) {
254 return;
255 }
256
257 url_ = UrlUtils.toUrlUnsafe(href);
258 url_ = UrlUtils.removeRedundantPort(url_);
259 }
260
261
262
263
264 @JsxGetter
265 public Object getOrigin() {
266 if (url_ == null) {
267 return null;
268 }
269
270 if (url_.getPort() < 0 || url_.getPort() == url_.getDefaultPort()) {
271 return url_.getProtocol() + "://" + url_.getHost();
272 }
273
274 return url_.getProtocol() + "://" + url_.getHost() + ':' + url_.getPort();
275 }
276
277
278
279
280 @JsxGetter
281 public URLSearchParams getSearchParams() {
282 if (url_ == null) {
283 return null;
284 }
285
286 final URLSearchParams searchParams = new URLSearchParams(this);
287 searchParams.setParentScope(getParentScope());
288 searchParams.setPrototype(getPrototype(searchParams.getClass()));
289 return searchParams;
290 }
291
292
293
294
295 @JsxGetter
296 public String getPassword() {
297 if (url_ == null) {
298 return null;
299 }
300
301 final String userInfo = url_.getUserInfo();
302 final int idx = userInfo == null ? -1 : userInfo.indexOf(':');
303 return idx == -1 ? "" : userInfo.substring(idx + 1);
304 }
305
306
307
308
309
310 @JsxSetter
311 public void setPassword(final String password) throws MalformedURLException {
312 if (url_ == null) {
313 return;
314 }
315
316 url_ = UrlUtils.getUrlWithNewUserPassword(url_, password.isEmpty() ? null : password);
317 }
318
319
320
321
322 @JsxGetter
323 public String getPathname() {
324 if (url_ == null) {
325 return null;
326 }
327
328 final String path = url_.getPath();
329 return path.isEmpty() ? "/" : path;
330 }
331
332
333
334
335
336 @JsxSetter
337 public void setPathname(final String path) throws MalformedURLException {
338 if (url_ == null) {
339 return;
340 }
341
342 url_ = UrlUtils.getUrlWithNewPath(url_, path.startsWith("/") ? path : "/" + path);
343 }
344
345
346
347
348
349 @JsxGetter
350 public String getPort() {
351 if (url_ == null) {
352 return null;
353 }
354
355 final int port = url_.getPort();
356 return port == -1 ? "" : Integer.toString(port);
357 }
358
359
360
361
362
363 @JsxSetter
364 public void setPort(final String port) throws MalformedURLException {
365 if (url_ == null) {
366 return;
367 }
368 final int portInt = port.isEmpty() ? -1 : Integer.parseInt(port);
369 url_ = UrlUtils.getUrlWithNewPort(url_, portInt);
370 url_ = UrlUtils.removeRedundantPort(url_);
371 }
372
373
374
375
376 @JsxGetter
377 public String getProtocol() {
378 if (url_ == null) {
379 return null;
380 }
381 final String protocol = url_.getProtocol();
382 return protocol.isEmpty() ? "" : (protocol + ":");
383 }
384
385
386
387
388
389 @JsxSetter
390 public void setProtocol(final String protocol) throws MalformedURLException {
391 if (url_ == null || protocol.isEmpty()) {
392 return;
393 }
394
395 final String bareProtocol = org.htmlunit.util.StringUtils.substringBefore(protocol, ":").trim();
396 if (!UrlUtils.isValidScheme(bareProtocol)) {
397 return;
398 }
399 if (!UrlUtils.isSpecialScheme(bareProtocol)) {
400 return;
401 }
402
403 try {
404 url_ = UrlUtils.getUrlWithNewProtocol(url_, bareProtocol);
405 url_ = UrlUtils.removeRedundantPort(url_);
406 }
407 catch (final MalformedURLException ignored) {
408
409 }
410 }
411
412
413
414
415 @JsxGetter
416 public String getSearch() {
417 if (url_ == null) {
418 return null;
419 }
420 final String search = url_.getQuery();
421 return search == null ? "" : "?" + search;
422 }
423
424
425
426
427
428 @JsxSetter
429 public void setSearch(final String search) throws MalformedURLException {
430 if (url_ == null) {
431 return;
432 }
433
434 String query;
435 if (search == null
436 || org.htmlunit.util.StringUtils.equalsChar('?', search)
437 || org.htmlunit.util.StringUtils.isEmptyString(search)) {
438 query = null;
439 }
440 else {
441 if (search.charAt(0) == '?') {
442 query = search.substring(1);
443 }
444 else {
445 query = search;
446 }
447 query = UrlUtils.encodeQuery(query);
448 }
449
450 url_ = UrlUtils.getUrlWithNewQuery(url_, query);
451 }
452
453
454
455
456
457
458 public void setSearch(final List<NameValuePair> nameValuePairs) throws MalformedURLException {
459 final StringBuilder newSearch = new StringBuilder();
460 for (final NameValuePair nameValuePair : nameValuePairs) {
461 if (newSearch.length() > 0) {
462 newSearch.append('&');
463 }
464 newSearch
465 .append(UrlUtils.encodeQueryPart(nameValuePair.getName()))
466 .append('=')
467 .append(UrlUtils.encodeQueryPart(nameValuePair.getValue()));
468 }
469
470 url_ = UrlUtils.getUrlWithNewQuery(url_, newSearch.toString());
471 }
472
473
474
475
476 @JsxGetter
477 public String getUsername() {
478 if (url_ == null) {
479 return null;
480 }
481
482 final String userInfo = url_.getUserInfo();
483 if (userInfo == null) {
484 return "";
485 }
486
487 return StringUtils.substringBefore(userInfo, ':');
488 }
489
490
491
492
493
494 @JsxSetter
495 public void setUsername(final String username) throws MalformedURLException {
496 if (url_ == null) {
497 return;
498 }
499 url_ = UrlUtils.getUrlWithNewUserName(url_, username.isEmpty() ? null : username);
500 }
501
502
503
504
505
506
507
508 @Override
509 public Object getDefaultValue(final Class<?> hint) {
510 if (url_ == null) {
511 return super.getDefaultValue(hint);
512 }
513
514 if (org.htmlunit.util.StringUtils.isEmptyOrNull(url_.getPath())) {
515 return url_.toExternalForm() + "/";
516 }
517 return url_.toExternalForm();
518 }
519
520
521
522
523
524 @JsxFunction
525 public String toJSON() {
526 return jsToString();
527 }
528
529
530
531
532
533 @JsxFunction(functionName = "toString")
534 public String jsToString() {
535 if (org.htmlunit.util.StringUtils.isEmptyOrNull(url_.getPath())) {
536 try {
537 return UrlUtils.getUrlWithNewPath(url_, "/").toExternalForm();
538 }
539 catch (final MalformedURLException e) {
540 return url_.toExternalForm();
541 }
542 }
543 return url_.toExternalForm();
544 }
545 }