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