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.host.event;
16  
17  import org.htmlunit.WebDriverTestCase;
18  import org.htmlunit.junit.annotation.Alerts;
19  import org.htmlunit.junit.annotation.BuggyWebDriver;
20  import org.htmlunit.junit.annotation.HtmlUnitNYI;
21  import org.junit.jupiter.api.Test;
22  import org.openqa.selenium.By;
23  import org.openqa.selenium.WebDriver;
24  import org.openqa.selenium.WebElement;
25  
26  /**
27   * Tests that the events triggered in the right order.
28   *
29   * @author Ronald Brill
30   * @author Frank Danek
31   */
32  public class Event2Test extends WebDriverTestCase {
33  
34      /**
35       * Test event order for clicking on a select option.
36       * @throws Exception if the test fails
37       */
38      @Test
39      @Alerts(DEFAULT = "[object Event] change b:true c:false [select] [-]"
40                  + " [object PointerEvent] click b:true c:true [select] [1]",
41              FF = "[object Event] change b:true c:false [select] [-]"
42                  + " [object PointerEvent] click b:true c:true [clickMe] [1]",
43              FF_ESR = "[object Event] change b:true c:false [select] [-]"
44                  + " [object MouseEvent] click b:true c:true [clickMe] [1]")
45      @BuggyWebDriver(CHROME = "[object Event] change b:true c:false [select] [-]"
46                  + " [object MouseEvent] click b:true c:true [select] [1]",
47              EDGE = "[object Event] change b:true c:false [select] [-]"
48                  + " [object MouseEvent] click b:true c:true [select] [1]",
49              FF = "[object Event] change b:true c:true [select] [-]"
50                  + " [object Event] click b:true c:true [select] [-]",
51              FF_ESR = "[object Event] change b:true c:true [select] [-]"
52                  + " [object Event] click b:true c:true [select] [-]")
53      @HtmlUnitNYI(CHROME = "[object Event] change b:true c:false [select] [-]"
54                  + " [object PointerEvent] click b:true c:true [clickMe] [1]",
55              EDGE = "[object Event] change b:true c:false [select] [-]"
56                  + " [object PointerEvent] click b:true c:true [clickMe] [1]")
57      public void optionClick() throws Exception {
58          final String firstSnippet = "       <select name='select' id='select' size='2'\n";
59          final String secondSnippet = ">\n"
60                  + "               <option id='o_id1' value='o_value1'>option1</option>\n"
61                  + "               <option id='clickMe' value='o_value2'>option2</option>\n"
62                  + "               <option id='o_id3' value='o_value3'>option3</option>\n"
63                  + "       </select>\n";
64  
65          testClickEvents(firstSnippet, secondSnippet);
66      }
67  
68      /**
69       * Test event order for clicking on a select option.
70       * @throws Exception if the test fails
71       */
72      @Test
73      @Alerts(DEFAULT = "[object PointerEvent] click b:true c:true [clickMe] [1]",
74              FF_ESR = "[object MouseEvent] click b:true c:true [clickMe] [1]")
75      @BuggyWebDriver(CHROME = "",
76                      EDGE = "",
77                      FF = "",
78                      FF_ESR = "")
79      // ChromeDriver does not generate a "[object MouseEvent] click b:true c:true [clickMe] [1]" but it occurs manually
80      public void optionClick2() throws Exception {
81          final String firstSnippet = "       <select name='select' id='select' size='2'>\n"
82                  + "               <option id='o_id1' value='o_value1'>option1</option>\n"
83                  + "               <option id='clickMe' value='o_value2'\n";
84          final String secondSnippet = ">option2</option>\n"
85                  + "               <option id='o_id3' value='o_value3'>option3</option>\n"
86                  + "       </select>\n";
87  
88          testClickEvents(firstSnippet, secondSnippet);
89      }
90  
91      /**
92       * Test event order for clicking on a radio button.
93       * @throws Exception if the test fails
94       */
95      @Test
96      @Alerts(DEFAULT = "[object PointerEvent] click b:true c:true [radio] [1]"
97                  + " [object Event] change b:true c:false [radio] [-]",
98              FF_ESR = "[object MouseEvent] click b:true c:true [radio] [1]"
99                  + " [object Event] change b:true c:false [radio] [-]")
100     public void radioClick() throws Exception {
101         final String firstSnippet = "       <input type='radio' name='radio' id='clickMe' value='2'\n";
102         final String secondSnippet = ">Radio\n";
103 
104         testClickEvents(firstSnippet, secondSnippet);
105     }
106 
107     /**
108      * Test event order for clicking on a check box.
109      * @throws Exception if the test fails
110      */
111     @Test
112     @Alerts(DEFAULT = "[object PointerEvent] click b:true c:true [checkbox] [1]"
113                 + " [object Event] change b:true c:false [checkbox] [-]",
114             FF_ESR = "[object MouseEvent] click b:true c:true [checkbox] [1]"
115                 + " [object Event] change b:true c:false [checkbox] [-]")
116     public void checkboxClick() throws Exception {
117         final String firstSnippet = "       <input type='checkbox' name='checkbox' id='clickMe' value='2'\n";
118         final String secondSnippet = ">Checkbox\n";
119 
120         testClickEvents(firstSnippet, secondSnippet);
121     }
122 
123     /**
124      * Test event order for clicking on an entry field.
125      * @throws Exception if the test fails
126      */
127     @Test
128     @Alerts(DEFAULT = "[object PointerEvent] click b:true c:true [clickMe] [1]",
129             FF_ESR = "[object MouseEvent] click b:true c:true [clickMe] [1]")
130     public void inputTextClick() throws Exception {
131         final String firstSnippet = "       <input type='text' name='clickMe' id='clickMe' size='2'\n";
132         final String secondSnippet = ">\n";
133 
134         testClickEvents(firstSnippet, secondSnippet);
135     }
136 
137     /**
138      * Test event order for clicking on a password field.
139      * @throws Exception if the test fails
140      */
141     @Test
142     @Alerts(DEFAULT = "[object PointerEvent] click b:true c:true [clickMe] [1]",
143             FF_ESR = "[object MouseEvent] click b:true c:true [clickMe] [1]")
144     public void inputPasswordClick() throws Exception {
145         final String firstSnippet = "       <input type='password' name='clickMe' id='clickMe' size='2'\n";
146         final String secondSnippet = ">\n";
147 
148         testClickEvents(firstSnippet, secondSnippet);
149     }
150 
151     /**
152      * Test event order for clicking on a text area.
153      * @throws Exception if the test fails
154      */
155     @Test
156     @Alerts(DEFAULT = "[object PointerEvent] click b:true c:true [clickMe] [1]",
157             FF_ESR = "[object MouseEvent] click b:true c:true [clickMe] [1]")
158     public void textareaClick() throws Exception {
159         final String firstSnippet = "       <textarea name='clickMe' id='clickMe' size='2'\n";
160         final String secondSnippet = "></textarea>\n";
161 
162         testClickEvents(firstSnippet, secondSnippet);
163     }
164 
165     /**
166      * Test event order for clicking on a submit button.
167      * @throws Exception if the test fails
168      */
169     @Test
170     @Alerts("")
171     public void submitClick() throws Exception {
172         final String firstSnippet = "       <input type='submit' name='clickMe' id='clickMe'\n";
173         final String secondSnippet = ">\n";
174 
175         testClickEvents(firstSnippet, secondSnippet);
176     }
177 
178     /**
179      * Test event order for clicking on a reset button.
180      * @throws Exception if the test fails
181      */
182     @Test
183     @Alerts(DEFAULT = "[object PointerEvent] click b:true c:true [clickMe] [1]",
184             FF_ESR = "[object MouseEvent] click b:true c:true [clickMe] [1]")
185     public void resetClick() throws Exception {
186         final String firstSnippet = "       <input type='reset' name='clickMe' id='clickMe'\n";
187         final String secondSnippet = ">\n";
188 
189         testClickEvents(firstSnippet, secondSnippet);
190     }
191 
192     /**
193      * Test event order for clicking on a reset button.
194      * @throws Exception if the test fails
195      */
196     @Test
197     @Alerts(DEFAULT = "[object PointerEvent] click b:true c:true [clickMe] [1]",
198             FF_ESR = "[object MouseEvent] click b:true c:true [clickMe] [1]")
199     public void buttonClick() throws Exception {
200         final String firstSnippet = "       <input type='button' name='clickMe' id='clickMe'\n";
201         final String secondSnippet = ">\n";
202 
203         testClickEvents(firstSnippet, secondSnippet);
204     }
205 
206     /**
207      * Test event order for clicking on an anchor.
208      * @throws Exception if the test fails
209      */
210     @Test
211     @Alerts(DEFAULT = "[object PointerEvent] click b:true c:true [clickMe] [1]",
212             FF_ESR = "[object MouseEvent] click b:true c:true [clickMe] [1]")
213     public void anchorClick() throws Exception {
214         final String firstSnippet = "       <a href='#' name='clickMe' id='clickMe'\n";
215         final String secondSnippet = ">anchor</a>\n";
216 
217         testClickEvents(firstSnippet, secondSnippet);
218     }
219 
220     private void testClickEvents(final String firstSnippet, final String secondSnippet) throws Exception {
221         final String html = DOCTYPE_HTML
222                 + "<html>\n"
223                 + "<head>\n"
224                 + "  <title></title>\n"
225                 + "  <script type='text/javascript'>\n"
226                 + "  <!--\n"
227                 + "    function dumpEvent(event) {\n"
228                 + "      var msg = event;\n"
229                 + "      msg = msg + ' ' + event.type;\n"
230                 + "      msg = msg + ' b:' + event.bubbles;\n"
231                 + "      msg = msg + ' c:' + event.cancelable;\n"
232                 + "\n"
233                 + "      // target\n"
234                 + "      var eTarget;\n"
235                 + "      if (event.target) {\n"
236                 + "        eTarget = event.target;\n"
237                 + "      } else if (event.srcElement) {\n"
238                 + "        eTarget = event.srcElement;\n"
239                 + "      }\n"
240                 + "      // defeat Safari bug\n"
241                 + "      if (eTarget.nodeType == 3) {\n"
242                 + "        eTarget = eTarget.parentNode;\n"
243                 + "      }\n"
244                 + "\n"
245                 + "      if (eTarget.name) {\n"
246                 + "        msg = msg + ' [' + eTarget.name + ']';\n"
247                 + "      } else {\n"
248                 + "        msg = msg + ' [' + eTarget.id + ']';\n"
249                 + "      }\n"
250                 + "\n"
251                 + "      // key code\n"
252                 + "      var eCode;\n"
253                 + "      if (event.keyCode) {\n"
254                 + "        eCode = event.keyCode;\n"
255                 + "      } else if (event.which) {\n"
256                 + "        eCode = event.which;\n"
257                 + "      } else if (event.button) {\n"
258                 + "        eCode = event.button;\n"
259                 + "      }\n"
260                 + "      if (eCode) {\n"
261                 + "        var char = String.fromCharCode(eCode);\n"
262                 + "        msg = msg + ' [' + eCode + ']';\n"
263                 + "      } else {\n"
264                 + "        msg = msg + ' [-]';\n"
265                 + "      }\n"
266                 + "\n"
267                 + "      document.title += ' ' + msg;\n"
268                 + "    }\n"
269                 + "  //-->\n"
270                 + "  </script>\n"
271                 + "</head>\n"
272                 + "  <form id='form' name='form' action='#'>\n"
273                 + "    <input type='text' id='start' name='startText'/>\n"
274                 + "\n"
275                 + firstSnippet
276                 + "      onclick='dumpEvent(event);'\n"
277 //                + "      ondblclick='dumpEvent(event);'\n"
278 //                + "      oncontextmenu='dumpEvent(event);'\n"
279 //                + "      onfocus='dumpEvent(event);'\n"
280 //                + "      onblur='dumpEvent(event);'\n"
281 //                + "      onmousedown = 'dumpEvent(event);'\n"
282 //                + "      onmouseup = 'dumpEvent(event);'\n"
283 //                + "      onkeydown = 'dumpEvent(event);'\n"
284 //                + "      onkeyup = 'dumpEvent(event);'\n"
285 //                + "      onkeypress = 'dumpEvent(event);'\n"
286 //                + "      onselect = 'dumpEvent(event);'\n"
287                 + "      onchange = 'dumpEvent(event);'"
288                 + secondSnippet
289                 + "   </form>\n"
290                 + "</body>\n"
291                 + "</html>\n";
292 
293         final WebDriver driver = loadPage2(html);
294         driver.findElement(By.id("clickMe")).click();
295 
296         assertTitle(driver, getExpectedAlerts()[0]);
297     }
298 
299     /**
300      * Test event order for typing into an entry field.
301      * @throws Exception if the test fails
302      */
303     @Test
304     @Alerts("[object KeyboardEvent] keydown b:true c:true [typeHere] [65] "
305             + "[object KeyboardEvent] keypress b:true c:true [typeHere] [97] "
306             + "[object KeyboardEvent] keyup b:true c:true [typeHere] [65]")
307     public void inputTextType() throws Exception {
308         final String firstSnippet = "       <input type='text' id='typeHere'\n";
309         final String secondSnippet = "/>\n";
310 
311         testTypeEvents(firstSnippet, secondSnippet);
312     }
313 
314     /**
315      * Test event order for typing into a password field.
316      * @throws Exception if the test fails
317      */
318     @Test
319     @Alerts("[object KeyboardEvent] keydown b:true c:true [typeHere] [65] "
320             + "[object KeyboardEvent] keypress b:true c:true [typeHere] [97] "
321             + "[object KeyboardEvent] keyup b:true c:true [typeHere] [65]")
322     public void inputPasswordType() throws Exception {
323         final String firstSnippet = "       <input type='password' id='typeHere'\n";
324         final String secondSnippet = "/>\n";
325 
326         testTypeEvents(firstSnippet, secondSnippet);
327     }
328 
329     /**
330      * Test event order for typing into a password field.
331      * @throws Exception if the test fails
332      */
333     @Test
334     @Alerts("[object KeyboardEvent] keydown b:true c:true [typeHere] [65] "
335             + "[object KeyboardEvent] keypress b:true c:true [typeHere] [97] "
336             + "[object KeyboardEvent] keyup b:true c:true [typeHere] [65]")
337     public void textAreaType() throws Exception {
338         final String firstSnippet = "       <textarea id='typeHere' rows='4' cols='2'\n";
339         final String secondSnippet = "></textarea >\n";
340 
341         testTypeEvents(firstSnippet, secondSnippet);
342     }
343 
344     private void testTypeEvents(final String firstSnippet, final String secondSnippet) throws Exception {
345         final String html = DOCTYPE_HTML
346                 + "<html>\n"
347                 + "<head>\n"
348                 + "  <script type='text/javascript'>\n"
349                 + "  <!--\n"
350                 + "    function dumpEvent(event) {\n"
351                 + "      var msg = event;\n"
352                 + "      msg = msg + ' ' + event.type;\n"
353                 + "      msg = msg + ' b:' + event.bubbles;\n"
354                 + "      msg = msg + ' c:' + event.cancelable;\n"
355                 + "\n"
356                 + "      // target\n"
357                 + "      var eTarget;\n"
358                 + "      if (event.target) {\n"
359                 + "        eTarget = event.target;\n"
360                 + "      } else if (event.srcElement) {\n"
361                 + "        eTarget = event.srcElement;\n"
362                 + "      }\n"
363                 + "      // defeat Safari bug\n"
364                 + "      if (eTarget.nodeType == 3) {\n"
365                 + "        eTarget = eTarget.parentNode;\n"
366                 + "      }\n"
367                 + "\n"
368                 + "      if (eTarget.name) {\n"
369                 + "        msg = msg + ' [' + eTarget.name + ']';\n"
370                 + "      } else {\n"
371                 + "        msg = msg + ' [' + eTarget.id + ']';\n"
372                 + "      }\n"
373                 + "\n"
374                 + "      // key code\n"
375                 + "      var eCode;\n"
376                 + "      if (event.keyCode) {\n"
377                 + "        eCode = event.keyCode;\n"
378                 + "      } else if (event.which) {\n"
379                 + "        eCode = event.which;\n"
380                 + "      } else if (event.button) {\n"
381                 + "        eCode = event.button;\n"
382                 + "      }\n"
383                 + "      if (eCode) {\n"
384                 + "        var char = String.fromCharCode(eCode);\n"
385                 + "        msg = msg + ' [' + eCode + ']';\n"
386                 + "      } else {\n"
387                 + "        msg = msg + ' [-]';\n"
388                 + "      }\n"
389                 + "\n"
390                 + "      document.title += ' ' + msg;\n"
391                 + "    }\n"
392                 + "  //-->\n"
393                 + "  </script>\n"
394                 + "</head>\n"
395                 + "  <form id='form' name='form' action='#'>\n"
396                 + "    <input type='text' id='start' name='startText'/>\n"
397                 + "\n"
398                 + firstSnippet
399                 + "    onclick='dumpEvent(event);'\n"
400 //                + "    ondblclick='dumpEvent(event);'\n"
401 //                + "    oncontextmenu='dumpEvent(event);'\n"
402 //                + "    onfocus='dumpEvent(event);'\n"
403 //                + "    onblur='dumpEvent(event);'\n"
404 //                + "    onmousedown = 'dumpEvent(event);'\n"
405 //                + "    onmouseup = 'dumpEvent(event);'\n"
406                 + "    onkeydown = 'dumpEvent(event);'\n"
407                 + "    onkeyup = 'dumpEvent(event);'\n"
408                 + "    onkeypress = 'dumpEvent(event);'\n"
409 //                + "    onselect = 'dumpEvent(event);'\n"
410                 + "    onchange = 'dumpEvent(event);'"
411                 + secondSnippet
412                 + "   </form>\n"
413                 + "</body>\n"
414                 + "</html>\n";
415 
416         final WebDriver driver = loadPage2(html);
417         driver.findElement(By.id("typeHere")).sendKeys("a");
418 
419         assertTitle(driver, getExpectedAlerts()[0]);
420     }
421 
422     /**
423      * Tests that event fires on key press.
424      * @throws Exception if the test fails
425      */
426     @Test
427     @Alerts({"pass", "fail:66", "fail:undefined"})
428     public void eventOnKeyDown() throws Exception {
429         final String html = DOCTYPE_HTML
430             + "<html><head>"
431             + "<script>\n"
432             + LOG_TITLE_FUNCTION
433             + "</script>"
434             + "</head>\n"
435             + "<body>\n"
436             + "  <button type='button' id='clickId'>Click Me</button>\n"
437             + "  <script>\n"
438             + "    function handler(_e) {\n"
439             + "      var e = _e ? _e : window.event;\n"
440             + "      if (e.keyCode == 65)\n"
441             + "        log('pass');\n"
442             + "      else\n"
443             + "        log('fail:' + e.keyCode);\n"
444             + "    }\n"
445             + "    document.getElementById('clickId').onkeydown = handler;\n"
446             + "    document.getElementById('clickId').onclick = handler;\n"
447             + "  </script>\n"
448             + "</body></html>";
449 
450         final WebDriver driver = loadPage2(html);
451         final WebElement element = driver.findElement(By.id("clickId"));
452         element.sendKeys("a");
453         verifyTitle2(driver, getExpectedAlerts()[0]);
454 
455         element.sendKeys("b");
456         verifyTitle2(driver, getExpectedAlerts()[0], getExpectedAlerts()[1]);
457 
458         element.click();
459         verifyTitle2(driver, getExpectedAlerts());
460     }
461 
462     /**
463      * Verifies that in IE, the <tt>shiftKey</tt>, <tt>ctrlKey</tt> and <tt>altKey</tt>
464      * event attributes are defined for all events, but <tt>metaKey</tt> is not defined
465      * for any events.
466      * Verifies that in FF, the <tt>shiftKey</tt>, <tt>ctrlKey</tt>, <tt>altKey</tt> and
467      * <tt>metaKey</tt> attributes are defined for mouse events only.
468      * @throws Exception if an error occurs
469      */
470     @Test
471     @Alerts({"object", "undefined", "undefined", "undefined", "undefined",
472              "object", "false", "false", "false", "false"})
473     public void keys() throws Exception {
474         final String html = DOCTYPE_HTML
475             + "<html><body onload='test(event)'><script>\n"
476             + LOG_TITLE_FUNCTION
477             + "  function test(e) {\n"
478             + "    log(typeof e);\n"
479             + "    log(e.shiftKey);\n"
480             + "    log(e.ctrlKey);\n"
481             + "    log(e.altKey);\n"
482             + "    log(e.metaKey);\n"
483             + "  }\n"
484             + "</script>\n"
485             + "<div id='div' onclick='test(event)'>abc</div>\n"
486             + "</body></html>";
487 
488         final String[] alerts = getExpectedAlerts();
489         int i = 0;
490 
491         final WebDriver driver = loadPage2(html);
492         verifyTitle2(driver, alerts[i++], alerts[i++], alerts[i++], alerts[i++], alerts[i++]);
493 
494         final WebElement element = driver.findElement(By.id("div"));
495         element.click();
496         verifyTitle2(driver, alerts);
497     }
498 
499     /**
500      * @throws Exception if an error occurs
501      */
502     @Test
503     public void preventDefault() throws Exception {
504         final String html = DOCTYPE_HTML
505             + "<html><head>\n"
506             + "<script>\n"
507             + "function block(e) {\n"
508             + "  if (e && e.preventDefault)\n"
509             + "    e.preventDefault();\n"
510             + "  else\n"
511             + "    return false;\n"
512             + "}\n"
513             + "\n"
514             + "function test() {\n"
515             + "  document.getElementById('myForm').onsubmit = block;\n"
516             + "}\n"
517             + "</script>\n"
518             + "</head><body onload='test()'>\n"
519             + "<form id='myForm' action='doesnt_exist.html'>\n"
520             + "  <input type='submit' id='mySubmit' value='Continue'></p>\n"
521             + "</form>\n"
522             + "</body></html>";
523 
524         final WebDriver driver = loadPage2(html);
525         driver.findElement(By.id("mySubmit"));
526         assertEquals(URL_FIRST.toExternalForm(), driver.getCurrentUrl());
527     }
528 
529     /**
530      * Test DOMContentLoaded event.
531      * @throws Exception if the test fails
532      */
533     @Test
534     @Alerts({"DOMContentLoaded type=DOMContentLoaded", "onLoad"})
535     public void dOMContentLoaded() throws Exception {
536         final String html = DOCTYPE_HTML
537             + "<html>\n"
538             + "<head>\n"
539             + "<script>\n"
540             + LOG_TITLE_FUNCTION
541             + "  document.addEventListener('DOMContentLoaded', onDCL, false);\n"
542             + "  function onDCL(e) {\n"
543             + "    log('DOMContentLoaded type=' + e.type);\n"
544             + "  }\n"
545             + "</script>\n"
546             + "</head>\n"
547             + "<body onload='log(\"onLoad\")'>\n"
548             + "</body></html>";
549 
550         loadPageVerifyTitle2(html);
551     }
552 
553     /**
554      * @throws Exception if the test fails
555      */
556     @Test
557     @Alerts({"false", "not canceled", "true", "canceled", "true"})
558     public void testPreventDefault() throws Exception {
559         final String html = DOCTYPE_HTML
560             + "<html>\n"
561             + "<head>\n"
562             + "<script>\n"
563             + LOG_TITLE_FUNCTION
564             + "  function preventDef(event) {\n"
565             + "    event.preventDefault();\n"
566             + "  }\n"
567 
568             + "  function addHandler() {\n"
569             + "    document.getElementById('checkbox').addEventListener('click', preventDef, false);\n"
570             + "  }\n"
571 
572             + "  function simulateClick() {\n"
573             + "    var evt = document.createEvent('MouseEvents');\n"
574             + "    evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0,"
575                         + " false, false, false, false, 0, null);\n"
576             + "    var cb = document.getElementById('checkbox');\n"
577             + "    var canceled = !cb.dispatchEvent(evt);\n"
578             + "    if(canceled) {\n"
579             + "      // A handler called preventDefault\n"
580             + "      log('canceled');\n"
581             + "    } else {\n"
582             + "      // None of the handlers called preventDefault\n"
583             + "      log('not canceled');\n"
584             + "    }\n"
585             + "  }\n"
586 
587             + "  function test() {\n"
588             + "    log(document.getElementById('checkbox').checked);\n"
589             + "    simulateClick();\n"
590             + "    log(document.getElementById('checkbox').checked);\n"
591             + "    addHandler();\n"
592             + "    simulateClick();\n"
593             + "    log(document.getElementById('checkbox').checked);\n"
594             + "  }\n"
595             + "</script>\n"
596             + "</head>\n"
597             + "<body onload='test()'>\n"
598             + "  <input type='checkbox' id='checkbox'/><label for='checkbox'>Checkbox</label>\n"
599             + "</body></html>";
600 
601         loadPageVerifyTitle2(html);
602     }
603 
604     /**
605      * Test event transmission to event handler.
606      * @throws Exception if the test fails
607      */
608     @Test
609     @Alerts({"false", "false", "SPAN"})
610     public void eventTransmission() throws Exception {
611         final String html = DOCTYPE_HTML
612             + "<html>\n"
613             + "<body>\n"
614             + "  <span id='clickMe'>foo</span>\n"
615             + "  <script>\n"
616             + LOG_TITLE_FUNCTION
617             + "    function handler(e) {\n"
618             + "      log(e == null);\n"
619             + "      log(window.event == null);\n"
620             + "      var theEvent = (e != null) ? e : window.event;\n"
621             + "      var target = theEvent.target ? theEvent.target : theEvent.srcElement;\n"
622             + "      log(target.tagName);\n"
623             + "    }\n"
624             + "    document.getElementById('clickMe').onclick = handler;\n"
625             + "</script>\n"
626             + "</body></html>";
627 
628         final WebDriver driver = loadPage2(html);
629         driver.findElement(By.id("clickMe")).click();
630 
631         verifyTitle2(driver, getExpectedAlerts());
632     }
633 
634     /**
635      * @throws Exception if an error occurs
636      */
637     @Test
638     @Alerts({"capturing", "at target", "bubbling"})
639     public void eventPhase() throws Exception {
640         final String html = DOCTYPE_HTML
641             + "<html>\n"
642             + "<head><script>\n"
643             + LOG_TITLE_FUNCTION
644             + "  function init() {\n"
645             + "    var form = document.forms[0];\n"
646             + "    form.addEventListener('click', alertPhase, true);\n"
647             + "    form.addEventListener('click', alertPhase, false);\n"
648             + "  }\n"
649 
650             + "  function alertPhase(e) {\n"
651             + "    switch (e.eventPhase) {\n"
652             + "      case 1: log('capturing'); break;\n"
653             + "      case 2: log('at target'); break;\n"
654             + "      case 3: log('bubbling'); break;\n"
655             + "      default: log('unknown');\n"
656             + "    }\n"
657             + "  }\n"
658             + "</script></head>\n"
659             + "<body onload='init()'>\n"
660             + "  <form>\n"
661             + "    <input type='button' onclick='alertPhase(event)' id='b'>\n"
662             + "  </form>\n"
663             + "</body></html>";
664 
665         final WebDriver driver = loadPage2(html);
666         driver.findElement(By.id("b")).click();
667 
668         verifyTitle2(driver, getExpectedAlerts());
669     }
670 
671     /**
672      * Test for event capturing and bubbling in FF.
673      * @throws Exception if the test fails
674      */
675     @Test
676     @Alerts({"window capturing", "div capturing", "span capturing",
677                 "span bubbling", "div", "div bubbling", "window bubbling"})
678     public void eventCapturingAndBubbling() throws Exception {
679         final String html = DOCTYPE_HTML
680             + "<html>\n"
681             + "<head>\n"
682             + "<script>\n"
683             + LOG_TITLE_FUNCTION
684             + "  function t(_s) {\n"
685             + "    return function() { log(_s) };\n"
686             + "  }\n"
687 
688             + "  function init() {\n"
689             + "    window.addEventListener('click', t('window capturing'), true);\n"
690             + "    window.addEventListener('click', t('window bubbling'), false);\n"
691             + "    var oDiv = document.getElementById('theDiv');\n"
692             + "    oDiv.addEventListener('click', t('div capturing'), true);\n"
693             + "    oDiv.addEventListener('click', t('div bubbling'), false);\n"
694             + "    var oSpan = document.getElementById('theSpan');\n"
695             + "    oSpan.addEventListener('click', t('span capturing'), true);\n"
696             + "    oSpan.addEventListener('click', t('span bubbling'), false);\n"
697             + "  }\n"
698             + "</script>\n"
699             + "</head>\n"
700             + "<body onload='init()'>\n"
701             + "  <div onclick=\"log('div')\" id='theDiv'>\n"
702             + "    <span id='theSpan'>blabla</span>\n"
703             + "  </div>\n"
704             + "</body></html>";
705 
706         final WebDriver driver = loadPage2(html);
707         driver.findElement(By.id("theSpan")).click();
708 
709         verifyTitle2(driver, getExpectedAlerts());
710     }
711 
712     /**
713      * Test for event capturing and bubbling.
714      * @throws Exception if the test fails
715      */
716     @Test
717     @Alerts({"window capturing", "div capturing", "span capturing", "div", "window capturing", "false",
718                 "true"})
719     public void stopPropagation() throws Exception {
720         stopPropagation("stopPropagation()");
721     }
722 
723     /**
724      * Test for event capturing and bubbling in FF.
725      * @throws Exception if the test fails
726      */
727     @Test
728     @Alerts({"window capturing", "div capturing", "span capturing", "div", "window capturing", "false", "true"})
729     public void stopPropagationCancelBubble() throws Exception {
730         stopPropagation("cancelBubble=true");
731     }
732 
733     private void stopPropagation(final String cancelMethod) throws Exception {
734         final String html = DOCTYPE_HTML
735             + "<html>\n"
736             + "<head>\n"
737             + "<script>\n"
738             + LOG_TITLE_FUNCTION
739             + "  var counter = 0;\n"
740             + "  function t(_s) {\n"
741             + "    return function(e) {\n"
742             + "      log(_s); counter++;\n"
743             + "      if (counter >= 4) {\n"
744             + "        log(e.cancelBubble);\n"
745             + "        e." + cancelMethod + ";\n"
746             + "        log(e.cancelBubble);\n"
747             + "      }\n"
748             + "    };\n"
749             + "  }\n"
750             + "  function init() {\n"
751             + "    window.addEventListener('click', t('window capturing'), true);\n"
752             + "    var oDiv = document.getElementById('theDiv');\n"
753             + "    oDiv.addEventListener('click', t('div capturing'), true);\n"
754             + "    var oSpan = document.getElementById('theSpan');\n"
755             + "    oSpan.addEventListener('click', t('span capturing'), true);\n"
756             + "  }\n"
757             + "</script>\n"
758             + "</head>\n"
759             + "<body onload='init()'>\n"
760             + "  <div onclick=\"log('div')\" id='theDiv'>\n"
761             + "    <span id='theSpan'>blabla</span>\n"
762             + "  </div>\n"
763             + "</body></html>";
764 
765         final String[] alerts = getExpectedAlerts();
766         int i = 0;
767 
768         final WebDriver driver = loadPage2(html);
769         driver.findElement(By.id("theSpan")).click();
770         verifyTitle2(driver, alerts[i++], alerts[i++], alerts[i++], alerts[i++]);
771 
772         driver.findElement(By.id("theSpan")).click();
773         verifyTitle2(driver, alerts);
774     }
775 
776     /**
777      * @throws Exception if an error occurs
778      */
779     @Test
780     @Alerts({"w", "w 2", "d", "d 2", "s", "s 2", "w", "w 2"})
781     public void stopPropagation_WithMultipleEventHandlers() throws Exception {
782         final String html = DOCTYPE_HTML
783             + "<html>\n"
784             + "<head>\n"
785             + "<script>\n"
786             + LOG_TITLE_FUNCTION
787             + "  var counter = 0;\n"
788             + "  function t(_s) {\n"
789             + "    return function(e) { log(_s); counter++; if (counter >= 5) e.stopPropagation(); };\n"
790             + "  }\n"
791             + "  function init() {\n"
792             + "    window.addEventListener('click', t('w'), true);\n"
793             + "    window.addEventListener('click', t('w 2'), true);\n"
794             + "    var oDiv = document.getElementById('theDiv');\n"
795             + "    oDiv.addEventListener('click', t('d'), true);\n"
796             + "    oDiv.addEventListener('click', t('d 2'), true);\n"
797             + "    var oSpan = document.getElementById('theSpan');\n"
798             + "    oSpan.addEventListener('click', t('s'), true);\n"
799             + "    oSpan.addEventListener('click', t('s 2'), true);\n"
800             + "  }\n"
801             + "</script>\n"
802             + "</head><body onload='init()'>\n"
803             + "<div id='theDiv'>\n"
804             + "<span id='theSpan'>blabla</span>\n"
805             + "</div>\n"
806             + "</body></html>";
807 
808         final String[] alerts = getExpectedAlerts();
809         int i = 0;
810 
811         final WebDriver driver = loadPage2(html);
812         driver.findElement(By.id("theSpan")).click();
813         verifyTitle2(driver, alerts[i++], alerts[i++], alerts[i++], alerts[i++], alerts[i++], alerts[i++]);
814 
815         driver.findElement(By.id("theSpan")).click();
816         verifyTitle2(driver, alerts);
817     }
818 
819     /**
820      * Test for event capturing and bubbling.
821      * @throws Exception if the test fails
822      */
823     @Test
824     @Alerts({"window capturing", "div capturing", "span capturing", "div", "window capturing", "false",
825                 "true"})
826     public void stopImmediatePropagation() throws Exception {
827         stopPropagation("stopImmediatePropagation()");
828     }
829 
830     /**
831      * @throws Exception if an error occurs
832      */
833     @Test
834     @Alerts({"w", "w 2", "d", "d 2", "s", "w"})
835     public void stopImmediatePropagation_WithMultipleEventHandlers() throws Exception {
836         final String html = DOCTYPE_HTML
837             + "<html>\n"
838             + "<head>\n"
839             + "<script>\n"
840             + LOG_TITLE_FUNCTION
841             + "  var counter = 0;\n"
842             + "  function t(_s) {\n"
843             + "    return function(e) { log(_s); counter++; if (counter >= 5) e.stopImmediatePropagation(); };\n"
844             + "  }\n"
845             + "  function init() {\n"
846             + "    window.addEventListener('click', t('w'), true);\n"
847             + "    window.addEventListener('click', t('w 2'), true);\n"
848             + "    var oDiv = document.getElementById('theDiv');\n"
849             + "    oDiv.addEventListener('click', t('d'), true);\n"
850             + "    oDiv.addEventListener('click', t('d 2'), true);\n"
851             + "    var oSpan = document.getElementById('theSpan');\n"
852             + "    oSpan.addEventListener('click', t('s'), true);\n"
853             + "    oSpan.addEventListener('click', t('s 2'), true);\n"
854             + "  }\n"
855             + "</script>\n"
856             + "</head><body onload='init()'>\n"
857             + "<div id='theDiv'>\n"
858             + "<span id='theSpan'>blabla</span>\n"
859             + "</div>\n"
860             + "</body></html>";
861 
862         final String[] alerts = getExpectedAlerts();
863         int i = 0;
864 
865         final WebDriver driver = loadPage2(html);
866         driver.findElement(By.id("theSpan")).click();
867         verifyTitle2(driver, alerts[i++], alerts[i++], alerts[i++], alerts[i++], alerts[i++]);
868 
869         driver.findElement(By.id("theSpan")).click();
870         verifyTitle2(driver, alerts);
871     }
872 
873     /**
874      * Test event order for clicking on a select option.
875      * @throws Exception if the test fails
876      */
877     @Test
878     @Alerts("[object Event]")
879     public void windowEvent() throws Exception {
880         final String html = DOCTYPE_HTML
881                 + "<html>\n"
882                 + "<head>\n"
883                 + "<script>\n"
884                 + LOG_TITLE_FUNCTION
885                 + "  function test() {\n"
886                 + "    log(window.event);\n"
887                 + "  }\n"
888                 + "</script>\n"
889                 + "</head><body onload='test()'>\n"
890                 + "</body></html>";
891 
892         loadPageVerifyTitle2(html);
893     }
894 
895     /**
896      * @throws Exception if an error occurs
897      */
898     @Test
899     @Alerts({"anchor onclick prevented=false",
900                 "document onclick prevented=false",
901                 "window onclick prevented=true"})
902     public void returnPriority() throws Exception {
903         final String html = DOCTYPE_HTML
904                 + "<html><head>\n"
905                 + "<script>\n"
906                 + "  function log(msg) {\n"
907                 + "    window.document.title += msg + ';';\n"
908                 + "  }\n"
909                 + "</script>\n"
910                 + "</head>\n"
911                 + "<body>\n"
912                 + "<a id='tester' href='javascript:log(\"FIRED\")'>test: onclick return value</a>\n"
913                 + "<script>\n"
914                 + "  tester.onclick = function (event) { "
915                                     + "log('anchor onclick prevented=' + event.defaultPrevented); return true }\n"
916                 + "  document.onclick = function (event) { "
917                                     + "log('document onclick prevented=' + event.defaultPrevented); return false }\n"
918                 + "  window.onclick = function (event) { "
919                                     + "log('window onclick prevented=' + event.defaultPrevented); return true; }\n"
920                 + "</script>\n"
921                 + "</body></html>";
922 
923         final WebDriver driver = loadPage2(html);
924         driver.findElement(By.id("tester")).click();
925 
926         final String text = driver.getTitle().trim().replaceAll(";", "\n").trim();
927         assertEquals(String.join("\n", getExpectedAlerts()), text);
928     }
929 
930     /**
931      * BeforeUnloadEvent's returnValue behaves differently to other events.
932      * @throws Exception if an error occurs
933      */
934     @Test
935     @Alerts({"nullwindow at beforeunload rv=",
936              "window onbeforeunload rv=1",
937              "window at beforeunload rv=1"})
938     public void returnPriority2() throws Exception {
939         final String html = DOCTYPE_HTML
940                 + "<html><head>\n"
941                 + "<script>\n"
942                 + "  output = '';\n"
943                 + "  function log(msg) {\n"
944                 + "    var output = localStorage.getItem('output');\n"
945                 + "    output += msg + ';';\n"
946                 + "    localStorage.setItem('output', output);\n"
947                 + "  }\n"
948                 + "  function unload1(event) {\n"
949                 + "    log('window at beforeunload rv=' + event.returnValue);\n"
950                 + "    event.returnValue='1';\n"
951                 + "  }\n"
952                 + "  function unload2(event) {\n"
953                 + "    log('window onbeforeunload rv=' + event.returnValue);\n"
954                 + "    return '2';\n"
955                 + "  }\n"
956                 + "  function unload3(event) {\n"
957                 + "    log('window at beforeunload rv=' + event.returnValue);\n"
958                 + "    event.returnValue='3';\n"
959                 + "  }\n"
960                 + "</script>\n"
961                 + "</head>\n"
962                 + "<body>\n"
963                 + "<button id='tester' onclick='window.location.reload()'>test: onbeforeunload return value</button>\n"
964 
965                 + "<button id='getResult' "
966                             + "onclick='window.removeEventListener(\"beforeunload\", unload1);"
967                              + "window.onbeforeunload = undefined;"
968                             + "window.removeEventListener(\"beforeunload\", unload3);"
969                             + "window.document.title=localStorage.getItem(\"output\")'>get result</button>\n"
970                 + "<script>\n"
971                 + "  window.addEventListener('beforeunload', unload1);\n"
972                 + "  window.onbeforeunload = unload2\n"
973                 + "  window.addEventListener('beforeunload', unload3);\n"
974                 + "</script>\n"
975                 + "</body></html>";
976 
977         final WebDriver driver = loadPage2(html);
978         driver.findElement(By.id("tester")).click();
979         Thread.sleep(200);
980         driver.switchTo().alert().accept();
981         Thread.sleep(200);
982 
983         driver.findElement(By.id("getResult")).click();
984         final String text = driver.getTitle().trim().replaceAll(";", "\n").trim();
985         assertEquals(String.join("\n", getExpectedAlerts()), text);
986     }
987 }