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.HTML_OBJECT_CLASSID;
18  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
19  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
20  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF;
21  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.IE;
22  
23  import java.applet.Applet;
24  import java.lang.reflect.Method;
25  import java.util.Map;
26  
27  import com.gargoylesoftware.htmlunit.WebClient;
28  import com.gargoylesoftware.htmlunit.html.DomNode;
29  import com.gargoylesoftware.htmlunit.html.HtmlForm;
30  import com.gargoylesoftware.htmlunit.html.HtmlObject;
31  import com.gargoylesoftware.htmlunit.javascript.HtmlUnitContextFactory;
32  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
33  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
34  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
35  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxGetter;
36  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxSetter;
37  import com.gargoylesoftware.htmlunit.javascript.host.ActiveXObjectImpl;
38  
39  import net.sourceforge.htmlunit.corejs.javascript.BaseFunction;
40  import net.sourceforge.htmlunit.corejs.javascript.Context;
41  import net.sourceforge.htmlunit.corejs.javascript.Function;
42  import net.sourceforge.htmlunit.corejs.javascript.NativeJavaObject;
43  import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
44  import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;
45  import net.sourceforge.htmlunit.corejs.javascript.Wrapper;
46  
47  /**
48   * The JavaScript object {@code HTMLObjectElement}.
49   *
50   * @author Ahmed Ashour
51   * @author Ronald Brill
52   * @author Frank Danek
53   */
54  @JsxClass(domClass = HtmlObject.class)
55  public class HTMLObjectElement extends HTMLElement implements Wrapper {
56  
57      private Scriptable wrappedActiveX_;
58  
59      /**
60       * Creates an instance.
61       */
62      @JsxConstructor({CHROME, FF, EDGE})
63      public HTMLObjectElement() {
64      }
65  
66      /**
67       * {@inheritDoc}
68       */
69      @Override
70      public void setDomNode(final DomNode domNode) {
71          super.setDomNode(domNode);
72  
73          if (domNode.getPage().getWebClient().getOptions().isAppletEnabled()) {
74              try {
75                  createAppletMethodAndProperties();
76              }
77              catch (final Exception e) {
78                  throw new RuntimeException(e);
79              }
80          }
81      }
82  
83      private void createAppletMethodAndProperties() throws Exception {
84          final HtmlObject appletNode = (HtmlObject) getDomNodeOrDie();
85          final Applet applet = appletNode.getApplet();
86          if (applet == null) {
87              return;
88          }
89  
90          // Rhino should provide the possibility to declare delegate for Functions as it does for properties!!!
91          for (final Method method : applet.getClass().getMethods()) {
92              final Function f = new BaseFunction() {
93                  @Override
94                  public Object call(final Context cx, final Scriptable scope,
95                          final Scriptable thisObj, final Object[] args) {
96  
97                      final Object[] realArgs = new Object[method.getParameterTypes().length];
98                      for (int i = 0; i < realArgs.length; i++) {
99                          final Object arg;
100                         if (i > args.length) {
101                             arg = null;
102                         }
103                         else {
104                             arg = Context.jsToJava(args[i], method.getParameterTypes()[i]);
105                         }
106                         realArgs[i] = arg;
107                     }
108                     try {
109                         return method.invoke(applet, realArgs);
110                     }
111                     catch (final Exception e) {
112                         throw Context.throwAsScriptRuntimeEx(e);
113                     }
114                 }
115             };
116             ScriptableObject.defineProperty(this, method.getName(), f, ScriptableObject.READONLY);
117         }
118     }
119 
120     /**
121      * Returns the value of the {@code alt} property.
122      * @return the value of the {@code alt} property
123      */
124     @JsxGetter(IE)
125     public String getAlt() {
126         final String alt = getDomNodeOrDie().getAttribute("alt");
127         return alt;
128     }
129 
130     /**
131      * Returns the value of the {@code alt} property.
132      * @param alt the value
133      */
134     @JsxSetter(IE)
135     public void setAlt(final String alt) {
136         getDomNodeOrDie().setAttribute("alt", alt);
137     }
138 
139     /**
140      * Gets the {@code border} attribute.
141      * @return the {@code border} attribute
142      */
143     @JsxGetter
144     public String getBorder() {
145         final String border = getDomNodeOrDie().getAttribute("border");
146         return border;
147     }
148 
149     /**
150      * Sets the {@code border} attribute.
151      * @param border the {@code border} attribute
152      */
153     @JsxSetter
154     public void setBorder(final String border) {
155         getDomNodeOrDie().setAttribute("border", border);
156     }
157 
158     /**
159      * Gets the {@code classid} attribute.
160      * @return the {@code classid} attribute
161      */
162     @JsxGetter(IE)
163     public String getClassid() {
164         final String classid = getDomNodeOrDie().getAttribute("classid");
165         return classid;
166     }
167 
168     /**
169      * Sets the {@code classid} attribute.
170      * @param classid the {@code classid} attribute
171      */
172     @JsxSetter(IE)
173     public void setClassid(final String classid) {
174         getDomNodeOrDie().setAttribute("classid", classid);
175         if (classid.indexOf(':') != -1 && getBrowserVersion().hasFeature(HTML_OBJECT_CLASSID)) {
176             final WebClient webClient = getWindow().getWebWindow().getWebClient();
177             final Map<String, String> map = webClient.getActiveXObjectMap();
178             if (map != null) {
179                 final String xClassString = map.get(classid);
180                 if (xClassString != null) {
181                     try {
182                         final Class<?> xClass = Class.forName(xClassString);
183                         final Object object = xClass.newInstance();
184                         boolean contextCreated = false;
185                         if (Context.getCurrentContext() == null) {
186                             new HtmlUnitContextFactory(webClient).enterContext();
187                             contextCreated = true;
188                         }
189                         wrappedActiveX_ = Context.toObject(object, getParentScope());
190                         if (contextCreated) {
191                             Context.exit();
192                         }
193                     }
194                     catch (final Exception e) {
195                         throw Context.reportRuntimeError("ActiveXObject Error: failed instantiating class "
196                                 + xClassString + " because " + e.getMessage() + ".");
197                     }
198                     return;
199                 }
200             }
201             if (webClient.getOptions().isActiveXNative()
202                     && System.getProperty("os.name").contains("Windows")) {
203                 try {
204                     wrappedActiveX_ = new ActiveXObjectImpl(classid);
205                     wrappedActiveX_.setParentScope(getParentScope());
206                 }
207                 catch (final Exception e) {
208                     Context.throwAsScriptRuntimeEx(e);
209                 }
210             }
211         }
212     }
213 
214     /**
215      * {@inheritDoc}
216      */
217     @Override
218     public Object get(final String name, final Scriptable start) {
219         // for java mocks do a bit more, we have handle unknown properties
220         // ourself
221         if (wrappedActiveX_ instanceof NativeJavaObject) {
222             final NativeJavaObject obj = (NativeJavaObject) wrappedActiveX_;
223             final Object result = obj.get(name, start);
224             if (Scriptable.NOT_FOUND != result) {
225                 return result;
226             }
227             return super.get(name, start);
228         }
229 
230         if (wrappedActiveX_ != null) {
231             return wrappedActiveX_.get(name, start);
232         }
233         return super.get(name, start);
234     }
235 
236     /**
237      * {@inheritDoc}
238      */
239     @Override
240     public void put(final String name, final Scriptable start, final Object value) {
241         // for java mocks do a bit more, we have handle unknown properties
242         // ourself
243         if (wrappedActiveX_ instanceof NativeJavaObject) {
244             if (wrappedActiveX_.has(name, start)) {
245                 wrappedActiveX_.put(name, start, value);
246             }
247             else {
248                 super.put(name, start, value);
249             }
250             return;
251         }
252 
253         if (wrappedActiveX_ != null) {
254             wrappedActiveX_.put(name, start, value);
255             return;
256         }
257 
258         super.put(name, start, value);
259     }
260 
261     /**
262      * {@inheritDoc}
263      */
264     @Override
265     public Object unwrap() {
266         if (wrappedActiveX_ instanceof Wrapper) {
267             return ((Wrapper) wrappedActiveX_).unwrap();
268         }
269         return wrappedActiveX_;
270     }
271 
272     /**
273      * Returns the value of the {@code width} property.
274      * @return the value of the {@code width} property
275      */
276     @JsxGetter(propertyName = "width")
277     public String getWidth_js() {
278         return getWidthOrHeight("width", Boolean.TRUE);
279     }
280 
281     /**
282      * Sets the value of the {@code width} property.
283      * @param width the value of the {@code width} property
284      */
285     @JsxSetter
286     public void setWidth(final String width) {
287         setWidthOrHeight("width", width, true);
288     }
289 
290     /**
291      * Returns the value of the {@code height} property.
292      * @return the value of the {@code height} property
293      */
294     @JsxGetter(propertyName = "height")
295     public String getHeight_js() {
296         return getWidthOrHeight("height", Boolean.TRUE);
297     }
298 
299     /**
300      * Sets the value of the {@code height} property.
301      * @param height the value of the {@code height} property
302      */
303     @JsxSetter
304     public void setHeight(final String height) {
305         setWidthOrHeight("height", height, true);
306     }
307 
308     /**
309      * Returns the value of the {@code align} property.
310      * @return the value of the {@code align} property
311      */
312     @JsxGetter
313     public String getAlign() {
314         return getAlign(true);
315     }
316 
317     /**
318      * Sets the value of the {@code align} property.
319      * @param align the value of the {@code align} property
320      */
321     @JsxSetter
322     public void setAlign(final String align) {
323         setAlign(align, false);
324     }
325 
326     /**
327      * Returns the {@code name} attribute.
328      * @return the {@code name} attribute
329      */
330     @JsxGetter
331     public String getName() {
332         return getDomNodeOrDie().getAttribute("name");
333     }
334 
335     /**
336      * Sets the {@code name} attribute.
337      * @param name the {@code name} attribute
338      */
339     @JsxSetter
340     public void setName(final String name) {
341         getDomNodeOrDie().setAttribute("name", name);
342     }
343 
344     /**
345      * Returns the value of the JavaScript {@code form} attribute.
346      *
347      * @return the value of the JavaScript {@code form} attribute
348      */
349     @JsxGetter
350     public HTMLFormElement getForm() {
351         final HtmlForm form = getDomNodeOrDie().getEnclosingForm();
352         if (form == null) {
353             return null;
354         }
355         return (HTMLFormElement) getScriptableFor(form);
356     }
357 
358     /**
359      * Checks whether the element has any constraints and whether it satisfies them.
360      * @return if the element is valid
361      */
362     @JsxFunction
363     public boolean checkValidity() {
364         return getDomNodeOrDie().isValid();
365     }
366 
367 }