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

org.htmlunit.html.BaseFrameElement Maven / Gradle / Ivy

Go to download

XLT (Xceptance LoadTest) is an extensive load and performance test tool developed and maintained by Xceptance.

The newest version!
/*
 * 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.html;

import static org.htmlunit.BrowserVersionFeatures.FRAME_LOCATION_ABOUT_BLANK_FOR_ABOUT_SCHEME;
import static org.htmlunit.BrowserVersionFeatures.URL_MINIMAL_QUERY_ENCODING;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.htmlunit.FailingHttpStatusCodeException;
import org.htmlunit.FrameContentHandler;
import org.htmlunit.Page;
import org.htmlunit.SgmlPage;
import org.htmlunit.WebClient;
import org.htmlunit.WebClientOptions;
import org.htmlunit.WebRequest;
import org.htmlunit.WebWindow;
import org.htmlunit.javascript.AbstractJavaScriptEngine;
import org.htmlunit.javascript.PostponedAction;
import org.htmlunit.protocol.javascript.JavaScriptURLConnection;
import org.htmlunit.util.UrlUtils;
import org.w3c.dom.Attr;

/**
 * Base class for frame and iframe.
 *
 * @author Mike Bowler
 * @author David K. Taylor
 * @author Christian Sell
 * @author Marc Guillemot
 * @author David D. Kilzer
 * @author Stefan Anzinger
 * @author Ahmed Ashour
 * @author Dmitri Zoubkov
 * @author Daniel Gredler
 * @author Ronald Brill
 * @author Frank Danek
 */
public abstract class BaseFrameElement extends HtmlElement {

    private static final Log LOG = LogFactory.getLog(BaseFrameElement.class);
    private FrameWindow enclosedWindow_;
    private boolean contentLoaded_;
    private boolean loadSrcWhenAddedToPage_;

    /**
     * Creates an instance of BaseFrame.
     *
     * @param qualifiedName the qualified name of the element type to instantiate
     * @param page the HtmlPage that contains this element
     * @param attributes the initial attributes
     */
    protected BaseFrameElement(final String qualifiedName, final SgmlPage page,
            final Map attributes) {
        super(qualifiedName, page, attributes);

        init();

        if (null != page && page.isHtmlPage() && ((HtmlPage) page).isParsingHtmlSnippet()) {
            // if created by the HTMLParser the src attribute is not set via setAttribute() or some other method but is
            // part of the given attributes already.
            final String src = getSrcAttribute();
            if (ATTRIBUTE_NOT_DEFINED != src && !UrlUtils.ABOUT_BLANK.equals(src)) {
                loadSrcWhenAddedToPage_ = true;
            }
        }
    }

    private void init() {
        FrameWindow enclosedWindow = null;
        try {
            final HtmlPage htmlPage = getHtmlPageOrNull();
            if (null != htmlPage) { // if loaded as part of XHR.responseXML, don't load content
                enclosedWindow = new FrameWindow(this);
                // put about:blank in the window to allow JS to run on this frame before the
                // real content is loaded
                final WebClient webClient = htmlPage.getWebClient();
                final HtmlPage temporaryPage = webClient.getPage(enclosedWindow, WebRequest.newAboutBlankRequest());
                temporaryPage.setReadyState(READY_STATE_LOADING);
            }
        }
        catch (final FailingHttpStatusCodeException | IOException e) {
            // should never occur
        }
        enclosedWindow_ = enclosedWindow;
    }

    /**
     * INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.
* * Called after the node for the {@code frame} or {@code iframe} has been added to the containing page. * The node needs to be added first to allow JavaScript in the frame to see the frame in the parent. * @throws FailingHttpStatusCodeException if the server returns a failing status code AND the property * {@link org.htmlunit.WebClientOptions#setThrowExceptionOnFailingStatusCode(boolean)} is * set to true */ public void loadInnerPage() throws FailingHttpStatusCodeException { String source = getSrcAttribute(); if (source.isEmpty()) { source = UrlUtils.ABOUT_BLANK; } else if (StringUtils.startsWithIgnoreCase(source, UrlUtils.ABOUT_SCHEME) && hasFeature(FRAME_LOCATION_ABOUT_BLANK_FOR_ABOUT_SCHEME)) { source = UrlUtils.ABOUT_BLANK; } loadInnerPageIfPossible(source); final Page enclosedPage = getEnclosedPage(); if (enclosedPage != null && enclosedPage.isHtmlPage()) { final HtmlPage htmlPage = (HtmlPage) enclosedPage; final AbstractJavaScriptEngine jsEngine = htmlPage.getWebClient().getJavaScriptEngine(); if (jsEngine != null && jsEngine.isScriptRunning()) { final PostponedAction action = new PostponedAction(getPage(), "BaseFrame.loadInnerPage") { @Override public void execute() { htmlPage.setReadyState(READY_STATE_COMPLETE); } }; jsEngine.addPostponedAction(action); } else { htmlPage.setReadyState(READY_STATE_COMPLETE); } } } /** * Indicates if the content specified by the {@code src} attribute has been loaded or not. * The initial state of a frame contains an "about:blank" that is not loaded like * something specified in {@code src} attribute. * @return {@code false} if the frame is still in its initial state. */ boolean isContentLoaded() { return contentLoaded_; } /** * Changes the state of the {@code contentLoaded_} attribute to true. * This is needed, if the content is set from javascript to avoid * later overwriting from method org.htmlunit.html.HtmlPage.loadFrames(). */ void setContentLoaded() { contentLoaded_ = true; } /** * @throws FailingHttpStatusCodeException if the server returns a failing status code AND the property * {@link WebClientOptions#setThrowExceptionOnFailingStatusCode(boolean)} is set to true */ private void loadInnerPageIfPossible(final String src) throws FailingHttpStatusCodeException { setContentLoaded(); String source = src; final SgmlPage page = getPage(); final WebClient webClient = page.getWebClient(); final FrameContentHandler handler = webClient.getFrameContentHandler(); if (null != handler && !handler.loadFrameDocument(this)) { source = UrlUtils.ABOUT_BLANK; } if (!source.isEmpty()) { final URL url; try { url = ((HtmlPage) page).getFullyQualifiedUrl(source); } catch (final MalformedURLException e) { notifyIncorrectness("Invalid src attribute of " + getTagName() + ": url=[" + source + "]. Ignored."); return; } final WebRequest request = new WebRequest(url, page.getCharset(), page.getUrl()); if (isAlreadyLoadedByAncestor(url, request.getCharset())) { notifyIncorrectness("Recursive src attribute of " + getTagName() + ": url=[" + source + "]. Ignored."); return; } try { webClient.getPage(enclosedWindow_, request); } catch (final IOException e) { if (LOG.isErrorEnabled()) { LOG.error("IOException when getting content for " + getTagName() + ": url=[" + url + "]", e); } } } } /** * Test if the provided URL is the one of the parents which would cause an infinite loop. * @param url the URL to test * @param charset the request charset * @return {@code false} if no parent has already this URL */ private boolean isAlreadyLoadedByAncestor(final URL url, final Charset charset) { WebWindow window = getPage().getEnclosingWindow(); int nesting = 0; while (window instanceof FrameWindow) { nesting++; if (nesting > 9) { return true; } final URL encUrl = UrlUtils.encodeUrl(url, window.getWebClient().getBrowserVersion().hasFeature(URL_MINIMAL_QUERY_ENCODING), charset); if (UrlUtils.sameFile(encUrl, window.getEnclosedPage().getUrl())) { return true; } if (window == window.getParentWindow()) { // TODO: should getParentWindow() return null on top windows? window = null; } else { window = window.getParentWindow(); } } return false; } /** * Returns the value of the attribute {@code longdesc}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code longdesc} or an empty string if that attribute isn't defined */ public final String getLongDescAttribute() { return getAttributeDirect("longdesc"); } /** * Returns the value of the attribute {@code name}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code name} or an empty string if that attribute isn't defined */ public final String getNameAttribute() { return getAttributeDirect(NAME_ATTRIBUTE); } /** * Sets the value of the {@code name} attribute. * * @param name the new window name */ public final void setNameAttribute(final String name) { setAttribute(NAME_ATTRIBUTE, name); } /** * Returns the value of the attribute {@code src}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code src} or an empty string if that attribute isn't defined */ public final String getSrcAttribute() { return getSrcAttributeNormalized(); } /** * Returns the value of the attribute {@code frameborder}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code frameborder} or an empty string if that attribute isn't defined */ public final String getFrameBorderAttribute() { return getAttributeDirect("frameborder"); } /** * Returns the value of the attribute {@code marginwidth}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code marginwidth} or an empty string if that attribute isn't defined */ public final String getMarginWidthAttribute() { return getAttributeDirect("marginwidth"); } /** * Returns the value of the attribute {@code marginheight}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code marginheight} or an empty string if that attribute isn't defined */ public final String getMarginHeightAttribute() { return getAttributeDirect("marginheight"); } /** * Returns the value of the attribute {@code noresize}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code noresize} or an empty string if that attribute isn't defined */ public final String getNoResizeAttribute() { return getAttributeDirect("noresize"); } /** * Returns the value of the attribute {@code scrolling}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code scrolling} or an empty string if that attribute isn't defined */ public final String getScrollingAttribute() { return getAttributeDirect("scrolling"); } /** * Returns the value of the attribute {@code onload}. This attribute is not * actually supported by the HTML specification however it is supported * by the popular browsers. * * @return the value of the attribute {@code onload} or an empty string if that attribute isn't defined */ public final String getOnLoadAttribute() { return getAttributeDirect("onload"); } /** * Returns the currently loaded page in the enclosed window. * This is a facility method for getEnclosedWindow().getEnclosedPage(). * @see WebWindow#getEnclosedPage() * @return the currently loaded page in the enclosed window, or {@code null} if no page has been loaded */ public Page getEnclosedPage() { return getEnclosedWindow().getEnclosedPage(); } /** * Gets the window enclosed in this frame. * @return the window enclosed in this frame */ public FrameWindow getEnclosedWindow() { return enclosedWindow_; } /** * Sets the value of the {@code src} attribute. Also loads the frame with the specified URL, if possible. * @param attribute the new value of the {@code src} attribute */ public final void setSrcAttribute(final String attribute) { setAttribute(SRC_ATTRIBUTE, attribute); } /** * {@inheritDoc} */ @Override protected void setAttributeNS(final String namespaceURI, final String qualifiedName, String attributeValue, final boolean notifyAttributeChangeListeners, final boolean notifyMutationObserver) { final String qualifiedNameLC = org.htmlunit.util.StringUtils.toRootLowerCase(qualifiedName); if (null != attributeValue && SRC_ATTRIBUTE.equals(qualifiedNameLC)) { attributeValue = attributeValue.trim(); } super.setAttributeNS(namespaceURI, qualifiedNameLC, attributeValue, notifyAttributeChangeListeners, notifyMutationObserver); // do not use equals() here // see HTMLIFrameElement2Test.documentCreateElement_onLoad_srcAboutBlank() if (SRC_ATTRIBUTE.equals(qualifiedNameLC) && UrlUtils.ABOUT_BLANK != attributeValue) { if (isAttachedToPage()) { loadSrc(); } else { loadSrcWhenAddedToPage_ = true; } } } /** * {@inheritDoc} */ @Override public Attr setAttributeNode(final Attr attribute) { final String qualifiedName = attribute.getName(); String attributeValue = null; if (SRC_ATTRIBUTE.equals(qualifiedName)) { attributeValue = attribute.getValue().trim(); } final Attr result = super.setAttributeNode(attribute); if (SRC_ATTRIBUTE.equals(qualifiedName) && !UrlUtils.ABOUT_BLANK.equals(attributeValue)) { if (isAttachedToPage()) { loadSrc(); } else { loadSrcWhenAddedToPage_ = true; } } return result; } private void loadSrc() { loadSrcWhenAddedToPage_ = false; final String src = getSrcAttribute(); // recreate a window if the old one was closed if (enclosedWindow_.isClosed()) { init(); } final AbstractJavaScriptEngine jsEngine = getPage().getWebClient().getJavaScriptEngine(); // When src is set from a script, loading is postponed until script finishes // in fact this implementation is probably wrong: JavaScript URL should be // first evaluated and only loading, when any, should be postponed. if (jsEngine == null || !jsEngine.isScriptRunning() || src.startsWith(JavaScriptURLConnection.JAVASCRIPT_PREFIX)) { loadInnerPageIfPossible(src); } else { final Page pageInFrame = getEnclosedPage(); final PostponedAction action = new PostponedAction(getPage(), "BaseFrame.loadSrc") { @Override public void execute() throws Exception { if (!src.isEmpty() && getSrcAttribute().equals(src)) { loadInnerPage(); } } @Override public boolean isStillAlive() { // skip if page in frame has already been changed return super.isStillAlive() && pageInFrame == getEnclosedPage(); } }; jsEngine.addPostponedAction(action); } } /** * Creates a new {@link WebWindow} for the new clone. * {@inheritDoc} */ @Override public DomNode cloneNode(final boolean deep) { final BaseFrameElement clone = (BaseFrameElement) super.cloneNode(deep); clone.init(); return clone; } @Override protected void onAddedToPage() { super.onAddedToPage(); if (loadSrcWhenAddedToPage_) { loadSrc(); } } @Override public void remove() { super.remove(); loadSrcWhenAddedToPage_ = true; getEnclosedWindow().close(); } @Override public final void removeAttribute(final String attributeName) { super.removeAttribute(attributeName); // TODO find a better implementation without all the code duplication if (isAttachedToPage()) { loadSrcWhenAddedToPage_ = false; final String src = getSrcAttribute(); final AbstractJavaScriptEngine jsEngine = getPage().getWebClient().getJavaScriptEngine(); // When src is set from a script, loading is postponed until script finishes // in fact this implementation is probably wrong: JavaScript URL should be // first evaluated and only loading, when any, should be postponed. if (jsEngine == null || !jsEngine.isScriptRunning()) { loadInnerPageIfPossible(src); } else { final Page pageInFrame = getEnclosedPage(); final PostponedAction action = new PostponedAction(getPage(), "BaseFrame.removeAttribute") { @Override public void execute() throws Exception { loadInnerPage(); } @Override public boolean isStillAlive() { // skip if page in frame has already been changed return super.isStillAlive() && pageInFrame == getEnclosedPage(); } }; jsEngine.addPostponedAction(action); } } else { loadSrcWhenAddedToPage_ = true; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy