11cb0ef41Sopenharmony_ci<!doctype html> 21cb0ef41Sopenharmony_ci<meta charset="utf8"> 31cb0ef41Sopenharmony_ci<meta name="timeout" content="long"> 41cb0ef41Sopenharmony_ci<title>Events must dispatch on disabled elements</title> 51cb0ef41Sopenharmony_ci<script src="/resources/testharness.js"></script> 61cb0ef41Sopenharmony_ci<script src="/resources/testharnessreport.js"></script> 71cb0ef41Sopenharmony_ci<script src="/resources/testdriver.js"></script> 81cb0ef41Sopenharmony_ci<script src="/resources/testdriver-vendor.js"></script> 91cb0ef41Sopenharmony_ci<style> 101cb0ef41Sopenharmony_ci @keyframes fade { 111cb0ef41Sopenharmony_ci 0% { 121cb0ef41Sopenharmony_ci opacity: 1; 131cb0ef41Sopenharmony_ci } 141cb0ef41Sopenharmony_ci 100% { 151cb0ef41Sopenharmony_ci opacity: 0; 161cb0ef41Sopenharmony_ci } 171cb0ef41Sopenharmony_ci } 181cb0ef41Sopenharmony_ci</style> 191cb0ef41Sopenharmony_ci<body> 201cb0ef41Sopenharmony_ci<script> 211cb0ef41Sopenharmony_ci// HTML elements that can be disabled 221cb0ef41Sopenharmony_ciconst formElements = ["button", "fieldset", "input", "select", "textarea"]; 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_citest(() => { 251cb0ef41Sopenharmony_ci for (const localName of formElements) { 261cb0ef41Sopenharmony_ci const elem = document.createElement(localName); 271cb0ef41Sopenharmony_ci elem.disabled = true; 281cb0ef41Sopenharmony_ci // pass becomes true if the event is called and it's the right type. 291cb0ef41Sopenharmony_ci let pass = false; 301cb0ef41Sopenharmony_ci const listener = ({ type }) => { 311cb0ef41Sopenharmony_ci pass = type === "click"; 321cb0ef41Sopenharmony_ci }; 331cb0ef41Sopenharmony_ci elem.addEventListener("click", listener, { once: true }); 341cb0ef41Sopenharmony_ci elem.dispatchEvent(new Event("click")); 351cb0ef41Sopenharmony_ci assert_true( 361cb0ef41Sopenharmony_ci pass, 371cb0ef41Sopenharmony_ci `Untrusted "click" Event didn't dispatch on ${elem.constructor.name}.` 381cb0ef41Sopenharmony_ci ); 391cb0ef41Sopenharmony_ci } 401cb0ef41Sopenharmony_ci}, "Can dispatch untrusted 'click' Events at disabled HTML elements."); 411cb0ef41Sopenharmony_ci 421cb0ef41Sopenharmony_citest(() => { 431cb0ef41Sopenharmony_ci for (const localName of formElements) { 441cb0ef41Sopenharmony_ci const elem = document.createElement(localName); 451cb0ef41Sopenharmony_ci elem.disabled = true; 461cb0ef41Sopenharmony_ci // pass becomes true if the event is called and it's the right type. 471cb0ef41Sopenharmony_ci let pass = false; 481cb0ef41Sopenharmony_ci const listener = ({ type }) => { 491cb0ef41Sopenharmony_ci pass = type === "pass"; 501cb0ef41Sopenharmony_ci }; 511cb0ef41Sopenharmony_ci elem.addEventListener("pass", listener, { once: true }); 521cb0ef41Sopenharmony_ci elem.dispatchEvent(new Event("pass")); 531cb0ef41Sopenharmony_ci assert_true( 541cb0ef41Sopenharmony_ci pass, 551cb0ef41Sopenharmony_ci `Untrusted "pass" Event didn't dispatch on ${elem.constructor.name}` 561cb0ef41Sopenharmony_ci ); 571cb0ef41Sopenharmony_ci } 581cb0ef41Sopenharmony_ci}, "Can dispatch untrusted Events at disabled HTML elements."); 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_citest(() => { 611cb0ef41Sopenharmony_ci for (const localName of formElements) { 621cb0ef41Sopenharmony_ci const elem = document.createElement(localName); 631cb0ef41Sopenharmony_ci elem.disabled = true; 641cb0ef41Sopenharmony_ci // pass becomes true if the event is called and it's the right type. 651cb0ef41Sopenharmony_ci let pass = false; 661cb0ef41Sopenharmony_ci const listener = ({ type }) => { 671cb0ef41Sopenharmony_ci pass = type === "custom-pass"; 681cb0ef41Sopenharmony_ci }; 691cb0ef41Sopenharmony_ci elem.addEventListener("custom-pass", listener, { once: true }); 701cb0ef41Sopenharmony_ci elem.dispatchEvent(new CustomEvent("custom-pass")); 711cb0ef41Sopenharmony_ci assert_true( 721cb0ef41Sopenharmony_ci pass, 731cb0ef41Sopenharmony_ci `CustomEvent "custom-pass" didn't dispatch on ${elem.constructor.name}` 741cb0ef41Sopenharmony_ci ); 751cb0ef41Sopenharmony_ci } 761cb0ef41Sopenharmony_ci}, "Can dispatch CustomEvents at disabled HTML elements."); 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_citest(() => { 791cb0ef41Sopenharmony_ci for (const localName of formElements) { 801cb0ef41Sopenharmony_ci const elem = document.createElement(localName); 811cb0ef41Sopenharmony_ci 821cb0ef41Sopenharmony_ci // Element is disabled... so this click() MUST NOT fire an event. 831cb0ef41Sopenharmony_ci elem.disabled = true; 841cb0ef41Sopenharmony_ci let pass = true; 851cb0ef41Sopenharmony_ci elem.onclick = e => { 861cb0ef41Sopenharmony_ci pass = false; 871cb0ef41Sopenharmony_ci }; 881cb0ef41Sopenharmony_ci elem.click(); 891cb0ef41Sopenharmony_ci assert_true( 901cb0ef41Sopenharmony_ci pass, 911cb0ef41Sopenharmony_ci `.click() must not dispatch "click" event on disabled ${ 921cb0ef41Sopenharmony_ci elem.constructor.name 931cb0ef41Sopenharmony_ci }.` 941cb0ef41Sopenharmony_ci ); 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci // Element is (re)enabled... so this click() fires an event. 971cb0ef41Sopenharmony_ci elem.disabled = false; 981cb0ef41Sopenharmony_ci pass = false; 991cb0ef41Sopenharmony_ci elem.onclick = e => { 1001cb0ef41Sopenharmony_ci pass = true; 1011cb0ef41Sopenharmony_ci }; 1021cb0ef41Sopenharmony_ci elem.click(); 1031cb0ef41Sopenharmony_ci assert_true( 1041cb0ef41Sopenharmony_ci pass, 1051cb0ef41Sopenharmony_ci `.click() must dispatch "click" event on enabled ${ 1061cb0ef41Sopenharmony_ci elem.constructor.name 1071cb0ef41Sopenharmony_ci }.` 1081cb0ef41Sopenharmony_ci ); 1091cb0ef41Sopenharmony_ci } 1101cb0ef41Sopenharmony_ci}, "Calling click() on disabled elements must not dispatch events."); 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_cipromise_test(async () => { 1131cb0ef41Sopenharmony_ci // For each form element type, set up transition event handlers. 1141cb0ef41Sopenharmony_ci for (const localName of formElements) { 1151cb0ef41Sopenharmony_ci const elem = document.createElement(localName); 1161cb0ef41Sopenharmony_ci elem.disabled = true; 1171cb0ef41Sopenharmony_ci document.body.appendChild(elem); 1181cb0ef41Sopenharmony_ci const eventPromises = [ 1191cb0ef41Sopenharmony_ci "transitionrun", 1201cb0ef41Sopenharmony_ci "transitionstart", 1211cb0ef41Sopenharmony_ci "transitionend", 1221cb0ef41Sopenharmony_ci ].map(eventType => { 1231cb0ef41Sopenharmony_ci return new Promise(r => { 1241cb0ef41Sopenharmony_ci elem.addEventListener(eventType, r); 1251cb0ef41Sopenharmony_ci }); 1261cb0ef41Sopenharmony_ci }); 1271cb0ef41Sopenharmony_ci // Flushing style triggers transition. 1281cb0ef41Sopenharmony_ci getComputedStyle(elem).opacity; 1291cb0ef41Sopenharmony_ci elem.style.transition = "opacity .1s"; 1301cb0ef41Sopenharmony_ci elem.style.opacity = 0; 1311cb0ef41Sopenharmony_ci getComputedStyle(elem).opacity; 1321cb0ef41Sopenharmony_ci // All the events fire... 1331cb0ef41Sopenharmony_ci await Promise.all(eventPromises); 1341cb0ef41Sopenharmony_ci elem.remove(); 1351cb0ef41Sopenharmony_ci } 1361cb0ef41Sopenharmony_ci}, "CSS Transitions transitionrun, transitionstart, transitionend events fire on disabled form elements"); 1371cb0ef41Sopenharmony_ci 1381cb0ef41Sopenharmony_cipromise_test(async () => { 1391cb0ef41Sopenharmony_ci // For each form element type, set up transition event handlers. 1401cb0ef41Sopenharmony_ci for (const localName of formElements) { 1411cb0ef41Sopenharmony_ci const elem = document.createElement(localName); 1421cb0ef41Sopenharmony_ci elem.disabled = true; 1431cb0ef41Sopenharmony_ci document.body.appendChild(elem); 1441cb0ef41Sopenharmony_ci getComputedStyle(elem).opacity; 1451cb0ef41Sopenharmony_ci elem.style.transition = "opacity 100s"; 1461cb0ef41Sopenharmony_ci // We use ontransitionstart to cancel the event. 1471cb0ef41Sopenharmony_ci elem.ontransitionstart = () => { 1481cb0ef41Sopenharmony_ci elem.style.display = "none"; 1491cb0ef41Sopenharmony_ci }; 1501cb0ef41Sopenharmony_ci const promiseToCancel = new Promise(r => { 1511cb0ef41Sopenharmony_ci elem.ontransitioncancel = r; 1521cb0ef41Sopenharmony_ci }); 1531cb0ef41Sopenharmony_ci // Flushing style triggers the transition. 1541cb0ef41Sopenharmony_ci elem.style.opacity = 0; 1551cb0ef41Sopenharmony_ci getComputedStyle(elem).opacity; 1561cb0ef41Sopenharmony_ci await promiseToCancel; 1571cb0ef41Sopenharmony_ci // And we are done with this element. 1581cb0ef41Sopenharmony_ci elem.remove(); 1591cb0ef41Sopenharmony_ci } 1601cb0ef41Sopenharmony_ci}, "CSS Transitions transitioncancel event fires on disabled form elements"); 1611cb0ef41Sopenharmony_ci 1621cb0ef41Sopenharmony_cipromise_test(async () => { 1631cb0ef41Sopenharmony_ci // For each form element type, set up transition event handlers. 1641cb0ef41Sopenharmony_ci for (const localName of formElements) { 1651cb0ef41Sopenharmony_ci const elem = document.createElement(localName); 1661cb0ef41Sopenharmony_ci document.body.appendChild(elem); 1671cb0ef41Sopenharmony_ci elem.disabled = true; 1681cb0ef41Sopenharmony_ci const animationStartPromise = new Promise(r => { 1691cb0ef41Sopenharmony_ci elem.addEventListener("animationstart", () => { 1701cb0ef41Sopenharmony_ci // Seek to the second iteration to trigger the animationiteration event 1711cb0ef41Sopenharmony_ci elem.style.animationDelay = "-100s" 1721cb0ef41Sopenharmony_ci r(); 1731cb0ef41Sopenharmony_ci }); 1741cb0ef41Sopenharmony_ci }); 1751cb0ef41Sopenharmony_ci const animationIterationPromise = new Promise(r => { 1761cb0ef41Sopenharmony_ci elem.addEventListener("animationiteration", ()=>{ 1771cb0ef41Sopenharmony_ci elem.style.animationDelay = "-200s" 1781cb0ef41Sopenharmony_ci r(); 1791cb0ef41Sopenharmony_ci }); 1801cb0ef41Sopenharmony_ci }); 1811cb0ef41Sopenharmony_ci const animationEndPromise = new Promise(r => { 1821cb0ef41Sopenharmony_ci elem.addEventListener("animationend", r); 1831cb0ef41Sopenharmony_ci }); 1841cb0ef41Sopenharmony_ci elem.style.animation = "fade 100s 2"; 1851cb0ef41Sopenharmony_ci elem.classList.add("animate"); 1861cb0ef41Sopenharmony_ci // All the events fire... 1871cb0ef41Sopenharmony_ci await Promise.all([ 1881cb0ef41Sopenharmony_ci animationStartPromise, 1891cb0ef41Sopenharmony_ci animationIterationPromise, 1901cb0ef41Sopenharmony_ci animationEndPromise, 1911cb0ef41Sopenharmony_ci ]); 1921cb0ef41Sopenharmony_ci elem.remove(); 1931cb0ef41Sopenharmony_ci } 1941cb0ef41Sopenharmony_ci}, "CSS Animation animationstart, animationiteration, animationend fire on disabled form elements"); 1951cb0ef41Sopenharmony_ci 1961cb0ef41Sopenharmony_cipromise_test(async () => { 1971cb0ef41Sopenharmony_ci // For each form element type, set up transition event handlers. 1981cb0ef41Sopenharmony_ci for (const localName of formElements) { 1991cb0ef41Sopenharmony_ci const elem = document.createElement(localName); 2001cb0ef41Sopenharmony_ci document.body.appendChild(elem); 2011cb0ef41Sopenharmony_ci elem.disabled = true; 2021cb0ef41Sopenharmony_ci 2031cb0ef41Sopenharmony_ci const promiseToCancel = new Promise(r => { 2041cb0ef41Sopenharmony_ci elem.addEventListener("animationcancel", r); 2051cb0ef41Sopenharmony_ci }); 2061cb0ef41Sopenharmony_ci 2071cb0ef41Sopenharmony_ci elem.addEventListener("animationstart", () => { 2081cb0ef41Sopenharmony_ci // Cancel the animation by hiding it. 2091cb0ef41Sopenharmony_ci elem.style.display = "none"; 2101cb0ef41Sopenharmony_ci }); 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_ci // Trigger the animation 2131cb0ef41Sopenharmony_ci elem.style.animation = "fade 100s"; 2141cb0ef41Sopenharmony_ci elem.classList.add("animate"); 2151cb0ef41Sopenharmony_ci await promiseToCancel; 2161cb0ef41Sopenharmony_ci // And we are done with this element. 2171cb0ef41Sopenharmony_ci elem.remove(); 2181cb0ef41Sopenharmony_ci } 2191cb0ef41Sopenharmony_ci}, "CSS Animation's animationcancel event fires on disabled form elements"); 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_cipromise_test(async () => { 2221cb0ef41Sopenharmony_ci for (const localName of formElements) { 2231cb0ef41Sopenharmony_ci const elem = document.createElement(localName); 2241cb0ef41Sopenharmony_ci elem.disabled = true; 2251cb0ef41Sopenharmony_ci document.body.appendChild(elem); 2261cb0ef41Sopenharmony_ci // Element is disabled, so clicking must not fire events 2271cb0ef41Sopenharmony_ci let pass = true; 2281cb0ef41Sopenharmony_ci elem.onclick = e => { 2291cb0ef41Sopenharmony_ci pass = false; 2301cb0ef41Sopenharmony_ci }; 2311cb0ef41Sopenharmony_ci // Disabled elements are not clickable. 2321cb0ef41Sopenharmony_ci await test_driver.click(elem); 2331cb0ef41Sopenharmony_ci assert_true( 2341cb0ef41Sopenharmony_ci pass, 2351cb0ef41Sopenharmony_ci `${elem.constructor.name} is disabled, so onclick must not fire.` 2361cb0ef41Sopenharmony_ci ); 2371cb0ef41Sopenharmony_ci // Element is (re)enabled... so this click() will fire an event. 2381cb0ef41Sopenharmony_ci pass = false; 2391cb0ef41Sopenharmony_ci elem.disabled = false; 2401cb0ef41Sopenharmony_ci elem.onclick = () => { 2411cb0ef41Sopenharmony_ci pass = true; 2421cb0ef41Sopenharmony_ci }; 2431cb0ef41Sopenharmony_ci await test_driver.click(elem); 2441cb0ef41Sopenharmony_ci assert_true( 2451cb0ef41Sopenharmony_ci pass, 2461cb0ef41Sopenharmony_ci `${elem.constructor.name} is enabled, so onclick must fire.` 2471cb0ef41Sopenharmony_ci ); 2481cb0ef41Sopenharmony_ci elem.remove(); 2491cb0ef41Sopenharmony_ci } 2501cb0ef41Sopenharmony_ci}, "Real clicks on disabled elements must not dispatch events."); 2511cb0ef41Sopenharmony_ci</script> 252