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 @JsxConstructor
162 public static Location jsConstructor(final Context cx, final VarScope scope,
163 final Object[] args, final Function ctorObj, final boolean inNewExpr) {
164
165 final Location location = new Location();
166 location.setParentScope(scope);
167 location.setPrototype(((FunctionObject) ctorObj).getClassPrototype());
168
169 location.initialize(scope, null, null);
170
171 return location;
172 }
173
174
175
176
177
178
179
180
181 public void initialize(final VarScope scope, final Window window, final Page page) {
182 final int attributes = ScriptableObject.PERMANENT | ScriptableObject.READONLY;
183
184 FunctionObject functionObject = new FunctionObject(METHOD_ASSIGN.getName(), METHOD_ASSIGN, scope);
185 defineProperty(METHOD_ASSIGN.getName(), functionObject, attributes);
186
187 functionObject = new FunctionObject(METHOD_RELOAD.getName(), METHOD_RELOAD, scope);
188 defineProperty(METHOD_RELOAD.getName(), functionObject, attributes);
189
190 functionObject = new FunctionObject(METHOD_REPLACE.getName(), METHOD_REPLACE, scope);
191 defineProperty(METHOD_REPLACE.getName(), functionObject, attributes);
192
193 functionObject = new FunctionObject(METHOD_TO_STRING.getName(), METHOD_TO_STRING, scope);
194 defineProperty("toString", functionObject, attributes);
195
196 defineProperty(scope, "hash", null, GETTER_HASH, SETTER_HASH, attributes);
197 defineProperty(scope, "host", null, GETTER_HOST, SETTER_HOST, attributes);
198 defineProperty(scope, "hostname", null, GETTER_HOSTNAME, SETTER_HOSTNAME, attributes);
199 defineProperty(scope, "href", null, GETTER_HREF, SETTER_HREF, attributes);
200 defineProperty(scope, "origin", null, GETTER_ORIGIN, null, attributes);
201 defineProperty(scope, "pathname", null, GETTER_PATHNAME, SETTER_PATHNAME, attributes);
202 defineProperty(scope, "port", null, GETTER_PORT, SETTER_PORT, attributes);
203 defineProperty(scope, "protocol", null, GETTER_PROTOCOL, SETTER_PROTOCOL, attributes);
204 defineProperty(scope, "search", null, GETTER_SEARCH, SETTER_SEARCH, attributes);
205
206 window_ = window;
207 if (window_ != null && page != null) {
208 setHash(null, page.getUrl().getRef());
209 }
210 }
211
212
213
214
215 @Override
216 public Object getDefaultValue(final Class<?> hint) {
217 if (getPrototype() != null
218 && window_ != null
219 && (hint == null || String.class.equals(hint))) {
220 return getHref();
221 }
222 return super.getDefaultValue(hint);
223 }
224
225
226
227
228
229
230
231 public void assign(final String url) throws IOException {
232 setHref(url);
233 }
234
235
236
237
238
239
240
241
242 public void reload(final boolean force) throws IOException {
243 final WebWindow webWindow = window_.getWebWindow();
244 final HtmlPage htmlPage = (HtmlPage) webWindow.getEnclosedPage();
245 final WebRequest request = htmlPage.getWebResponse().getWebRequest();
246
247
248 request.setUrl(new URL(getHref()));
249 if (getBrowserVersion().hasFeature(JS_LOCATION_RELOAD_REFERRER)) {
250 request.setRefererHeader(htmlPage.getUrl());
251 }
252
253 webWindow.getWebClient().download(webWindow, "", request, false, null, "JS location.reload");
254 }
255
256
257
258
259
260
261
262 public void replace(final String url) throws IOException {
263 window_.getWebWindow().getHistory().removeCurrent();
264 setHref(url);
265 }
266
267
268
269
270
271 public String jsToString() {
272 if (window_ != null) {
273 return getHref();
274 }
275 return "";
276 }
277
278
279
280
281
282
283 public String getHref() {
284 final WebWindow webWindow = window_.getWebWindow();
285 final Page page = webWindow.getEnclosedPage();
286 if (page == null) {
287 return UNKNOWN;
288 }
289 try {
290 URL url = page.getUrl();
291 final String hash = getHash(true);
292 if (hash != null) {
293 url = UrlUtils.getUrlWithNewRef(url, hash);
294 }
295 String s = url.toExternalForm();
296 if (s.startsWith("file:/") && !s.startsWith("file:///")) {
297
298
299 s = "file:///" + s.substring("file:/".length());
300 }
301 return s;
302 }
303 catch (final MalformedURLException e) {
304 if (LOG.isErrorEnabled()) {
305 LOG.error(e.getMessage(), e);
306 }
307 return page.getUrl().toExternalForm();
308 }
309 }
310
311
312
313
314
315
316
317 public void setHref(final String newLocation) throws IOException {
318 if (newLocation.startsWith(JavaScriptURLConnection.JAVASCRIPT_PREFIX)) {
319 final String script = newLocation.substring(11);
320 final HtmlPage page = (HtmlPage) window_.getWebWindow().getEnclosedPage();
321 page.executeJavaScript(script, "new location value", 1);
322 return;
323 }
324
325 try {
326
327 WebWindow webWindow = ((Window) JavaScriptEngine.getTopCallScope().getGlobalThis()).getWebWindow();
328 final HtmlPage callingPage = (HtmlPage) webWindow.getEnclosedPage();
329
330 final BrowserVersion browserVersion = webWindow.getWebClient().getBrowserVersion();
331
332 URL url = callingPage.getFullyQualifiedUrl(newLocation);
333
334 if (StringUtils.isEmptyOrNull(newLocation)) {
335 url = UrlUtils.getUrlWithNewRef(url, null);
336 }
337
338 final WebRequest request = new WebRequest(url,
339 browserVersion.getHtmlAcceptHeader(), browserVersion.getAcceptEncodingHeader());
340 request.setRefererHeader(callingPage.getUrl());
341
342 webWindow = window_.getWebWindow();
343 webWindow.getWebClient().download(webWindow, "", request, true, null, "JS set location");
344 }
345 catch (final MalformedURLException e) {
346 if (LOG.isErrorEnabled()) {
347 LOG.error("setHref('" + newLocation + "') got MalformedURLException", e);
348 }
349 throw e;
350 }
351 }
352
353
354
355
356
357
358 public String getSearch() {
359 final URL url = getUrl();
360 final String search = url.getQuery();
361 if (StringUtils.isEmptyOrNull(search)) {
362 return "";
363 }
364
365 if (StringUtils.startsWithIgnoreCase(url.getProtocol(), UrlUtils.ABOUT)
366 && window_.getWebWindow().getWebClient().getBrowserVersion()
367 .hasFeature(JS_LOCATION_IGNORE_QUERY_FOR_ABOUT_PROTOCOL)) {
368 return "";
369 }
370 return "?" + search;
371 }
372
373
374
375
376
377
378
379 public void setSearch(final String search) throws Exception {
380 setUrl(UrlUtils.getUrlWithNewQuery(getUrl(), search));
381 }
382
383
384
385
386
387
388 public String getHash() {
389 if (StringUtils.isEmptyOrNull(hash_)) {
390 return "";
391 }
392
393 if (hash_.indexOf('%') == -1) {
394 return "#" + UrlUtils.encodeHash(hash_);
395 }
396 return "#" + UrlUtils.encodeHash(UrlUtils.decode(hash_));
397 }
398
399 private String getHash(final boolean encoded) {
400 if (hash_ == null || hash_.isEmpty()) {
401 return null;
402 }
403 if (encoded) {
404 return UrlUtils.encodeHash(hash_);
405 }
406 return hash_;
407 }
408
409
410
411
412
413
414
415 public void setHash(final String hash) {
416
417
418 setHash(getHref(), hash, true);
419 }
420
421
422
423
424
425
426
427 public void setHash(final String oldURL, final String hash) {
428 setHash(oldURL, hash, true);
429 }
430
431
432
433
434
435
436
437
438
439
440 public void setHash(final String oldURL, String hash, final boolean triggerHashChanged) {
441
442
443 if (hash != null && !hash.isEmpty() && hash.charAt(0) == '#') {
444 hash = hash.substring(1);
445 }
446 final boolean hasChanged = hash != null && !hash.equals(hash_);
447 hash_ = hash;
448
449 if (triggerHashChanged && hasChanged) {
450 final Window w = getWindow();
451 final Event event = new HashChangeEvent(w, Event.TYPE_HASH_CHANGE, oldURL, getHref());
452 w.executeEventLocally(event);
453 }
454 }
455
456
457
458
459
460
461 public String getHostname() {
462 return getUrl().getHost();
463 }
464
465
466
467
468
469
470
471 public void setHostname(final String hostname) throws Exception {
472 setUrl(UrlUtils.getUrlWithNewHost(getUrl(), hostname));
473 }
474
475
476
477
478
479
480 public String getHost() {
481 final URL url = getUrl();
482 final int port = url.getPort();
483 final String host = url.getHost();
484
485 if (port == -1) {
486 return host;
487 }
488 return host + ":" + port;
489 }
490
491
492
493
494
495
496
497 public void setHost(final String host) throws Exception {
498 final String hostname;
499 final int port;
500 final int index = host.indexOf(':');
501 if (index == -1) {
502 hostname = host;
503 port = -1;
504 }
505 else {
506 hostname = host.substring(0, index);
507 port = Integer.parseInt(host.substring(index + 1));
508 }
509 final URL url = UrlUtils.getUrlWithNewHostAndPort(getUrl(), hostname, port);
510 setUrl(url);
511 }
512
513
514
515
516
517
518 public String getPathname() {
519 if (UrlUtils.URL_ABOUT_BLANK == getUrl()) {
520 return "blank";
521 }
522 return getUrl().getPath();
523 }
524
525
526
527
528
529
530
531 public void setPathname(final String pathname) throws Exception {
532 setUrl(UrlUtils.getUrlWithNewPath(getUrl(), pathname));
533 }
534
535
536
537
538
539
540 public String getPort() {
541 final int port = getUrl().getPort();
542 if (port == -1) {
543 return "";
544 }
545 return Integer.toString(port);
546 }
547
548
549
550
551
552
553
554 public void setPort(final String port) throws Exception {
555 setUrl(UrlUtils.getUrlWithNewPort(getUrl(), Integer.parseInt(port)));
556 }
557
558
559
560
561
562
563 public String getProtocol() {
564 return getUrl().getProtocol() + ":";
565 }
566
567
568
569
570
571
572
573 public void setProtocol(final String protocol) throws Exception {
574 setUrl(UrlUtils.getUrlWithNewProtocol(getUrl(), protocol));
575 }
576
577
578
579
580
581 private URL getUrl() {
582 return window_.getWebWindow().getEnclosedPage().getUrl();
583 }
584
585
586
587
588
589
590
591 private void setUrl(final URL url) throws IOException {
592 final WebWindow webWindow = window_.getWebWindow();
593 final BrowserVersion browserVersion = webWindow.getWebClient().getBrowserVersion();
594
595 final WebRequest webRequest = new WebRequest(url,
596 browserVersion.getHtmlAcceptHeader(), browserVersion.getAcceptEncodingHeader());
597 webRequest.setRefererHeader(getUrl());
598
599 webWindow.getWebClient().getPage(webWindow, webRequest);
600 }
601
602
603
604
605
606 public String getOrigin() {
607 return getUrl().getProtocol() + "://" + getHost();
608 }
609 }