Bug 1530239 - css transition events must fire even if an element is disabled. r=smaug
authorMarcos Cáceres <mcaceres@mozilla.com>
Thu, 28 Feb 2019 13:54:59 +0000
changeset 461719 c89f5d4d2e86a66411ad91a5e7241e3242ac3d7a
parent 461718 9d1e9834a326d6b9ebdef03cf4622d0482aa17a9
child 461720 46950990b58ac4c10f94dbc14222b43657c73b6c
push id35627
push useropoprus@mozilla.com
push dateThu, 28 Feb 2019 21:44:07 +0000
treeherdermozilla-central@db533ea3d561 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1530239
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1530239 - css transition events must fire even if an element is disabled. r=smaug CSS Transitions were not firing when elements were disabled. Differential Revision: https://phabricator.services.mozilla.com/D21166
dom/html/nsGenericHTMLElement.cpp
testing/web-platform/tests/dom/events/Event-dispatch-on-disabled-elements.html
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1974,16 +1974,20 @@ bool nsGenericHTMLFormElement::IsElement
     case eMouseOut:
     case eMouseEnter:
     case eMouseLeave:
     case ePointerMove:
     case ePointerOver:
     case ePointerOut:
     case ePointerEnter:
     case ePointerLeave:
+    case eTransitionCancel:
+    case eTransitionEnd:
+    case eTransitionRun:
+    case eTransitionStart:
     case eWheel:
     case eLegacyMouseLineOrPageScroll:
     case eLegacyMousePixelScroll:
       return false;
     default:
       break;
   }
 
--- a/testing/web-platform/tests/dom/events/Event-dispatch-on-disabled-elements.html
+++ b/testing/web-platform/tests/dom/events/Event-dispatch-on-disabled-elements.html
@@ -94,39 +94,109 @@ test(() => {
       `.click() must dispatch "click" event on enabled ${
         elem.constructor.name
       }.`
     );
   }
 }, "Calling click() on disabled elements must not dispatch events.");
 
 promise_test(async () => {
+  // Style sheet that controls transition.
+  const style = document.createElement("style");
+  style.innerText = `
+    ${formElements.join(", ")} {
+      opacity: 0.1;
+      transition-property: opacity;
+      transition-duration: .1s;
+    }
+    .transition {
+      opacity: 1;
+    }
+  `;
+  document.head.appendChild(style);
+
+  // Triggers the transition in the element being tested.
+  const transitionTrigger = document.createElement("button");
+  transitionTrigger.innerText = "Trigger button";
+  document.body.appendChild(transitionTrigger);
+
+  // 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 transitionPromises = [
+      "transitionrun",
+      "transitionstart",
+      "transitionend",
+    ].map(eventType => {
+      return new Promise(r => {
+        const handlerName = `on${eventType}`;
+        elem[handlerName] = ev => {
+          elem[handlerName] = null;
+          r();
+        };
+      });
+    });
 
+    // Trigger transitions specifically on this element
+    // it requires a trusted event.
+    transitionTrigger.onclick = () => {
+      elem.classList.toggle("transition");
+    };
+    await test_driver.click(transitionTrigger);
+
+    // All the events fire...
+    await Promise.all(transitionPromises);
+    elem.classList.remove("transition");
+
+    // Let's now test the "transitioncancel" event.
+    elem.ontransitionstart = () => {
+      // Cancel the transition by hiding it.
+      elem.style.display = "none";
+      elem.classList.remove("transition");
+    };
+
+    // Trigger the transition again!
+    const promiseToCancel = new Promise(r => {
+      elem.ontransitioncancel = r;
+    });
+    await test_driver.click(transitionTrigger);
+    await promiseToCancel;
+    // And we are done with this element.
+    elem.remove();
+  }
+  // And we are done with the test... clean up.
+  transitionTrigger.remove();
+  style.remove();
+}, "CSS Transitions events fire 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;
     };
-    await test_driver.click(elem); // triggers "onclick"
+    // 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); // triggers "onclick"
+    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>