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.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.htmlunit.BrowserVersion;
28 import org.htmlunit.Page;
29 import org.htmlunit.WebRequest;
30 import org.htmlunit.WebWindow;
31 import org.htmlunit.corejs.javascript.FunctionObject;
32 import org.htmlunit.corejs.javascript.ScriptableObject;
33 import org.htmlunit.html.HtmlPage;
34 import org.htmlunit.javascript.HtmlUnitScriptable;
35 import org.htmlunit.javascript.configuration.JsxClass;
36 import org.htmlunit.javascript.configuration.JsxConstructor;
37 import org.htmlunit.javascript.host.event.Event;
38 import org.htmlunit.javascript.host.event.HashChangeEvent;
39 import org.htmlunit.protocol.javascript.JavaScriptURLConnection;
40 import org.htmlunit.util.StringUtils;
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, 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.isEmptyOrNull(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, 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.isEmptyOrNull(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 if (StringUtils.isEmptyOrNull(hash_)) {
365 return "";
366 }
367
368 if (hash_.indexOf('%') == -1) {
369 return "#" + UrlUtils.encodeHash(hash_);
370 }
371 return "#" + UrlUtils.encodeHash(UrlUtils.decode(hash_));
372 }
373
374 private String getHash(final boolean encoded) {
375 if (hash_ == null || hash_.isEmpty()) {
376 return null;
377 }
378 if (encoded) {
379 return UrlUtils.encodeHash(hash_);
380 }
381 return hash_;
382 }
383
384
385
386
387
388
389
390 public void setHash(final String hash) {
391
392
393 setHash(getHref(), hash, true);
394 }
395
396
397
398
399
400
401
402 public void setHash(final String oldURL, final String hash) {
403 setHash(oldURL, hash, true);
404 }
405
406
407
408
409
410
411
412
413
414
415 public void setHash(final String oldURL, String hash, final boolean triggerHashChanged) {
416
417
418 if (hash != null && !hash.isEmpty() && hash.charAt(0) == '#') {
419 hash = hash.substring(1);
420 }
421 final boolean hasChanged = hash != null && !hash.equals(hash_);
422 hash_ = hash;
423
424 if (triggerHashChanged && hasChanged) {
425 final Window w = getWindow();
426 final Event event = new HashChangeEvent(w, Event.TYPE_HASH_CHANGE, oldURL, getHref());
427 w.executeEventLocally(event);
428 }
429 }
430
431
432
433
434
435
436 public String getHostname() {
437 return getUrl().getHost();
438 }
439
440
441
442
443
444
445
446 public void setHostname(final String hostname) throws Exception {
447 setUrl(UrlUtils.getUrlWithNewHost(getUrl(), hostname));
448 }
449
450
451
452
453
454
455 public String getHost() {
456 final URL url = getUrl();
457 final int port = url.getPort();
458 final String host = url.getHost();
459
460 if (port == -1) {
461 return host;
462 }
463 return host + ":" + port;
464 }
465
466
467
468
469
470
471
472 public void setHost(final String host) throws Exception {
473 final String hostname;
474 final int port;
475 final int index = host.indexOf(':');
476 if (index == -1) {
477 hostname = host;
478 port = -1;
479 }
480 else {
481 hostname = host.substring(0, index);
482 port = Integer.parseInt(host.substring(index + 1));
483 }
484 final URL url = UrlUtils.getUrlWithNewHostAndPort(getUrl(), hostname, port);
485 setUrl(url);
486 }
487
488
489
490
491
492
493 public String getPathname() {
494 if (UrlUtils.URL_ABOUT_BLANK == getUrl()) {
495 return "blank";
496 }
497 return getUrl().getPath();
498 }
499
500
501
502
503
504
505
506 public void setPathname(final String pathname) throws Exception {
507 setUrl(UrlUtils.getUrlWithNewPath(getUrl(), pathname));
508 }
509
510
511
512
513
514
515 public String getPort() {
516 final int port = getUrl().getPort();
517 if (port == -1) {
518 return "";
519 }
520 return Integer.toString(port);
521 }
522
523
524
525
526
527
528
529 public void setPort(final String port) throws Exception {
530 setUrl(UrlUtils.getUrlWithNewPort(getUrl(), Integer.parseInt(port)));
531 }
532
533
534
535
536
537
538 public String getProtocol() {
539 return getUrl().getProtocol() + ":";
540 }
541
542
543
544
545
546
547
548 public void setProtocol(final String protocol) throws Exception {
549 setUrl(UrlUtils.getUrlWithNewProtocol(getUrl(), protocol));
550 }
551
552
553
554
555
556 private URL getUrl() {
557 return window_.getWebWindow().getEnclosedPage().getUrl();
558 }
559
560
561
562
563
564
565
566 private void setUrl(final URL url) throws IOException {
567 final WebWindow webWindow = window_.getWebWindow();
568 final BrowserVersion browserVersion = webWindow.getWebClient().getBrowserVersion();
569
570 final WebRequest webRequest = new WebRequest(url,
571 browserVersion.getHtmlAcceptHeader(), browserVersion.getAcceptEncodingHeader());
572 webRequest.setRefererHeader(getUrl());
573
574 webWindow.getWebClient().getPage(webWindow, webRequest);
575 }
576
577
578
579
580
581 public String getOrigin() {
582 return getUrl().getProtocol() + "://" + getHost();
583 }
584 }