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
19 import java.io.IOException;
20 import java.net.MalformedURLException;
21 import java.net.URL;
22 import java.util.Locale;
23 import java.util.Map;
24
25 import org.apache.commons.lang3.ArrayUtils;
26 import org.apache.commons.lang3.StringUtils;
27 import org.apache.commons.lang3.Strings;
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.htmlunit.BrowserVersion;
31 import org.htmlunit.HttpHeader;
32 import org.htmlunit.HttpMethod;
33 import org.htmlunit.Page;
34 import org.htmlunit.SgmlPage;
35 import org.htmlunit.WebClient;
36 import org.htmlunit.WebRequest;
37 import org.htmlunit.WebWindow;
38 import org.htmlunit.javascript.host.event.Event;
39 import org.htmlunit.javascript.host.html.HTMLElement;
40 import org.htmlunit.protocol.javascript.JavaScriptURLConnection;
41 import org.htmlunit.util.UrlUtils;
42
43
44
45
46
47
48
49
50
51
52
53
54
55 public class HtmlAnchor extends HtmlElement {
56
57 private static final Log LOG = LogFactory.getLog(HtmlAnchor.class);
58
59
60 public static final String TAG_NAME = "a";
61
62
63
64
65
66
67
68
69 HtmlAnchor(final String qualifiedName, final SgmlPage page,
70 final Map<String, DomAttr> attributes) {
71 super(qualifiedName, page, attributes);
72 }
73
74
75
76
77 @Override
78 @SuppressWarnings("unchecked")
79 public <P extends Page> P click(final Event event,
80 final boolean shiftKey, final boolean ctrlKey, final boolean altKey,
81 final boolean ignoreVisibility) throws IOException {
82 WebWindow oldWebWindow = null;
83 if (ctrlKey) {
84 oldWebWindow = ((HTMLElement) event.getSrcElement()).getDomNodeOrDie()
85 .getPage().getWebClient().getCurrentWindow();
86 }
87
88 P page = super.click(event, shiftKey, ctrlKey, altKey, ignoreVisibility);
89
90 if (ctrlKey) {
91 page.getEnclosingWindow().getWebClient().setCurrentWindow(oldWebWindow);
92 page = (P) oldWebWindow.getEnclosedPage();
93 }
94
95 return page;
96 }
97
98
99
100
101
102
103
104
105
106
107
108 protected void doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey, final String hrefSuffix)
109 throws IOException {
110 final String href = (getHrefAttribute() + hrefSuffix).trim();
111 if (LOG.isDebugEnabled()) {
112 final String w = getPage().getEnclosingWindow().getName();
113 LOG.debug("do click action in window '" + w + "', using href '" + href + "'");
114 }
115 if (ATTRIBUTE_NOT_DEFINED == getHrefAttribute()) {
116 return;
117 }
118 final String downloadAttribute = getDownloadAttribute();
119 HtmlPage page = (HtmlPage) getPage();
120 if (Strings.CI.startsWith(href, JavaScriptURLConnection.JAVASCRIPT_PREFIX)) {
121 final StringBuilder builder = new StringBuilder(href.length());
122 builder.append(JavaScriptURLConnection.JAVASCRIPT_PREFIX);
123 for (int i = JavaScriptURLConnection.JAVASCRIPT_PREFIX.length(); i < href.length(); i++) {
124 final char ch = href.charAt(i);
125 if (ch == '%' && i + 2 < href.length()) {
126 final char ch1 = Character.toUpperCase(href.charAt(i + 1));
127 final char ch2 = Character.toUpperCase(href.charAt(i + 2));
128 if ((Character.isDigit(ch1) || ch1 >= 'A' && ch1 <= 'F')
129 && (Character.isDigit(ch2) || ch2 >= 'A' && ch2 <= 'F')) {
130 builder.append((char) Integer.parseInt(href.substring(i + 1, i + 3), 16));
131 i += 2;
132 continue;
133 }
134 }
135 builder.append(ch);
136 }
137
138 final String target;
139 if (shiftKey || ctrlKey || ATTRIBUTE_NOT_DEFINED != downloadAttribute) {
140 target = WebClient.TARGET_BLANK;
141 }
142 else {
143 target = page.getResolvedTarget(getTargetAttribute());
144 }
145 final WebWindow win = page.getWebClient().openTargetWindow(page.getEnclosingWindow(),
146 target, WebClient.TARGET_SELF);
147 Page enclosedPage = win.getEnclosedPage();
148 if (enclosedPage == null) {
149 win.getWebClient().getPage(win, WebRequest.newAboutBlankRequest());
150 enclosedPage = win.getEnclosedPage();
151 }
152 if (enclosedPage != null && enclosedPage.isHtmlPage()) {
153 page = (HtmlPage) enclosedPage;
154 page.executeJavaScript(builder.toString(), "javascript url", getStartLineNumber());
155 }
156 return;
157 }
158
159 final URL url = getTargetUrl(href, page);
160
161 final WebClient webClient = page.getWebClient();
162 final BrowserVersion browser = webClient.getBrowserVersion();
163 if (ATTRIBUTE_NOT_DEFINED != getPingAttribute() && browser.hasFeature(ANCHOR_SEND_PING_REQUEST)) {
164 final URL pingUrl = getTargetUrl(getPingAttribute(), page);
165 final WebRequest pingRequest = new WebRequest(pingUrl, HttpMethod.POST);
166 pingRequest.setAdditionalHeader(HttpHeader.PING_FROM, page.getUrl().toExternalForm());
167 pingRequest.setAdditionalHeader(HttpHeader.PING_TO, url.toExternalForm());
168 pingRequest.setRequestBody("PING");
169 webClient.loadWebResponse(pingRequest);
170 }
171
172 final WebRequest webRequest = new WebRequest(url, browser.getHtmlAcceptHeader(),
173 browser.getAcceptEncodingHeader());
174
175 webRequest.setCharset(page.getCharset());
176
177 if (!relContainsNoreferrer()) {
178 webRequest.setRefererHeader(page.getUrl());
179 }
180
181 if (LOG.isDebugEnabled()) {
182 LOG.debug(
183 "Getting page for " + url.toExternalForm()
184 + ", derived from href '" + href
185 + "', using the originating URL "
186 + page.getUrl());
187 }
188
189 final String target;
190 if (shiftKey || ctrlKey
191 || (webClient.getAttachmentHandler() == null
192 && ATTRIBUTE_NOT_DEFINED != downloadAttribute)) {
193 target = WebClient.TARGET_BLANK;
194 }
195 else {
196 target = page.getResolvedTarget(getTargetAttribute());
197 }
198 page.getWebClient().download(page.getEnclosingWindow(), target, webRequest,
199 true, (ATTRIBUTE_NOT_DEFINED != downloadAttribute) ? downloadAttribute : null, "Link click");
200 }
201
202 private boolean relContainsNoreferrer() {
203 String rel = getRelAttribute();
204 if (rel != null) {
205 rel = rel.toLowerCase(Locale.ROOT);
206 return ArrayUtils.contains(org.htmlunit.util.StringUtils.splitAtBlank(rel), "noreferrer");
207 }
208 return false;
209 }
210
211
212
213
214
215
216
217
218
219 public static URL getTargetUrl(final String href, final HtmlPage page) throws MalformedURLException {
220 URL url = page.getFullyQualifiedUrl(href);
221
222 if (StringUtils.isEmpty(href)) {
223 url = UrlUtils.getUrlWithNewRef(url, null);
224 }
225 return url;
226 }
227
228
229
230
231 @Override
232 protected boolean doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey) throws IOException {
233 doClickStateUpdate(shiftKey, ctrlKey, "");
234 return false;
235 }
236
237
238
239
240
241
242
243
244 public final String getCharsetAttribute() {
245 return getAttributeDirect("charset");
246 }
247
248
249
250
251
252
253
254
255 public final String getTypeAttribute() {
256 return getAttributeDirect(TYPE_ATTRIBUTE);
257 }
258
259
260
261
262
263
264
265
266 public final String getNameAttribute() {
267 return getAttributeDirect(NAME_ATTRIBUTE);
268 }
269
270
271
272
273
274
275
276
277 public final String getHrefAttribute() {
278 return getAttributeDirect("href").trim();
279 }
280
281
282
283
284
285
286
287
288 public final String getHrefLangAttribute() {
289 return getAttributeDirect("hreflang");
290 }
291
292
293
294
295
296
297
298
299 public final String getRelAttribute() {
300 return getAttributeDirect("rel");
301 }
302
303
304
305
306
307
308
309
310 public final String getRevAttribute() {
311 return getAttributeDirect("rev");
312 }
313
314
315
316
317
318
319
320
321 public final String getAccessKeyAttribute() {
322 return getAttributeDirect("accesskey");
323 }
324
325
326
327
328
329
330
331
332 public final String getShapeAttribute() {
333 return getAttributeDirect("shape");
334 }
335
336
337
338
339
340
341
342
343 public final String getCoordsAttribute() {
344 return getAttributeDirect("coords");
345 }
346
347
348
349
350
351
352
353
354 public final String getTabIndexAttribute() {
355 return getAttributeDirect("tabindex");
356 }
357
358
359
360
361
362
363
364
365 public final String getOnFocusAttribute() {
366 return getAttributeDirect("onfocus");
367 }
368
369
370
371
372
373
374
375
376 public final String getOnBlurAttribute() {
377 return getAttributeDirect("onblur");
378 }
379
380
381
382
383
384
385
386
387 public final String getTargetAttribute() {
388 return getAttributeDirect("target");
389 }
390
391
392
393
394
395
396
397
398
399
400
401
402 public final Page openLinkInNewWindow() throws MalformedURLException {
403 final URL target = ((HtmlPage) getPage()).getFullyQualifiedUrl(getHrefAttribute());
404 final String windowName = "HtmlAnchor.openLinkInNewWindow() target";
405 final WebWindow newWindow = getPage().getWebClient().openWindow(target, windowName);
406 return newWindow.getEnclosedPage();
407 }
408
409 @Override
410 protected boolean isEmptyXmlTagExpanded() {
411 return true;
412 }
413
414
415
416
417 @Override
418 public DisplayStyle getDefaultStyleDisplay() {
419 return DisplayStyle.INLINE;
420 }
421
422
423
424
425 @Override
426 public boolean handles(final Event event) {
427 if (Event.TYPE_BLUR.equals(event.getType()) || Event.TYPE_FOCUS.equals(event.getType())) {
428 return true;
429 }
430 return super.handles(event);
431 }
432
433
434
435
436
437
438 public final String getPingAttribute() {
439 return getAttributeDirect("ping");
440 }
441
442
443
444
445
446
447 public final String getDownloadAttribute() {
448 return getAttributeDirect("download");
449 }
450 }