PageRenderTime 86ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/htmlunit-2.8/src/main/java/com/gargoylesoftware/htmlunit/html/HtmlImage.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 459 lines | 212 code | 35 blank | 212 comment | 31 complexity | 07b5d1c703ce328e569102176b620216 MD5 | raw file
  1. /*
  2. * Copyright (c) 2002-2010 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. import java.io.File;
  17. import java.io.IOException;
  18. import java.net.URL;
  19. import java.util.Iterator;
  20. import java.util.Map;
  21. import javax.imageio.ImageIO;
  22. import javax.imageio.ImageReader;
  23. import javax.imageio.stream.ImageInputStream;
  24. import org.apache.commons.logging.Log;
  25. import org.apache.commons.logging.LogFactory;
  26. import org.apache.http.HttpStatus;
  27. import com.gargoylesoftware.htmlunit.Page;
  28. import com.gargoylesoftware.htmlunit.SgmlPage;
  29. import com.gargoylesoftware.htmlunit.WebClient;
  30. import com.gargoylesoftware.htmlunit.WebRequest;
  31. import com.gargoylesoftware.htmlunit.WebResponse;
  32. import com.gargoylesoftware.htmlunit.javascript.PostponedAction;
  33. import com.gargoylesoftware.htmlunit.javascript.host.Event;
  34. import com.gargoylesoftware.htmlunit.javascript.host.Node;
  35. /**
  36. * Wrapper for the HTML element "img".
  37. *
  38. * @version $Revision: 5805 $
  39. * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
  40. * @author David K. Taylor
  41. * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
  42. * @author Ahmed Ashour
  43. * @author <a href="mailto:knut.johannes.dahle@gmail.com">Knut Johannes Dahle</a>
  44. */
  45. public class HtmlImage extends HtmlElement {
  46. private static final long serialVersionUID = -2304247017681577696L;
  47. private static final Log LOG = LogFactory.getLog(HtmlImage.class);
  48. /** The HTML tag represented by this element. */
  49. public static final String TAG_NAME = "img";
  50. private int lastClickX_;
  51. private int lastClickY_;
  52. private WebResponse imageWebResponse_;
  53. private ImageReader imageReader_;
  54. private boolean downloaded_;
  55. private boolean onloadInvoked_;
  56. /**
  57. * Creates a new instance.
  58. *
  59. * @param namespaceURI the URI that identifies an XML namespace
  60. * @param qualifiedName the qualified name of the element type to instantiate
  61. * @param page the page that contains this element
  62. * @param attributes the initial attributes
  63. */
  64. HtmlImage(final String namespaceURI, final String qualifiedName, final SgmlPage page,
  65. final Map<String, DomAttr> attributes) {
  66. super(namespaceURI, qualifiedName, page, attributes);
  67. }
  68. /**
  69. * {@inheritDoc}
  70. */
  71. @Override
  72. protected void onAddedToPage() {
  73. doOnLoad();
  74. super.onAddedToPage();
  75. }
  76. /**
  77. * {@inheritDoc}
  78. */
  79. @Override
  80. public void setAttributeNS(final String namespaceURI, final String qualifiedName, final String value) {
  81. if ("src".equals(qualifiedName) && value != ATTRIBUTE_NOT_DEFINED && getPage() instanceof HtmlPage) {
  82. final String oldValue = getAttributeNS(namespaceURI, qualifiedName);
  83. if (!oldValue.equals(value)) {
  84. // onload handlers may need to be invoked again, and a new image may need to be downloaded
  85. onloadInvoked_ = false;
  86. downloaded_ = false;
  87. }
  88. }
  89. super.setAttributeNS(namespaceURI, qualifiedName, value);
  90. }
  91. /**
  92. * <p><span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span></p>
  93. *
  94. * <p>Executes this element's <tt>onload</tt> handler if it has one. This method also downloads the image
  95. * if this element has an <tt>onload</tt> handler (prior to invoking said handler), because applications
  96. * sometimes use images to send information to the server and use the <tt>onload</tt> handler to get notified
  97. * when the information has been received by the server.</p>
  98. *
  99. * <p>See <a href="http://www.nabble.com/How-should-we-handle-image.onload--tt9850876.html">here</a> and
  100. * <a href="http://www.nabble.com/Image-Onload-Support-td18895781.html">here</a> for the discussion which
  101. * lead up to this method.</p>
  102. *
  103. * <p>This method may be called multiple times, but will only attempt to execute the <tt>onload</tt>
  104. * handler the first time it is invoked.</p>
  105. */
  106. public void doOnLoad() {
  107. if (!(getPage() instanceof HtmlPage)) {
  108. return; // nothing to do if embedded in XML code
  109. }
  110. else if (onloadInvoked_) {
  111. return;
  112. }
  113. onloadInvoked_ = true;
  114. final HtmlPage htmlPage = (HtmlPage) getPage();
  115. if (hasEventHandlers("onload")) {
  116. // An onload handler is defined; we need to download the image and then call the onload handler.
  117. boolean ok;
  118. try {
  119. downloadImageIfNeeded();
  120. final int i = imageWebResponse_.getStatusCode();
  121. ok = (i >= HttpStatus.SC_OK && i < HttpStatus.SC_MULTIPLE_CHOICES) || i == HttpStatus.SC_USE_PROXY;
  122. }
  123. catch (final IOException e) {
  124. ok = false;
  125. }
  126. // If the download was a success, trigger the onload handler.
  127. if (ok) {
  128. final Event event = new Event(this, Event.TYPE_LOAD);
  129. final Node scriptObject = (Node) getScriptObject();
  130. final PostponedAction action = new PostponedAction(getPage()) {
  131. @Override
  132. public void execute() throws Exception {
  133. scriptObject.executeEvent(event);
  134. }
  135. };
  136. final String readyState = htmlPage.getReadyState();
  137. if (READY_STATE_LOADING.equals(readyState)) {
  138. htmlPage.addAfterLoadAction(action);
  139. }
  140. else {
  141. htmlPage.getWebClient().getJavaScriptEngine().addPostponedAction(action);
  142. }
  143. }
  144. else {
  145. if (LOG.isDebugEnabled()) {
  146. LOG.debug("Unable to download image for tag " + this + "; not firing onload event.");
  147. }
  148. }
  149. }
  150. }
  151. /**
  152. * Returns the value of the attribute "src". Refer to the
  153. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  154. * documentation for details on the use of this attribute.
  155. *
  156. * @return the value of the attribute "src" or an empty string if that attribute isn't defined
  157. */
  158. public final String getSrcAttribute() {
  159. return getAttribute("src");
  160. }
  161. /**
  162. * Returns the value of the attribute "alt". Refer to the
  163. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  164. * documentation for details on the use of this attribute.
  165. *
  166. * @return the value of the attribute "alt" or an empty string if that attribute isn't defined
  167. */
  168. public final String getAltAttribute() {
  169. return getAttribute("alt");
  170. }
  171. /**
  172. * Returns the value of the attribute "name". Refer to the
  173. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  174. * documentation for details on the use of this attribute.
  175. *
  176. * @return the value of the attribute "name" or an empty string if that attribute isn't defined
  177. */
  178. public final String getNameAttribute() {
  179. return getAttribute("name");
  180. }
  181. /**
  182. * Returns the value of the attribute "longdesc". Refer to the
  183. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  184. * documentation for details on the use of this attribute.
  185. *
  186. * @return the value of the attribute "longdesc" or an empty string if that attribute isn't defined
  187. */
  188. public final String getLongDescAttribute() {
  189. return getAttribute("longdesc");
  190. }
  191. /**
  192. * Returns the value of the attribute "height". Refer to the
  193. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  194. * documentation for details on the use of this attribute.
  195. *
  196. * @return the value of the attribute "height" or an empty string if that attribute isn't defined
  197. */
  198. public final String getHeightAttribute() {
  199. return getAttribute("height");
  200. }
  201. /**
  202. * Returns the value of the attribute "width". Refer to the
  203. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  204. * documentation for details on the use of this attribute.
  205. *
  206. * @return the value of the attribute "width" or an empty string if that attribute isn't defined
  207. */
  208. public final String getWidthAttribute() {
  209. return getAttribute("width");
  210. }
  211. /**
  212. * Returns the value of the attribute "usemap". Refer to the
  213. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  214. * documentation for details on the use of this attribute.
  215. *
  216. * @return the value of the attribute "usemap" or an empty string if that attribute isn't defined
  217. */
  218. public final String getUseMapAttribute() {
  219. return getAttribute("usemap");
  220. }
  221. /**
  222. * Returns the value of the attribute "ismap". Refer to the
  223. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  224. * documentation for details on the use of this attribute.
  225. *
  226. * @return the value of the attribute "ismap" or an empty string if that attribute isn't defined
  227. */
  228. public final String getIsmapAttribute() {
  229. return getAttribute("ismap");
  230. }
  231. /**
  232. * Returns the value of the attribute "align". Refer to the
  233. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  234. * documentation for details on the use of this attribute.
  235. *
  236. * @return the value of the attribute "align" or an empty string if that attribute isn't defined
  237. */
  238. public final String getAlignAttribute() {
  239. return getAttribute("align");
  240. }
  241. /**
  242. * Returns the value of the attribute "border". Refer to the
  243. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  244. * documentation for details on the use of this attribute.
  245. *
  246. * @return the value of the attribute "border" or an empty string if that attribute isn't defined
  247. */
  248. public final String getBorderAttribute() {
  249. return getAttribute("border");
  250. }
  251. /**
  252. * Returns the value of the attribute "hspace". Refer to the
  253. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  254. * documentation for details on the use of this attribute.
  255. *
  256. * @return the value of the attribute "hspace" or an empty string if that attribute isn't defined
  257. */
  258. public final String getHspaceAttribute() {
  259. return getAttribute("hspace");
  260. }
  261. /**
  262. * Returns the value of the attribute "vspace". Refer to the
  263. * <a href='http://www.w3.org/TR/html401/'>HTML 4.01</a>
  264. * documentation for details on the use of this attribute.
  265. *
  266. * @return the value of the attribute "vspace" or an empty string if that attribute isn't defined
  267. */
  268. public final String getVspaceAttribute() {
  269. return getAttribute("vspace");
  270. }
  271. /**
  272. * <p>Returns the image's actual height (<b>not</b> the image's {@link #getHeightAttribute() height attribute}).</p>
  273. * <p><span style="color:red">POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK</span></p>
  274. * <p>If the image has not already been downloaded, this method triggers a download and caches the image.</p>
  275. *
  276. * @return the image's actual height
  277. * @throws IOException if an error occurs while downloading or reading the image
  278. */
  279. public int getHeight() throws IOException {
  280. readImageIfNeeded();
  281. return imageReader_.getHeight(0);
  282. }
  283. /**
  284. * <p>Returns the image's actual width (<b>not</b> the image's {@link #getWidthAttribute() width attribute}).</p>
  285. * <p><span style="color:red">POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK</span></p>
  286. * <p>If the image has not already been downloaded, this method triggers a download and caches the image.</p>
  287. *
  288. * @return the image's actual width
  289. * @throws IOException if an error occurs while downloading or reading the image
  290. */
  291. public int getWidth() throws IOException {
  292. readImageIfNeeded();
  293. return imageReader_.getWidth(0);
  294. }
  295. /**
  296. * <p>Returns the <tt>ImageReader</tt> which can be used to read the image contained by this image element.</p>
  297. * <p><span style="color:red">POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK</span></p>
  298. * <p>If the image has not already been downloaded, this method triggers a download and caches the image.</p>
  299. *
  300. * @return the <tt>ImageReader</tt> which can be used to read the image contained by this image element
  301. * @throws IOException if an error occurs while downloading or reading the image
  302. */
  303. public ImageReader getImageReader() throws IOException {
  304. readImageIfNeeded();
  305. return imageReader_;
  306. }
  307. /**
  308. * <p>Returns the <tt>WebResponse</tt> for the image contained by this image element.</p>
  309. * <p><span style="color:red">POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK</span></p>
  310. * <p>If the image has not already been downloaded and <tt>downloadIfNeeded</tt> is <tt>true</tt>, this method
  311. * triggers a download and caches the image.</p>
  312. *
  313. * @param downloadIfNeeded whether or not the image should be downloaded (if it hasn't already been downloaded)
  314. * @return <tt>null</tt> if no download should be performed and one hasn't already been triggered; otherwise,
  315. * the response received when performing a request for the image referenced by this element
  316. * @throws IOException if an error occurs while downloading the image
  317. */
  318. public WebResponse getWebResponse(final boolean downloadIfNeeded) throws IOException {
  319. if (downloadIfNeeded) {
  320. downloadImageIfNeeded();
  321. }
  322. return imageWebResponse_;
  323. }
  324. /**
  325. * <p>Downloads the image contained by this image element.</p>
  326. * <p><span style="color:red">POTENTIAL PERFORMANCE KILLER - DOWNLOADS THE IMAGE - USE AT YOUR OWN RISK</span></p>
  327. * <p>If the image has not already been downloaded, this method triggers a download and caches the image.</p>
  328. *
  329. * @throws IOException if an error occurs while downloading the image
  330. */
  331. private void downloadImageIfNeeded() throws IOException {
  332. if (!downloaded_) {
  333. final HtmlPage page = (HtmlPage) getPage();
  334. final WebClient webclient = page.getWebClient();
  335. final URL url = page.getFullyQualifiedUrl(getSrcAttribute());
  336. final WebRequest request = new WebRequest(url);
  337. request.setAdditionalHeader("Referer", page.getWebResponse().getWebRequest().getUrl().toExternalForm());
  338. imageWebResponse_ = webclient.loadWebResponse(request);
  339. downloaded_ = true;
  340. }
  341. }
  342. private void readImageIfNeeded() throws IOException {
  343. downloadImageIfNeeded();
  344. if (imageReader_ == null) {
  345. final ImageInputStream iis = ImageIO.createImageInputStream(imageWebResponse_.getContentAsStream());
  346. final Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);
  347. if (!iter.hasNext()) {
  348. throw new IOException("No image detected in response");
  349. }
  350. imageReader_ = iter.next();
  351. imageReader_.setInput(iis);
  352. }
  353. }
  354. /**
  355. * Simulates clicking this element at the specified position. This only makes sense for
  356. * an image map (currently only server side), where the position matters. This method
  357. * returns the page contained by this image's window after the click, which may or may not
  358. * be the same as the original page, depending on JavaScript event handlers, etc.
  359. *
  360. * @param x the x position of the click
  361. * @param y the y position of the click
  362. * @return the page contained by this image's window after the click
  363. * @exception IOException if an IO error occurs
  364. */
  365. public Page click(final int x, final int y) throws IOException {
  366. lastClickX_ = x;
  367. lastClickY_ = y;
  368. return super.click();
  369. }
  370. /**
  371. * Simulates clicking this element at the position <tt>(0, 0)</tt>. This method returns
  372. * the page contained by this image's window after the click, which may or may not be the
  373. * same as the original page, depending on JavaScript event handlers, etc.
  374. *
  375. * @return the page contained by this image's window after the click
  376. * @exception IOException if an IO error occurs
  377. */
  378. @Override
  379. @SuppressWarnings("unchecked")
  380. public Page click() throws IOException {
  381. return click(0, 0);
  382. }
  383. /**
  384. * Performs the click action on the enclosing A tag (if any).
  385. * @throws IOException if an IO error occurred
  386. */
  387. @Override
  388. protected void doClickAction() throws IOException {
  389. if (getUseMapAttribute() != ATTRIBUTE_NOT_DEFINED) {
  390. // remove initial '#'
  391. final String mapName = getUseMapAttribute().substring(1);
  392. final HtmlElement doc = ((HtmlPage) getPage()).getDocumentElement();
  393. final HtmlMap map = doc.getOneHtmlElementByAttribute("map", "name", mapName);
  394. for (final HtmlElement element : map.getChildElements()) {
  395. if (element instanceof HtmlArea) {
  396. final HtmlArea area = (HtmlArea) element;
  397. if (area.containsPoint(lastClickX_, lastClickY_)) {
  398. area.doClickAction();
  399. return;
  400. }
  401. }
  402. }
  403. }
  404. final HtmlAnchor anchor = (HtmlAnchor) getEnclosingElement("a");
  405. if (anchor == null) {
  406. return;
  407. }
  408. if (getIsmapAttribute() != ATTRIBUTE_NOT_DEFINED) {
  409. final String suffix = "?" + lastClickX_ + "," + lastClickY_;
  410. anchor.doClickAction(suffix);
  411. return;
  412. }
  413. anchor.doClickAction();
  414. }
  415. /**
  416. * Saves this image as the specified file.
  417. * @param file the file to save to
  418. * @throws IOException if an IO error occurs
  419. */
  420. public void saveAs(final File file) throws IOException {
  421. final ImageReader reader = getImageReader();
  422. ImageIO.write(reader.read(0), reader.getFormatName(), file);
  423. }
  424. }