1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.html;
16
17 import static org.htmlunit.BrowserVersionFeatures.ANCHOR_SEND_PING_REQUEST;
18 import static org.htmlunit.BrowserVersionFeatures.FORM_SUBMISSION_HEADER_CACHE_CONTROL_MAX_AGE;
19 import static org.htmlunit.html.DomElement.ATTRIBUTE_NOT_DEFINED;
20
21 import java.io.IOException;
22 import java.net.MalformedURLException;
23 import java.net.URL;
24 import java.util.Locale;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.htmlunit.BrowserVersion;
29 import org.htmlunit.FormEncodingType;
30 import org.htmlunit.HttpHeader;
31 import org.htmlunit.HttpMethod;
32 import org.htmlunit.Page;
33 import org.htmlunit.WebClient;
34 import org.htmlunit.WebRequest;
35 import org.htmlunit.WebWindow;
36 import org.htmlunit.protocol.javascript.JavaScriptURLConnection;
37 import org.htmlunit.util.ArrayUtils;
38 import org.htmlunit.util.StringUtils;
39 import org.htmlunit.util.UrlUtils;
40
41
42
43
44
45
46
47
48
49
50 public final class HyperlinkElementSupport {
51
52 private static final Log LOG = LogFactory.getLog(HyperlinkElementSupport.class);
53
54 private HyperlinkElementSupport() {
55
56 }
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public static void doClickStateUpdate(final HyperlinkElement element, final boolean shiftKey,
72 final boolean ctrlKey, final String hrefSuffix) throws IOException {
73 final HtmlElement htmlElement = (HtmlElement) element;
74
75 final String href = (element.getHrefAttribute() + hrefSuffix).trim();
76 if (LOG.isDebugEnabled()) {
77 final String w = htmlElement.getPage().getEnclosingWindow().getName();
78 LOG.debug("do click action in window '" + w + "', using href '" + href + "'");
79 }
80 if (ATTRIBUTE_NOT_DEFINED == element.getHrefAttribute()) {
81 return;
82 }
83 final String downloadAttribute = element.getDownloadAttribute();
84 HtmlPage page = (HtmlPage) htmlElement.getPage();
85 if (StringUtils.startsWithIgnoreCase(href, JavaScriptURLConnection.JAVASCRIPT_PREFIX)) {
86 final StringBuilder builder = new StringBuilder(href.length());
87 builder.append(JavaScriptURLConnection.JAVASCRIPT_PREFIX);
88 for (int i = JavaScriptURLConnection.JAVASCRIPT_PREFIX.length(); i < href.length(); i++) {
89 final char ch = href.charAt(i);
90 if (ch == '%' && i + 2 < href.length()) {
91 final char ch1 = Character.toUpperCase(href.charAt(i + 1));
92 final char ch2 = Character.toUpperCase(href.charAt(i + 2));
93 if ((Character.isDigit(ch1) || ch1 >= 'A' && ch1 <= 'F')
94 && (Character.isDigit(ch2) || ch2 >= 'A' && ch2 <= 'F')) {
95 builder.append((char) Integer.parseInt(href.substring(i + 1, i + 3), 16));
96 i += 2;
97 continue;
98 }
99 }
100 builder.append(ch);
101 }
102
103 final String target;
104 if (shiftKey || ctrlKey || ATTRIBUTE_NOT_DEFINED != downloadAttribute) {
105 target = WebClient.TARGET_BLANK;
106 }
107 else {
108 target = page.getResolvedTarget(element.getTargetAttribute());
109 }
110 final WebWindow win = page.getWebClient().openTargetWindow(page.getEnclosingWindow(),
111 target, WebClient.TARGET_SELF);
112 Page enclosedPage = win.getEnclosedPage();
113 if (enclosedPage == null) {
114 win.getWebClient().getPage(win, WebRequest.newAboutBlankRequest());
115 enclosedPage = win.getEnclosedPage();
116 }
117 if (enclosedPage != null && enclosedPage.isHtmlPage()) {
118 page = (HtmlPage) enclosedPage;
119 page.executeJavaScript(builder.toString(), "javascript url", htmlElement.getStartLineNumber());
120 }
121 return;
122 }
123
124 final URL url = getTargetUrl(href, page);
125
126 final URL pageUrl = page.getUrl();
127
128 final WebClient webClient = page.getWebClient();
129 final BrowserVersion browser = webClient.getBrowserVersion();
130 if (ATTRIBUTE_NOT_DEFINED != element.getPingAttribute() && browser.hasFeature(ANCHOR_SEND_PING_REQUEST)) {
131 final URL pingUrl = getTargetUrl(element.getPingAttribute(), page);
132 final WebRequest pingRequest = new WebRequest(pingUrl, HttpMethod.POST);
133 pingRequest.setAdditionalHeader(HttpHeader.ACCEPT_ENCODING, browser.getAcceptEncodingHeader());
134 pingRequest.setAdditionalHeader(HttpHeader.PING_FROM, pageUrl.toExternalForm());
135 pingRequest.setAdditionalHeader(HttpHeader.PING_TO, url.toExternalForm());
136 pingRequest.setEncodingType(FormEncodingType.TEXT_PLAIN);
137 try {
138 pingRequest.setAdditionalHeader(HttpHeader.ORIGIN,
139 UrlUtils.getUrlWithProtocolAndAuthority(pageUrl).toExternalForm());
140 }
141 catch (final MalformedURLException e) {
142 if (LOG.isInfoEnabled()) {
143 LOG.info("Invalid origin url '" + pageUrl + "'");
144 }
145 }
146 if (browser.hasFeature(FORM_SUBMISSION_HEADER_CACHE_CONTROL_MAX_AGE)) {
147 pingRequest.setAdditionalHeader(HttpHeader.CACHE_CONTROL, "max-age=0");
148 }
149 pingRequest.setRequestBody("PING");
150
151
152
153 pingRequest.setFetchDestination(WebRequest.FetchDestination.EMPTY);
154 pingRequest.setFetchModeOverride(WebRequest.FetchMode.NO_CORS);
155 pingRequest.setRequestingUrl(pageUrl);
156
157 try {
158 webClient.loadWebResponse(pingRequest);
159 }
160 catch (final Exception e) {
161
162 }
163 }
164
165 final WebRequest webRequest = new WebRequest(url, browser.getHtmlAcceptHeader(),
166 browser.getAcceptEncodingHeader());
167
168 webRequest.setCharset(page.getCharset());
169
170
171
172
173
174
175
176
177
178
179
180 webRequest.markAsNavigation(page.getUrl(), true);
181
182 if (!relContainsNoreferrer(element)) {
183 webRequest.setRefererHeader(page.getUrl());
184 }
185
186 if (LOG.isDebugEnabled()) {
187 LOG.debug(
188 "Getting page for " + url.toExternalForm()
189 + ", derived from href '" + href
190 + "', using the originating URL "
191 + page.getUrl());
192 }
193
194 final String target;
195 if (shiftKey || ctrlKey
196 || (webClient.getAttachmentHandler() == null
197 && ATTRIBUTE_NOT_DEFINED != downloadAttribute)) {
198 target = WebClient.TARGET_BLANK;
199 }
200 else {
201 target = page.getResolvedTarget(element.getTargetAttribute());
202 }
203 page.getWebClient().download(page.getEnclosingWindow(), target, webRequest,
204 true, (ATTRIBUTE_NOT_DEFINED == downloadAttribute) ? null : downloadAttribute,
205 htmlElement.getClass().getSimpleName() + " click");
206 }
207
208
209
210
211
212
213
214 public static boolean relContainsNoreferrer(final HyperlinkElement element) {
215 String rel = element.getRelAttribute();
216 if (rel != null) {
217 rel = rel.toLowerCase(Locale.ROOT);
218 return ArrayUtils.contains(StringUtils.splitAtBlank(rel), "noreferrer");
219 }
220 return false;
221 }
222
223
224
225
226
227
228
229
230
231 public static URL getTargetUrl(final String href, final HtmlPage page) throws MalformedURLException {
232 URL url = page.getFullyQualifiedUrl(href);
233
234 if (StringUtils.isEmptyOrNull(href)) {
235 url = UrlUtils.getUrlWithNewRef(url, null);
236 }
237 return url;
238 }
239 }