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 org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.annotation.Alerts;
19  import org.junit.jupiter.api.Test;
20  
21  /**
22   * Function is a native JavaScript object and therefore provided by Rhino but some tests are needed here
23   * to be sure that we have the expected results.
24   *
25   * @author Marc Guillemot
26   * @author Ahmed Ashour
27   * @author Frank Danek
28   * @author Ronald Brill
29   */
30  public class NativeFunctionTest extends WebDriverTestCase {
31  
32      /**
33       * Test for the methods with the same expectations for all browsers.
34       * @throws Exception if the test fails
35       */
36      @Test
37      @Alerts({"apply: function", "arguments: object", "call: function", "constructor: function",
38               "toString: function"})
39      public void methods_common() throws Exception {
40          final String[] methods = {"apply", "arguments", "call", "constructor", "toString"};
41          final String html = NativeDateTest.createHTMLTestMethods("function() {}", methods);
42          loadPageVerifyTitle2(html);
43      }
44  
45      /**
46       * @throws Exception if the test fails
47       */
48      @Test
49      @Alerts("toSource: undefined")
50      public void methods_toSource() throws Exception {
51          final String html = NativeDateTest.createHTMLTestMethods("function() {}", "toSource");
52          loadPageVerifyTitle2(html);
53      }
54  
55      /**
56       * @throws Exception if the test fails
57       */
58      @Test
59      @Alerts("bind: function")
60      public void methods_bind() throws Exception {
61          final String html = NativeDateTest.createHTMLTestMethods("function() {}", "bind");
62          loadPageVerifyTitle2(html);
63      }
64  
65      /**
66       * Ensure that "arguments" object doesn't see anything from Array's prototype.
67       * This was a bug in Rhino from Head as of 06.01.2010 due to adaptation to ES5 (or to some early state
68       * of the draft).
69       * @throws Exception if the test fails
70       */
71      @Test
72      @Alerts("true")
73      public void arguments_prototype() throws Exception {
74          final String html = DOCTYPE_HTML
75              + "<html><head><script>\n"
76              + LOG_TITLE_FUNCTION
77              + "var f1 = function() {};\n"
78              + "var f2 = function() {};\n"
79              + "Object.prototype.myFunction = f1;\n"
80              + "Array.prototype.myFunction = f2;\n"
81              + "var a = (function() { return arguments;})();\n"
82              + "log(a.myFunction == f1);\n"
83              + "</script></head><body>\n"
84              + "</body></html>";
85  
86          loadPageVerifyTitle2(html);
87      }
88  
89      /**
90       * Regression test for bug 3076362.
91       * @see <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=600479">Rhino Bug 600479</a>
92       * @throws Exception if the test fails
93       */
94      @Test
95      @Alerts("true")
96      public void newFunctionWithSlashSlash() throws Exception {
97          final String html = DOCTYPE_HTML
98              + "<html><head><script>\n"
99              + LOG_TITLE_FUNCTION
100             + "var f1 = new Function('log(true) //');\n"
101             + "f1.call();\n"
102             + "</script></head><body>\n"
103             + "</body></html>";
104 
105         loadPageVerifyTitle2(html);
106     }
107 
108     /**
109      * @throws Exception if the test fails
110      */
111     @Test
112     @Alerts("function\\sanonymous(\\n)\\s{\\n\\s\\s\\s\\svar\\sx\\s=\\s1;\\n}")
113     public void newFunctionToString() throws Exception {
114         final String html = DOCTYPE_HTML
115             + "<html><head><script>\n"
116             + LOG_TITLE_FUNCTION_NORMALIZE
117             + "var f1 = new Function('    var x = 1;');\n"
118             + "log(f1);\n"
119             + "</script></head><body>\n"
120             + "</body></html>";
121 
122         loadPageVerifyTitle2(html);
123     }
124 
125     /**
126      * @throws Exception if the test fails
127      */
128     @Test
129     @Alerts("function\\sanonymous(\\n)\\s{\\n\\n}")
130     public void newEmptyFunctionToString() throws Exception {
131         final String html = DOCTYPE_HTML
132             + "<html><head><script>\n"
133             + LOG_TITLE_FUNCTION_NORMALIZE
134             + "var f1 = new Function();\n"
135             + "log(f1);\n"
136             + "</script></head><body>\n"
137             + "</body></html>";
138 
139         loadPageVerifyTitle2(html);
140     }
141 
142     /**
143      * @throws Exception if the test fails
144      */
145     @Test
146     @Alerts("function\\sfoo()\\s{\\n\\s\\sreturn\\s1;\\n}")
147     public void functionToString() throws Exception {
148         final String html = DOCTYPE_HTML
149             + "<html><head><script>\n"
150             + LOG_TITLE_FUNCTION_NORMALIZE
151             + "function foo() {\n"
152             + "  return 1;\n"
153             + "}\n"
154             + "log(foo);\n"
155             + "</script></head><body>\n"
156             + "</body></html>";
157 
158         loadPageVerifyTitle2(html);
159     }
160 
161     /**
162      * @throws Exception if the test fails
163      */
164     @Test
165     @Alerts({"function\\sfoo(){return\\s1;}",
166              "function\\sfoo(\\s)\\s\\s{\\s\\treturn\\s1\\s\\s\\n\\s;\\n\\s\\s\\s\\s;\\s}" })
167     public void functionToStringMinimized() throws Exception {
168         final String html = DOCTYPE_HTML
169             + "<html>\n"
170             + "<head>\n"
171             + "<script>\n"
172             + LOG_TITLE_FUNCTION_NORMALIZE
173             + "  var my = function foo(){return 1;}\n"
174             + "  log(my.toString());\n"
175 
176             + "  var my = function foo( )  { \treturn 1  \n ;\n"
177             + "    ; }\n"
178             + "  log(my.toString());\n"
179             + "</script></head><body>\n"
180             + "</body></html>";
181 
182         loadPageVerifyTitle2(html);
183     }
184 
185     /**
186      * Function properties "arguments" and "caller" were wrongly enumerated.
187      * @throws Exception if the test fails
188      */
189     @Test
190     @Alerts("foo1 done")
191     public void in() throws Exception {
192         final String html = DOCTYPE_HTML
193             + "<html><body><script>\n"
194             + LOG_TITLE_FUNCTION
195             + "function foo1() {\n"
196             + "  for (var i in foo1) {\n"
197             + "    log(i);\n"
198             + "  }\n"
199             + "  log('foo1 done');\n"
200             + "}\n"
201             + "function foo0() {\n"
202             + "  foo1();\n"
203             + "}\n"
204             + "foo0();\n"
205             + "</script></body></html>";
206 
207         loadPageVerifyTitle2(html);
208     }
209 
210     /**
211      * Function defined in a scope should not overwrite function in top level scope.
212      * @throws Exception if the test fails
213      */
214     @Test
215     @Alerts("true")
216     public void definitionInScope() throws Exception {
217         final String html = DOCTYPE_HTML
218             + "<html><body><script>\n"
219             + LOG_TITLE_FUNCTION
220             + "var $ = function() { return 1; };\n"
221             + "var ori = $;\n"
222             + "function foo() {\n"
223             + "  var $ = function $() { return 2; };\n"
224             + "}\n"
225             + "foo();\n"
226             + "log(ori == $);\n"
227             + "</script></body></html>";
228 
229         loadPageVerifyTitle2(html);
230     }
231 
232     /**
233      * @throws Exception if the test fails
234      */
235     @Test
236     @Alerts({"2", "eat", "bananas"})
237     public void apply() throws Exception {
238         final String html = DOCTYPE_HTML
239             + "<html><head><script>\n"
240             + LOG_TITLE_FUNCTION
241             + "  var myObject = {'length': 2, '0': 'eat', '1': 'bananas'};\n"
242             + "  function test() {\n"
243             + "    test2.apply(null, myObject);\n"
244             + "  }\n"
245             + "\n"
246             + "  function test2() {\n"
247             + "    log(arguments.length);\n"
248             + "    for (var i in arguments) {\n"
249             + "      log(arguments[i]);\n"
250             + "    }\n"
251             + "  }\n"
252             + "</script></head><body onload='test()'>\n"
253             + "</body></html>";
254 
255         loadPageVerifyTitle2(html);
256     }
257 
258     /**
259      * @throws Exception if the test fails
260      */
261     @Test
262     @Alerts({"t: [object Window]", "0", "t: ", "1", "a0: x",
263              "t: ab", "2", "a0: x", "a1: y"})
264     public void bind() throws Exception {
265         final String html = DOCTYPE_HTML
266             + "<html>\n"
267             + "<head>\n"
268             + "  <script>\n"
269             + LOG_TITLE_FUNCTION
270             + "  function bindTest() {\n"
271             + "    log('t: ' + this);\n"
272             + "    log(arguments.length);\n"
273             + "    for (var i in arguments) {\n"
274             + "      log('a' + i + ': ' + arguments[i]);\n"
275             + "    }\n"
276             + "  }\n"
277 
278             + "  function test() {\n"
279             + "    if (!Function.prototype.bind) { log('bind not supported'); return }\n"
280 
281             + "    var foo = bindTest.bind(null);\n"
282             + "    foo();\n"
283 
284             + "    foo = bindTest.bind('', 'x');\n"
285             + "    foo();\n"
286 
287             + "    foo = bindTest.bind('ab', 'x', 'y');\n"
288             + "    foo();\n"
289             + "  }\n"
290             + "  </script>\n"
291             + "</head>\n"
292             + "<body onload='test()'>\n"
293             + "</body></html>";
294 
295         loadPageVerifyTitle2(html);
296     }
297 
298     /**
299      * @throws Exception if the test fails
300      */
301     @Test
302     @Alerts({"t: ab", "1", "a0: x,y"})
303     public void bindArrayParam() throws Exception {
304         final String html = DOCTYPE_HTML
305             + "<html>\n"
306             + "<head>\n"
307             + "  <script>\n"
308             + LOG_TITLE_FUNCTION
309             + "  function bindTest() {\n"
310             + "    log('t: ' + this);\n"
311             + "    log(arguments.length);\n"
312             + "    for (var i in arguments) {\n"
313             + "      log('a' + i + ': ' + arguments[i]);\n"
314             + "    }\n"
315             + "  }\n"
316 
317             + "  function test() {\n"
318             + "    if (!Function.prototype.bind) { log('bind not supported'); return }\n"
319 
320             + "    var foo = bindTest.bind('ab', ['x', 'y']);\n"
321             + "    foo();\n"
322             + "  }\n"
323             + "  </script>\n"
324             + "</head>\n"
325             + "<body onload='test()'>\n"
326             + "</body></html>";
327 
328         loadPageVerifyTitle2(html);
329     }
330 
331     /**
332      * @throws Exception if the test fails
333      */
334     @Test
335     @Alerts("my y var")
336     public void commaOperator() throws Exception {
337         final String html = DOCTYPE_HTML
338             + "<html><head><script>\n"
339             + LOG_TITLE_FUNCTION
340             + "function test() {\n"
341             + "  var obj = {default: eval};\n"
342             + "  (0, obj.default)('var y=\"my y var\"');\n"
343             + "  log(y);\n"
344             + "  }\n"
345             + "</script></head><body onload='test()'>\n"
346             + "</body></html>";
347 
348         loadPageVerifyTitle2(html);
349     }
350 
351     /**
352      * @throws Exception if the test fails
353      */
354     @Test
355     @Alerts("my y var")
356     public void commaOperatorFunction() throws Exception {
357         final String html = DOCTYPE_HTML
358             + "<html><head><script>\n"
359             + LOG_TITLE_FUNCTION
360             + "function setFunction(o) {\n"
361             + "  o.default = eval;\n"
362             + "}\n"
363             + "function test() {\n"
364             + "  var obj = {};\n"
365             + "  setFunction(obj);\n"
366             + "  (0, obj.default)('var y=\"my y var\"');\n"
367             + "  log(y);\n"
368             + "  }\n"
369             + "</script></head><body onload='test()'>\n"
370             + "</body></html>";
371 
372         loadPageVerifyTitle2(html);
373     }
374 
375     /**
376      * @throws Exception if the test fails
377      */
378     @Test
379     @Alerts({"my x var", "my y var"})
380     public void commaOperatorTwice() throws Exception {
381         final String html = DOCTYPE_HTML
382             + "<html><head><script>\n"
383             + LOG_TITLE_FUNCTION
384             + "function test() {\n"
385             + "  var obj = {default: eval};\n"
386             + "  (0, obj.default)('var x=\"my x var\"');\n"
387             + "  log(x);\n"
388             + "  (0, obj.default)('var y=\"my y var\"');\n"
389             + "  log(y);\n"
390             + "  }\n"
391             + "</script></head><body onload='test()'>\n"
392             + "</body></html>";
393 
394         loadPageVerifyTitle2(html);
395     }
396 
397     /**
398      * @throws Exception if the test fails
399      */
400     @Test
401     @Alerts("my y var")
402     public void commaOperatorFunctionTry() throws Exception {
403         final String html = DOCTYPE_HTML
404             + "<html><head><script>\n"
405             + LOG_TITLE_FUNCTION
406             + "function setFunction(o) {\n"
407             + "  o.default = eval;\n"
408             + "}\n"
409             + "function test() {\n"
410             + "  var obj = {};\n"
411             + "  setFunction(obj);\n"
412             + "  try {\n"
413             + "    (0, obj.default)('var y=\"my y var\"');\n"
414             + "    log(y);\n"
415             + "  } catch(e) { logEx(e) }\n"
416             + "  }\n"
417             + "</script></head><body onload='test()'>\n"
418             + "</body></html>";
419 
420         loadPageVerifyTitle2(html);
421     }
422 
423     /**
424      * @throws Exception if the test fails
425      */
426     @Test
427     @Alerts("my y var")
428     public void commaOperatorFunctionCall() throws Exception {
429         final String html = DOCTYPE_HTML
430             + "<html><head><script>\n"
431             + LOG_TITLE_FUNCTION
432             + "function setFunction(o) {\n"
433             + "  o.default = eval;\n"
434             + "}\n"
435             + "function test() {\n"
436             + "  var obj = {};\n"
437             + "  setFunction(obj);\n"
438 
439             + "  function b() {\n"
440             + "    (0, obj.default)('var y=\"my y var\"');\n"
441             + "    log(y);\n"
442             + "  }\n"
443 
444             + "  b();\n"
445             + "}\n"
446             + "</script></head><body onload='test()'>\n"
447             + "</body></html>";
448 
449         loadPageVerifyTitle2(html);
450     }
451 
452     /**
453      * @throws Exception if the test fails
454      */
455     @Test
456     @Alerts("my y var")
457     public void commaOperatorFunctionAnonymous() throws Exception {
458         final String html = DOCTYPE_HTML
459             + "<html><head><script>\n"
460             + LOG_TITLE_FUNCTION
461             + "function setFunction(o) {\n"
462             + "  o.default = eval;\n"
463             + "}\n"
464             + "function test() {\n"
465             + "  var obj = {};\n"
466             + "  setFunction(obj);\n"
467 
468             + "  (function b() {\n"
469             + "    (0, obj.default)('var y=\"my y var\"');\n"
470             + "    log(y);\n"
471             + "  })();\n"
472             + "}\n"
473             + "</script></head><body onload='test()'>\n"
474             + "</body></html>";
475 
476         loadPageVerifyTitle2(html);
477     }
478 
479     /**
480      * @throws Exception if the test fails
481      */
482     @Test
483     @Alerts("a=[object Window]")
484     public void callWithNullContext() throws Exception {
485         final String html = DOCTYPE_HTML
486             + "<html>\n"
487             + "<head>\n"
488             + "<script>\n"
489             + LOG_TITLE_FUNCTION
490             + "  function foo() { return this }\n"
491 
492             + "  function test() {\n"
493             + "    try {\n"
494             + "      var a = foo.call(null);\n"
495             + "      log('a=' + a);\n"
496             + "    } catch(e) { log(e); }\n"
497             + "  }\n"
498             + "</script>\n"
499             + "</head>\n"
500             + "<body onload='test()'>\n"
501             + "</body></html>";
502 
503         loadPageVerifyTitle2(html);
504     }
505 
506     /**
507      * @throws Exception if the test fails
508      */
509     @Test
510     @Alerts("a=[object Window]")
511     public void callWithUndefinedContext() throws Exception {
512         final String html = DOCTYPE_HTML
513             + "<html>\n"
514             + "<head>\n"
515             + "<script>\n"
516             + LOG_TITLE_FUNCTION
517             + "  function foo() { return this }\n"
518 
519             + "  function test() {\n"
520             + "    try {\n"
521             + "      var a = foo.call(undefined);\n"
522             + "      log('a=' + a);\n"
523             + "    } catch(e) { log(e); }\n"
524             + "  }\n"
525             + "</script>\n"
526             + "</head>\n"
527             + "<body onload='test()'>\n"
528             + "</body></html>";
529 
530         loadPageVerifyTitle2(html);
531     }
532 
533     /**
534      * @throws Exception if the test fails
535      */
536     @Test
537     @Alerts({"configurable: true", "enumerable: false", "writable: false"})
538     public void functionLength() throws Exception {
539         final String html = DOCTYPE_HTML
540             + "<html>\n"
541             + "<head>\n"
542             + "<script>\n"
543             + LOG_TITLE_FUNCTION
544             + "  function test() {\n"
545             + "    var desc = Object.getOwnPropertyDescriptor(alert, 'length');\n"
546             + "    if (desc === undefined) { log('no values'); return; }\n"
547             + "    log('configurable: ' + desc.configurable);\n"
548             + "    log('enumerable: ' + desc.enumerable);\n"
549             + "    log('writable: ' + desc.writable);"
550             + "  }\n"
551             + "</script>\n"
552             + "</head>\n"
553             + "<body onload='test()'>\n"
554             + "</body></html>";
555 
556         loadPageVerifyTitle2(html);
557     }
558 }