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