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

com.gargoylesoftware.htmlunit.html.HtmlImage Maven / Gradle / Ivy

There is a newer version: 2.70.0
Show newest version
/*
 * Copyright (c) 2002-2022 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 com.gargoylesoftware.htmlunit.html;

import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.HTMLIMAGE_BLANK_SRC_AS_EMPTY;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.HTMLIMAGE_EMPTY_SRC_DISPLAY_FALSE;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.HTMLIMAGE_HTMLELEMENT;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.HTMLIMAGE_HTMLUNKNOWNELEMENT;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.HTMLIMAGE_INVISIBLE_NO_SRC;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_IMAGE_COMPLETE_RETURNS_TRUE_FOR_NO_REQUEST;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_IMAGE_WIDTH_HEIGHT_EMPTY_SOURCE_RETURNS_0x0;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_IMAGE_WIDTH_HEIGHT_RETURNS_16x16_0x0;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_IMAGE_WIDTH_HEIGHT_RETURNS_24x24_0x0;
import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.ScriptResult;
import com.gargoylesoftware.htmlunit.SgmlPage;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.javascript.PostponedAction;
import com.gargoylesoftware.htmlunit.javascript.host.dom.Document;
import com.gargoylesoftware.htmlunit.javascript.host.event.Event;
import com.gargoylesoftware.htmlunit.javascript.host.event.MouseEvent;
import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLElement;
import com.gargoylesoftware.htmlunit.util.UrlUtils;

/**
 * Wrapper for the HTML element "img".
 *
 * @author Mike Bowler
 * @author David K. Taylor
 * @author Christian Sell
 * @author Ahmed Ashour
 * @author Knut Johannes Dahle
 * @author Ronald Brill
 * @author Frank Danek
 * @author Carsten Steul
 * @author Alex Gorbatovsky
 */
public class HtmlImage extends HtmlElement {

    private static final Log LOG = LogFactory.getLog(HtmlImage.class);

    /** The HTML tag represented by this element. */
    public static final String TAG_NAME = "img";
    /** Another HTML tag represented by this element. */
    public static final String TAG_NAME2 = "image";

    private final String originalQualifiedName_;

    private int lastClickX_ = -1;
    private int lastClickY_ = -1;
    private WebResponse imageWebResponse_;
    private transient ImageData imageData_;
    private int width_ = -1;
    private int height_ = -1;
    private boolean downloaded_;
    private boolean isComplete_;
    private boolean onloadProcessed_;
    private boolean createdByJavascript_;

