View Javadoc
1   /*
2    * Copyright (c) 2002-2017 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package com.gargoylesoftware.htmlunit.html;
16  
17  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.HTMLIMAGE_BLANK_SRC_AS_EMPTY;
18  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.HTMLIMAGE_NAME_VALUE_PARAMS;
19  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_IMAGE_COMPLETE_RETURNS_TRUE_FOR_NO_REQUEST;
20  
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.net.URL;
26  import java.util.Map;
27  
28  import org.apache.commons.io.IOUtils;
29  import org.apache.commons.lang3.StringUtils;
30  
31  import com.gargoylesoftware.htmlunit.ElementNotFoundException;
32  import com.gargoylesoftware.htmlunit.HttpHeader;
33  import com.gargoylesoftware.htmlunit.Page;
34  import com.gargoylesoftware.htmlunit.SgmlPage;
35  import com.gargoylesoftware.htmlunit.WebClient;
36  import com.gargoylesoftware.htmlunit.WebRequest;
37  import com.gargoylesoftware.htmlunit.WebResponse;
38  import com.gargoylesoftware.htmlunit.html.HtmlImage.ImageData;
39  import com.gargoylesoftware.htmlunit.javascript.host.event.Event;
40  import com.gargoylesoftware.htmlunit.util.NameValuePair;
41  
42  /**
43   * Wrapper for the HTML element "input".
44   *
45   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
46   * @author David K. Taylor
47   * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
48   * @author Marc Guillemot
49   * @author Daniel Gredler
50   * @author Ahmed Ashour
51   * @author Ronald Brill
52   * @author Frank Danek
53   */
54  public class HtmlImageInput extends HtmlInput {
55  
56      // For click with x, y position.
57      private boolean wasPositionSpecified_;
58      private int xPosition_;
59      private int yPosition_;
60      private WebResponse imageWebResponse_;
61      private transient ImageData imageData_;
62      private boolean downloaded_;
63  
64      /**
65       * Creates an instance.
66       *
67       * @param qualifiedName the qualified name of the element type to instantiate
68       * @param page the page that contains this element
69       * @param attributes the initial attributes
70       */
71      HtmlImageInput(final String qualifiedName, final SgmlPage page, final Map<String, DomAttr> attributes) {
72          super(qualifiedName, page, attributes);
73      }
74  
75      /**
76       * {@inheritDoc}
77       */
78      @Override
79      public NameValuePair[] getSubmitNameValuePairs() {
80          final String name = getNameAttribute();
81          final String prefix;
82          // a clicked image without name sends parameter x and y
83          if (StringUtils.isEmpty(name)) {
84              prefix = "";
85          }
86          else {
87              prefix = name + ".";
88          }
89  
90          if (wasPositionSpecified_) {
91              final NameValuePair valueX = new NameValuePair(prefix + 'x', Integer.toString(xPosition_));
92              final NameValuePair valueY = new NameValuePair(prefix + 'y', Integer.toString(yPosition_));
93              if (!prefix.isEmpty() && hasFeature(HTMLIMAGE_NAME_VALUE_PARAMS) && !getValueAttribute().isEmpty()) {
94                  return new NameValuePair[] {valueX, valueY,
95                      new NameValuePair(getNameAttribute(), getValueAttribute()) };
96              }
97              return new NameValuePair[] {valueX, valueY};
98          }
99          return new NameValuePair[]{new NameValuePair(getNameAttribute(), getValueAttribute())};
100     }
101 
102     /**
103      * Submit the form that contains this input. Only a couple of the inputs
104      * support this method so it is made protected here. Those subclasses
105      * that wish to expose it will override and make it public.
106      *
107      * @return the Page that is the result of submitting this page to the server
108      * @exception IOException If an IO error occurs
109      */
110     @Override
111     @SuppressWarnings("unchecked")
112     public Page click() throws IOException {
113         return click(0, 0);
114     }
115 
116     /**
117      * {@inheritDoc}
118      * @throws IOException if an IO error occurred
119      */
120     @Override
121     protected boolean doClickStateUpdate(final boolean shiftKey, final boolean ctrlKey) throws IOException {
122         final HtmlForm form = getEnclosingForm();
123         if (form != null) {
124             form.submit(this);
125             return false;
126         }
127         super.doClickStateUpdate(shiftKey, ctrlKey);
128         return false;
129     }
130 
131     /**
132      * Simulate clicking this input with a pointing device. The x and y coordinates
133      * of the pointing device will be sent to the server.
134      *
135      * @param <P> the page type
136      * @param x the x coordinate of the pointing device at the time of clicking
137      * @param y the y coordinate of the pointing device at the time of clicking
138      * @return the page that is loaded after the click has taken place
139      * @exception IOException If an IO error occurs
140      * @exception ElementNotFoundException If a particular XML element could not be found in the DOM model
141      */
142     public <P extends Page> P click(final int x, final int y) throws IOException, ElementNotFoundException {
143         wasPositionSpecified_ = true;
144         xPosition_ = x;
145         yPosition_ = y;
146         return super.click();
147     }
148 
149     /**
150      * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
151      *
152      * Simulates clicking on this element, returning the page in the window that has the focus
153      * after the element has been clicked. Note that the returned page may or may not be the same
154      * as the original page, depending on the type of element being clicked, the presence of JavaScript
155      * action listeners, etc.
156      *
157      * @param event the click event used
158      * @param <P> the page type
159      * @return the page contained in the current window as returned by
160      *         {@link com.gargoylesoftware.htmlunit.WebClient#getCurrentWindow()}
161      * @exception IOException if an IO error occurs
162      */
163     @Override
164     public <P extends Page> P click(final Event event, final boolean ignoreVisibility) throws IOException {
165         wasPositionSpecified_ = true;
166         return super.click(event, ignoreVisibility);
167     }
168 
169     /**
170      * {@inheritDoc} Also sets the value to the new default value, just like IE.
171      * @see SubmittableElement#setDefaultValue(String)
172      */
173     @Override
174     public void setDefaultValue(final String defaultValue) {
175         super.setDefaultValue(defaultValue);
176         setValueAttribute(defaultValue);
177     }
178 
179     /**
180      * {@inheritDoc}
181      */
182     @Override
183     protected void setAttributeNS(final String namespaceURI, final String qualifiedName, final String attributeValue,
184             final boolean notifyAttributeChangeListeners, final boolean notifyMutationObservers) {
185         if ("value".equals(qualifiedName)) {
186             setDefaultValue(attributeValue, false);
187         }
188         super.setAttributeNS(namespaceURI, qualifiedName, attributeValue, notifyAttributeChangeListeners,
189                 notifyMutationObservers);
190     }
191 
192     /**
193      * {@inheritDoc}
194      */
195     @Override
196     protected boolean isRequiredSupported() {
197         return false;
198     }
199 
200     /**
201      * <p>Downloads the image contained by this image element.</p>
202      * <p><span style="color:red">POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK</span></p>
203      * <p>If the image has not already been downloaded, this method triggers a download and caches the image.</p>
204      *
205      * @throws IOException if an error occurs while downloading the image
206      */
207     private void downloadImageIfNeeded() throws IOException {
208         if (!downloaded_) {
209             // HTMLIMAGE_BLANK_SRC_AS_EMPTY
210             final String src = getSrcAttribute();
211             if (!"".equals(src)
212                     && !(hasFeature(HTMLIMAGE_BLANK_SRC_AS_EMPTY) && StringUtils.isBlank(src))) {
213                 final HtmlPage page = (HtmlPage) getPage();
214                 final WebClient webclient = page.getWebClient();
215 
216                 final URL url = page.getFullyQualifiedUrl(src);
217                 final String accept = webclient.getBrowserVersion().getImgAcceptHeader();
218                 final WebRequest request = new WebRequest(url, accept);
219                 request.setAdditionalHeader(HttpHeader.REFERER, page.getUrl().toExternalForm());
220                 imageWebResponse_ = webclient.loadWebResponse(request);
221             }
222 
223             if (imageData_ != null) {
224                 imageData_.close();
225                 imageData_ = null;
226             }
227             downloaded_ = hasFeature(JS_IMAGE_COMPLETE_RETURNS_TRUE_FOR_NO_REQUEST)
228                     || (imageWebResponse_ != null && imageWebResponse_.getContentType().contains("image"));
229         }
230     }
231 
232     /**
233      * Saves this image as the specified file.
234      * @param file the file to save to
235      * @throws IOException if an IO error occurs
236      */
237     public void saveAs(final File file) throws IOException {
238         downloadImageIfNeeded();
239         if (null != imageWebResponse_) {
240             try (FileOutputStream fileOut = new FileOutputStream(file);
241                     InputStream inputStream = imageWebResponse_.getContentAsStream()) {
242                 IOUtils.copy(inputStream, fileOut);
243             }
244         }
245     }
246 
247 }