org.htmlunit.javascript.host.Location Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xlt Show documentation
Show all versions of xlt Show documentation
XLT (Xceptance LoadTest) is an extensive load and performance test tool developed and maintained by Xceptance.
/*
* Copyright (c) 2002-2024 Gargoyle Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.htmlunit.javascript.host;
import static org.htmlunit.BrowserVersionFeatures.ANCHOR_EMPTY_HREF_NO_FILENAME;
import static org.htmlunit.BrowserVersionFeatures.EVENT_TYPE_HASHCHANGEEVENT;
import static org.htmlunit.BrowserVersionFeatures.JS_LOCATION_HASH_HASH_IS_ENCODED;
import static org.htmlunit.BrowserVersionFeatures.JS_LOCATION_HASH_IS_DECODED;
import static org.htmlunit.BrowserVersionFeatures.JS_LOCATION_HASH_RETURNS_HASH_FOR_EMPTY_DEFINED;
import static org.htmlunit.BrowserVersionFeatures.JS_LOCATION_HREF_HASH_IS_ENCODED;
import static org.htmlunit.BrowserVersionFeatures.JS_LOCATION_RELOAD_REFERRER;
import static org.htmlunit.BrowserVersionFeatures.URL_ABOUT_BLANK_HAS_BLANK_PATH;
import static org.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
import static org.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
import static org.htmlunit.javascript.configuration.SupportedBrowser.FF;
import static org.htmlunit.javascript.configuration.SupportedBrowser.FF_ESR;
import static org.htmlunit.javascript.configuration.SupportedBrowser.IE;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.htmlunit.BrowserVersion;
import org.htmlunit.Page;
import org.htmlunit.WebRequest;
import org.htmlunit.WebWindow;
import org.htmlunit.corejs.javascript.FunctionObject;
import org.htmlunit.corejs.javascript.ScriptableObject;
import org.htmlunit.html.HtmlPage;
import org.htmlunit.javascript.HtmlUnitScriptable;
import org.htmlunit.javascript.configuration.JsxClass;
import org.htmlunit.javascript.configuration.JsxConstructor;
import org.htmlunit.javascript.configuration.JsxFunction;
import org.htmlunit.javascript.configuration.JsxGetter;
import org.htmlunit.javascript.configuration.JsxSetter;
import org.htmlunit.javascript.host.event.Event;
import org.htmlunit.javascript.host.event.HashChangeEvent;
import org.htmlunit.protocol.javascript.JavaScriptURLConnection;
import org.htmlunit.util.UrlUtils;
/**
* A JavaScript object for {@code Location}.
*
* @author Mike Bowler
* @author Michael Ottati
* @author Marc Guillemot
* @author Chris Erskine
* @author Daniel Gredler
* @author David K. Taylor
* @author Ahmed Ashour
* @author Ronald Brill
* @author Frank Danek
* @author Adam Afeltowicz
* @author Atsushi Nakagawa
*
* @see MSDN Documentation
*/
@JsxClass
public class Location extends HtmlUnitScriptable {
private static final Log LOG = LogFactory.getLog(Location.class);
private static final String UNKNOWN = "null";
/**
* The window which owns this location object.
*/
private Window window_;
private static final Method methodAssign;
private static final Method methodReload;
private static final Method methodReplace;
private static final Method methodToString;
private static final Method getterHash;
private static final Method setterHash;
private static final Method getterHost;
private static final Method setterHost;
private static final Method getterHostname;
private static final Method setterHostname;
private static final Method getterHref;
private static final Method setterHref;
private static final Method getterOrigin;
private static final Method getterPathname;
private static final Method setterPathname;
private static final Method getterPort;
private static final Method setterPort;
private static final Method getterProtocol;
private static final Method setterProtocol;
private static final Method getterSearch;
private static final Method setterSearch;
static {
try {
methodAssign = Location.class.getDeclaredMethod("assign", String.class);
methodReload = Location.class.getDeclaredMethod("reload", boolean.class);
methodReplace = Location.class.getDeclaredMethod("replace", String.class);
methodToString = Location.class.getDeclaredMethod("jsToString");
getterHash = Location.class.getDeclaredMethod("getHash");
setterHash = Location.class.getDeclaredMethod("setHash", String.class);
getterHost = Location.class.getDeclaredMethod("getHost");
setterHost = Location.class.getDeclaredMethod("setHost", String.class);
getterHostname = Location.class.getDeclaredMethod("getHostname");
setterHostname = Location.class.getDeclaredMethod("setHostname", String.class);
getterHref = Location.class.getDeclaredMethod("getHref");
setterHref = Location.class.getDeclaredMethod("setHref", String.class);
getterOrigin = Location.class.getDeclaredMethod("getOrigin");
getterPathname = Location.class.getDeclaredMethod("getPathname");
setterPathname = Location.class.getDeclaredMethod("setPathname", String.class);
getterPort = Location.class.getDeclaredMethod("getPort");
setterPort = Location.class.getDeclaredMethod("setPort", String.class);
getterProtocol = Location.class.getDeclaredMethod("getProtocol");
setterProtocol = Location.class.getDeclaredMethod("setProtocol", String.class);
getterSearch = Location.class.getDeclaredMethod("getSearch");
setterSearch = Location.class.getDeclaredMethod("setSearch", String.class);
}
catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
/**
* The current hash; we cache it locally because we don't want to modify the page's URL and
* force a page reload each time this changes.
*/
private String hash_;
/**
* Creates an instance.
*/
public Location() {
}
/**
* Creates an instance.
*/
@JsxConstructor({CHROME, EDGE, FF, FF_ESR})
public void jsConstructor() {
final int attributes = ScriptableObject.PERMANENT | ScriptableObject.READONLY;
FunctionObject functionObject = new FunctionObject(methodAssign.getName(), methodAssign, this);
defineProperty(methodAssign.getName(), functionObject, attributes);
functionObject = new FunctionObject(methodReload.getName(), methodReload, this);
defineProperty(methodReload.getName(), functionObject, attributes);
functionObject = new FunctionObject(methodReplace.getName(), methodReplace, this);
defineProperty(methodReplace.getName(), functionObject, attributes);
functionObject = new FunctionObject(methodToString.getName(), methodToString, this);
defineProperty("toString", functionObject, attributes);
defineProperty("hash", null, getterHash, setterHash, attributes);
defineProperty("host", null, getterHost, setterHost, attributes);
defineProperty("hostname", null, getterHostname, setterHostname, attributes);
defineProperty("href", null, getterHref, setterHref, attributes);
defineProperty("origin", null, getterOrigin, null, attributes);
defineProperty("pathname", null, getterPathname, setterPathname, attributes);
defineProperty("port", null, getterPort, setterPort, attributes);
defineProperty("protocol", null, getterProtocol, setterProtocol, attributes);
defineProperty("search", null, getterSearch, setterSearch, attributes);
}
/**
* Initializes this Location.
*
* @param window the window that this location belongs to
* @param page the page that will become the enclosing page
*/
public void initialize(final Window window, final Page page) {
window_ = window;
if (window_ != null && page != null) {
setHash(null, page.getUrl().getRef());
}
}
/**
* {@inheritDoc}
*/
@Override
public Object getDefaultValue(final Class> hint) {
if (getPrototype() != null
&& window_ != null
&& (hint == null || String.class.equals(hint))) {
return getHref();
}
return super.getDefaultValue(hint);
}
/**
* Loads the new HTML document corresponding to the specified URL.
* @param url the location of the new HTML document to load
* @throws IOException if loading the specified location fails
* @see MSDN Documentation
*/
@JsxFunction(IE)
public void assign(final String url) throws IOException {
setHref(url);
}
/**
* Reloads the current page, possibly forcing retrieval from the server even if
* the browser cache contains the latest version of the document.
* @param force if {@code true}, force reload from server; otherwise, may reload from cache
* @throws IOException if there is a problem reloading the page
* @see MSDN Documentation
*/
@JsxFunction(IE)
public void reload(final boolean force) throws IOException {
final WebWindow webWindow = window_.getWebWindow();
final HtmlPage htmlPage = (HtmlPage) webWindow.getEnclosedPage();
final WebRequest request = htmlPage.getWebResponse().getWebRequest();
if (webWindow.getWebClient().getBrowserVersion().hasFeature(JS_LOCATION_RELOAD_REFERRER)) {
request.setRefererlHeader(htmlPage.getUrl());
}
webWindow.getWebClient().download(webWindow, "", request, true, false, false, "JS location.reload");
}
/**
* Reloads the window using the specified URL via a postponed action.
* @param url the new URL to use to reload the window
* @throws IOException if loading the specified location fails
* @see MSDN Documentation
*/
@JsxFunction(IE)
public void replace(final String url) throws IOException {
window_.getWebWindow().getHistory().removeCurrent();
setHref(url);
}
/**
* Returns the location URL.
* @return the location URL
*/
@JsxFunction(functionName = "toString", value = IE)
public String jsToString() {
if (window_ != null) {
return getHref();
}
return "";
}
/**
* Returns the location URL.
* @return the location URL
* @see MSDN Documentation
*/
@JsxGetter(IE)
public String getHref() {
final WebWindow webWindow = window_.getWebWindow();
final Page page = webWindow.getEnclosedPage();
if (page == null) {
return UNKNOWN;
}
try {
URL url = page.getUrl();
final boolean encodeHash = webWindow.getWebClient()
.getBrowserVersion().hasFeature(JS_LOCATION_HREF_HASH_IS_ENCODED);
final String hash = getHash(encodeHash);
if (hash != null) {
url = UrlUtils.getUrlWithNewRef(url, hash);
}
String s = url.toExternalForm();
if (s.startsWith("file:/") && !s.startsWith("file:///")) {
// Java (sometimes?) returns file URLs with a single slash; however, browsers return
// three slashes. See http://www.cyanwerks.com/file-url-formats.html for more info.
s = "file:///" + s.substring("file:/".length());
}
return s;
}
catch (final MalformedURLException e) {
if (LOG.isErrorEnabled()) {
LOG.error(e.getMessage(), e);
}
return page.getUrl().toExternalForm();
}
}
/**
* Sets the location URL to an entirely new value.
* @param newLocation the new location URL
* @throws IOException if loading the specified location fails
* @see MSDN Documentation
*/
@JsxSetter(IE)
public void setHref(final String newLocation) throws IOException {
WebWindow webWindow = getWindow(getStartingScope()).getWebWindow();
final HtmlPage page = (HtmlPage) webWindow.getEnclosedPage();
if (newLocation.startsWith(JavaScriptURLConnection.JAVASCRIPT_PREFIX)) {
final String script = newLocation.substring(11);
page.executeJavaScript(script, "new location value", 1);
return;
}
try {
final BrowserVersion browserVersion = webWindow.getWebClient().getBrowserVersion();
URL url = page.getFullyQualifiedUrl(newLocation);
// fix for empty url
if (StringUtils.isEmpty(newLocation)) {
final boolean dropFilename = browserVersion.hasFeature(ANCHOR_EMPTY_HREF_NO_FILENAME);
if (dropFilename) {
String path = url.getPath();
path = path.substring(0, path.lastIndexOf('/') + 1);
url = UrlUtils.getUrlWithNewPath(url, path);
}
url = UrlUtils.getUrlWithNewRef(url, null);
}
final WebRequest request = new WebRequest(url,
browserVersion.getHtmlAcceptHeader(), browserVersion.getAcceptEncodingHeader());
request.setRefererlHeader(page.getUrl());
webWindow = window_.getWebWindow();
webWindow.getWebClient().download(webWindow, "", request, true, false, false, "JS set location");
}
catch (final MalformedURLException e) {
if (LOG.isErrorEnabled()) {
LOG.error("setHref('" + newLocation + "') got MalformedURLException", e);
}
throw e;
}
}
/**
* Returns the search portion of the location URL (the portion following the '?').
* @return the search portion of the location URL
* @see MSDN Documentation
*/
@JsxGetter(IE)
public String getSearch() {
final String search = getUrl().getQuery();
if (search == null) {
return "";
}
return "?" + search;
}
/**
* Sets the search portion of the location URL (the portion following the '?').
* @param search the new search portion of the location URL
* @throws Exception if an error occurs
* @see MSDN Documentation
*/
@JsxSetter(IE)
public void setSearch(final String search) throws Exception {
setUrl(UrlUtils.getUrlWithNewQuery(getUrl(), search));
}
/**
* Returns the hash portion of the location URL (the portion following the '#').
* @return the hash portion of the location URL
* @see MSDN Documentation
*/
@JsxGetter(IE)
public String getHash() {
final BrowserVersion browserVersion = getBrowserVersion();
final boolean decodeHash = browserVersion.hasFeature(JS_LOCATION_HASH_IS_DECODED);
String hash = hash_;
if (hash_ != null && (decodeHash || hash_.equals(getUrl().getRef()))) {
hash = decodeHash(hash);
}
if (StringUtils.isEmpty(hash)) {
if (browserVersion.hasFeature(JS_LOCATION_HASH_RETURNS_HASH_FOR_EMPTY_DEFINED)
&& getHref().endsWith("#")) {
return "#";
}
}
else if (browserVersion.hasFeature(JS_LOCATION_HASH_HASH_IS_ENCODED)) {
return "#" + UrlUtils.encodeHash(hash);
}
else {
return "#" + hash;
}
return "";
}
private String getHash(final boolean encoded) {
if (hash_ == null || hash_.isEmpty()) {
return null;
}
if (encoded) {
return UrlUtils.encodeHash(hash_);
}
return hash_;
}
/**
* Sets the hash portion of the location URL (the portion following the '#').
*
* @param hash the new hash portion of the location URL
* @see MSDN Documentation
*/
@JsxSetter(IE)
public void setHash(final String hash) {
// IMPORTANT: This method must not call setUrl(), because
// we must not hit the server just to change the hash!
setHash(getHref(), hash, true);
}
/**
* Sets the hash portion of the location URL (the portion following the '#').
*
* @param oldURL the old URL
* @param hash the new hash portion of the location URL
*/
public void setHash(final String oldURL, final String hash) {
setHash(oldURL, hash, true);
}
/**
* INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.
*
* Sets the hash portion of the location URL (the portion following the '#').
*
* @param oldURL the old URL
* @param hash the new hash portion of the location URL
* @param triggerHashChanged option to disable event triggering
*/
public void setHash(final String oldURL, String hash, final boolean triggerHashChanged) {
// IMPORTANT: This method must not call setUrl(), because
// we must not hit the server just to change the hash!
if (hash != null && !hash.isEmpty() && hash.charAt(0) == '#') {
hash = hash.substring(1);
}
final boolean hasChanged = hash != null && !hash.equals(hash_);
hash_ = hash;
if (triggerHashChanged && hasChanged) {
final Window w = getWindow();
final Event event;
if (getBrowserVersion().hasFeature(EVENT_TYPE_HASHCHANGEEVENT)) {
event = new HashChangeEvent(w, Event.TYPE_HASH_CHANGE, oldURL, getHref());
}
else {
event = new Event(w, Event.TYPE_HASH_CHANGE);
event.initEvent(Event.TYPE_HASH_CHANGE, false, false);
}
w.executeEventLocally(event);
}
}
private static String decodeHash(final String hash) {
if (hash.indexOf('%') == -1) {
return hash;
}
return UrlUtils.decode(hash);
}
/**
* Returns the hostname portion of the location URL.
* @return the hostname portion of the location URL
* @see MSDN Documentation
*/
@JsxGetter(IE)
public String getHostname() {
return getUrl().getHost();
}
/**
* Sets the hostname portion of the location URL.
* @param hostname the new hostname portion of the location URL
* @throws Exception if an error occurs
* @see MSDN Documentation
*/
@JsxSetter(IE)
public void setHostname(final String hostname) throws Exception {
setUrl(UrlUtils.getUrlWithNewHost(getUrl(), hostname));
}
/**
* Returns the host portion of the location URL (the '[hostname]:[port]' portion).
* @return the host portion of the location URL
* @see MSDN Documentation
*/
@JsxGetter(IE)
public String getHost() {
final URL url = getUrl();
final int port = url.getPort();
final String host = url.getHost();
if (port == -1) {
return host;
}
return host + ":" + port;
}
/**
* Sets the host portion of the location URL (the '[hostname]:[port]' portion).
* @param host the new host portion of the location URL
* @throws Exception if an error occurs
* @see MSDN Documentation
*/
@JsxSetter(IE)
public void setHost(final String host) throws Exception {
final String hostname;
final int port;
final int index = host.indexOf(':');
if (index == -1) {
hostname = host;
port = -1;
}
else {
hostname = host.substring(0, index);
port = Integer.parseInt(host.substring(index + 1));
}
final URL url = UrlUtils.getUrlWithNewHostAndPort(getUrl(), hostname, port);
setUrl(url);
}
/**
* Returns the pathname portion of the location URL.
* @return the pathname portion of the location URL
* @see MSDN Documentation
*/
@JsxGetter(IE)
public String getPathname() {
if (UrlUtils.URL_ABOUT_BLANK == getUrl()) {
if (getBrowserVersion().hasFeature(URL_ABOUT_BLANK_HAS_BLANK_PATH)) {
return "blank";
}
return "/blank";
}
return getUrl().getPath();
}
/**
* Sets the pathname portion of the location URL.
* @param pathname the new pathname portion of the location URL
* @throws Exception if an error occurs
* @see MSDN Documentation
*/
@JsxSetter(IE)
public void setPathname(final String pathname) throws Exception {
setUrl(UrlUtils.getUrlWithNewPath(getUrl(), pathname));
}
/**
* Returns the port portion of the location URL.
* @return the port portion of the location URL
* @see MSDN Documentation
*/
@JsxGetter(IE)
public String getPort() {
final int port = getUrl().getPort();
if (port == -1) {
return "";
}
return Integer.toString(port);
}
/**
* Sets the port portion of the location URL.
* @param port the new port portion of the location URL
* @throws Exception if an error occurs
* @see MSDN Documentation
*/
@JsxSetter(IE)
public void setPort(final String port) throws Exception {
setUrl(UrlUtils.getUrlWithNewPort(getUrl(), Integer.parseInt(port)));
}
/**
* Returns the protocol portion of the location URL, including the trailing ':'.
* @return the protocol portion of the location URL, including the trailing ':'
* @see MSDN Documentation
*/
@JsxGetter(IE)
public String getProtocol() {
return getUrl().getProtocol() + ":";
}
/**
* Sets the protocol portion of the location URL.
* @param protocol the new protocol portion of the location URL
* @throws Exception if an error occurs
* @see MSDN Documentation
*/
@JsxSetter(IE)
public void setProtocol(final String protocol) throws Exception {
setUrl(UrlUtils.getUrlWithNewProtocol(getUrl(), protocol));
}
/**
* Returns this location's current URL.
* @return this location's current URL
*/
private URL getUrl() {
return window_.getWebWindow().getEnclosedPage().getUrl();
}
/**
* Sets this location's URL, triggering a server hit and loading the resultant document
* into this location's window.
* @param url This location's new URL
* @throws IOException if there is a problem loading the new location
*/
private void setUrl(final URL url) throws IOException {
final WebWindow webWindow = window_.getWebWindow();
final BrowserVersion browserVersion = webWindow.getWebClient().getBrowserVersion();
final WebRequest webRequest = new WebRequest(url,
browserVersion.getHtmlAcceptHeader(), browserVersion.getAcceptEncodingHeader());
webRequest.setRefererlHeader(getUrl());
webWindow.getWebClient().getPage(webWindow, webRequest);
}
/**
* Returns the {@code origin} property.
* @return the {@code origin} property
*/
@JsxGetter(IE)
public String getOrigin() {
return getUrl().getProtocol() + "://" + getHost();
}
}