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