    /**
     * Creates a new instance.
     *
     * @param qualifiedName the qualified name of the element type to instantiate
     * @param page the page that contains this element
     * @param attributes the initial attributes
     */
    HtmlImage(final String qualifiedName, final SgmlPage page, final Map attributes) {
        super(unifyLocalName(qualifiedName), page, attributes);
        originalQualifiedName_ = qualifiedName;
        if (page.getWebClient().getOptions().isDownloadImages()) {
            try {
                downloadImageIfNeeded();
            }
            catch (final IOException e) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Unable to download image for element " + this);
                }
            }
        }
    }

    private static String unifyLocalName(final String qualifiedName) {
        if (qualifiedName != null && qualifiedName.endsWith(TAG_NAME2)) {
            final int pos = qualifiedName.lastIndexOf(TAG_NAME2);
            return qualifiedName.substring(0, pos) + TAG_NAME;
        }
        return qualifiedName;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onAddedToPage() {
        doOnLoad();
        super.onAddedToPage();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void setAttributeNS(final String namespaceURI, final String qualifiedName, final String value,
            final boolean notifyAttributeChangeListeners, final boolean notifyMutationObservers) {

        final HtmlPage htmlPage = getHtmlPageOrNull();
        if (SRC_ATTRIBUTE.equals(qualifiedName) && value != ATTRIBUTE_NOT_DEFINED && htmlPage != null) {
            final String oldValue = getAttributeNS(namespaceURI, qualifiedName);
            if (!oldValue.equals(value)) {
                super.setAttributeNS(namespaceURI, qualifiedName, value, notifyAttributeChangeListeners,
                        notifyMutationObservers);

                // onload handlers may need to be invoked again, and a new image may need to be downloaded
                onloadProcessed_ = false;
                downloaded_ = false;
                isComplete_ = false;
                width_ = -1;
                height_ = -1;
                if (imageData_ != null) {
                    imageData_.close();
                    imageData_ = null;
                }

                final String readyState = htmlPage.getReadyState();
                if (READY_STATE_LOADING.equals(readyState)) {
                    final PostponedAction action = new PostponedAction(getPage(), "HtmlImage.setAttributeNS") {
                        @Override
                        public void execute() throws Exception {
                            doOnLoad();
                        }
                    };
                    htmlPage.addAfterLoadAction(action);
                    return;
                }
                doOnLoad();
                return;
            }
        }

        super.setAttributeNS(namespaceURI, qualifiedName, value, notifyAttributeChangeListeners,
                notifyMutationObservers);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void processImportNode(final Document doc) {
        URL oldUrl = null;
        final String src = getSrcAttribute();
        HtmlPage htmlPage = getHtmlPageOrNull();
        try {
            if (htmlPage != null) {
                oldUrl = htmlPage.getFullyQualifiedUrl(src);
            }
        }
        catch (final MalformedURLException e) {
            // ignore
        }

        super.processImportNode(doc);

        URL url = null;
        htmlPage = getHtmlPageOrNull();
        try {
            if (htmlPage != null) {
                url = htmlPage.getFullyQualifiedUrl(src);
            }
        }
        catch (final MalformedURLException e) {
            // ignore
        }

        if (oldUrl == null || !UrlUtils.sameFile(oldUrl, url)) {
            // image has to be reloaded
            lastClickX_ = -1;
            lastClickY_ = -1;
            imageWebResponse_ = null;
            imageData_ = null;
            width_ = -1;
            height_ = -1;
            downloaded_ = false;
            isComplete_ = false;
            onloadProcessed_ = false;
            createdByJavascript_ = true;
        }

        if (htmlPage == null) {
            return; // nothing to do if embedded in XML code
        }

        if (htmlPage.getWebClient().getOptions().isDownloadImages()) {
            try {
                downloadImageIfNeeded();
            }
            catch (final IOException e) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Unable to download image for element " + this);
                }
            }
        }
    }

    /**
     * 

INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.

* *

Executes this element's onload or onerror handler. This method downloads the image * if either of these handlers are present (prior to invoking the resulting handler), because applications * sometimes use images to send information to the server and use these handlers to get notified when the * information has been received by the server.

* *

See here and * here for the discussion which * lead up to this method.

* *

This method may be called multiple times, but will only attempt to execute the onload or * onerror handler the first time it is invoked.

*/ public void doOnLoad() { if (onloadProcessed_) { return; } final HtmlPage htmlPage = getHtmlPageOrNull(); if (htmlPage == null) { return; // nothing to do if embedded in XML code } final WebClient client = htmlPage.getWebClient(); final boolean hasEventHandler = hasEventHandlers("onload") || hasEventHandlers("onerror"); if (((hasEventHandler && client.isJavaScriptEnabled()) || client.getOptions().isDownloadImages()) && hasAttribute(SRC_ATTRIBUTE)) { boolean loadSuccessful = false; final boolean tryDownload; if (hasFeature(HTMLIMAGE_BLANK_SRC_AS_EMPTY)) { tryDownload = !StringUtils.isBlank(getSrcAttribute()); } else { tryDownload = !getSrcAttribute().isEmpty(); } if (tryDownload) { // We need to download the image and then call the resulting handler. try { downloadImageIfNeeded(); // if the download was a success if (imageWebResponse_.isSuccess()) { loadSuccessful = true; // Trigger the onload handler } } catch (final IOException e) { if (LOG.isDebugEnabled()) { LOG.debug("IOException while downloading image for '" + this + "' : " + e.getMessage()); } } } if (!client.isJavaScriptEnabled()) { onloadProcessed_ = true; return; } if (!hasEventHandler) { return; } onloadProcessed_ = true; final Event event = new Event(this, loadSuccessful ? Event.TYPE_LOAD : Event.TYPE_ERROR); if (LOG.isDebugEnabled()) { LOG.debug("Firing the " + event.getType() + " event for '" + this + "'."); } if (READY_STATE_LOADING.equals(htmlPage.getReadyState())) { final PostponedAction action = new PostponedAction(getPage(), "HtmlImage.doOnLoad") { @Override public void execute() throws Exception { HtmlImage.this.fireEvent(event); } }; htmlPage.addAfterLoadAction(action); } else { fireEvent(event); } } } /** * 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 {@code src} value. * @return the value of the {@code src} value */ public String getSrc() { final String src = getSrcAttribute(); if ("".equals(src)) { return src; } try { final HtmlPage page = (HtmlPage) getPage(); return page.getFullyQualifiedUrl(src).toExternalForm(); } catch (final MalformedURLException e) { final String msg = "Unable to create fully qualified URL for src attribute of image " + e.getMessage(); throw new RuntimeException(msg, e); } } /** * Returns the value of the attribute {@code alt}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code alt} or an empty string if that attribute isn't defined */ public final String getAltAttribute() { return getAttributeDirect("alt"); } /** * 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"); } /** * 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 height}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code height} or an empty string if that attribute isn't defined */ public final String getHeightAttribute() { return getAttributeDirect("height"); } /** * Returns the value of the attribute {@code width}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code width} or an empty string if that attribute isn't defined */ public final String getWidthAttribute() { return getAttributeDirect("width"); } /** * Returns the value of the attribute {@code usemap}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code usemap} or an empty string if that attribute isn't defined */ public final String getUseMapAttribute() { return getAttributeDirect("usemap"); } /** * Returns the value of the attribute {@code ismap}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code ismap} or an empty string if that attribute isn't defined */ public final String getIsmapAttribute() { return getAttributeDirect("ismap"); } /** * Returns the value of the attribute {@code align}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code align} or an empty string if that attribute isn't defined */ public final String getAlignAttribute() { return getAttributeDirect("align"); } /** * Returns the value of the attribute {@code border}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code border} or an empty string if that attribute isn't defined */ public final String getBorderAttribute() { return getAttributeDirect("border"); } /** * Returns the value of the attribute {@code hspace}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code hspace} or an empty string if that attribute isn't defined */ public final String getHspaceAttribute() { return getAttributeDirect("hspace"); } /** * Returns the value of the attribute {@code vspace}. Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute {@code vspace} or an empty string if that attribute isn't defined */ public final String getVspaceAttribute() { return getAttributeDirect("vspace"); } /** *

Returns the image's actual height (not the image's {@link #getHeightAttribute() height attribute}).

*

POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK

*

If the image has not already been downloaded, this method triggers a download and caches the image.

* * @return the image's actual height * @throws IOException if an error occurs while downloading or reading the image */ public int getHeight() throws IOException { if (height_ < 0) { determineWidthAndHeight(); } return height_; } /** * Returns the value same value as the js height property. * @return the value of the {@code height} property */ public int getHeightOrDefault() { final String height = getHeightAttribute(); if (ATTRIBUTE_NOT_DEFINED != height) { try { return Integer.parseInt(height); } catch (final NumberFormatException e) { // ignore } } final String src = getSrcAttribute(); if (ATTRIBUTE_NOT_DEFINED == src) { final BrowserVersion browserVersion = getPage().getWebClient().getBrowserVersion(); if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30)) { return 30; } if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_16x16_0x0) || browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_24x24_0x0)) { return 0; } return 24; } final WebClient webClient = getPage().getWebClient(); final BrowserVersion browserVersion = webClient.getBrowserVersion(); if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_EMPTY_SOURCE_RETURNS_0x0) && StringUtils.isEmpty(src)) { return 0; } if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_16x16_0x0) && StringUtils.isBlank(src)) { return 0; } try { return getHeight(); } catch (final IOException e) { if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30)) { return 30; } if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_16x16_0x0)) { return 16; } return 24; } } /** *

Returns the image's actual width (not the image's {@link #getWidthAttribute() width attribute}).

*

POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK

*

If the image has not already been downloaded, this method triggers a download and caches the image.

* * @return the image's actual width * @throws IOException if an error occurs while downloading or reading the image */ public int getWidth() throws IOException { if (width_ < 0) { determineWidthAndHeight(); } return width_; } /** * Returns the value same value as the js width property. * @return the value of the {@code width} property */ public int getWidthOrDefault() { final String widthAttrib = getWidthAttribute(); if (ATTRIBUTE_NOT_DEFINED != widthAttrib) { try { return Integer.parseInt(widthAttrib); } catch (final NumberFormatException e) { // ignore } } final String src = getSrcAttribute(); if (ATTRIBUTE_NOT_DEFINED == src) { final BrowserVersion browserVersion = getPage().getWebClient().getBrowserVersion(); if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30)) { return 28; } if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_16x16_0x0) || browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_24x24_0x0)) { return 0; } return 24; } final WebClient webClient = getPage().getWebClient(); final BrowserVersion browserVersion = webClient.getBrowserVersion(); if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_EMPTY_SOURCE_RETURNS_0x0) && StringUtils.isEmpty(src)) { return 0; } if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_16x16_0x0) && StringUtils.isBlank(src)) { return 0; } try { return getWidth(); } catch (final IOException e) { if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30)) { return 28; } if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_16x16_0x0)) { return 16; } return 24; } } /** *

Returns the ImageReader which can be used to read the image contained by this image element.

*

POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK

*

If the image has not already been downloaded, this method triggers a download and caches the image.

* * @return the ImageReader which can be used to read the image contained by this image element * @throws IOException if an error occurs while downloading or reading the image */ public ImageReader getImageReader() throws IOException { readImageIfNeeded(); return imageData_.getImageReader(); } private void determineWidthAndHeight() throws IOException { final ImageReader imgReader = getImageReader(); width_ = imgReader.getWidth(0); height_ = imgReader.getHeight(0); // ImageIO creates temp files; to save file handles // we will cache the values and close this directly to free the resources if (imageData_ != null) { imageData_.close(); imageData_ = null; } } /** *

Returns the WebResponse for the image contained by this image element.

*

POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK

*

If the image has not already been downloaded and downloadIfNeeded is {@code true}, this method * triggers a download and caches the image.

* * @param downloadIfNeeded whether or not the image should be downloaded (if it hasn't already been downloaded) * @return {@code null} if no download should be performed and one hasn't already been triggered; otherwise, * the response received when performing a request for the image referenced by this element * @throws IOException if an error occurs while downloading the image */ public WebResponse getWebResponse(final boolean downloadIfNeeded) throws IOException { if (downloadIfNeeded) { downloadImageIfNeeded(); } return imageWebResponse_; } /** *

Downloads the image contained by this image element.

*

POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK

*

If the image has not already been downloaded, this method triggers a download and caches the image.

* * @throws IOException if an error occurs while downloading the image */ private void downloadImageIfNeeded() throws IOException { if (!downloaded_) { // HTMLIMAGE_BLANK_SRC_AS_EMPTY final String src = getSrcAttribute(); if (!"".equals(src)) { final HtmlPage page = (HtmlPage) getPage(); final WebClient webClient = page.getWebClient(); final BrowserVersion browser = webClient.getBrowserVersion(); if (!(browser.hasFeature(HTMLIMAGE_BLANK_SRC_AS_EMPTY) && StringUtils.isBlank(src))) { final URL url = page.getFullyQualifiedUrl(src); final WebRequest request = new WebRequest(url, browser.getImgAcceptHeader(), browser.getAcceptEncodingHeader()); request.setCharset(page.getCharset()); request.setRefererlHeader(page.getUrl()); imageWebResponse_ = webClient.loadWebResponse(request); } } if (imageData_ != null) { imageData_.close(); imageData_ = null; } downloaded_ = true; isComplete_ = hasFeature(JS_IMAGE_COMPLETE_RETURNS_TRUE_FOR_NO_REQUEST) || (imageWebResponse_ != null && imageWebResponse_.getContentType().contains("image")); width_ = -1; height_ = -1; } } private void readImageIfNeeded() throws IOException { downloadImageIfNeeded(); if (imageData_ == null) { if (null == imageWebResponse_) { throw new IOException("No image response available (src='" + getSrcAttribute() + "')"); } @SuppressWarnings("resource") final ImageInputStream iis = ImageIO.createImageInputStream(imageWebResponse_.getContentAsStream()); final Iterator iter = ImageIO.getImageReaders(iis); if (!iter.hasNext()) { iis.close(); throw new IOException("No image detected in response"); } final ImageReader imageReader = iter.next(); imageReader.setInput(iis); imageData_ = new ImageData(imageReader); // dispose all others while (iter.hasNext()) { iter.next().dispose(); } } } /** * Simulates clicking this element at the specified position. This only makes sense for * an image map (currently only server side), where the position matters. This method * returns the page contained by this image's window after the click, which may or may not * be the same as the original page, depending on JavaScript event handlers, etc. * * @param x the x position of the click * @param y the y position of the click * @return the page contained by this image's window after the click * @exception IOException if an IO error occurs */ public Page click(final int x, final int y) throws IOException { lastClickX_ = x; lastClickY_ = y; try { return super.click(); } finally { lastClickX_ = -1; lastClickY_ = -1; } } /** * Simulates clicking this element at the position (0, 0). This method returns * the page contained by this image's window after the click, which may or may not be the * same as the original page, depending on JavaScript event handlers, etc. * * @return the page contained by this image's window after the click * @exception IOException if an IO error occurs */ @Override @SuppressWarnings("unchecked") public Page click() throws IOException { return click(0, 0); } /** * Performs the click action on the enclosing A tag (if any). * {@inheritDoc} * @throws IOException if an IO error occurred */ @Override protected boolean doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey) throws IOException { if (ATTRIBUTE_NOT_DEFINED != getUseMapAttribute()) { // remove initial '#' final String mapName = getUseMapAttribute().substring(1); final HtmlElement doc = ((HtmlPage) getPage()).getDocumentElement(); final HtmlMap map = doc.getOneHtmlElementByAttribute("map", "name", mapName); for (final DomElement element : map.getChildElements()) { if (element instanceof HtmlArea) { final HtmlArea area = (HtmlArea) element; if (area.containsPoint(Math.max(lastClickX_, 0), Math.max(lastClickY_, 0))) { area.doClickStateUpdate(shiftKey, ctrlKey); return false; } } } } final HtmlAnchor anchor = (HtmlAnchor) getEnclosingElement("a"); if (anchor == null) { return false; } if (ATTRIBUTE_NOT_DEFINED != getIsmapAttribute()) { final String suffix = "?" + Math.max(lastClickX_, 0) + "," + Math.max(lastClickY_, 0); anchor.doClickStateUpdate(false, false, suffix); return false; } anchor.doClickStateUpdate(shiftKey, ctrlKey); return false; } /** * Saves this image as the specified file. * @param file the file to save to * @throws IOException if an IO error occurs */ public void saveAs(final File file) throws IOException { downloadImageIfNeeded(); if (null != imageWebResponse_) { try (OutputStream fos = Files.newOutputStream(file.toPath()); InputStream inputStream = imageWebResponse_.getContentAsStream()) { IOUtils.copy(inputStream, fos); } } } /** * {@inheritDoc} */ @Override public DisplayStyle getDefaultStyleDisplay() { return DisplayStyle.INLINE; } /** * Wraps the ImageReader for an HtmlImage. This is necessary because an object with a finalize() * method is only garbage collected after the method has been run. Which causes all referenced * objects to also not be garbage collected until this happens. Because a HtmlImage references a lot * of objects which could all be garbage collected without impacting the ImageReader it is better to * wrap it in another class. */ static final class ImageData implements AutoCloseable { private final ImageReader imageReader_; ImageData(final ImageReader imageReader) { imageReader_ = imageReader; } public ImageReader getImageReader() { return imageReader_; } /** * {@inheritDoc} */ @Override protected void finalize() throws Throwable { close(); super.finalize(); } @Override public void close() { if (imageReader_ != null) { try { try (ImageInputStream stream = (ImageInputStream) imageReader_.getInput()) { // nothing } } catch (final IOException e) { LOG.error(e.getMessage(), e); } finally { imageReader_.setInput(null); imageReader_.dispose(); } } } } /** * @return true if the image was successfully downloaded */ public boolean isComplete() { return isComplete_ || (hasFeature(JS_IMAGE_COMPLETE_RETURNS_TRUE_FOR_NO_REQUEST) ? ATTRIBUTE_NOT_DEFINED == getSrcAttribute() : imageData_ != null); } /** * {@inheritDoc} */ @Override public boolean isDisplayed() { final String src = getSrcAttribute(); if (hasFeature(HTMLIMAGE_INVISIBLE_NO_SRC)) { if (ATTRIBUTE_NOT_DEFINED == src) { return false; } if (hasFeature(HTMLIMAGE_BLANK_SRC_AS_EMPTY) && StringUtils.isBlank(src)) { return false; } if (hasFeature(HTMLIMAGE_EMPTY_SRC_DISPLAY_FALSE) && StringUtils.isEmpty(src)) { return false; } } return super.isDisplayed(); } /** * INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.
* * Marks this frame as created by javascript. This is needed to handle * some special IE behavior. */ public void markAsCreatedByJavascript() { createdByJavascript_ = true; } /** * INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.
* * Returns true if this frame was created by javascript. This is needed to handle * some special IE behavior. * @return true or false */ public boolean wasCreatedByJavascript() { return createdByJavascript_; } /** * Returns the original element qualified name, * this is needed to differentiate between img and image. * @return the original element qualified name */ public String getOriginalQualifiedName() { return originalQualifiedName_; } /** * {@inheritDoc} */ @Override public String getLocalName() { if (wasCreatedByJavascript() && (hasFeature(HTMLIMAGE_HTMLELEMENT) || hasFeature(HTMLIMAGE_HTMLUNKNOWNELEMENT))) { return originalQualifiedName_; } return super.getLocalName(); } /** * {@inheritDoc} */ @Override public ScriptResult fireEvent(final Event event) { if (event instanceof MouseEvent) { final MouseEvent mouseEvent = (MouseEvent) event; final HTMLElement scriptableObject = getScriptableObject(); if (lastClickX_ >= 0) { mouseEvent.setClientX(scriptableObject.getPosX() + lastClickX_); } if (lastClickY_ >= 0) { mouseEvent.setClientY(scriptableObject.getPosX() + lastClickY_); } } return super.fireEvent(event); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy