1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host.html;
16
17 import static org.htmlunit.BrowserVersionFeatures.JS_ANCHOR_HOSTNAME_IGNORE_BLANK;
18 import static org.htmlunit.BrowserVersionFeatures.JS_ANCHOR_PATHNAME_DETECT_WIN_DRIVES_URL_REPLACE;
19 import static org.htmlunit.BrowserVersionFeatures.JS_ANCHOR_PATHNAME_PREFIX_WIN_DRIVES_URL;
20 import static org.htmlunit.BrowserVersionFeatures.JS_ANCHOR_PROTOCOL_COLON_UPPER_CASE_DRIVE_LETTERS;
21 import static org.htmlunit.html.DomElement.ATTRIBUTE_NOT_DEFINED;
22
23 import java.net.MalformedURLException;
24 import java.net.URL;
25 import java.util.Arrays;
26 import java.util.List;
27 import java.util.Locale;
28
29 import org.htmlunit.BrowserVersion;
30 import org.htmlunit.HttpHeader;
31 import org.htmlunit.SgmlPage;
32 import org.htmlunit.html.DomElement;
33 import org.htmlunit.html.DomNode;
34 import org.htmlunit.html.HtmlAnchor;
35 import org.htmlunit.html.HtmlElement;
36 import org.htmlunit.html.HtmlPage;
37 import org.htmlunit.javascript.JavaScriptEngine;
38 import org.htmlunit.javascript.configuration.JsxClass;
39 import org.htmlunit.javascript.configuration.JsxConstructor;
40 import org.htmlunit.javascript.configuration.JsxGetter;
41 import org.htmlunit.javascript.configuration.JsxSetter;
42 import org.htmlunit.javascript.host.dom.DOMTokenList;
43 import org.htmlunit.util.StringUtils;
44 import org.htmlunit.util.UrlUtils;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 @JsxClass(domClass = HtmlAnchor.class)
60 public class HTMLAnchorElement extends HTMLElement {
61 private static final List<String> REFERRER_POLICIES = Arrays.asList(
62 "no-referrer", HttpHeader.ORIGIN_LC, "unsafe-url");
63
64
65
66
67 @Override
68 @JsxConstructor
69 public void jsConstructor() {
70 super.jsConstructor();
71 }
72
73
74
75
76
77 @JsxSetter
78 public void setHref(final String href) {
79 getDomNodeOrDie().setAttribute("href", href);
80 }
81
82
83
84
85
86 @JsxGetter
87 public String getHref() {
88 final HtmlAnchor anchor = (HtmlAnchor) getDomNodeOrDie();
89 final String hrefAttr = anchor.getHrefAttribute();
90
91 if (ATTRIBUTE_NOT_DEFINED == hrefAttr) {
92 return "";
93 }
94
95 try {
96 return getUrl().toString();
97 }
98 catch (final MalformedURLException e) {
99 return hrefAttr;
100 }
101 }
102
103
104
105
106 @Override
107 public void focus() {
108 final HtmlAnchor anchor = (HtmlAnchor) getDomNodeOrDie();
109 final String hrefAttr = anchor.getHrefAttribute();
110
111 if (ATTRIBUTE_NOT_DEFINED != hrefAttr) {
112 anchor.focus();
113 }
114 }
115
116
117
118
119
120 @JsxSetter
121 @Override
122 public void setName(final String name) {
123 getDomNodeOrDie().setAttribute(DomElement.NAME_ATTRIBUTE, name);
124 }
125
126
127
128
129
130 @JsxGetter
131 @Override
132 public String getName() {
133 return getDomNodeOrDie().getAttributeDirect(DomElement.NAME_ATTRIBUTE);
134 }
135
136
137
138
139
140 @JsxSetter
141 public void setTarget(final String target) {
142 getDomNodeOrDie().setAttribute("target", target);
143 }
144
145
146
147
148
149 @JsxGetter
150 public String getTarget() {
151 return getDomNodeOrDie().getAttributeDirect("target");
152 }
153
154
155
156
157
158
159 private URL getUrl() throws MalformedURLException {
160 final HtmlAnchor anchor = (HtmlAnchor) getDomNodeOrDie();
161 return ((HtmlPage) anchor.getPage()).getFullyQualifiedUrl(anchor.getHrefAttribute());
162 }
163
164
165
166
167
168 private void setUrl(final URL url) {
169 getDomNodeOrDie().setAttribute("href", url.toString());
170 }
171
172
173
174
175
176 @JsxSetter
177 public void setRel(final String rel) {
178 getDomNodeOrDie().setAttribute("rel", rel);
179 }
180
181
182
183
184
185 @JsxGetter
186 public String getRel() {
187 return ((HtmlAnchor) getDomNodeOrDie()).getRelAttribute();
188 }
189
190
191
192
193
194 @JsxGetter
195 public String getRev() {
196 return ((HtmlAnchor) getDomNodeOrDie()).getRevAttribute();
197 }
198
199
200
201
202
203 @JsxSetter
204 public void setRev(final String rel) {
205 getDomNodeOrDie().setAttribute("rev", rel);
206 }
207
208
209
210
211
212 @JsxGetter
213 public String getReferrerPolicy() {
214 String attrib = getDomNodeOrDie().getAttribute("referrerPolicy");
215 if (StringUtils.isEmptyOrNull(attrib)) {
216 return "";
217 }
218 attrib = attrib.toLowerCase(Locale.ROOT);
219 if (REFERRER_POLICIES.contains(attrib)) {
220 return attrib;
221 }
222 return "";
223 }
224
225
226
227
228
229 @JsxSetter
230 public void setReferrerPolicy(final String referrerPolicy) {
231 getDomNodeOrDie().setAttribute("referrerPolicy", referrerPolicy);
232 }
233
234
235
236
237
238
239
240 @JsxGetter
241 public String getSearch() {
242 try {
243 final String query = getUrl().getQuery();
244 if (query == null) {
245 return "";
246 }
247 return "?" + query;
248 }
249 catch (final MalformedURLException e) {
250 return "";
251 }
252 }
253
254
255
256
257
258
259
260
261 @JsxSetter
262 public void setSearch(final String search) throws Exception {
263 final String query;
264 if (search == null
265 || StringUtils.isEmptyString(search)
266 || StringUtils.equalsChar('?', search)) {
267 query = null;
268 }
269 else if (search.charAt(0) == '?') {
270 query = search.substring(1);
271 }
272 else {
273 query = search;
274 }
275
276 setUrl(UrlUtils.getUrlWithNewQuery(getUrl(), query));
277 }
278
279
280
281
282
283
284 @JsxGetter
285 public String getHash() {
286 try {
287 final String hash = getUrl().getRef();
288 if (hash == null) {
289 return "";
290 }
291 return "#" + hash;
292 }
293 catch (final MalformedURLException e) {
294 return "";
295 }
296 }
297
298
299
300
301
302
303
304 @JsxSetter
305 public void setHash(final String hash) throws Exception {
306 setUrl(UrlUtils.getUrlWithNewRef(getUrl(), hash));
307 }
308
309
310
311
312
313
314 @JsxGetter
315 public String getHost() {
316 try {
317 final URL url = getUrl();
318 final int port = url.getPort();
319 final String host = url.getHost();
320
321 if (port == -1) {
322 return host;
323 }
324 return host + ":" + port;
325 }
326 catch (final MalformedURLException e) {
327 return "";
328 }
329 }
330
331
332
333
334
335
336
337 @JsxSetter
338 public void setHost(final String host) throws Exception {
339 final String hostname;
340 final int port;
341 final int index = host.indexOf(':');
342 if (index != -1) {
343 hostname = host.substring(0, index);
344 port = Integer.parseInt(host.substring(index + 1));
345 }
346 else {
347 hostname = host;
348 port = -1;
349 }
350 final URL url = UrlUtils.getUrlWithNewHostAndPort(getUrl(), hostname, port);
351 setUrl(url);
352 }
353
354
355
356
357
358
359 @JsxGetter
360 public String getHostname() {
361 try {
362 return UrlUtils.encodeAnchor(getUrl().getHost());
363 }
364 catch (final MalformedURLException e) {
365 return "";
366 }
367 }
368
369
370
371
372
373
374
375 @JsxSetter
376 public void setHostname(final String hostname) throws Exception {
377 if (getBrowserVersion().hasFeature(JS_ANCHOR_HOSTNAME_IGNORE_BLANK)) {
378 if (!StringUtils.isBlank(hostname)) {
379 setUrl(UrlUtils.getUrlWithNewHost(getUrl(), hostname));
380 }
381 }
382 else if (!StringUtils.isEmptyOrNull(hostname)) {
383 setUrl(UrlUtils.getUrlWithNewHost(getUrl(), hostname));
384 }
385 }
386
387
388
389
390
391
392 @JsxGetter
393 public String getPathname() {
394 final BrowserVersion browser = getBrowserVersion();
395 try {
396 if (browser.hasFeature(JS_ANCHOR_PATHNAME_DETECT_WIN_DRIVES_URL_REPLACE)) {
397 final HtmlAnchor anchor = (HtmlAnchor) getDomNodeOrDie();
398 String href = anchor.getHrefAttribute();
399 if (href.length() > 1 && Character.isLetter(href.charAt(0)) && ':' == href.charAt(1)) {
400 if (browser.hasFeature(JS_ANCHOR_PROTOCOL_COLON_UPPER_CASE_DRIVE_LETTERS)) {
401 href = org.apache.commons.lang3.StringUtils.capitalize(href);
402 }
403 if (browser.hasFeature(JS_ANCHOR_PATHNAME_PREFIX_WIN_DRIVES_URL)) {
404 href = "/" + href;
405 }
406 return href;
407 }
408 }
409 return getUrl().getPath();
410 }
411 catch (final MalformedURLException e) {
412 final HtmlAnchor anchor = (HtmlAnchor) getDomNodeOrDie();
413 if (anchor.getHrefAttribute().startsWith("http")) {
414 return "";
415 }
416 return "/";
417 }
418 }
419
420
421
422
423
424
425
426 @JsxSetter
427 public void setPathname(final String pathname) throws Exception {
428 setUrl(UrlUtils.getUrlWithNewPath(getUrl(), pathname));
429 }
430
431
432
433
434
435
436 @JsxGetter
437 public String getPort() {
438 try {
439 final int port = getUrl().getPort();
440 if (port == -1) {
441 return "";
442 }
443 return Integer.toString(port);
444 }
445 catch (final MalformedURLException e) {
446 return "";
447 }
448 }
449
450
451
452
453
454
455
456 @JsxSetter
457 public void setPort(final String port) throws Exception {
458 setUrl(UrlUtils.getUrlWithNewPort(getUrl(), Integer.parseInt(port)));
459 }
460
461
462
463
464
465
466 @JsxGetter
467 public String getProtocol() {
468 final BrowserVersion browser = getBrowserVersion();
469 try {
470 if (browser.hasFeature(JS_ANCHOR_PATHNAME_DETECT_WIN_DRIVES_URL_REPLACE)) {
471 final HtmlAnchor anchor = (HtmlAnchor) getDomNodeOrDie();
472 final String href = anchor.getHrefAttribute().toLowerCase(Locale.ROOT);
473 if (href.length() > 1 && Character.isLetter(href.charAt(0)) && ':' == href.charAt(1)) {
474 return "file:";
475 }
476 }
477
478 return getUrl().getProtocol() + ":";
479 }
480 catch (final MalformedURLException e) {
481 final HtmlAnchor anchor = (HtmlAnchor) getDomNodeOrDie();
482 if (anchor.getHrefAttribute().startsWith("http")) {
483 return ":";
484 }
485 return StringUtils.substringBefore(anchor.getHrefAttribute(), "/");
486 }
487 }
488
489
490
491
492
493
494
495 @JsxSetter
496 public void setProtocol(final String protocol) throws Exception {
497 if (protocol.isEmpty()) {
498 return;
499 }
500
501 final String bareProtocol = StringUtils.substringBefore(protocol, ":").trim();
502 if (!UrlUtils.isValidScheme(bareProtocol)) {
503 return;
504 }
505 if (!UrlUtils.isSpecialScheme(bareProtocol)) {
506 return;
507 }
508
509 try {
510 URL url = UrlUtils.getUrlWithNewProtocol(getUrl(), bareProtocol);
511 url = UrlUtils.removeRedundantPort(url);
512 setUrl(url);
513 }
514 catch (final MalformedURLException ignored) {
515
516 }
517 }
518
519
520
521
522
523
524
525 @Override
526 public Object getDefaultValue(final Class<?> hint) {
527 final HtmlElement element = getDomNodeOrNull();
528 if (element == null) {
529 return super.getDefaultValue(null);
530 }
531 return getDefaultValue(element);
532 }
533
534 static String getDefaultValue(final HtmlElement element) {
535 String href = element.getAttributeDirect("href");
536
537 if (ATTRIBUTE_NOT_DEFINED == href) {
538 return "";
539 }
540
541 href = href.trim();
542
543 final SgmlPage page = element.getPage();
544 if (page == null || !page.isHtmlPage()) {
545 return href;
546 }
547
548 try {
549 return HtmlAnchor.getTargetUrl(href, (HtmlPage) page).toExternalForm();
550 }
551 catch (final MalformedURLException e) {
552 return href;
553 }
554 }
555
556
557
558
559
560 @JsxGetter
561 public String getText() {
562 final DomNode htmlElement = getDomNodeOrDie();
563 return htmlElement.asNormalizedText();
564 }
565
566
567
568
569
570 @JsxSetter
571 public void setText(final String text) {
572 final DomNode htmlElement = getDomNodeOrDie();
573 htmlElement.setTextContent(text);
574 }
575
576
577
578
579
580 @JsxGetter
581 public String getCharset() {
582 return getDomNodeOrDie().getAttributeDirect("charset");
583 }
584
585
586
587
588
589 @JsxSetter
590 public void setCharset(final String charset) {
591 getDomNodeOrDie().setAttribute("charset", charset);
592 }
593
594
595
596
597
598 @JsxGetter
599 public String getCoords() {
600 return getDomNodeOrDie().getAttributeDirect("coords");
601 }
602
603
604
605
606
607 @JsxSetter
608 public void setCoords(final String coords) {
609 getDomNodeOrDie().setAttribute("coords", coords);
610 }
611
612
613
614
615
616 @JsxGetter
617 public String getHreflang() {
618 return getDomNodeOrDie().getAttributeDirect("hreflang");
619 }
620
621
622
623
624
625 @JsxSetter
626 public void setHreflang(final String hreflang) {
627 getDomNodeOrDie().setAttribute("hreflang", hreflang);
628 }
629
630
631
632
633
634 @JsxGetter
635 public String getOrigin() {
636 if (!getDomNodeOrDie().hasAttribute("href")) {
637 return "";
638 }
639
640 try {
641 return getUrl().getProtocol() + "://" + getHost();
642 }
643 catch (final Exception e) {
644 return "";
645 }
646 }
647
648
649
650
651
652 @JsxGetter
653 public String getUsername() {
654 try {
655 final String userInfo = getUrl().getUserInfo();
656 if (userInfo == null) {
657 return "";
658 }
659 return org.apache.commons.lang3.StringUtils.substringBefore(userInfo, ':');
660 }
661 catch (final MalformedURLException e) {
662 return "";
663 }
664 }
665
666
667
668
669
670 @JsxSetter
671 public void setUsername(final String username) {
672 try {
673 final HtmlAnchor anchor = (HtmlAnchor) getDomNodeOrDie();
674 final String href = anchor.getHrefAttribute();
675 if (ATTRIBUTE_NOT_DEFINED == href) {
676 return;
677 }
678
679 final URL url = ((HtmlPage) anchor.getPage()).getFullyQualifiedUrl(href);
680 setUrl(UrlUtils.getUrlWithNewUserName(url, username));
681 }
682 catch (final MalformedURLException ignored) {
683
684 }
685 }
686
687
688
689
690
691 @JsxGetter
692 public String getPassword() {
693 try {
694 final String userName = getUrl().getUserInfo();
695 if (userName == null) {
696 return "";
697 }
698 return StringUtils.substringAfter(userName, ":");
699 }
700 catch (final MalformedURLException e) {
701 return "";
702 }
703 }
704
705
706
707
708
709 @JsxSetter
710 public void setPassword(final String password) {
711 try {
712 final HtmlAnchor anchor = (HtmlAnchor) getDomNodeOrDie();
713 final String href = anchor.getHrefAttribute();
714 if (ATTRIBUTE_NOT_DEFINED == href) {
715 return;
716 }
717
718 final URL url = ((HtmlPage) anchor.getPage()).getFullyQualifiedUrl(href);
719 setUrl(UrlUtils.getUrlWithNewUserPassword(url, password));
720 }
721 catch (final MalformedURLException ignored) {
722
723 }
724 }
725
726
727
728
729
730 @JsxGetter
731 public String getDownload() {
732 return ((HtmlAnchor) getDomNodeOrDie()).getDownloadAttribute();
733 }
734
735
736
737
738
739 @JsxSetter
740 public void setDownload(final String download) {
741 getDomNodeOrDie().setAttribute("download", download);
742 }
743
744
745
746
747
748 @JsxGetter
749 public String getPing() {
750 return ((HtmlAnchor) getDomNodeOrDie()).getPingAttribute();
751 }
752
753
754
755
756
757 @JsxSetter
758 public void setPing(final String ping) {
759 getDomNodeOrDie().setAttribute("ping", ping);
760 }
761
762
763
764
765
766 @JsxGetter
767 public String getShape() {
768 return getDomNodeOrDie().getAttribute("shape");
769 }
770
771
772
773
774
775 @JsxSetter
776 public void setShape(final String shape) {
777 getDomNodeOrDie().setAttribute("shape", shape);
778 }
779
780
781
782
783
784 @JsxGetter
785 public String getType() {
786 return getDomNodeOrDie().getAttributeDirect(DomElement.TYPE_ATTRIBUTE);
787 }
788
789
790
791
792
793 @JsxSetter
794 public void setType(final String type) {
795 getDomNodeOrDie().setAttribute(DomElement.TYPE_ATTRIBUTE, type);
796 }
797
798
799
800
801
802 @JsxGetter
803 public DOMTokenList getRelList() {
804 return new DOMTokenList(this, "rel");
805 }
806
807
808
809
810
811 @JsxSetter
812 public void setRelList(final Object rel) {
813 setRel(JavaScriptEngine.toString(rel));
814 }
815 }