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.javascript.host.html;
16  
17  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_ALIGN_ACCEPTS_ARBITRARY_VALUES;
18  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_IMAGE_WIDTH_HEIGHT_RETURNS_0x0_0x0;
19  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_IMAGE_WIDTH_HEIGHT_RETURNS_18x20_0x0;
20  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30;
21  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
22  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
23  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF;
24  
25  import java.io.IOException;
26  import java.net.MalformedURLException;
27  import java.util.HashMap;
28  import java.util.Locale;
29  import java.util.Map;
30  
31  import org.apache.commons.lang3.StringUtils;
32  import org.xml.sax.helpers.AttributesImpl;
33  
34  import com.gargoylesoftware.htmlunit.BrowserVersion;
35  import com.gargoylesoftware.htmlunit.SgmlPage;
36  import com.gargoylesoftware.htmlunit.html.DomElement;
37  import com.gargoylesoftware.htmlunit.html.DomNode;
38  import com.gargoylesoftware.htmlunit.html.HTMLParser;
39  import com.gargoylesoftware.htmlunit.html.HtmlElement;
40  import com.gargoylesoftware.htmlunit.html.HtmlImage;
41  import com.gargoylesoftware.htmlunit.html.HtmlPage;
42  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
43  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
44  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxGetter;
45  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxSetter;
46  
47  import net.sourceforge.htmlunit.corejs.javascript.Context;
48  
49  /**
50   * The JavaScript object {@code HTMLImageElement}.
51   *
52   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
53   * @author <a href="mailto:george@murnock.com">George Murnock</a>
54   * @author Chris Erskine
55   * @author Marc Guillemot
56   * @author Ahmed Ashour
57   * @author Ronald Brill
58   */
59  @JsxClass(domClass = HtmlImage.class)
60  public class HTMLImageElement extends HTMLElement {
61      private static final Map<String, String> NORMALIZED_ALIGN_VALUES;
62      static {
63          NORMALIZED_ALIGN_VALUES = new HashMap<>();
64          NORMALIZED_ALIGN_VALUES.put("center", "center");
65          NORMALIZED_ALIGN_VALUES.put("left", "left");
66          NORMALIZED_ALIGN_VALUES.put("right", "right");
67          NORMALIZED_ALIGN_VALUES.put("bottom", "bottom");
68          NORMALIZED_ALIGN_VALUES.put("middle", "middle");
69          NORMALIZED_ALIGN_VALUES.put("top", "top");
70          NORMALIZED_ALIGN_VALUES.put("absbottom", "absBottom");
71          NORMALIZED_ALIGN_VALUES.put("absmiddle", "absMiddle");
72          NORMALIZED_ALIGN_VALUES.put("baseline", "baseline");
73          NORMALIZED_ALIGN_VALUES.put("texttop", "textTop");
74      }
75  
76      private boolean endTagForbidden_ = true;
77  
78      /**
79       * JavaScript constructor.
80       */
81      @JsxConstructor({CHROME, FF, EDGE})
82      public void jsConstructor() {
83          final SgmlPage page = (SgmlPage) getWindow().getWebWindow().getEnclosedPage();
84          final DomElement fake =
85                  HTMLParser.getFactory(HtmlImage.TAG_NAME).createElement(page, HtmlImage.TAG_NAME, new AttributesImpl());
86          setDomNode(fake);
87      }
88  
89      /**
90       * {@inheritDoc}
91       */
92      @Override
93      public void setDomNode(final DomNode domNode) {
94          super.setDomNode(domNode);
95          if ("image".equalsIgnoreCase(domNode.getLocalName())) {
96              endTagForbidden_ = false;
97          }
98      }
99  
100     /**
101      * Sets the {@code src} attribute.
102      * @param src the {@code src} attribute value
103      */
104     @JsxSetter
105     public void setSrc(final String src) {
106         final HtmlElement img = getDomNodeOrDie();
107         img.setAttribute("src", src);
108     }
109 
110     /**
111      * Returns the value of the {@code src} attribute.
112      * @return the value of the {@code src} attribute
113      */
114     @JsxGetter
115     public String getSrc() {
116         final HtmlImage img = (HtmlImage) getDomNodeOrDie();
117         final String src = img.getSrcAttribute();
118         if ("".equals(src)) {
119             return src;
120         }
121         try {
122             final HtmlPage page = (HtmlPage) img.getPage();
123             return page.getFullyQualifiedUrl(src).toExternalForm();
124         }
125         catch (final MalformedURLException e) {
126             final String msg = "Unable to create fully qualified URL for src attribute of image " + e.getMessage();
127             throw Context.reportRuntimeError(msg);
128         }
129     }
130 
131     /**
132      * Sets the {@code onload} event handler for this element.
133      * @param onload the {@code onload} event handler for this element
134      */
135     @Override
136     public void setOnload(final Object onload) {
137         super.setOnload(onload);
138 
139         // maybe the onload handler was not called so far
140         final HtmlImage img = (HtmlImage) getDomNodeOrDie();
141         img.doOnLoad();
142     }
143 
144     /**
145      * Returns the value of the {@code alt} property.
146      * @return the value of the {@code alt} property
147      */
148     @JsxGetter
149     public String getAlt() {
150         final String alt = getDomNodeOrDie().getAttribute("alt");
151         return alt;
152     }
153 
154     /**
155      * Sets the value of the {@code alt} property.
156      * @param alt the value
157      */
158     @JsxSetter
159     public void setAlt(final String alt) {
160         getDomNodeOrDie().setAttribute("alt", alt);
161     }
162 
163     /**
164      * Gets the {@code border} attribute.
165      * @return the {@code border} attribute
166      */
167     @JsxGetter
168     public String getBorder() {
169         final String border = getDomNodeOrDie().getAttribute("border");
170         return border;
171     }
172 
173     /**
174      * Sets the {@code border} attribute.
175      * @param border the {@code border} attribute
176      */
177     @JsxSetter
178     public void setBorder(final String border) {
179         getDomNodeOrDie().setAttribute("border", border);
180     }
181 
182     /**
183      * Returns the value of the {@code align} property.
184      * @return the value of the {@code align} property
185      */
186     @JsxGetter
187     public String getAlign() {
188         final boolean acceptArbitraryValues = getBrowserVersion().hasFeature(JS_ALIGN_ACCEPTS_ARBITRARY_VALUES);
189 
190         final String align = getDomNodeOrDie().getAttribute("align");
191         if (acceptArbitraryValues) {
192             return align;
193         }
194 
195         final String normalizedValue = NORMALIZED_ALIGN_VALUES.get(align.toLowerCase(Locale.ROOT));
196         if (null != normalizedValue) {
197             return normalizedValue;
198         }
199         return "";
200     }
201 
202     /**
203      * Sets the value of the {@code align} property.
204      * @param align the value of the {@code align} property
205      */
206     @JsxSetter
207     public void setAlign(final String align) {
208         final boolean acceptArbitraryValues = getBrowserVersion().hasFeature(JS_ALIGN_ACCEPTS_ARBITRARY_VALUES);
209         if (acceptArbitraryValues) {
210             getDomNodeOrDie().setAttribute("align", align);
211             return;
212         }
213 
214         final String normalizedValue = NORMALIZED_ALIGN_VALUES.get(align.toLowerCase(Locale.ROOT));
215         if (null != normalizedValue) {
216             getDomNodeOrDie().setAttribute("align", normalizedValue);
217             return;
218         }
219 
220         throw Context.reportRuntimeError("Cannot set the align property to invalid value: '" + align + "'");
221     }
222 
223     /**
224      * Returns the value of the {@code width} property.
225      * @return the value of the {@code width} property
226      */
227     @Override
228     @JsxGetter
229     public int getWidth() {
230         final HtmlImage img = (HtmlImage) getDomNodeOrDie();
231         final String widthAttrib = img.getWidthAttribute();
232 
233         if (DomElement.ATTRIBUTE_NOT_DEFINED != widthAttrib) {
234             try {
235                 return Integer.parseInt(widthAttrib);
236             }
237             catch (final NumberFormatException e) {
238                 // ignore
239             }
240         }
241 
242         final String src = img.getSrcAttribute();
243         if (DomElement.ATTRIBUTE_NOT_DEFINED != src) {
244             try {
245                 return img.getWidth();
246             }
247             catch (final IOException e) {
248                 final BrowserVersion browserVersion = getBrowserVersion();
249                 if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30)) {
250                     return 28;
251                 }
252                 if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_18x20_0x0)) {
253                     if (StringUtils.isBlank(src)) {
254                         return 0;
255                     }
256                     return 20;
257                 }
258                 return 24;
259             }
260         }
261 
262         final BrowserVersion browserVersion = getBrowserVersion();
263         if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30)) {
264             return 28;
265         }
266         if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_18x20_0x0)
267                 || browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_0x0_0x0)) {
268             return 0;
269         }
270         return 24;
271     }
272 
273     /**
274      * Sets the value of the {@code width} property.
275      * @param width the value of the {@code width} property
276      */
277     @JsxSetter
278     public void setWidth(final String width) {
279         getDomNodeOrDie().setAttribute("width", width);
280     }
281 
282     /**
283      * Returns the value of the {@code height} property.
284      * @return the value of the {@code height} property
285      */
286     @Override
287     @JsxGetter
288     public int getHeight() {
289         final HtmlImage img = (HtmlImage) getDomNodeOrDie();
290         final String height = img.getHeightAttribute();
291 
292         if (DomElement.ATTRIBUTE_NOT_DEFINED != height) {
293             try {
294                 return Integer.parseInt(height);
295             }
296             catch (final NumberFormatException e) {
297                 // ignore
298             }
299         }
300 
301         final String src = img.getSrcAttribute();
302         if (DomElement.ATTRIBUTE_NOT_DEFINED != src) {
303             try {
304                 return img.getHeight();
305             }
306             catch (final IOException e) {
307                 final BrowserVersion browserVersion = getBrowserVersion();
308                 if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30)) {
309                     return 30;
310                 }
311                 if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_18x20_0x0)) {
312                     if (StringUtils.isBlank(src)) {
313                         return 0;
314                     }
315                     return 20;
316                 }
317                 return 24;
318             }
319         }
320 
321         final BrowserVersion browserVersion = getBrowserVersion();
322         if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_28x30_28x30)) {
323             return 30;
324         }
325         if (browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_18x20_0x0)
326                 || browserVersion.hasFeature(JS_IMAGE_WIDTH_HEIGHT_RETURNS_0x0_0x0)) {
327             return 0;
328         }
329         return 24;
330     }
331 
332     /**
333      * Sets the value of the {@code height} property.
334      * @param height the value of the {@code height} property
335      */
336     @JsxSetter
337     public void setHeight(final String height) {
338         getDomNodeOrDie().setAttribute("height", height);
339     }
340 
341     /**
342      * {@inheritDoc}
343      */
344     @Override
345     protected boolean isEndTagForbidden() {
346         return endTagForbidden_;
347     }
348 
349     /**
350      * Support for the image.complete property.
351      * @return the value of the {@code complete} property
352      */
353     @JsxGetter
354     public boolean isComplete() {
355         return ((HtmlImage) getDomNodeOrDie()).isComplete();
356     }
357 
358     /**
359      * Returns the value of the {@code naturalWidth} property.
360      * @return the value of the {@code naturalWidth} property
361      */
362     @JsxGetter
363     public int getNaturalWidth() {
364         final HtmlImage img = (HtmlImage) getDomNodeOrDie();
365         try {
366             return img.getWidth();
367         }
368         catch (final IOException e) {
369             return 0;
370         }
371     }
372 
373     /**
374      * Returns the value of the {@code naturalHeight} property.
375      * @return the value of the {@code naturalHeight} property
376      */
377     @JsxGetter
378     public int getNaturalHeight() {
379         final HtmlImage img = (HtmlImage) getDomNodeOrDie();
380         try {
381             return img.getHeight();
382         }
383         catch (final IOException e) {
384             return 0;
385         }
386     }
387 
388     /**
389      * Returns the {@code name} attribute.
390      * @return the {@code name} attribute
391      */
392     @JsxGetter
393     public String getName() {
394         return getDomNodeOrDie().getAttribute("name");
395     }
396 
397     /**
398      * Sets the {@code name} attribute.
399      * @param name the {@code name} attribute
400      */
401     @JsxSetter
402     public void setName(final String name) {
403         getDomNodeOrDie().setAttribute("name", name);
404     }
405 
406 }