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.html;
16  
17  import static org.apache.commons.lang3.StringUtils.right;
18  
19  import java.awt.image.BufferedImage;
20  import java.io.InputStream;
21  import java.net.URL;
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.LinkedList;
25  import java.util.List;
26  
27  import javax.imageio.ImageIO;
28  import javax.imageio.stream.ImageInputStream;
29  
30  import org.htmlunit.CollectingAlertHandler;
31  import org.htmlunit.HttpHeader;
32  import org.htmlunit.HttpMethod;
33  import org.htmlunit.MockWebConnection;
34  import org.htmlunit.Page;
35  import org.htmlunit.SimpleWebTestCase;
36  import org.htmlunit.TopLevelWindow;
37  import org.htmlunit.UnexpectedPage;
38  import org.htmlunit.WebClient;
39  import org.htmlunit.WebResponse;
40  import org.htmlunit.WebWindow;
41  import org.htmlunit.attachment.AttachmentHandler;
42  import org.htmlunit.junit.annotation.Alerts;
43  import org.htmlunit.util.MimeType;
44  import org.junit.jupiter.api.Test;
45  
46  /**
47   * Tests for {@link HtmlAnchor}.
48   *
49   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
50   * @author Marc Guillemot
51   * @author Stefan Anzinger
52   * @author Ahmed Ashour
53   * @author Ronald Brill
54   * @author Lai Quang Duong
55   */
56  public class HtmlAnchor2Test extends SimpleWebTestCase {
57  
58      /**
59       * @throws Exception if the test fails
60       */
61      @Test
62      public void click_onClickHandler() throws Exception {
63          final String firstContent = DOCTYPE_HTML
64              + "<html><head><title>First</title></head><body>\n"
65              + "<a href='http://www.foo1.com' id='a1'>link to foo1</a>\n"
66              + "<a href='" + URL_SECOND + "' id='a2' "
67              + "onClick='alert(\"clicked\")'>link to foo2</a>\n"
68              + "<a href='http://www.foo3.com' id='a3'>link to foo3</a>\n"
69              + "</body></html>";
70          final String secondContent = DOCTYPE_HTML
71              + "<html><head><title>Second</title></head><body></body></html>";
72  
73          final WebClient client = getWebClient();
74          final List<String> collectedAlerts = new ArrayList<>();
75          client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
76  
77          final MockWebConnection webConnection = new MockWebConnection();
78          webConnection.setResponse(URL_FIRST, firstContent);
79          webConnection.setResponse(URL_SECOND, secondContent);
80          client.setWebConnection(webConnection);
81  
82          final HtmlPage page = client.getPage(URL_FIRST);
83          final HtmlAnchor anchor = page.getHtmlElementById("a2");
84  
85          assertEquals(Collections.EMPTY_LIST, collectedAlerts);
86  
87          final HtmlPage secondPage = anchor.click();
88  
89          assertEquals(new String[] {"clicked"}, collectedAlerts);
90          assertEquals("Second", secondPage.getTitleText());
91      }
92  
93      /**
94       * @throws Exception if the test fails
95       */
96      @Test
97      public void click_onClickHandler_returnFalse() throws Exception {
98          final String firstContent = DOCTYPE_HTML
99              + "<html><head><title>First</title></head><body>\n"
100             + "<a href='http://www.foo1.com' id='a1'>link to foo1</a>\n"
101             + "<a href='" + URL_SECOND + "' id='a2' "
102             + "onClick='alert(\"clicked\");return false;'>link to foo2</a>\n"
103             + "<a href='http://www.foo3.com' id='a3'>link to foo3</a>\n"
104             + "</body></html>";
105         final String secondContent = DOCTYPE_HTML + "<html><head><title>Second</title></head><body></body></html>";
106 
107         final WebClient client = getWebClient();
108         final List<String> collectedAlerts = new ArrayList<>();
109         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
110 
111         final MockWebConnection webConnection = new MockWebConnection();
112         webConnection.setResponse(URL_FIRST, firstContent);
113         webConnection.setResponse(URL_SECOND, secondContent);
114         client.setWebConnection(webConnection);
115 
116         final HtmlPage page = client.getPage(URL_FIRST);
117         final HtmlAnchor anchor = page.getHtmlElementById("a2");
118 
119         assertEquals(Collections.EMPTY_LIST, collectedAlerts);
120 
121         final HtmlPage secondPage = anchor.click();
122 
123         assertEquals(new String[] {"clicked"}, collectedAlerts);
124         assertSame(page, secondPage);
125     }
126 
127     /**
128      * @throws Exception if the test fails
129      */
130     @Test
131     public void click_onClickHandler_javascriptDisabled() throws Exception {
132         final String htmlContent = DOCTYPE_HTML
133             + "<html><head><title>foo</title></head><body>\n"
134             + "<a href='http://www.foo1.com' id='a1'>link to foo1</a>\n"
135             + "<a href='http://www.foo2.com' id='a2' "
136             + "onClick='alert(\"clicked\")'>link to foo2</a>\n"
137             + "<a href='http://www.foo3.com' id='a3'>link to foo3</a>\n"
138             + "</body></html>";
139         final WebClient client = getWebClient();
140         client.getOptions().setJavaScriptEnabled(false);
141 
142         final List<String> collectedAlerts = new ArrayList<>();
143         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
144 
145         final MockWebConnection webConnection = new MockWebConnection();
146         webConnection.setDefaultResponse(htmlContent);
147         client.setWebConnection(webConnection);
148 
149         final HtmlPage page = client.getPage(URL_FIRST);
150         final HtmlAnchor anchor = page.getHtmlElementById("a2");
151 
152         assertEquals(Collections.EMPTY_LIST, collectedAlerts);
153 
154         final HtmlPage secondPage = anchor.click();
155 
156         assertEquals(Collections.EMPTY_LIST, collectedAlerts);
157         final List<?> expectedParameters = Collections.EMPTY_LIST;
158 
159         assertEquals("url", "http://www.foo2.com/", secondPage.getUrl());
160         assertSame("method", HttpMethod.GET, webConnection.getLastMethod());
161         assertEquals("parameters", expectedParameters, webConnection.getLastParameters());
162     }
163 
164     /**
165      * @throws Exception if the test fails
166      */
167     @Test
168     public void click_javascriptUrl() throws Exception {
169         final String htmlContent = DOCTYPE_HTML
170             + "<html><head><title>foo</title></head><body>\n"
171             + "<a href='http://www.foo1.com' id='a1'>link to foo1</a>\n"
172             + "<a href='javascript:alert(\"clicked\")' id='a2'>link to foo2</a>\n"
173             + "<a href='http://www.foo3.com' id='a3'>link to foo3</a>\n"
174             + "</body></html>";
175         final List<String> collectedAlerts = new ArrayList<>();
176         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
177 
178         final HtmlAnchor anchor = page.getHtmlElementById("a2");
179 
180         assertEquals(Collections.EMPTY_LIST, collectedAlerts);
181 
182         final HtmlPage secondPage = anchor.click();
183 
184         assertEquals(new String[] {"clicked"}, collectedAlerts);
185         assertSame(page, secondPage);
186     }
187 
188     /**
189      * @throws Exception if the test fails
190      */
191     @Test
192     public void click_javascriptUrlMixedCase() throws Exception {
193         final String htmlContent = DOCTYPE_HTML
194             + "<html><head><title>foo</title></head><body>\n"
195             + "<a href='JAVAscrIpt:alert(\"clicked\")' id='a2'>link to foo2</a>\n"
196             + "</body></html>";
197         final List<String> collectedAlerts = new ArrayList<>();
198         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
199 
200         final HtmlAnchor anchor = page.getHtmlElementById("a2");
201 
202         assertEquals(Collections.EMPTY_LIST, collectedAlerts);
203 
204         final HtmlPage secondPage = anchor.click();
205 
206         assertEquals(new String[] {"clicked"}, collectedAlerts);
207         assertSame(page, secondPage);
208     }
209 
210     /**
211      * @throws Exception if the test fails
212      */
213     @Test
214     public void click_javascriptUrlLeadingWhitespace() throws Exception {
215         final String htmlContent = DOCTYPE_HTML
216             + "<html><head><title>foo</title></head><body>\n"
217             + "<a href='  javascript:alert(\"clicked\")' id='a2'>link to foo2</a>\n"
218             + "</body></html>";
219         final List<String> collectedAlerts = new ArrayList<>();
220         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
221 
222         final HtmlAnchor anchor = page.getHtmlElementById("a2");
223 
224         assertEquals(Collections.EMPTY_LIST, collectedAlerts);
225 
226         final HtmlPage secondPage = anchor.click();
227 
228         assertEquals(new String[] {"clicked"}, collectedAlerts);
229         assertSame(page, secondPage);
230     }
231 
232     /**
233      * @throws Exception if the test fails
234      */
235     @Test
236     public void click_javascriptUrl_javascriptDisabled() throws Exception {
237         final String htmlContent = DOCTYPE_HTML
238             + "<html><head><title>foo</title></head><body>\n"
239             + "<a href='http://www.foo1.com' id='a1'>link to foo1</a>\n"
240             + "<a href='javascript:alert(\"clicked\")' id='a2'>link to foo2</a>\n"
241             + "<a href='http://www.foo3.com' id='a3'>link to foo3</a>\n"
242             + "</body></html>";
243         final WebClient client = getWebClient();
244         client.getOptions().setJavaScriptEnabled(false);
245 
246         final List<String> collectedAlerts = new ArrayList<>();
247         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
248 
249         final MockWebConnection webConnection = new MockWebConnection();
250         webConnection.setDefaultResponse(htmlContent);
251         client.setWebConnection(webConnection);
252 
253         final HtmlPage page = client.getPage(URL_FIRST);
254         final HtmlAnchor anchor = page.getHtmlElementById("a2");
255 
256         assertEquals(Collections.EMPTY_LIST, collectedAlerts);
257 
258         final HtmlPage secondPage = anchor.click();
259 
260         assertEquals(Collections.EMPTY_LIST, collectedAlerts);
261         assertSame(page, secondPage);
262     }
263 
264     /**
265      * @throws Exception if the test fails
266      */
267     @Test
268     public void click_javascriptUrl_InvalidReturn_RegressionTest() throws Exception {
269         final String htmlContent = DOCTYPE_HTML
270             + "<html><head><SCRIPT lang=\"JavaScript\">\n"
271             + "function doSubmit(formName) {\n"
272             + "  return false;\n"
273             + "}\n"
274             + "</SCRIPT></head><body>\n"
275             + "<form name='formName' method='POST' action='../foo'>\n"
276             + "<a href='.' id='testJavascript' name='testJavascript'"
277             + "onclick='return false'>Test Link </a>\n"
278             + "<input type='submit' value='Login' name='loginButton'>\n"
279             + "</form></body></html>";
280 
281         final HtmlPage page = loadPage(htmlContent);
282         final HtmlAnchor testAnchor = page.getAnchorByName("testJavascript");
283         testAnchor.click();  // blows up here
284     }
285 
286     /**
287      * @throws Exception if the test fails
288      */
289     @Test
290     public void click_javascriptUrl_targetPageWithIframe() throws Exception {
291         final String firstContent = DOCTYPE_HTML
292             + " <html>\n"
293             + "<head><title>Page A</title></head>\n"
294             + "<body><a href='#' onclick=\"document.location.href='" + URL_SECOND + "'\" id='link'>link</a></body>\n"
295             + "</html>";
296         final String secondContent = DOCTYPE_HTML
297             + "<html>\n"
298             + "<head><title>Page B</title></head>\n"
299             + "<body><iframe src='" + URL_THIRD + "'></iframe></body>\n"
300             + "</html>";
301         final String thirdContent = DOCTYPE_HTML
302             + "<html>\n"
303             + "<head><title>Page C</title></head>\n"
304             + "<body>test</body>\n"
305             + "</html>";
306 
307         final WebClient client = getWebClient();
308         final MockWebConnection conn = new MockWebConnection();
309         conn.setResponse(URL_FIRST, firstContent);
310         conn.setResponse(URL_SECOND, secondContent);
311         conn.setResponse(URL_THIRD, thirdContent);
312         client.setWebConnection(conn);
313         final HtmlPage firstPage = client.getPage(URL_FIRST);
314         final HtmlAnchor a = firstPage.getHtmlElementById("link");
315         final HtmlPage secondPage = a.click();
316         assertEquals("url", URL_SECOND, secondPage.getUrl());
317         assertEquals("title", "Page B", secondPage.getTitleText());
318     }
319 
320     /**
321      * Regression test for bug #894.
322      * @throws Exception if the test fails
323      */
324     @Test
325     public void click_javascriptUrl_encoded() throws Exception {
326         final String htmlContent = DOCTYPE_HTML
327             + "<html><body><script>function hello() { alert('hello') }</script>\n"
328             + "<a href='javascript:%20hello%28%29' id='a1'>a1</a>\n"
329             + "<a href='javascript: hello%28%29' id='a2'>a2</a>\n"
330             + "<a href='javascript:hello%28%29' id='a3'>a3</a>\n"
331             + "</body></html>";
332 
333         final List<String> collectedAlerts = new ArrayList<>();
334         final HtmlPage page = loadPage(htmlContent, collectedAlerts);
335         assertEquals(Collections.EMPTY_LIST, collectedAlerts);
336 
337         page.getHtmlElementById("a1").click();
338         page.getHtmlElementById("a2").click();
339         page.getHtmlElementById("a3").click();
340         assertEquals(new String[] {"hello", "hello", "hello"}, collectedAlerts);
341     }
342 
343     /**
344      * Test for new openLinkInNewWindow() method.
345      * @throws Exception on test failure
346      */
347     @Test
348     public void openLinkInNewWindow() throws Exception {
349         final String htmlContent = DOCTYPE_HTML
350             + "<html><head><title>foo</title></head><body>\n"
351             + "<a href='http://www.foo1.com' id='a1'>link to foo1</a>\n"
352             + "</body></html>";
353 
354         final HtmlPage page = loadPage(htmlContent);
355         final HtmlAnchor anchor = page.getHtmlElementById("a1");
356 
357         assertEquals("size incorrect before test", 1, page.getWebClient().getWebWindows().size());
358 
359         final HtmlPage secondPage = (HtmlPage) anchor.openLinkInNewWindow();
360 
361         assertNotSame("new page not returned", page, secondPage);
362         assertTrue("new page in wrong window type",
363                 TopLevelWindow.class.isInstance(secondPage.getEnclosingWindow()));
364         assertEquals("new window not created", 2, page.getWebClient().getWebWindows().size());
365         assertNotSame("new window not used", page.getEnclosingWindow(), secondPage
366                 .getEnclosingWindow());
367     }
368 
369     /**
370      * Links with an href and a non-false returning onclick that opens a new window should still
371      * open the href in the first window.
372      *
373      * http://sourceforge.net/p/htmlunit/bugs/394/
374      *
375      * @throws Exception on test failure
376      */
377     @Test
378     public void correctLinkTargetWhenOnclickOpensWindow() throws Exception {
379         final String firstContent = DOCTYPE_HTML
380             + "<html><head><title>First</title></head><body>\n"
381             + "<a href='page2.html' id='clickme' onclick=\"window.open('popup.html', 'newWindow');\">X</a>\n"
382             + "</body></html>";
383         final String html2 = DOCTYPE_HTML + "<html><head><title>Second</title></head><body></body></html>";
384         final String htmlPopup = DOCTYPE_HTML + "<html><head><title>Popup</title></head><body></body></html>";
385 
386         final WebClient client = getWebClient();
387         final List<String> collectedAlerts = new ArrayList<>();
388         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
389 
390         final MockWebConnection webConnection = new MockWebConnection();
391         webConnection.setResponse(URL_FIRST, firstContent);
392         webConnection.setResponse(new URL(URL_FIRST, "page2.html"), html2);
393         webConnection.setResponse(new URL(URL_FIRST, "popup.html"), htmlPopup);
394         client.setWebConnection(webConnection);
395 
396         final HtmlPage firstPage = client.getPage(URL_FIRST);
397         final HtmlAnchor anchor = firstPage.getHtmlElementById("clickme");
398         final HtmlPage pageAfterClick = anchor.click();
399 
400         assertEquals("Second window did not open", 2, client.getWebWindows().size());
401         assertNotSame("New Page was not returned", firstPage, pageAfterClick);
402         assertEquals("Wrong new Page returned", "Popup", pageAfterClick.getTitleText());
403         assertEquals("Original window not updated", "Second",
404             ((HtmlPage) firstPage.getEnclosingWindow().getEnclosedPage()).getTitleText());
405     }
406 
407     /**
408      * @throws Exception if an error occurs
409      */
410     @Test
411     public void preventDefault1() throws Exception {
412         final String html = DOCTYPE_HTML
413             + "<html><head><script>\n"
414             + "  function handler(e) {\n"
415             + "    if (e)\n"
416             + "      e.preventDefault();\n"
417             + "    else\n"
418             + "      return false;\n"
419             + "  }\n"
420             + "  function init() {\n"
421             + "    document.getElementById('a1').onclick = handler;\n"
422             + "  }\n"
423             + "</script></head>\n"
424             + "<body onload='init()'>\n"
425             + "<a href='" + URL_SECOND + "' id='a1'>Test</a>\n"
426             + "</body></html>";
427 
428         final HtmlPage page = loadPage(html);
429         final HtmlAnchor a1 = page.getHtmlElementById("a1");
430         final HtmlPage secondPage = a1.click();
431         assertEquals(URL_FIRST, secondPage.getUrl());
432     }
433 
434     /**
435      * @throws Exception if an error occurs
436      */
437     @Test
438     public void preventDefault2() throws Exception {
439         final String html = DOCTYPE_HTML
440             + "<html><head><script>\n"
441             + "  function handler(e) {\n"
442             + "    if (e.preventDefault)\n"
443             + "      e.preventDefault();\n"
444             + "    else\n"
445             + "      e.returnValue = false;\n"
446             + "  }\n"
447             + "</script></head>\n"
448             + "<body>\n"
449             + "<a href='" + URL_SECOND + "' id='a1' onclick='handler(event)'>Test</a>\n"
450             + "</body></html>";
451 
452         final HtmlPage page = loadPage(html);
453         final HtmlAnchor a1 = page.getHtmlElementById("a1");
454         final HtmlPage secondPage = a1.click();
455         assertEquals(URL_FIRST, secondPage.getUrl());
456     }
457 
458     /**
459      * @throws Exception if an error occurs
460      */
461     @Test
462     public void preventDefault3() throws Exception {
463         final String html = DOCTYPE_HTML
464             + "<html><body>\n"
465             + "<a href='" + URL_SECOND + "' id='a1' onclick='return false'>Test</a>\n"
466             + "</body></html>";
467 
468         final HtmlPage page = loadPage(html);
469         final HtmlAnchor a1 = page.getHtmlElementById("a1");
470         final HtmlPage secondPage = a1.click();
471         assertEquals(URL_FIRST, secondPage.getUrl());
472     }
473 
474     /**
475      * Test for bug #826.
476      * @throws Exception if an error occurs
477      */
478     @Test
479     public void hashAnchor() throws Exception {
480         final String html = DOCTYPE_HTML
481                 + "<html><body>\n"
482                 + "<a id='a' href='#a'>a</a>\n"
483                 + "<a id='a_target' href='#target' target='_blank'>target</a>\n"
484                 + "</body></html>";
485         HtmlPage page = loadPage(html);
486         HtmlPage targetPage = page.getHtmlElementById("a").click();
487         assertEquals(new URL(URL_FIRST, "#a"), page.getUrl());
488         assertEquals(page.getEnclosingWindow(), targetPage.getEnclosingWindow());
489 
490         page = loadPage(html);
491         targetPage = page.getHtmlElementById("a_target").click();
492         assertEquals(new URL(URL_FIRST, "#target"), targetPage.getUrl());
493         assertFalse(page.getEnclosingWindow().equals(targetPage.getEnclosingWindow()));
494     }
495 
496     /**
497      * @throws Exception if an error occurs
498      */
499     @Test
500     public void targetWithRelativeUrl() throws Exception {
501         final WebClient client = getWebClient();
502 
503         final URL url = getClass().getResource("HtmlAnchorTest_targetWithRelativeUrl_a.html");
504         assertNotNull(url);
505 
506         final HtmlPage page = client.getPage(url);
507         final WebWindow a = page.getEnclosingWindow();
508         final WebWindow b = page.getFrameByName("b");
509         final WebWindow c = page.getFrameByName("c");
510 
511         assertEquals("a.html", right(getUrl(a), 6));
512         assertEquals("b.html", right(getUrl(b), 6));
513         assertEquals("c.html", right(getUrl(c), 6));
514 
515         ((HtmlPage) c.getEnclosedPage()).getAnchorByHref("#foo").click();
516 
517         assertEquals("a.html", right(getUrl(a), 6));
518         assertEquals("c.html#foo", right(getUrl(b), 10));
519         assertEquals("c.html", right(getUrl(c), 6));
520     }
521 
522     /**
523      * Returns the URL of the page loaded in the specified window.
524      * @param w the window
525      * @return the URL of the page loaded in the specified window
526      */
527     private static String getUrl(final WebWindow w) {
528         return w.getEnclosedPage().getUrl().toString();
529     }
530 
531     /**
532      * @throws Exception if an error occurs
533      */
534     @Test
535     public void clickNestedElement_jsDisabled() throws Exception {
536         final String html = DOCTYPE_HTML
537             + "<html>\n"
538             + "<body>\n"
539             + "<a href='page2.html'>\n"
540             + "<span id='theSpan'>My Link</span></a>\n"
541             + "</body></html>";
542 
543         getMockWebConnection().setDefaultResponse("");
544         getWebClient().getOptions().setJavaScriptEnabled(false);
545         final HtmlPage page = loadPage(html);
546         final HtmlElement span = page.getHtmlElementById("theSpan");
547         assertEquals("span", span.getTagName());
548         final HtmlPage page2 = span.click();
549         assertEquals(new URL(URL_FIRST, "page2.html"), page2.getUrl());
550     }
551 
552     /**
553      * @throws Exception if the test fails
554      */
555     @Test
556     public void asXml_emptyTag() throws Exception {
557         final String html = DOCTYPE_HTML
558             + "<html><body>\n"
559             + "<a name='foo'></a>\n"
560             + "</body></html>";
561 
562         final HtmlPage page = loadPage(html);
563         final HtmlAnchor htmlAnchor = page.getAnchorByName("foo");
564         assertEquals("<a name=\"foo\"></a>", htmlAnchor.asXml());
565     }
566 
567     /**
568      * @throws Exception if the test fails
569      */
570     @Test
571     public void clickShift() throws Exception {
572         final String first = DOCTYPE_HTML
573             + "<html><head><title>First</title></head><body>\n"
574             + "  <a href='" + URL_SECOND + "' id='a2'>link to foo2</a>\n"
575             + "</body></html>";
576         final String second = DOCTYPE_HTML
577             + "<html><head><title>Second</title></head><body></body></html>";
578 
579         final WebClient client = getWebClient();
580         final List<String> collectedAlerts = new ArrayList<>();
581         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
582 
583         final MockWebConnection webConnection = new MockWebConnection();
584         webConnection.setResponse(URL_FIRST, first);
585         webConnection.setResponse(URL_SECOND, second);
586         client.setWebConnection(webConnection);
587 
588         assertEquals(1, getWebClient().getTopLevelWindows().size());
589         final HtmlPage page = client.getPage(URL_FIRST);
590         final HtmlAnchor anchor = page.getHtmlElementById("a2");
591 
592         final HtmlPage secondPage = anchor.click(true, false, false);
593         assertEquals(2, getWebClient().getTopLevelWindows().size());
594         assertEquals("Second", secondPage.getTitleText());
595     }
596 
597     /**
598      * @throws Exception if the test fails
599      */
600     @Test
601     public void clickCtrl() throws Exception {
602         final String first = DOCTYPE_HTML
603             + "<html><head><title>First</title></head><body>\n"
604             + "  <a href='" + URL_SECOND + "' id='a2'>link to foo2</a>\n"
605             + "</body></html>";
606         final String second = DOCTYPE_HTML
607             + "<html><head><title>Second</title></head><body></body></html>";
608 
609         final WebClient client = getWebClient();
610         final List<String> collectedAlerts = new ArrayList<>();
611         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
612 
613         final MockWebConnection webConnection = new MockWebConnection();
614         webConnection.setResponse(URL_FIRST, first);
615         webConnection.setResponse(URL_SECOND, second);
616         client.setWebConnection(webConnection);
617 
618         assertEquals(1, getWebClient().getTopLevelWindows().size());
619         final HtmlPage page = client.getPage(URL_FIRST);
620         final HtmlAnchor anchor = page.getHtmlElementById("a2");
621 
622         final HtmlPage secondPage = anchor.click(false, true, false);
623         assertEquals(2, getWebClient().getTopLevelWindows().size());
624         assertEquals("First", secondPage.getTitleText());
625     }
626 
627     /**
628      * @throws Exception if the test fails
629      */
630     @Test
631     public void clickCtrlShift() throws Exception {
632         final String first = DOCTYPE_HTML
633             + "<html><head><title>First</title></head><body>\n"
634             + "  <a href='" + URL_SECOND + "' id='a2'>link to foo2</a>\n"
635             + "</body></html>";
636         final String second = DOCTYPE_HTML
637             + "<html><head><title>Second</title></head><body></body></html>";
638 
639         final WebClient client = getWebClient();
640         final List<String> collectedAlerts = new ArrayList<>();
641         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
642 
643         final MockWebConnection webConnection = new MockWebConnection();
644         webConnection.setResponse(URL_FIRST, first);
645         webConnection.setResponse(URL_SECOND, second);
646         client.setWebConnection(webConnection);
647 
648         assertEquals(1, getWebClient().getTopLevelWindows().size());
649         final HtmlPage page = client.getPage(URL_FIRST);
650         final HtmlAnchor anchor = page.getHtmlElementById("a2");
651 
652         final HtmlPage secondPage = anchor.click(true, true, false);
653         assertEquals(2, getWebClient().getTopLevelWindows().size());
654         assertEquals("First", secondPage.getTitleText());
655     }
656 
657     /**
658      * @throws Exception if the test fails
659      */
660     @Test
661     public void clickShiftJavascript() throws Exception {
662         final String first = DOCTYPE_HTML
663             + "<html><head><title>First</title></head><body>\n"
664             + "  <a href='javascript: window.location=\"" + URL_SECOND + "\"' id='a2'>link to foo2</a>\n"
665             + "</body></html>";
666         final String second = DOCTYPE_HTML
667             + "<html><head><title>Second</title></head><body></body></html>";
668 
669         final WebClient client = getWebClient();
670         final List<String> collectedAlerts = new ArrayList<>();
671         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
672 
673         final MockWebConnection webConnection = new MockWebConnection();
674         webConnection.setResponse(URL_FIRST, first);
675         webConnection.setResponse(URL_SECOND, second);
676         client.setWebConnection(webConnection);
677 
678         assertEquals(1, getWebClient().getTopLevelWindows().size());
679         final HtmlPage page = client.getPage(URL_FIRST);
680         final HtmlAnchor anchor = page.getHtmlElementById("a2");
681 
682         final HtmlPage secondPage = anchor.click(true, false, false);
683         assertEquals(2, getWebClient().getTopLevelWindows().size());
684         assertEquals("Second", secondPage.getTitleText());
685     }
686 
687     /**
688      * @throws Exception if the test fails
689      */
690     @Test
691     public void clickCtrlJavascript() throws Exception {
692         final String first = DOCTYPE_HTML
693             + "<html><head><title>First</title></head><body>\n"
694             + "  <a href='javascript: window.location=\"" + URL_SECOND + "\"' id='a2'>link to foo2</a>\n"
695             + "</body></html>";
696         final String second = DOCTYPE_HTML
697             + "<html><head><title>Second</title></head><body></body></html>";
698 
699         final WebClient client = getWebClient();
700         final List<String> collectedAlerts = new ArrayList<>();
701         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
702 
703         final MockWebConnection webConnection = new MockWebConnection();
704         webConnection.setResponse(URL_FIRST, first);
705         webConnection.setResponse(URL_SECOND, second);
706         client.setWebConnection(webConnection);
707 
708         assertEquals(1, getWebClient().getTopLevelWindows().size());
709         final HtmlPage page = client.getPage(URL_FIRST);
710         final HtmlAnchor anchor = page.getHtmlElementById("a2");
711 
712         final HtmlPage secondPage = anchor.click(false, true, false);
713         assertEquals(2, getWebClient().getTopLevelWindows().size());
714         assertEquals("First", secondPage.getTitleText());
715     }
716 
717     /**
718      * @throws Exception if the test fails
719      */
720     @Test
721     public void clickShiftCtrlJavascript() throws Exception {
722         final String first = DOCTYPE_HTML
723             + "<html><head><title>First</title></head><body>\n"
724             + "  <a href='javascript: window.location=\"" + URL_SECOND + "\"' id='a2'>link to foo2</a>\n"
725             + "</body></html>";
726         final String second = DOCTYPE_HTML
727             + "<html><head><title>Second</title></head><body></body></html>";
728 
729         final WebClient client = getWebClient();
730         final List<String> collectedAlerts = new ArrayList<>();
731         client.setAlertHandler(new CollectingAlertHandler(collectedAlerts));
732 
733         final MockWebConnection webConnection = new MockWebConnection();
734         webConnection.setResponse(URL_FIRST, first);
735         webConnection.setResponse(URL_SECOND, second);
736         client.setWebConnection(webConnection);
737 
738         assertEquals(1, getWebClient().getTopLevelWindows().size());
739         final HtmlPage page = client.getPage(URL_FIRST);
740         final HtmlAnchor anchor = page.getHtmlElementById("a2");
741 
742         final HtmlPage secondPage = anchor.click(true, true, false);
743         assertEquals(2, getWebClient().getTopLevelWindows().size());
744         assertEquals("First", secondPage.getTitleText());
745     }
746 
747     /**
748      * @throws Exception if the test fails
749      */
750     @Test
751     public void asText_getTextContent() throws Exception {
752         final String html = DOCTYPE_HTML
753             + "<html>\n"
754             + "<head></head>\n"
755             + "<body>\n"
756             + "  <a href='localhost' id='a2'>\n"
757             + "    <span>Last Name</span>, <span>First Name</span>\n"
758             + "  </a>\n"
759             + "</body></html>";
760 
761         final HtmlPage page = loadPage(html);
762         final HtmlAnchor anchor = page.getHtmlElementById("a2");
763 
764         assertEquals("Last Name, First Name", anchor.asNormalizedText());
765         assertEquals("\n    Last Name, First Name\n  ", anchor.getTextContent());
766     }
767 
768     /**
769      * Not testable with Selenium.
770      *
771      * @exception Exception If the test fails
772      */
773     @Test
774     @Alerts({"1", "First"})
775     public void clickWithDownloadAttribute() throws Exception {
776         final String html = DOCTYPE_HTML
777                 + "<html>\n"
778                 + "<head>\n"
779                 + "  <title>First</title>\n"
780                 + "</head>\n"
781                 + "<body>\n"
782                 + "  <a id='clickMe' href='" + URL_SECOND + "' download='lora.html'>Click Me</a>\n"
783                 + "</body></html>";
784 
785         getMockWebConnection().setResponse(URL_SECOND, "<head><title>Second</title>");
786         final int windowsSize = getWebClient().getWebWindows().size();
787         final HtmlPage page = loadPage(html);
788 
789         page.getElementById("clickMe").click();
790 
791         assertEquals("Should have opened a new window",
792                 windowsSize + Integer.parseInt(getExpectedAlerts()[0]), getWebClient().getWebWindows().size());
793         assertEquals("Should not have navigated away", getExpectedAlerts()[1], page.getTitleText());
794     }
795 
796     /**
797      * Not testable with Selenium.
798      *
799      * @exception Exception If the test fails
800      */
801     @Test
802     @Alerts({"1", "First"})
803     public void clickWithDownloadAttributeFromJs() throws Exception {
804         final String html = DOCTYPE_HTML
805                 + "<html>\n"
806                 + "<head>\n"
807                 + "  <title>First</title>\n"
808                 + "  <script>\n"
809                 + "    function test(e) {\n"
810                 + "      var a = document.getElementById('clickMe');"
811                 + "      a.setAttribute('download', 'lora.png');"
812                 + "      a.click();"
813                 + "    }\n"
814                 + "  </script>\n"
815                 + "</head>\n"
816                 + "<body onload='test()'>\n"
817                 + "  <a id='clickMe' href='" + URL_SECOND + "' >Click Me</a>\n"
818                 + "</body></html>";
819 
820         getMockWebConnection().setResponse(URL_SECOND, "<head><title>Second</title>");
821 
822         final int windowsSize = getWebClient().getWebWindows().size();
823         final HtmlPage page = loadPage(html);
824 
825         assertEquals("Should have opened a new window",
826                 windowsSize + Integer.parseInt(getExpectedAlerts()[0]), getWebClient().getWebWindows().size());
827         assertEquals("Should not have navigated away", getExpectedAlerts()[1], page.getTitleText());
828     }
829 
830     /**
831      * Not testable with Selenium.
832      *
833      * @exception Exception If the test fails
834      */
835     @Test
836     @Alerts({"1", "First",
837              "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAAL"
838                     + "GPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1l"
839                     + "bnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C"
840                     + "7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI"
841                     + "97CER3N0vr4MkhoXe0rZigAAAABJRU5ErkJggg=="})
842     public void clickWithDownloadAttributeDataUrl() throws Exception {
843         final String html = DOCTYPE_HTML
844                 + "<html>\n"
845                 + "<head>\n"
846                 + "  <title>First</title>\n"
847                 + "</head>\n"
848                 + "<body>\n"
849                 + "  <a id='clickMe' href='data:image/png;base64,"
850                     + "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGP"
851                     + "C/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IA"
852                     + "AAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1J"
853                     + "REFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jq"
854                     + "ch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0"
855                     + "vr4MkhoXe0rZigAAAABJRU5ErkJggg==' download='lora.html'>Click Me</a>\n"
856                 + "</body></html>";
857 
858         getMockWebConnection().setResponse(URL_SECOND, "<head><title>Second</title>");
859         final int windowsSize = getWebClient().getWebWindows().size();
860         final HtmlPage page = loadPage(html);
861 
862         page.getElementById("clickMe").click();
863 
864         assertEquals("Should have opened a new window",
865                 windowsSize + Integer.parseInt(getExpectedAlerts()[0]), getWebClient().getWebWindows().size());
866         assertEquals("Should not have navigated away", getExpectedAlerts()[1], page.getTitleText());
867 
868         final WebWindow dataWindow = getWebClient().getWebWindows().get(getWebClient().getWebWindows().size() - 1);
869         final Page dataPage = dataWindow.getEnclosedPage();
870         assertTrue("Should be an UnexpectedPage", dataPage instanceof UnexpectedPage);
871 
872         try (InputStream resultInputStream = ((UnexpectedPage) dataPage).getInputStream()) {
873             final ImageInputStream resultImageIS = ImageIO.createImageInputStream(resultInputStream);
874             final BufferedImage resultBufferedIIS = ImageIO.read(resultImageIS);
875             compareImages(getExpectedAlerts()[2], null, resultBufferedIIS);
876         }
877     }
878 
879     /**
880      * @throws Exception if the test fails
881      */
882     @Test
883     public void click_unexpectedPage() throws Exception {
884         final String html = DOCTYPE_HTML
885             + "<html><head></head>\n"
886             + "<body>\n"
887             + "  <a href='" + URL_SECOND + "' id='link'>link</a>\n"
888             + "</body>\n"
889             + "</html>";
890 
891         getMockWebConnection().setResponse(URL_SECOND, "{name: \"Test\"};", MimeType.APPLICATION_JSON);
892 
893         final HtmlPage page = loadPage(html);
894         assertEquals(1, getWebClient().getWebWindows().size());
895 
896         page.getElementById("link").click();
897         assertEquals(1, getWebClient().getWebWindows().size());
898         assertTrue(page.getEnclosingWindow().getEnclosedPage() instanceof UnexpectedPage);
899     }
900 
901     /**
902      * @throws Exception if the test fails
903      */
904     @Test
905     public void click_unexpectedPageAttachmentHandlerDoesNotHandleContentType() throws Exception {
906         final String html = DOCTYPE_HTML
907             + "<html><head></head>\n"
908             + "<body>\n"
909             + "  <a href='" + URL_SECOND + "' id='link'>link</a>\n"
910             + "</body>\n"
911             + "</html>";
912 
913         getMockWebConnection().setResponse(URL_SECOND, "{name: \"Test\"};", MimeType.APPLICATION_JSON);
914 
915         final LinkedList<Page> pages = new LinkedList<>();
916         getWebClient().setAttachmentHandler(new AttachmentHandler() {
917 
918             @Override
919             public void handleAttachment(final Page page, final String attachmentFilename) {
920                 pages.add(page);
921             }
922         });
923 
924         try {
925             final HtmlPage page = loadPage(html);
926             assertEquals(1, getWebClient().getWebWindows().size());
927 
928             page.getElementById("link").click();
929 
930             assertEquals(1, getWebClient().getWebWindows().size());
931             assertTrue(getWebClient().getCurrentWindow().getEnclosedPage() instanceof UnexpectedPage);
932 
933             assertEquals(0, pages.size());
934         }
935         finally {
936             getWebClient().setAttachmentHandler(null);
937         }
938     }
939 
940 
941     /**
942      * @throws Exception if the test fails
943      */
944     @Test
945     public void click_unexpectedPageAttachmentHandlerHandleContentType() throws Exception {
946         final String html = DOCTYPE_HTML
947             + "<html><head></head>\n"
948             + "<body>\n"
949             + "  <a href='" + URL_SECOND + "' id='link'>link</a>\n"
950             + "</body>\n"
951             + "</html>";
952 
953         getMockWebConnection().setResponse(URL_SECOND, "{name: \"Test\"};", MimeType.APPLICATION_JSON);
954 
955         final LinkedList<Page> pages = new LinkedList<>();
956         getWebClient().setAttachmentHandler(new AttachmentHandler() {
957 
958             @Override
959             public boolean isAttachment(final WebResponse response) {
960                 return MimeType.APPLICATION_JSON
961                         .equalsIgnoreCase(response.getResponseHeaderValue(HttpHeader.CONTENT_TYPE));
962             }
963 
964             @Override
965             public void handleAttachment(final Page page, final String attachmentFilename) {
966                 pages.add(page);
967             }
968         });
969 
970         try {
971             final HtmlPage page = loadPage(html);
972             assertEquals(1, getWebClient().getWebWindows().size());
973 
974             page.getElementById("link").click();
975 
976             final WebWindow newWindow = getWebClient().getWebWindows().get(getWebClient().getWebWindows().size() - 1);
977             assertTrue(newWindow.getEnclosedPage() instanceof UnexpectedPage);
978 
979             assertEquals(1, pages.size());
980         }
981         finally {
982             getWebClient().setAttachmentHandler(null);
983         }
984     }
985 
986     /**
987      * @throws Exception if the test fails
988      */
989     @Test
990     public void click_unexpectedPageAttachmentHandlerHandleResponseDoesNotHandleContentType() throws Exception {
991         final String html = DOCTYPE_HTML
992             + "<html><head></head>\n"
993             + "<body>\n"
994             + "  <a href='" + URL_SECOND + "' id='link'>link</a>\n"
995             + "</body>\n"
996             + "</html>";
997 
998         getMockWebConnection().setResponse(URL_SECOND, "{name: \"Test\"};", MimeType.APPLICATION_JSON);
999 
1000         final LinkedList<WebResponse> pages = new LinkedList<>();
1001         getWebClient().setAttachmentHandler(new AttachmentHandler() {
1002 
1003             @Override
1004             public boolean handleAttachment(final WebResponse response, final String attachmentFilename) {
1005                 pages.add(response);
1006                 return true;
1007             }
1008 
1009             @Override
1010             public void handleAttachment(final Page page, final String attachmentFilename) {
1011                 throw new IllegalAccessError("handleAttachment(Page) called");
1012             }
1013         });
1014 
1015         try {
1016             final HtmlPage page = loadPage(html);
1017             assertEquals(1, getWebClient().getWebWindows().size());
1018 
1019             page.getElementById("link").click();
1020             assertEquals(1, getWebClient().getWebWindows().size());
1021             assertTrue(page.getEnclosingWindow().getEnclosedPage() instanceof UnexpectedPage);
1022 
1023             assertEquals(0, pages.size());
1024         }
1025         finally {
1026             getWebClient().setAttachmentHandler(null);
1027         }
1028     }
1029 
1030     /**
1031      * @throws Exception if the test fails
1032      */
1033     @Test
1034     public void click_unexpectedPageAttachmentHandlerHandleResponseHandlesContentType() throws Exception {
1035         final String html = DOCTYPE_HTML
1036             + "<html><head></head>\n"
1037             + "<body>\n"
1038             + "  <a href='" + URL_SECOND + "' id='link'>link</a>\n"
1039             + "</body>\n"
1040             + "</html>";
1041 
1042         getMockWebConnection().setResponse(URL_SECOND, "{name: \"Test\"};", MimeType.APPLICATION_JSON);
1043 
1044         final LinkedList<WebResponse> pages = new LinkedList<>();
1045         getWebClient().setAttachmentHandler(new AttachmentHandler() {
1046 
1047             @Override
1048             public boolean isAttachment(final WebResponse response) {
1049                 return MimeType.APPLICATION_JSON
1050                         .equalsIgnoreCase(response.getResponseHeaderValue(HttpHeader.CONTENT_TYPE));
1051             }
1052 
1053             @Override
1054             public boolean handleAttachment(final WebResponse response, final String attachmentFilename) {
1055                 pages.add(response);
1056                 return true;
1057             }
1058 
1059             @Override
1060             public void handleAttachment(final Page page, final String attachmentFilename) {
1061                 throw new IllegalAccessError("handleAttachment(Page) called");
1062             }
1063         });
1064 
1065         try {
1066             final HtmlPage page = loadPage(html);
1067             assertEquals(1, getWebClient().getWebWindows().size());
1068 
1069             page.getElementById("link").click();
1070             assertEquals(1, getWebClient().getWebWindows().size());
1071             assertTrue(page.getEnclosingWindow().getEnclosedPage() == page);
1072 
1073             assertEquals(1, pages.size());
1074         }
1075         finally {
1076             getWebClient().setAttachmentHandler(null);
1077         }
1078     }
1079 
1080     /**
1081      * @throws Exception if the test fails
1082      */
1083     @Test
1084     public void click_unexpectedPageDownloadAttribute() throws Exception {
1085         final String html = DOCTYPE_HTML
1086             + "<html><head></head>\n"
1087             + "<body>\n"
1088             + "  <a href='" + URL_SECOND + "' id='link' download='test.json'>link</a>\n"
1089             + "</body>\n"
1090             + "</html>";
1091 
1092         getMockWebConnection().setResponse(URL_SECOND, "{name: \"Test\"};", MimeType.APPLICATION_JSON);
1093 
1094         final HtmlPage page = loadPage(html);
1095         assertEquals(1, getWebClient().getWebWindows().size());
1096 
1097         page.getElementById("link").click();
1098         assertEquals(2, getWebClient().getWebWindows().size());
1099 
1100         final WebWindow newWindow = getWebClient().getWebWindows().get(getWebClient().getWebWindows().size() - 1);
1101         assertTrue(newWindow.getEnclosedPage() instanceof UnexpectedPage);
1102     }
1103 
1104     /**
1105      * @throws Exception if the test fails
1106      */
1107     @Test
1108     public void click_unexpectedPageDownloadAttributeAttachmentHandler() throws Exception {
1109         final String html = DOCTYPE_HTML
1110             + "<html><head></head>\n"
1111             + "<body>\n"
1112             + "  <a href='" + URL_SECOND + "' id='link' download='test.json'>link</a>\n"
1113             + "</body>\n"
1114             + "</html>";
1115 
1116         getMockWebConnection().setResponse(URL_SECOND, "{name: \"Test\"};", MimeType.APPLICATION_JSON);
1117 
1118         final LinkedList<Page> pages = new LinkedList<>();
1119         getWebClient().setAttachmentHandler(new AttachmentHandler() {
1120             @Override
1121             public void handleAttachment(final Page page, final String attachmentFilename) {
1122                 pages.add(page);
1123             }
1124         });
1125 
1126         try {
1127             final HtmlPage page = loadPage(html);
1128             assertEquals(1, getWebClient().getWebWindows().size());
1129 
1130             page.getElementById("link").click();
1131             assertEquals(2, getWebClient().getWebWindows().size());
1132 
1133             final WebWindow newWindow = getWebClient().getWebWindows().get(getWebClient().getWebWindows().size() - 1);
1134             assertTrue(newWindow.getEnclosedPage() instanceof UnexpectedPage);
1135 
1136             assertEquals(1, pages.size());
1137             assertTrue(pages.get(0) instanceof UnexpectedPage);
1138         }
1139         finally {
1140             getWebClient().setAttachmentHandler(null);
1141         }
1142     }
1143 
1144     /**
1145      * @throws Exception if the test fails
1146      */
1147     @Test
1148     public void click_unexpectedPageDownloadAttributeAttachmentHandlerHandleResponse() throws Exception {
1149         final String html = DOCTYPE_HTML
1150             + "<html><head></head>\n"
1151             + "<body>\n"
1152             + "  <a href='" + URL_SECOND + "' id='link' download='test.json'>link</a>\n"
1153             + "</body>\n"
1154             + "</html>";
1155 
1156         getMockWebConnection().setResponse(URL_SECOND, "{name: \"Test\"};", MimeType.APPLICATION_JSON);
1157 
1158         final LinkedList<WebResponse> pages = new LinkedList<>();
1159         getWebClient().setAttachmentHandler(new AttachmentHandler() {
1160             @Override
1161             public boolean handleAttachment(final WebResponse response, final String attachmentFilename) {
1162                 pages.add(response);
1163                 return true;
1164             }
1165 
1166             @Override
1167             public void handleAttachment(final Page page, final String attachmentFilename) {
1168                 throw new IllegalAccessError("handleAttachment(Page) called");
1169             }
1170         });
1171 
1172         try {
1173             final HtmlPage page = loadPage(html);
1174             assertEquals(1, getWebClient().getWebWindows().size());
1175 
1176             page.getElementById("link").click();
1177             assertEquals(1, getWebClient().getWebWindows().size());
1178             assertTrue(page.getEnclosingWindow().getEnclosedPage() == page);
1179 
1180             assertEquals(1, pages.size());
1181         }
1182         finally {
1183             getWebClient().setAttachmentHandler(null);
1184         }
1185     }
1186 }