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 request = new WebRequest(url,
347 browserVersion.getHtmlAcceptHeader(), browserVersion.getAcceptEncodingHeader());
348 request.setRefererHeader(callingPage.getUrl());
349
350 webWindow = window_.getWebWindow();
351 webWindow.getWebClient().download(webWindow, "", request, true, null, "JS set location");
352 }
353 catch (final MalformedURLException e) {
354 if (LOG.isErrorEnabled()) {
355 LOG.error("setHref('" + newLocation + "') got MalformedURLException", e);
356 }
357 throw e;
358 }
359 }
360
361
362
363
364
365
366
367 public String getSearch() {
368 final URL url = getUrl();
369 final String search = url.getQuery();
370 if (StringUtils.isEmptyOrNull(search)) {
371 return "";
372 }
373
374 if (StringUtils.startsWithIgnoreCase(url.getProtocol(), UrlUtils.ABOUT)
375 && window_.getWebWindow().getWebClient().getBrowserVersion()
376 .hasFeature(JS_LOCATION_IGNORE_QUERY_FOR_ABOUT_PROTOCOL)) {
377 return "";
378 }
379 return "?" + search;
380 }
381
382
383
384
385
386
387
388
389 public void setSearch(final String search) throws Exception {
390 setUrl(UrlUtils.getUrlWithNewQuery(getUrl(), search));
391 }
392
393
394
395
396
397
398
399 public String getHash() {
400 if (StringUtils.isEmptyOrNull(hash_)) {
401 return "";
402 }
403
404 if (hash_.indexOf('%') == -1) {
405 return "#" + UrlUtils.encodeHash(hash_);
406 }
407 return "#" + UrlUtils.encodeHash(UrlUtils.decode(hash_));
408 }
409
410 private String getHash(final boolean encoded) {
411 if (hash_ == null || hash_.isEmpty()) {
412 return null;
413 }
414 if (encoded) {
415 return UrlUtils.encodeHash(hash_);
416 }
417 return hash_;
418 }
419
420
421
422
423
424
425
426
427 public void setHash(final String hash) {
428
429
430 setHash(getHref(), hash, true);
431 }
432
433
434
435
436
437
438
439 public void setHash(final String oldURL, final String hash) {
440 setHash(oldURL, hash, true);
441 }
442
443
444
445
446
447
448
449
450
451
452 public void setHash(final String oldURL, String hash, final boolean triggerHashChanged) {
453
454
455 if (hash != null && !hash.isEmpty() && hash.charAt(0) == '#') {
456 hash = hash.substring(1);
457 }
458 final boolean hasChanged = hash != null && !hash.equals(hash_);
459 hash_ = hash;
460
461 if (hasChanged) {
462 final Window w = getWindow();
463 final Page page = w.getWebWindow().getEnclosedPage();
464 if (page != null) {
465 final WebRequest request = page.getWebResponse().getWebRequest();
466 request.setUrl(UrlUtils.toUrlSafe(getHref()));
467 }
468
469 if (triggerHashChanged) {
470 final Event event = new HashChangeEvent(w, Event.TYPE_HASH_CHANGE, oldURL, getHref());
471 w.executeEventLocally(event);
472 }
473 }
474 }
475
476
477
478
479
480
481
482 public String getHostname() {
483 return getUrl().getHost();
484 }
485
486
487
488
489
490
491
492
493 public void setHostname(final String hostname) throws Exception {
494 setUrl(UrlUtils.getUrlWithNewHost(getUrl(), hostname));
495 }
496
497
498
499
500
501
502
503 public String getHost() {
504 final URL url = getUrl();
505 final int port = url.getPort();
506 final String host = url.getHost();
507
508 if (port == -1) {
509 return host;
510 }
511 return host + ":" + port;
512 }
513
514
515
516
517
518
519
520
521 public void setHost(final String host) throws Exception {
522 final String hostname;
523 final int port;
524 final int index = host.indexOf(':');
525 if (index == -1) {
526 hostname = host;
527 port = -1;
528 }
529 else {
530 hostname = host.substring(0, index);
531 port = Integer.parseInt(host.substring(index + 1));
532 }
533 final URL url = UrlUtils.getUrlWithNewHostAndPort(getUrl(), hostname, port);
534 setUrl(url);
535 }
536
537
538
539
540
541
542
543 public String getPathname() {
544 if (UrlUtils.URL_ABOUT_BLANK == getUrl()) {
545 return "blank";
546 }
547 return getUrl().getPath();
548 }
549
550
551
552
553
554
555
556
557 public void setPathname(final String pathname) throws Exception {
558 setUrl(UrlUtils.getUrlWithNewPath(getUrl(), pathname));
559 }
560
561
562
563
564
565
566
567 public String getPort() {
568 final int port = getUrl().getPort();
569 if (port == -1) {
570 return "";
571 }
572 return Integer.toString(port);
573 }
574
575
576
577
578
579
580
581
582 public void setPort(final String port) throws Exception {
583 setUrl(UrlUtils.getUrlWithNewPort(getUrl(), Integer.parseInt(port)));
584 }
585
586
587
588
589
590
591
592 public String getProtocol() {
593 return getUrl().getProtocol() + ":";
594 }
595
596
597
598
599
600
601
602
603 public void setProtocol(final String protocol) throws Exception {
604 setUrl(UrlUtils.getUrlWithNewProtocol(getUrl(), protocol));
605 }
606
607
608
609
610
611
612 private URL getUrl() {
613 return window_.getWebWindow().getEnclosedPage().getUrl();
614 }
615
616
617
618
619
620
621
622
623 private void setUrl(final URL url) throws IOException {
624 final WebWindow webWindow = window_.getWebWindow();
625 final BrowserVersion browserVersion = webWindow.getWebClient().getBrowserVersion();
626
627 final WebRequest webRequest = new WebRequest(url,
628 browserVersion.getHtmlAcceptHeader(), browserVersion.getAcceptEncodingHeader());
629 webRequest.setRefererHeader(getUrl());
630
631 webWindow.getWebClient().getPage(webWindow, webRequest);
632 }
633
634
635
636
637
638
639 public String getOrigin() {
640 return getUrl().getProtocol() + "://" + getHost();
641 }
642 }