1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host;
16
17 import static org.htmlunit.BrowserVersionFeatures.JS_LOCATION_IGNORE_QUERY_FOR_ABOUT_PROTOCOL;
18 import static org.htmlunit.BrowserVersionFeatures.JS_LOCATION_RELOAD_REFERRER;
19
20 import java.io.IOException;
21 import java.lang.reflect.Method;
22 import java.net.MalformedURLException;
23 import java.net.URL;
24
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.htmlunit.BrowserVersion;
29 import org.htmlunit.Page;
30 import org.htmlunit.WebRequest;
31 import org.htmlunit.WebWindow;
32 import org.htmlunit.corejs.javascript.FunctionObject;
33 import org.htmlunit.corejs.javascript.ScriptableObject;
34 import org.htmlunit.html.HtmlPage;
35 import org.htmlunit.javascript.HtmlUnitScriptable;
36 import org.htmlunit.javascript.configuration.JsxClass;
37 import org.htmlunit.javascript.configuration.JsxConstructor;
38 import org.htmlunit.javascript.host.event.Event;
39 import org.htmlunit.javascript.host.event.HashChangeEvent;
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
56
57
58
59
60
61
62 @JsxClass
63 public class Location extends HtmlUnitScriptable {
64
65 private static final Log LOG = LogFactory.getLog(Location.class);
66 private static final String UNKNOWN = "null";
67
68
69
70
71 private Window window_;
72
73 private static final Method METHOD_ASSIGN;
74 private static final Method METHOD_RELOAD;
75 private static final Method METHOD_REPLACE;
76 private static final Method METHOD_TO_STRING;
77
78 private static final Method GETTER_HASH;
79 private static final Method SETTER_HASH;
80
81 private static final Method GETTER_HOST;
82 private static final Method SETTER_HOST;
83
84 private static final Method GETTER_HOSTNAME;
85 private static final Method SETTER_HOSTNAME;
86
87 private static final Method GETTER_HREF;
88 private static final Method SETTER_HREF;
89
90 private static final Method GETTER_ORIGIN;
91
92 private static final Method GETTER_PATHNAME;
93 private static final Method SETTER_PATHNAME;
94
95 private static final Method GETTER_PORT;
96 private static final Method SETTER_PORT;
97
98 private static final Method GETTER_PROTOCOL;
99 private static final Method SETTER_PROTOCOL;
100
101 private static final Method GETTER_SEARCH;
102 private static final Method SETTER_SEARCH;
103
104 static {
105 try {
106 METHOD_ASSIGN = Location.class.getDeclaredMethod("assign", String.class);
107 METHOD_RELOAD = Location.class.getDeclaredMethod("reload", boolean.class);
108 METHOD_REPLACE = Location.class.getDeclaredMethod("replace", String.class);
109 METHOD_TO_STRING = Location.class.getDeclaredMethod("jsToString");
110
111 GETTER_HASH = Location.class.getDeclaredMethod("getHash");
112 SETTER_HASH = Location.class.getDeclaredMethod("setHash", String.class);
113
114 GETTER_HOST = Location.class.getDeclaredMethod("getHost");
115 SETTER_HOST = Location.class.getDeclaredMethod("setHost", String.class);
116
117 GETTER_HOSTNAME = Location.class.getDeclaredMethod("getHostname");
118 SETTER_HOSTNAME = Location.class.getDeclaredMethod("setHostname", String.class);
119
120 GETTER_HREF = Location.class.getDeclaredMethod("getHref");
121 SETTER_HREF = Location.class.getDeclaredMethod("setHref", String.class);
122
123 GETTER_ORIGIN = Location.class.getDeclaredMethod("getOrigin");
124
125 GETTER_PATHNAME = Location.class.getDeclaredMethod("getPathname");
126 SETTER_PATHNAME = Location.class.getDeclaredMethod("setPathname", String.class);
127
128 GETTER_PORT = Location.class.getDeclaredMethod("getPort");
129 SETTER_PORT = Location.class.getDeclaredMethod("setPort", String.class);
130
131 GETTER_PROTOCOL = Location.class.getDeclaredMethod("getProtocol");
132 SETTER_PROTOCOL = Location.class.getDeclaredMethod("setProtocol", String.class);
133
134 GETTER_SEARCH = Location.class.getDeclaredMethod("getSearch");
135 SETTER_SEARCH = Location.class.getDeclaredMethod("setSearch", String.class);
136 }
137 catch (NoSuchMethodException | SecurityException e) {
138 throw new RuntimeException(e);
139 }
140 }
141
142
143
144
145
146 private String hash_;
147
148
149
150
151 @JsxConstructor
152 public void jsConstructor() {
153 final int attributes = ScriptableObject.PERMANENT | ScriptableObject.READONLY;
154
155 FunctionObject functionObject = new FunctionObject(METHOD_ASSIGN.getName(), METHOD_ASSIGN, this);
156 defineProperty(METHOD_ASSIGN.getName(), functionObject, attributes);
157
158 functionObject = new FunctionObject(METHOD_RELOAD.getName(), METHOD_RELOAD, this);
159 defineProperty(METHOD_RELOAD.getName(), functionObject, attributes);
160
161 functionObject = new FunctionObject(METHOD_REPLACE.getName(), METHOD_REPLACE, this);
162 defineProperty(METHOD_REPLACE.getName(), functionObject, attributes);
163
164 functionObject = new FunctionObject(METHOD_TO_STRING.getName(), METHOD_TO_STRING, this);
165 defineProperty("toString", functionObject, attributes);
166
167 defineProperty("hash", null, GETTER_HASH, SETTER_HASH, attributes);
168 defineProperty("host", null, GETTER_HOST, SETTER_HOST, attributes);
169 defineProperty("hostname", null, GETTER_HOSTNAME, SETTER_HOSTNAME, attributes);
170 defineProperty("href", null, GETTER_HREF, SETTER_HREF, attributes);
171 defineProperty("origin", null, GETTER_ORIGIN, null, attributes);
172 defineProperty("pathname", null, GETTER_PATHNAME, SETTER_PATHNAME, attributes);
173 defineProperty("port", null, GETTER_PORT, SETTER_PORT, attributes);
174 defineProperty("protocol", null, GETTER_PROTOCOL, SETTER_PROTOCOL, attributes);
175 defineProperty("search", null, GETTER_SEARCH, SETTER_SEARCH, attributes);
176 }
177
178
179
180
181
182
183
184 public void initialize(final Window window, final Page page) {
185 window_ = window;
186 if (window_ != null && page != null) {
187 setHash(null, page.getUrl().getRef());
188 }
189 }
190
191
192
193
194 @Override
195 public Object getDefaultValue(final Class<?> hint) {
196 if (getPrototype() != null
197 && window_ != null
198 && (hint == null || String.class.equals(hint))) {
199 return getHref();
200 }
201 return super.getDefaultValue(hint);
202 }
203
204
205
206
207
208
209
210 public void assign(final String url) throws IOException {
211 setHref(url);
212 }
213
214
215
216
217
218
219
220
221 public void reload(final boolean force) throws IOException {
222 final WebWindow webWindow = window_.getWebWindow();
223 final HtmlPage htmlPage = (HtmlPage) webWindow.getEnclosedPage();
224 final WebRequest request = htmlPage.getWebResponse().getWebRequest();
225
226
227 request.setUrl(new URL(getHref()));
228 if (webWindow.getWebClient().getBrowserVersion().hasFeature(JS_LOCATION_RELOAD_REFERRER)) {
229 request.setRefererHeader(htmlPage.getUrl());
230 }
231
232 webWindow.getWebClient().download(webWindow, "", request, false, false, null, "JS location.reload");
233 }
234
235
236
237
238
239
240
241 public void replace(final String url) throws IOException {
242 window_.getWebWindow().getHistory().removeCurrent();
243 setHref(url);
244 }
245
246
247
248
249
250 public String jsToString() {
251 if (window_ != null) {
252 return getHref();
253 }
254 return "";
255 }
256
257
258
259
260
261
262 public String getHref() {
263 final WebWindow webWindow = window_.getWebWindow();
264 final Page page = webWindow.getEnclosedPage();
265 if (page == null) {
266 return UNKNOWN;
267 }
268 try {
269 URL url = page.getUrl();
270 final String hash = getHash(true);
271 if (hash != null) {
272 url = UrlUtils.getUrlWithNewRef(url, hash);
273 }
274 String s = url.toExternalForm();
275 if (s.startsWith("file:/") && !s.startsWith("file:///")) {
276
277
278 s = "file:///" + s.substring("file:/".length());
279 }
280 return s;
281 }
282 catch (final MalformedURLException e) {
283 if (LOG.isErrorEnabled()) {
284 LOG.error(e.getMessage(), e);
285 }
286 return page.getUrl().toExternalForm();
287 }
288 }
289
290
291
292
293
294
295
296 public void setHref(final String newLocation) throws IOException {
297 WebWindow webWindow = getWindowFromTopCallScope().getWebWindow();
298 final HtmlPage page = (HtmlPage) webWindow.getEnclosedPage();
299 if (newLocation.startsWith(JavaScriptURLConnection.JAVASCRIPT_PREFIX)) {
300 final String script = newLocation.substring(11);
301 page.executeJavaScript(script, "new location value", 1);
302 return;
303 }
304 try {
305 final BrowserVersion browserVersion = webWindow.getWebClient().getBrowserVersion();
306
307 URL url = page.getFullyQualifiedUrl(newLocation);
308
309 if (StringUtils.isEmpty(newLocation)) {
310 url = UrlUtils.getUrlWithNewRef(url, null);
311 }
312
313 final WebRequest request = new WebRequest(url,
314 browserVersion.getHtmlAcceptHeader(), browserVersion.getAcceptEncodingHeader());
315 request.setRefererHeader(page.getUrl());
316
317 webWindow = window_.getWebWindow();
318 webWindow.getWebClient().download(webWindow, "", request, true, false, null, "JS set location");
319 }
320 catch (final MalformedURLException e) {
321 if (LOG.isErrorEnabled()) {
322 LOG.error("setHref('" + newLocation + "') got MalformedURLException", e);
323 }
324 throw e;
325 }
326 }
327
328
329
330
331
332
333 public String getSearch() {
334 final URL url = getUrl();
335 final String search = url.getQuery();
336 if (StringUtils.isEmpty(search)) {
337 return "";
338 }
339
340 if (StringUtils.startsWithIgnoreCase(url.getProtocol(), UrlUtils.ABOUT)
341 && window_.getWebWindow().getWebClient().getBrowserVersion()
342 .hasFeature(JS_LOCATION_IGNORE_QUERY_FOR_ABOUT_PROTOCOL)) {
343 return "";
344 }
345 return "?" + search;
346 }
347
348
349
350
351
352
353
354 public void setSearch(final String search) throws Exception {
355 setUrl(UrlUtils.getUrlWithNewQuery(getUrl(), search));
356 }
357
358
359
360
361
362
363 public String getHash() {
364 String hash = hash_;
365
366 if (hash_ != null) {
367 hash = decodeHash(hash);
368 }
369
370 if (StringUtils.isEmpty(hash)) {
371
372 }
373 else {
374 return "#" + UrlUtils.encodeHash(hash);
375 }
376
377 return "";
378 }
379
380 private String getHash(final boolean encoded) {
381 if (hash_ == null || hash_.isEmpty()) {
382 return null;
383 }
384 if (encoded) {
385 return UrlUtils.encodeHash(hash_);
386 }
387 return hash_;
388 }
389
390
391
392
393
394
395
396 public void setHash(final String hash) {
397
398
399 setHash(getHref(), hash, true);
400 }
401
402
403
404
405
406
407
408 public void setHash(final String oldURL, final String hash) {
409 setHash(oldURL, hash, true);
410 }
411
412
413
414
415
416
417
418
419
420
421 public void setHash(final String oldURL, String hash, final boolean triggerHashChanged) {
422
423
424 if (hash != null && !hash.isEmpty() && hash.charAt(0) == '#') {
425 hash = hash.substring(1);
426 }
427 final boolean hasChanged = hash != null && !hash.equals(hash_);
428 hash_ = hash;
429
430 if (triggerHashChanged && hasChanged) {
431 final Window w = getWindow();
432 final Event event = new HashChangeEvent(w, Event.TYPE_HASH_CHANGE, oldURL, getHref());
433 w.executeEventLocally(event);
434 }
435 }
436
437 private static String decodeHash(final String hash) {
438 if (hash.indexOf('%') == -1) {
439 return hash;
440 }
441 return UrlUtils.decode(hash);
442 }
443
444
445
446
447
448
449 public String getHostname() {
450 return getUrl().getHost();
451 }
452
453
454
455
456
457
458
459 public void setHostname(final String hostname) throws Exception {
460 setUrl(UrlUtils.getUrlWithNewHost(getUrl(), hostname));
461 }
462
463
464
465
466
467
468 public String getHost() {
469 final URL url = getUrl();
470 final int port = url.getPort();
471 final String host = url.getHost();
472
473 if (port == -1) {
474 return host;
475 }
476 return host + ":" + port;
477 }
478
479
480
481
482
483
484
485 public void setHost(final String host) throws Exception {
486 final String hostname;
487 final int port;
488 final int index = host.indexOf(':');
489 if (index == -1) {
490 hostname = host;
491 port = -1;
492 }
493 else {
494 hostname = host.substring(0, index);
495 port = Integer.parseInt(host.substring(index + 1));
496 }
497 final URL url = UrlUtils.getUrlWithNewHostAndPort(getUrl(), hostname, port);
498 setUrl(url);
499 }
500
501
502
503
504
505
506 public String getPathname() {
507 if (UrlUtils.URL_ABOUT_BLANK == getUrl()) {
508 return "blank";
509 }
510 return getUrl().getPath();
511 }
512
513
514
515
516
517
518
519 public void setPathname(final String pathname) throws Exception {
520 setUrl(UrlUtils.getUrlWithNewPath(getUrl(), pathname));
521 }
522
523
524
525
526
527
528 public String getPort() {
529 final int port = getUrl().getPort();
530 if (port == -1) {
531 return "";
532 }
533 return Integer.toString(port);
534 }
535
536
537
538
539
540
541
542 public void setPort(final String port) throws Exception {
543 setUrl(UrlUtils.getUrlWithNewPort(getUrl(), Integer.parseInt(port)));
544 }
545
546
547
548
549
550
551 public String getProtocol() {
552 return getUrl().getProtocol() + ":";
553 }
554
555
556
557
558
559
560
561 public void setProtocol(final String protocol) throws Exception {
562 setUrl(UrlUtils.getUrlWithNewProtocol(getUrl(), protocol));
563 }
564
565
566
567
568
569 private URL getUrl() {
570 return window_.getWebWindow().getEnclosedPage().getUrl();
571 }
572
573
574
575
576
577
578
579 private void setUrl(final URL url) throws IOException {
580 final WebWindow webWindow = window_.getWebWindow();
581 final BrowserVersion browserVersion = webWindow.getWebClient().getBrowserVersion();
582
583 final WebRequest webRequest = new WebRequest(url,
584 browserVersion.getHtmlAcceptHeader(), browserVersion.getAcceptEncodingHeader());
585 webRequest.setRefererHeader(getUrl());
586
587 webWindow.getWebClient().getPage(webWindow, webRequest);
588 }
589
590
591
592
593
594 public String getOrigin() {
595 return getUrl().getProtocol() + "://" + getHost();
596 }
597 }