View Javadoc
1   /*
2    * Copyright (c) 2002-2026 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.html;
16  
17  import java.io.IOException;
18  import java.net.MalformedURLException;
19  import java.net.URL;
20  import java.util.Map;
21  
22  import org.htmlunit.Page;
23  import org.htmlunit.SgmlPage;
24  import org.htmlunit.WebWindow;
25  import org.htmlunit.javascript.host.event.Event;
26  import org.htmlunit.javascript.host.html.HTMLElement;
27  
28  /**
29   * Wrapper for the HTML element "a".
30   *
31   * @author Mike Bowler
32   * @author David K. Taylor
33   * @author Christian Sell
34   * @author Ahmed Ashour
35   * @author Dmitri Zoubkov
36   * @author Ronald Brill
37   * @author Frank Danek
38   * @author Lai Quang Duong
39   */
40  public class HtmlAnchor extends HtmlElement implements HyperlinkElement {
41  
42      /** The HTML tag represented by this element. */
43      public static final String TAG_NAME = "a";
44  
45      /**
46       * Creates a new instance.
47       *
48       * @param qualifiedName the qualified name of the element type to instantiate
49       * @param page the page that contains this element
50       * @param attributes the initial attributes
51       */
52      HtmlAnchor(final String qualifiedName, final SgmlPage page,
53              final Map<String, DomAttr> attributes) {
54          super(qualifiedName, page, attributes);
55      }
56  
57      /**
58       * {@inheritDoc}
59       */
60      @Override
61      @SuppressWarnings("unchecked")
62      public <P extends Page> P click(final Event event,
63              final boolean shiftKey, final boolean ctrlKey, final boolean altKey,
64              final boolean ignoreVisibility) throws IOException {
65          WebWindow oldWebWindow = null;
66          if (ctrlKey) {
67              oldWebWindow = ((HTMLElement) event.getSrcElement()).getDomNodeOrDie()
68                      .getPage().getWebClient().getCurrentWindow();
69          }
70  
71          P page = super.click(event, shiftKey, ctrlKey, altKey, ignoreVisibility);
72  
73          if (ctrlKey) {
74              page.getEnclosingWindow().getWebClient().setCurrentWindow(oldWebWindow);
75              page = (P) oldWebWindow.getEnclosedPage();
76          }
77  
78          return page;
79      }
80  
81      /**
82       * Same as {@link #doClickStateUpdate(boolean, boolean)}, except that it accepts an {@code href} suffix,
83       * needed when a click is performed on an image map to pass information on the click position.
84       *
85       * @param shiftKey {@code true} if SHIFT is pressed
86       * @param ctrlKey {@code true} if CTRL is pressed
87       * @param hrefSuffix the suffix to add to the anchor's {@code href} attribute
88       *        (for instance coordinates from an image map)
89       * @throws IOException if an IO error occurs
90       */
91      protected void doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey, final String hrefSuffix)
92              throws IOException {
93          HyperlinkElementSupport.doClickStateUpdate(this, shiftKey, ctrlKey, hrefSuffix);
94      }
95  
96      /**
97       * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
98       *
99       * @param href the href
100      * @param page the HtmlPage
101      * @return the calculated target url.
102      * @throws MalformedURLException if an IO error occurs
103      */
104     public static URL getTargetUrl(final String href, final HtmlPage page) throws MalformedURLException {
105         return HyperlinkElementSupport.getTargetUrl(href, page);
106     }
107 
108     /**
109      * {@inheritDoc}
110      */
111     @Override
112     protected boolean doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey) throws IOException {
113         doClickStateUpdate(shiftKey, ctrlKey, "");
114         return false;
115     }
116 
117     /**
118      * Returns the value of the attribute {@code charset}. Refer to the
119      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
120      * documentation for details on the use of this attribute.
121      *
122      * @return the value of the attribute {@code charset} or an empty string if that attribute isn't defined
123      */
124     public final String getCharsetAttribute() {
125         return getAttributeDirect("charset");
126     }
127 
128     /**
129      * Returns the value of the attribute {@code type}. Refer to the
130      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
131      * documentation for details on the use of this attribute.
132      *
133      * @return the value of the attribute {@code type} or an empty string if that attribute isn't defined
134      */
135     public final String getTypeAttribute() {
136         return getAttributeDirect(TYPE_ATTRIBUTE);
137     }
138 
139     /**
140      * Returns the value of the attribute {@code name}. Refer to the
141      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
142      * documentation for details on the use of this attribute.
143      *
144      * @return the value of the attribute {@code name} or an empty string if that attribute isn't defined
145      */
146     public final String getNameAttribute() {
147         return getAttributeDirect(NAME_ATTRIBUTE);
148     }
149 
150     /**
151      * Returns the value of the attribute {@code href}. Refer to the
152      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
153      * documentation for details on the use of this attribute.
154      *
155      * @return the value of the attribute {@code href} or an empty string if that attribute isn't defined
156      */
157     @Override
158     public final String getHrefAttribute() {
159         return getAttributeDirect("href").trim();
160     }
161 
162     /**
163      * Returns the value of the attribute {@code hreflang}. Refer to the
164      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
165      * documentation for details on the use of this attribute.
166      *
167      * @return the value of the attribute {@code hreflang} or an empty string if that attribute isn't defined
168      */
169     public final String getHrefLangAttribute() {
170         return getAttributeDirect("hreflang");
171     }
172 
173     /**
174      * Returns the value of the attribute {@code rel}. Refer to the
175      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
176      * documentation for details on the use of this attribute.
177      *
178      * @return the value of the attribute {@code rel} or an empty string if that attribute isn't defined
179      */
180     @Override
181     public final String getRelAttribute() {
182         return getAttributeDirect("rel");
183     }
184 
185     /**
186      * Returns the value of the attribute {@code rev}. Refer to the
187      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
188      * documentation for details on the use of this attribute.
189      *
190      * @return the value of the attribute {@code rev} or an empty string if that attribute isn't defined
191      */
192     public final String getRevAttribute() {
193         return getAttributeDirect("rev");
194     }
195 
196     /**
197      * Returns the value of the attribute {@code accesskey}. Refer to the
198      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
199      * documentation for details on the use of this attribute.
200      *
201      * @return the value of the attribute {@code accesskey} or an empty string if that attribute isn't defined
202      */
203     public final String getAccessKeyAttribute() {
204         return getAttributeDirect("accesskey");
205     }
206 
207     /**
208      * Returns the value of the attribute {@code shape}. Refer to the
209      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
210      * documentation for details on the use of this attribute.
211      *
212      * @return the value of the attribute {@code shape} or an empty string if that attribute isn't defined
213      */
214     public final String getShapeAttribute() {
215         return getAttributeDirect("shape");
216     }
217 
218     /**
219      * Returns the value of the attribute {@code coords}. Refer to the
220      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
221      * documentation for details on the use of this attribute.
222      *
223      * @return the value of the attribute {@code coords} or an empty string if that attribute isn't defined
224      */
225     public final String getCoordsAttribute() {
226         return getAttributeDirect("coords");
227     }
228 
229     /**
230      * Returns the value of the attribute {@code tabindex}. Refer to the
231      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
232      * documentation for details on the use of this attribute.
233      *
234      * @return the value of the attribute {@code tabindex} or an empty string if that attribute isn't defined
235      */
236     public final String getTabIndexAttribute() {
237         return getAttributeDirect("tabindex");
238     }
239 
240     /**
241      * Returns the value of the attribute {@code onfocus}. Refer to the
242      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
243      * documentation for details on the use of this attribute.
244      *
245      * @return the value of the attribute {@code onfocus} or an empty string if that attribute isn't defined
246      */
247     public final String getOnFocusAttribute() {
248         return getAttributeDirect("onfocus");
249     }
250 
251     /**
252      * Returns the value of the attribute {@code onblur}. Refer to the
253      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
254      * documentation for details on the use of this attribute.
255      *
256      * @return the value of the attribute {@code onblur} or an empty string if that attribute isn't defined
257      */
258     public final String getOnBlurAttribute() {
259         return getAttributeDirect("onblur");
260     }
261 
262     /**
263      * Returns the value of the attribute {@code target}. Refer to the
264      * <a href="http://www.w3.org/TR/html401/">HTML 4.01</a>
265      * documentation for details on the use of this attribute.
266      *
267      * @return the value of the attribute {@code target} or an empty string if that attribute isn't defined
268      */
269     @Override
270     public final String getTargetAttribute() {
271         return getAttributeDirect("target");
272     }
273 
274     /**
275      * Open this link in a new window, much as web browsers do when you shift-click a link or use the context
276      * menu to open in a new window.
277      * <p>
278      * It should be noted that even web browsers will sometimes not give the expected result when using this
279      * method of following links. Links that have no real href and rely on JavaScript to do their work will
280      * fail.
281      * </p>
282      *
283      * @return the page opened by this link, nested in a new {@link org.htmlunit.TopLevelWindow}
284      * @throws MalformedURLException if the href could not be converted to a valid URL
285      */
286     public final Page openLinkInNewWindow() throws MalformedURLException {
287         final URL target = ((HtmlPage) getPage()).getFullyQualifiedUrl(getHrefAttribute());
288         final String windowName = "HtmlAnchor.openLinkInNewWindow() target";
289         final WebWindow newWindow = getPage().getWebClient().openWindow(target, windowName);
290         return newWindow.getEnclosedPage();
291     }
292 
293     @Override
294     protected boolean isEmptyXmlTagExpanded() {
295         return true;
296     }
297 
298     /**
299      * {@inheritDoc}
300      */
301     @Override
302     public DisplayStyle getDefaultStyleDisplay() {
303         return DisplayStyle.INLINE;
304     }
305 
306     /**
307      * {@inheritDoc}
308      */
309     @Override
310     public boolean handles(final Event event) {
311         if (Event.TYPE_BLUR.equals(event.getType()) || Event.TYPE_FOCUS.equals(event.getType())) {
312             return true;
313         }
314         return super.handles(event);
315     }
316 
317     /**
318      * Returns the value of the attribute {@code ping}.
319      *
320      * @return the value of the attribute {@code ping}
321      */
322     @Override
323     public final String getPingAttribute() {
324         return getAttributeDirect("ping");
325     }
326 
327     /**
328      * Returns the value of the attribute {@code download}.
329      *
330      * @return the value of the attribute {@code download}
331      */
332     @Override
333     public final String getDownloadAttribute() {
334         return getAttributeDirect("download");
335     }
336 }