View Javadoc
1   /*
2    * Copyright (c) 2002-2018 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         return getDomNodeOrDie().getAttributeDirect("alt");
127     }
128 
129     /**
130      * Returns the value of the {@code alt} property.
131      * @param alt the value
132      */
133     @JsxSetter(IE)
134     public void setAlt(final String alt) {
135         getDomNodeOrDie().setAttribute("alt", alt);
136     }
137 
138     /**
139      * Gets the {@code border} attribute.
140      * @return the {@code border} attribute
141      */
142     @JsxGetter
143     public String getBorder() {
144         return getDomNodeOrDie().getAttributeDirect("border");
145     }
146 
147     /**
148      * Sets the {@code border} attribute.
149      * @param border the {@code border} attribute
150      */
151     @JsxSetter
152     public void setBorder(final String border) {
153         getDomNodeOrDie().setAttribute("border", border);
154     }
155 
156     /**
157      * Gets the {@code classid} attribute.
158      * @return the {@code classid} attribute
159      */
160     @JsxGetter(IE)
161     public String getClassid() {
162         return getDomNodeOrDie().getAttributeDirect("classid");
163     }
164 
165     /**
166      * Sets the {@code classid} attribute.
167      * @param classid the {@code classid} attribute
168      */
169     @JsxSetter(IE)
170     public void setClassid(final String classid) {
171         getDomNodeOrDie().setAttribute("classid", classid);
172         if (classid.indexOf(':') != -1 && getBrowserVersion().hasFeature(HTML_OBJECT_CLASSID)) {
173             final WebClient webClient = getWindow().getWebWindow().getWebClient();
174             final Map<String, String> map = webClient.getActiveXObjectMap();
175             if (map != null) {
176                 final String xClassString = map.get(classid);
177                 if (xClassString != null) {
178                     try {
179                         final Class<?> xClass = Class.forName(xClassString);
180                         final Object object = xClass.newInstance();
181                         boolean contextCreated = false;
182                         if (Context.getCurrentContext() == null) {
183                             new HtmlUnitContextFactory(webClient).enterContext();
184                             contextCreated = true;
185                         }
186                         wrappedActiveX_ = Context.toObject(object, getParentScope());
187                         if (contextCreated) {
188                             Context.exit();
189                         }
190                     }
191                     catch (final Exception e) {
192                         throw Context.reportRuntimeError("ActiveXObject Error: failed instantiating class "
193                                 + xClassString + " because " + e.getMessage() + ".");
194                     }
195                     return;
196                 }
197             }
198             if (webClient.getOptions().isActiveXNative()
199                     && System.getProperty("os.name").contains("Windows")) {
200                 try {
201                     wrappedActiveX_ = new ActiveXObjectImpl(classid);
202                     wrappedActiveX_.setParentScope(getParentScope());
203                 }
204                 catch (final Exception e) {
205                     Context.throwAsScriptRuntimeEx(e);
206                 }
207             }
208         }
209     }
210 
211     /**
212      * {@inheritDoc}
213      */
214     @Override
215     public Object get(final String name, final Scriptable start) {
216         // for java mocks do a bit more, we have handle unknown properties
217         // ourself
218         if (wrappedActiveX_ instanceof NativeJavaObject) {
219             final NativeJavaObject obj = (NativeJavaObject) wrappedActiveX_;
220             final Object result = obj.get(name, start);
221             if (Scriptable.NOT_FOUND != result) {
222                 return result;
223             }
224             return super.get(name, start);
225         }
226 
227         if (wrappedActiveX_ != null) {
228             return wrappedActiveX_.get(name, start);
229         }
230         return super.get(name, start);
231     }
232 
233     /**
234      * {@inheritDoc}
235      */
236     @Override
237     public void put(final String name, final Scriptable start, final Object value) {
238         // for java mocks do a bit more, we have handle unknown properties
239         // ourself
240         if (wrappedActiveX_ instanceof NativeJavaObject) {
241             if (wrappedActiveX_.has(name, start)) {
242                 wrappedActiveX_.put(name, start, value);
243             }
244             else {
245                 super.put(name, start, value);
246             }
247             return;
248         }
249 
250         if (wrappedActiveX_ != null) {
251             wrappedActiveX_.put(name, start, value);
252             return;
253         }
254 
255         super.put(name, start, value);
256     }
257 
258     /**
259      * {@inheritDoc}
260      */
261     @Override
262     public Object unwrap() {
263         if (wrappedActiveX_ instanceof Wrapper) {
264             return ((Wrapper) wrappedActiveX_).unwrap();
265         }
266         return wrappedActiveX_;
267     }
268 
269     /**
270      * Returns the value of the {@code width} property.
271      * @return the value of the {@code width} property
272      */
273     @JsxGetter(propertyName = "width")
274     public String getWidth_js() {
275         return getWidthOrHeight("width", Boolean.TRUE);
276     }
277 
278     /**
279      * Sets the value of the {@code width} property.
280      * @param width the value of the {@code width} property
281      */
282     @JsxSetter
283     public void setWidth(final String width) {
284         setWidthOrHeight("width", width, true);
285     }
286 
287     /**
288      * Returns the value of the {@code height} property.
289      * @return the value of the {@code height} property
290      */
291     @JsxGetter(propertyName = "height")
292     public String getHeight_js() {
293         return getWidthOrHeight("height", Boolean.TRUE);
294     }
295 
296     /**
297      * Sets the value of the {@code height} property.
298      * @param height the value of the {@code height} property
299      */
300     @JsxSetter
301     public void setHeight(final String height) {
302         setWidthOrHeight("height", height, true);
303     }
304 
305     /**
306      * Returns the value of the {@code align} property.
307      * @return the value of the {@code align} property
308      */
309     @JsxGetter
310     public String getAlign() {
311         return getAlign(true);
312     }
313 
314     /**
315      * Sets the value of the {@code align} property.
316      * @param align the value of the {@code align} property
317      */
318     @JsxSetter
319     public void setAlign(final String align) {
320         setAlign(align, false);
321     }
322 
323     /**
324      * Returns the {@code name} attribute.
325      * @return the {@code name} attribute
326      */
327     @JsxGetter
328     public String getName() {
329         return getDomNodeOrDie().getAttributeDirect("name");
330     }
331 
332     /**
333      * Sets the {@code name} attribute.
334      * @param name the {@code name} attribute
335      */
336     @JsxSetter
337     public void setName(final String name) {
338         getDomNodeOrDie().setAttribute("name", name);
339     }
340 
341     /**
342      * Returns the value of the JavaScript {@code form} attribute.
343      *
344      * @return the value of the JavaScript {@code form} attribute
345      */
346     @JsxGetter
347     public HTMLFormElement getForm() {
348         final HtmlForm form = getDomNodeOrDie().getEnclosingForm();
349         if (form == null) {
350             return null;
351         }
352         return (HTMLFormElement) getScriptableFor(form);
353     }
354 
355     /**
356      * Checks whether the element has any constraints and whether it satisfies them.
357      * @return if the element is valid
358      */
359     @JsxFunction
360     public boolean checkValidity() {
361         return getDomNodeOrDie().isValid();
362     }
363 
364 }