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