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