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