View Javadoc
1   /*
2    * Copyright (c) 2002-2026 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    * https://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 org.htmlunit.javascript.host.event;
16  
17  import org.htmlunit.corejs.javascript.BaseFunction;
18  import org.htmlunit.corejs.javascript.Context;
19  import org.htmlunit.corejs.javascript.Function;
20  import org.htmlunit.corejs.javascript.JavaScriptException;
21  import org.htmlunit.corejs.javascript.Scriptable;
22  import org.htmlunit.corejs.javascript.VarScope;
23  import org.htmlunit.html.DomNode;
24  
25  /**
26   * Wraps event handler code as a {@link Function} object.
27   *
28   * @author Marc Guillemot
29   * @author Ronald Brill
30   */
31  public class EventHandler extends BaseFunction {
32      private final DomNode node_;
33      private final String eventName_;
34      private final String jsSnippet_;
35      private Function realFunction_;
36  
37      /**
38       * Builds a function that will execute the given JavaScript code.
39       *
40       * @param scope the scope
41       * @param node the element for which the event is built
42       * @param eventName the event name for which this handler is created
43       * @param jsSnippet the JavaScript code snippet to execute
44       */
45      public EventHandler(final VarScope scope, final DomNode node, final String eventName, final String jsSnippet) {
46          super(scope);
47          node_ = node;
48          eventName_ = eventName;
49          jsSnippet_ = jsSnippet;
50      }
51  
52      /**
53       * {@inheritDoc}
54       */
55      @Override
56      public Object call(final Context cx, final Object nt, final VarScope scope,
57          final Object thisObj, final Object[] args)
58          throws JavaScriptException {
59  
60          // the js object to which this event is attached has to be the scope
61          // final HtmlUnitScriptable jsObj = node_.getScriptableObject();
62          // have changed this - the scope is now thisObj to fix
63          // https://github.com/HtmlUnit/htmlunit/issues/347
64          // but still have not found any description about the right scope
65  
66          // compile "just in time"
67          if (realFunction_ == null) {
68              final String js = "function on" + eventName_ + "(event) { " + jsSnippet_ + " \n}";
69              realFunction_ = cx.compileFunction(scope, js, eventName_ + " event for " + node_
70                                      + " in " + node_.getPage().getUrl(), 0, null);
71              realFunction_.setParentScope(scope);
72          }
73  
74          return realFunction_.call(cx, scope, thisObj, args);
75      }
76  
77      /**
78       * Returns the JavaScript source representation of this function declaration.
79       *
80       * @param typeHint the type hint
81       * @return the JavaScript source of the function declaration
82       * @see org.htmlunit.corejs.javascript.ScriptableObject#getDefaultValue(java.lang.Class)
83       */
84      @Override
85      public Object getDefaultValue(final Class<?> typeHint) {
86          return "function on" + eventName_ + "(event) { " + jsSnippet_ + " }";
87      }
88  
89      /**
90       * {@inheritDoc}
91       */
92      @Override
93      public Object get(final String name, final Scriptable start) {
94          // quick and dirty
95          if ("toString".equals(name)) {
96              return new BaseFunction(getParentScope()) {
97                  @Override
98                  public Object call(final Context cx, final Object nt, final VarScope scope,
99                          final Object thisObj, final Object[] args) {
100                     return "function on" + eventName_ + "(event) { " + jsSnippet_ + " }";
101                 }
102             };
103         }
104 
105         return super.get(name, start);
106     }
107 }