mirror of
https://github.com/servo/servo.git
synced 2025-07-12 18:03:49 +01:00
251 lines
7.6 KiB
HTML
251 lines
7.6 KiB
HTML
<!doctype html>
|
|
<meta charset="utf8">
|
|
<meta name="timeout" content="long">
|
|
<title>Events must dispatch on disabled elements</title>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="/resources/testdriver.js"></script>
|
|
<script src="/resources/testdriver-vendor.js"></script>
|
|
<style>
|
|
@keyframes fade {
|
|
0% {
|
|
opacity: 1;
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
</style>
|
|
<body>
|
|
<script>
|
|
// HTML elements that can be disabled
|
|
const formElements = ["button", "fieldset", "input", "select", "textarea"];
|
|
|
|
test(() => {
|
|
for (const localName of formElements) {
|
|
const elem = document.createElement(localName);
|
|
elem.disabled = true;
|
|
// pass becomes true if the event is called and it's the right type.
|
|
let pass = false;
|
|
const listener = ({ type }) => {
|
|
pass = type === "click";
|
|
};
|
|
elem.addEventListener("click", listener, { once: true });
|
|
elem.dispatchEvent(new Event("click"));
|
|
assert_true(
|
|
pass,
|
|
`Untrusted "click" Event didn't dispatch on ${elem.constructor.name}.`
|
|
);
|
|
}
|
|
}, "Can dispatch untrusted 'click' Events at disabled HTML elements.");
|
|
|
|
test(() => {
|
|
for (const localName of formElements) {
|
|
const elem = document.createElement(localName);
|
|
elem.disabled = true;
|
|
// pass becomes true if the event is called and it's the right type.
|
|
let pass = false;
|
|
const listener = ({ type }) => {
|
|
pass = type === "pass";
|
|
};
|
|
elem.addEventListener("pass", listener, { once: true });
|
|
elem.dispatchEvent(new Event("pass"));
|
|
assert_true(
|
|
pass,
|
|
`Untrusted "pass" Event didn't dispatch on ${elem.constructor.name}`
|
|
);
|
|
}
|
|
}, "Can dispatch untrusted Events at disabled HTML elements.");
|
|
|
|
test(() => {
|
|
for (const localName of formElements) {
|
|
const elem = document.createElement(localName);
|
|
elem.disabled = true;
|
|
// pass becomes true if the event is called and it's the right type.
|
|
let pass = false;
|
|
const listener = ({ type }) => {
|
|
pass = type === "custom-pass";
|
|
};
|
|
elem.addEventListener("custom-pass", listener, { once: true });
|
|
elem.dispatchEvent(new CustomEvent("custom-pass"));
|
|
assert_true(
|
|
pass,
|
|
`CustomEvent "custom-pass" didn't dispatch on ${elem.constructor.name}`
|
|
);
|
|
}
|
|
}, "Can dispatch CustomEvents at disabled HTML elements.");
|
|
|
|
test(() => {
|
|
for (const localName of formElements) {
|
|
const elem = document.createElement(localName);
|
|
|
|
// Element is disabled... so this click() MUST NOT fire an event.
|
|
elem.disabled = true;
|
|
let pass = true;
|
|
elem.onclick = e => {
|
|
pass = false;
|
|
};
|
|
elem.click();
|
|
assert_true(
|
|
pass,
|
|
`.click() must not dispatch "click" event on disabled ${
|
|
elem.constructor.name
|
|
}.`
|
|
);
|
|
|
|
// Element is (re)enabled... so this click() fires an event.
|
|
elem.disabled = false;
|
|
pass = false;
|
|
elem.onclick = e => {
|
|
pass = true;
|
|
};
|
|
elem.click();
|
|
assert_true(
|
|
pass,
|
|
`.click() must dispatch "click" event on enabled ${
|
|
elem.constructor.name
|
|
}.`
|
|
);
|
|
}
|
|
}, "Calling click() on disabled elements must not dispatch events.");
|
|
|
|
promise_test(async () => {
|
|
// For each form element type, set up transition event handlers.
|
|
for (const localName of formElements) {
|
|
const elem = document.createElement(localName);
|
|
elem.disabled = true;
|
|
document.body.appendChild(elem);
|
|
const eventPromises = [
|
|
"transitionrun",
|
|
"transitionstart",
|
|
"transitionend",
|
|
].map(eventType => {
|
|
return new Promise(r => {
|
|
elem.addEventListener(eventType, r);
|
|
});
|
|
});
|
|
// Flushing style triggers transition.
|
|
getComputedStyle(elem).opacity;
|
|
elem.style.transition = "opacity .1s";
|
|
elem.style.opacity = 0;
|
|
getComputedStyle(elem).opacity;
|
|
// All the events fire...
|
|
await Promise.all(eventPromises);
|
|
elem.remove();
|
|
}
|
|
}, "CSS Transitions transitionrun, transitionstart, transitionend events fire on disabled form elements");
|
|
|
|
promise_test(async () => {
|
|
// For each form element type, set up transition event handlers.
|
|
for (const localName of formElements) {
|
|
const elem = document.createElement(localName);
|
|
elem.disabled = true;
|
|
document.body.appendChild(elem);
|
|
getComputedStyle(elem).opacity;
|
|
elem.style.transition = "opacity 100s";
|
|
// We use ontransitionstart to cancel the event.
|
|
elem.ontransitionstart = () => {
|
|
elem.style.display = "none";
|
|
};
|
|
const promiseToCancel = new Promise(r => {
|
|
elem.ontransitioncancel = r;
|
|
});
|
|
// Flushing style triggers the transition.
|
|
elem.style.opacity = 0;
|
|
getComputedStyle(elem).opacity;
|
|
await promiseToCancel;
|
|
// And we are done with this element.
|
|
elem.remove();
|
|
}
|
|
}, "CSS Transitions transitioncancel event fires on disabled form elements");
|
|
|
|
promise_test(async () => {
|
|
// For each form element type, set up transition event handlers.
|
|
for (const localName of formElements) {
|
|
const elem = document.createElement(localName);
|
|
document.body.appendChild(elem);
|
|
elem.disabled = true;
|
|
const animationStartPromise = new Promise(r => {
|
|
elem.addEventListener("animationstart", () => {
|
|
// Seek to the second iteration to trigger the animationiteration event
|
|
elem.style.animationDelay = "-100s"
|
|
r();
|
|
});
|
|
});
|
|
const animationIterationPromise = new Promise(r => {
|
|
elem.addEventListener("animationiteration", ()=>{
|
|
elem.style.animationDelay = "-200s"
|
|
r();
|
|
});
|
|
});
|
|
const animationEndPromise = new Promise(r => {
|
|
elem.addEventListener("animationend", r);
|
|
});
|
|
elem.style.animation = "fade 100s 2";
|
|
elem.classList.add("animate");
|
|
// All the events fire...
|
|
await Promise.all([
|
|
animationStartPromise,
|
|
animationIterationPromise,
|
|
animationEndPromise,
|
|
]);
|
|
elem.remove();
|
|
}
|
|
}, "CSS Animation animationstart, animationiteration, animationend fire on disabled form elements");
|
|
|
|
promise_test(async () => {
|
|
// For each form element type, set up transition event handlers.
|
|
for (const localName of formElements) {
|
|
const elem = document.createElement(localName);
|
|
document.body.appendChild(elem);
|
|
elem.disabled = true;
|
|
|
|
const promiseToCancel = new Promise(r => {
|
|
elem.addEventListener("animationcancel", r);
|
|
});
|
|
|
|
elem.addEventListener("animationstart", () => {
|
|
// Cancel the animation by hiding it.
|
|
elem.style.display = "none";
|
|
});
|
|
|
|
// Trigger the animation
|
|
elem.style.animation = "fade 100s";
|
|
elem.classList.add("animate");
|
|
await promiseToCancel;
|
|
// And we are done with this element.
|
|
elem.remove();
|
|
}
|
|
}, "CSS Animation's animationcancel event fires on disabled form elements");
|
|
|
|
promise_test(async () => {
|
|
for (const localName of formElements) {
|
|
const elem = document.createElement(localName);
|
|
elem.disabled = true;
|
|
document.body.appendChild(elem);
|
|
// Element is disabled, so clicking must not fire events
|
|
let pass = true;
|
|
elem.onclick = e => {
|
|
pass = false;
|
|
};
|
|
// Disabled elements are not clickable.
|
|
await test_driver.click(elem);
|
|
assert_true(
|
|
pass,
|
|
`${elem.constructor.name} is disabled, so onclick must not fire.`
|
|
);
|
|
// Element is (re)enabled... so this click() will fire an event.
|
|
pass = false;
|
|
elem.disabled = false;
|
|
elem.onclick = () => {
|
|
pass = true;
|
|
};
|
|
await test_driver.click(elem);
|
|
assert_true(
|
|
pass,
|
|
`${elem.constructor.name} is enabled, so onclick must fire.`
|
|
);
|
|
elem.remove();
|
|
}
|
|
}, "Real clicks on disabled elements must not dispatch events.");
|
|
</script>
|