View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
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   * The JavaScript object that represents an anchor.
49   *
50   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
51   * @author <a href="mailto:gousseff@netscape.net">Alexei Goussev</a>
52   * @author David D. Kilzer
53   * @author Marc Guillemot
54   * @author Chris Erskine
55   * @author Ahmed Ashour
56   * @author Sudhan Moghe
57   * @author Daniel Gredler
58   * @author Ronald Brill
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       * JavaScript constructor.
67       */
68      @Override
69      @JsxConstructor
70      public void jsConstructor() {
71          super.jsConstructor();
72      }
73  
74      /**
75       * Sets the {@code href} property.
76       * @param href the {@code href} property value
77       */
78      @JsxSetter
79      public void setHref(final String href) {
80          getDomNodeOrDie().setAttribute("href", href);
81      }
82  
83      /**
84       * Returns the value of this link's {@code href} property.
85       * @return the value of this link's {@code href} property
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      * Sets the focus to this element.
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      * Sets the name property.
119      * @param name name attribute value
120      */
121     @JsxSetter
122     @Override
123     public void setName(final String name) {
124         getDomNodeOrDie().setAttribute(DomElement.NAME_ATTRIBUTE, name);
125     }
126 
127     /**
128      * Returns the value of the name property of this link.
129      * @return the name property
130      */
131     @JsxGetter
132     @Override
133     public String getName() {
134         return getDomNodeOrDie().getAttributeDirect(DomElement.NAME_ATTRIBUTE);
135     }
136 
137     /**
138      * Sets the target property of this link.
139      * @param target target attribute value
140      */
141     @JsxSetter
142     public void setTarget(final String target) {
143         getDomNodeOrDie().setAttribute("target", target);
144     }
145 
146     /**
147      * Returns the value of the target property of this link.
148      * @return the href property
149      */
150     @JsxGetter
151     public String getTarget() {
152         return getDomNodeOrDie().getAttributeDirect("target");
153     }
154 
155     /**
156      * Returns this link's current URL.
157      * @return this link's current URL
158      * @throws MalformedURLException if an error occurs
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      * Sets the {@code href} attribute of this link to the specified URL.
167      * @param url the new value of the {@code href} attribute
168      */
169     private void setUrl(final URL url) {
170         getDomNodeOrDie().setAttribute("href", url.toString());
171     }
172 
173     /**
174      * Sets the rel property.
175      * @param rel rel attribute value
176      */
177     @JsxSetter
178     public void setRel(final String rel) {
179         getDomNodeOrDie().setAttribute("rel", rel);
180     }
181 
182     /**
183      * Returns the value of the rel property.
184      * @return the rel property
185      */
186     @JsxGetter
187     public String getRel() {
188         return ((HtmlAnchor) getDomNodeOrDie()).getRelAttribute();
189     }
190 
191     /**
192      * Returns the value of the rev property.
193      * @return the rev property
194      */
195     @JsxGetter
196     public String getRev() {
197         return ((HtmlAnchor) getDomNodeOrDie()).getRevAttribute();
198     }
199 
200     /**
201      * Sets the rev property.
202      * @param rel rev attribute value
203      */
204     @JsxSetter
205     public void setRev(final String rel) {
206         getDomNodeOrDie().setAttribute("rev", rel);
207     }
208 
209     /**
210      * Returns the value of the rev property.
211      * @return the referrerPolicy property
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      * Sets the rev property.
228      * @param referrerPolicy referrerPolicy attribute value
229      */
230     @JsxSetter
231     public void setReferrerPolicy(final String referrerPolicy) {
232         getDomNodeOrDie().setAttribute("referrerPolicy", referrerPolicy);
233     }
234 
235     /**
236      * Returns the search portion of the link's URL (the portion starting with
237      * '?' and up to but not including any '#').
238      * @return the search portion of the link's URL
239      * @see <a href="http://msdn.microsoft.com/en-us/library/ms534620.aspx">MSDN Documentation</a>
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      * Sets the search portion of the link's URL (the portion starting with '?'
257      * and up to but not including any '#')..
258      * @param search the new search portion of the link's URL
259      * @throws Exception if an error occurs
260      * @see <a href="http://msdn.microsoft.com/en-us/library/ms534620.aspx">MSDN Documentation</a>
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      * Returns the hash portion of the link's URL (the portion following the '#', including the '#').
282      * @return the hash portion of the link's URL
283      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533775.aspx">MSDN Documentation</a>
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      * Sets the hash portion of the link's URL (the portion following the '#').
301      * @param hash the new hash portion of the link's URL
302      * @throws Exception if an error occurs
303      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533775.aspx">MSDN Documentation</a>
304      */
305     @JsxSetter
306     public void setHash(final String hash) throws Exception {
307         setUrl(UrlUtils.getUrlWithNewRef(getUrl(), hash));
308     }
309 
310     /**
311      * Returns the host portion of the link's URL (the '[hostname]:[port]' portion).
312      * @return the host portion of the link's URL
313      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533784.aspx">MSDN Documentation</a>
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      * Sets the host portion of the link's URL (the '[hostname]:[port]' portion).
334      * @param host the new host portion of the link's URL
335      * @throws Exception if an error occurs
336      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533784.aspx">MSDN Documentation</a>
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      * Returns the hostname portion of the link's URL.
357      * @return the hostname portion of the link's URL
358      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533785.aspx">MSDN Documentation</a>
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      * Sets the hostname portion of the link's URL.
372      * @param hostname the new hostname portion of the link's URL
373      * @throws Exception if an error occurs
374      * @see <a href="http://msdn.microsoft.com/en-us/library/ms533785.aspx">MSDN Documentation</a>
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      * Returns the pathname portion of the link's URL.
390      * @return the pathname portion of the link's URL
391      * @see <a href="http://msdn.microsoft.com/en-us/library/ms534332.aspx">MSDN Documentation</a>
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      * Sets the pathname portion of the link's URL.
430      * @param pathname the new pathname portion of the link's URL
431      * @throws Exception if an error occurs
432      * @see <a href="http://msdn.microsoft.com/en-us/library/ms534332.aspx">MSDN Documentation</a>
433      */
434     @JsxSetter
435     public void setPathname(final String pathname) throws Exception {
436         setUrl(UrlUtils.getUrlWithNewPath(getUrl(), pathname));
437     }
438 
439     /**
440      * Returns the port portion of the link's URL.
441      * @return the port portion of the link's URL
442      * @see <a href="http://msdn.microsoft.com/en-us/library/ms534342.aspx">MSDN Documentation</a>
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      * Sets the port portion of the link's URL.
460      * @param port the new port portion of the link's URL
461      * @throws Exception if an error occurs
462      * @see <a href="http://msdn.microsoft.com/en-us/library/ms534342.aspx">MSDN Documentation</a>
463      */
464     @JsxSetter
465     public void setPort(final String port) throws Exception {
466         setUrl(UrlUtils.getUrlWithNewPort(getUrl(), Integer.parseInt(port)));
467     }
468 
469     /**
470      * Returns the protocol portion of the link's URL, including the trailing ':'.
471      * @return the protocol portion of the link's URL, including the trailing ':'
472      * @see <a href="http://msdn.microsoft.com/en-us/library/ms534353.aspx">MSDN Documentation</a>
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      * Sets the protocol portion of the link's URL.
499      * @param protocol the new protocol portion of the link's URL
500      * @throws Exception if an error occurs
501      * @see <a href="http://msdn.microsoft.com/en-us/library/ms534353.aspx">MSDN Documentation</a>
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             // ignore
524         }
525     }
526 
527     /**
528      * Calls for instance for implicit conversion to string.
529      * @see org.htmlunit.javascript.HtmlUnitScriptable#getDefaultValue(java.lang.Class)
530      * @param hint the type hint
531      * @return the default value
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 ""; // for example for named anchors
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      * Returns the {@code text} attribute.
566      * @return the {@code text} attribute
567      */
568     @JsxGetter
569     public String getText() {
570         final DomNode htmlElement = getDomNodeOrDie();
571         return htmlElement.asNormalizedText();
572     }
573 
574     /**
575      * Sets the {@code text} attribute.
576      * @param text the {@code text} attribute
577      */
578     @JsxSetter
579     public void setText(final String text) {
580         final DomNode htmlElement = getDomNodeOrDie();
581         htmlElement.setTextContent(text);
582     }
583 
584     /**
585      * Returns the {@code charset} attribute.
586      * @return the {@code charset} attribute
587      */
588     @JsxGetter
589     public String getCharset() {
590         return getDomNodeOrDie().getAttributeDirect("charset");
591     }
592 
593     /**
594      * Sets the {@code charset} attribute.
595      * @param charset the {@code charset} attribute
596      */
597     @JsxSetter
598     public void setCharset(final String charset) {
599         getDomNodeOrDie().setAttribute("charset", charset);
600     }
601 
602     /**
603      * Returns the {@code coords} attribute.
604      * @return the {@code coords} attribute
605      */
606     @JsxGetter
607     public String getCoords() {
608         return getDomNodeOrDie().getAttributeDirect("coords");
609     }
610 
611     /**
612      * Sets the {@code coords} attribute.
613      * @param coords {@code coords} attribute
614      */
615     @JsxSetter
616     public void setCoords(final String coords) {
617         getDomNodeOrDie().setAttribute("coords", coords);
618     }
619 
620     /**
621      * Returns the {@code hreflang} attribute.
622      * @return the {@code hreflang} attribute
623      */
624     @JsxGetter
625     public String getHreflang() {
626         return getDomNodeOrDie().getAttributeDirect("hreflang");
627     }
628 
629     /**
630      * Sets the {@code hreflang} attribute.
631      * @param hreflang {@code hreflang} attribute
632      */
633     @JsxSetter
634     public void setHreflang(final String hreflang) {
635         getDomNodeOrDie().setAttribute("hreflang", hreflang);
636     }
637 
638     /**
639      * Returns the {@code origin} attribute.
640      * @return the {@code origin} attribute
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      * Returns the {@code username} attribute.
658      * @return the {@code username} attribute
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      * Sets the {@code username} attribute.
676      * @param username {@code username} attribute
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             // ignore
692         }
693     }
694 
695     /**
696      * Returns the {@code password} attribute.
697      * @return the {@code password} attribute
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      * Sets the {@code password} attribute.
715      * @param password {@code password} attribute
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             // ignore
731         }
732     }
733 
734     /**
735      * Returns the {@code download} attribute.
736      * @return the {@code download} attribute
737      */
738     @JsxGetter
739     public String getDownload() {
740         return ((HtmlAnchor) getDomNodeOrDie()).getDownloadAttribute();
741     }
742 
743     /**
744      * Sets the {@code download} attribute.
745      * @param download {@code download} attribute
746      */
747     @JsxSetter
748     public void setDownload(final String download) {
749         getDomNodeOrDie().setAttribute("download", download);
750     }
751 
752     /**
753      * Returns the {@code ping} attribute.
754      * @return the {@code ping} attribute
755      */
756     @JsxGetter
757     public String getPing() {
758         return ((HtmlAnchor) getDomNodeOrDie()).getPingAttribute();
759     }
760 
761     /**
762      * Sets the {@code ping} attribute.
763      * @param ping {@code ping} attribute
764      */
765     @JsxSetter
766     public void setPing(final String ping) {
767         getDomNodeOrDie().setAttribute("ping", ping);
768     }
769 
770     /**
771      * Returns the {@code shape} attribute.
772      * @return the {@code shape} attribute
773      */
774     @JsxGetter
775     public String getShape() {
776         return getDomNodeOrDie().getAttribute("shape");
777     }
778 
779     /**
780      * Sets the {@code shape} attribute.
781      * @param shape {@code shape} attribute
782      */
783     @JsxSetter
784     public void setShape(final String shape) {
785         getDomNodeOrDie().setAttribute("shape", shape);
786     }
787 
788     /**
789      * Returns the {@code type} attribute.
790      * @return the {@code type} attribute
791      */
792     @JsxGetter
793     public String getType() {
794         return getDomNodeOrDie().getAttributeDirect(DomElement.TYPE_ATTRIBUTE);
795     }
796 
797     /**
798      * Sets the {@code type} attribute.
799      * @param type {@code type} attribute
800      */
801     @JsxSetter
802     public void setType(final String type) {
803         getDomNodeOrDie().setAttribute(DomElement.TYPE_ATTRIBUTE, type);
804     }
805 
806     /**
807      * Returns the {@code relList} attribute.
808      * @return the {@code relList} attribute
809      */
810     @JsxGetter
811     public DOMTokenList getRelList() {
812         return new DOMTokenList(this, "rel");
813     }
814 
815     /**
816      * Sets the relList property.
817      * @param rel attribute value
818      */
819     @JsxSetter
820     public void setRelList(final Object rel) {
821         setRel(JavaScriptEngine.toString(rel));
822     }
823 }