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