View Javadoc
1   /*
2    * Copyright (c) 2002-2025 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;
16  
17  import static org.htmlunit.BrowserVersionFeatures.JS_NATIVE_FUNCTION_TOSTRING_COMPACT;
18  import static org.htmlunit.BrowserVersionFeatures.JS_NATIVE_FUNCTION_TOSTRING_NL;
19  
20  import org.htmlunit.BrowserVersion;
21  import org.htmlunit.corejs.javascript.BaseFunction;
22  import org.htmlunit.corejs.javascript.Context;
23  import org.htmlunit.corejs.javascript.Function;
24  import org.htmlunit.corejs.javascript.Scriptable;
25  import org.htmlunit.corejs.javascript.ScriptableObject;
26  
27  /**
28   * Replacement (in fact a wrapper) for Rhino's native toString function on Function prototype
29   * allowing to produce the desired formatting.
30   *
31   * @author Marc Guillemot
32   * @author Ronald Brill
33   * @author Ahmed Ashour
34   */
35  public final class NativeFunctionToStringFunction {
36  
37      /**
38       * Install the wrapper in place of the native toString function on Function's prototype.
39       * @param window the scope
40       * @param browserVersion the simulated browser
41       */
42      public static void installFix(final Scriptable window, final BrowserVersion browserVersion) {
43          if (browserVersion.hasFeature(JS_NATIVE_FUNCTION_TOSTRING_COMPACT)) {
44              final ScriptableObject fnPrototype =
45                      (ScriptableObject) ScriptableObject.getClassPrototype(window, "Function");
46              final Function originalToString = (Function) ScriptableObject.getProperty(fnPrototype, "toString");
47              final Function newToString = new NativeFunctionToStringFunctionChrome(originalToString);
48              ScriptableObject.putProperty(fnPrototype, "toString", newToString);
49          }
50          else if (browserVersion.hasFeature(JS_NATIVE_FUNCTION_TOSTRING_NL)) {
51              final ScriptableObject fnPrototype =
52                      (ScriptableObject) ScriptableObject.getClassPrototype(window, "Function");
53              final Function originalToString = (Function) ScriptableObject.getProperty(fnPrototype, "toString");
54              final Function newToString = new NativeFunctionToStringFunctionFF(originalToString);
55              ScriptableObject.putProperty(fnPrototype, "toString", newToString);
56          }
57      }
58  
59      private NativeFunctionToStringFunction() {
60          super();
61      }
62  
63      static class NativeFunctionToStringFunctionChrome extends FunctionWrapper {
64  
65          NativeFunctionToStringFunctionChrome(final Function wrapped) {
66              super(wrapped);
67          }
68  
69          /**
70           * {@inheritDoc}
71           */
72          @Override
73          public Object call(final Context cx, final Scriptable scope, final Scriptable thisObj, final Object[] args) {
74              final String s = (String) super.call(cx, scope, thisObj, args);
75  
76              if (thisObj instanceof BaseFunction && s.contains("[native code")) {
77                  final String functionName = ((BaseFunction) thisObj).getFunctionName();
78                  return "function " + functionName + "() { [native code] }";
79              }
80              return s.replace("function anonymous() {", "function anonymous(\n) {\n");
81          }
82      }
83  
84      static class NativeFunctionToStringFunctionFF extends FunctionWrapper {
85  
86          NativeFunctionToStringFunctionFF(final Function wrapped) {
87              super(wrapped);
88          }
89  
90          /**
91           * {@inheritDoc}
92           */
93          @Override
94          public Object call(final Context cx, final Scriptable scope, final Scriptable thisObj, final Object[] args) {
95              final String s = (String) super.call(cx, scope, thisObj, args);
96  
97              if (thisObj instanceof BaseFunction && s.contains("[native code")) {
98                  final String functionName = ((BaseFunction) thisObj).getFunctionName();
99                  return "function " + functionName + "() {\n    [native code]\n}";
100             }
101             return s.replace("function anonymous() {", "function anonymous(\n) {\n");
102         }
103     }
104 }