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