All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gargoylesoftware.htmlunit.javascript.host.Location Maven / Gradle / Ivy

/*
 * Copyright (c) 2002-2016 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
 * http://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 com.gargoylesoftware.htmlunit.javascript.host;

import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.ANCHOR_EMPTY_HREF_NO_FILENAME;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.EVENT_TYPE_HASHCHANGEEVENT;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_LOCATION_HASH_HASH_IS_ENCODED;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_LOCATION_HASH_IS_DECODED;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_LOCATION_HASH_RETURNS_HASH_FOR_EMPTY_DEFINED;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_LOCATION_HREF_HASH_IS_ENCODED;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.URL_ABOUT_BLANK_HAS_BLANK_PATH;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.URL_ABOUT_BLANK_HAS_EMPTY_PATH;
import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.CHROME;
import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.EDGE;
import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.FF;

import java.io.IOException;
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 com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebWindow;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxGetter;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxSetter;
import com.gargoylesoftware.htmlunit.javascript.configuration.WebBrowser;
import com.gargoylesoftware.htmlunit.javascript.host.event.Event;
import com.gargoylesoftware.htmlunit.javascript.host.event.HashChangeEvent;
import com.gargoylesoftware.htmlunit.protocol.javascript.JavaScriptURLConnection;
import com.gargoylesoftware.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
 *
 * @see MSDN Documentation
 */
@JsxClass
public class Location extends SimpleScriptable {

    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_;

    /**
     * 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.
     */
    @JsxConstructor({ @WebBrowser(CHROME), @WebBrowser(value = FF, minVersion = 38), @WebBrowser(EDGE) })
    public Location() {
    }

    /**
     * Initializes the object.
     *
     * @param window the window that this location belongs to
     */
    public void initialize(final Window window) {
        window_ = window;
        if (window_ != null && window_.getWebWindow().getEnclosedPage() != null) {
            setHash(window_.getWebWindow().getEnclosedPage().getUrl().getRef());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object getDefaultValue(final Class hint) {
        if (getPrototype() != 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
    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
    public void reload(final boolean force) throws IOException {
        final String url = getHref();
        if (UNKNOWN.equals(url)) {
            LOG.error("Unable to reload location: current URL is unknown.");
        }
        else {
            setHref(url);
        }
    }

    /**
     * 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
    public void replace(final String url) throws IOException {
        window_.getWebWindow().getHistory().removeCurrent();
        setHref(url);
    }

    /**
     * Returns the location URL.
     * @return the location URL
     */
    @Override
    @JsxFunction
    public String toString() {
        if (window_ != null) {
            return getHref();
        }
        return "";
    }

    /**
     * Returns the location URL.
     * @return the location URL
     * @see MSDN Documentation
     */
    @JsxGetter
    public String getHref() {
        final Page page = window_.getWebWindow().getEnclosedPage();
        if (page == null) {
            return UNKNOWN;
        }
        try {
            URL url = page.getUrl();
            final boolean encodeHash = 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) {
            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
    public void setHref(final String newLocation) throws IOException {
        setHref(newLocation, false, null);
    }

    /**
     * INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.
* * Sets the location URL to an entirely new value. * * @param newLocation the new location URL * @param justHistoryAPIPushState indicates if change is caused by using HTML5 HistoryAPI * @param state the state object passed down if justHistoryAPIPushState is true * @throws IOException if loading the specified location fails * @see MSDN Documentation */ public void setHref(final String newLocation, final boolean justHistoryAPIPushState, final Object state) throws IOException { final HtmlPage page = (HtmlPage) getWindow(getStartingScope()).getWebWindow().getEnclosedPage(); if (newLocation.startsWith(JavaScriptURLConnection.JAVASCRIPT_PREFIX)) { final String script = newLocation.substring(11); page.executeJavaScriptIfPossible(script, "new location value", 1); return; } try { URL url = page.getFullyQualifiedUrl(newLocation); // fix for empty url if (StringUtils.isEmpty(newLocation)) { final boolean dropFilename = page.getWebClient().getBrowserVersion(). 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); } else { url = UrlUtils.getUrlWithNewRef(url, null); } } final WebRequest request = new WebRequest(url); request.setAdditionalHeader("Referer", page.getUrl().toExternalForm()); final WebWindow webWindow = getWindow().getWebWindow(); webWindow.getWebClient().download(webWindow, "", request, true, false, "JS set location"); if (justHistoryAPIPushState) { webWindow.getWebClient().loadDownloadedResponses(); } } catch (final MalformedURLException e) { 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 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 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 public String getHash() { final boolean decodeHash = getBrowserVersion().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 (getBrowserVersion().hasFeature(JS_LOCATION_HASH_RETURNS_HASH_FOR_EMPTY_DEFINED) && getHref().endsWith("#")) { return "#"; } } else if (getBrowserVersion().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.encodeAnchor(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 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); } /** * 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, String hash) { // IMPORTANT: This method must not call setUrl(), because // we must not hit the server just to change the hash! if (hash != null) { if (!hash.isEmpty() && ('#' == hash.charAt(0))) { hash = hash.substring(1); } } final boolean hasChanged = hash != null && !hash.equals(hash_); hash_ = hash; final String newURL = getHref(); if (hasChanged) { final Event event; if (getBrowserVersion().hasFeature(EVENT_TYPE_HASHCHANGEEVENT)) { event = new HashChangeEvent(getWindow(), Event.TYPE_HASH_CHANGE, oldURL, newURL); } else { event = new Event(getWindow(), Event.TYPE_HASH_CHANGE); event.initEvent(Event.TYPE_HASH_CHANGE, false, false); } getWindow().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 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 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 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 public void setHost(final String host) throws Exception { final String hostname; final int port; final int index = host.indexOf(':'); if (index != -1) { hostname = host.substring(0, index); port = Integer.parseInt(host.substring(index + 1)); } else { hostname = host; port = -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 public String getPathname() { if (WebClient.URL_ABOUT_BLANK == getUrl()) { if (getBrowserVersion().hasFeature(URL_ABOUT_BLANK_HAS_EMPTY_PATH)) { return ""; } 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 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 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 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 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 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 { window_.getWebWindow().getWebClient().getPage(window_.getWebWindow(), new WebRequest(url)); } /** * Returns the {@code origin} property. * @return the {@code origin} property */ @JsxGetter public String getOrigin() { return getUrl().getProtocol() + "://" + getHost(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy