test_legacy_event.html (10208B)
1 <!DOCTYPE HTML> 2 <html> 3 <!-- 4 https://bugzilla.mozilla.org/show_bug.cgi?id=1236979 5 --> 6 <head> 7 <meta charset="utf-8"> 8 <title>Test for Bug 1236979 (events that have legacy alternative versions)</title> 9 <script src="/tests/SimpleTest/SimpleTest.js"></script> 10 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> 11 <style> 12 @keyframes anim1 { 13 0% { margin-left: 0px } 14 100% { margin-left: 100px } 15 } 16 </style> 17 </head> 18 <body> 19 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1236979">Mozilla Bug 1236979</a> 20 <p id="display"></p> 21 <div id="content" style="display: none"> 22 23 </div> 24 <pre id="test"> 25 <script type="application/javascript"> 26 27 /** Test for Bug 1236979 */ 28 29 'use strict'; 30 SimpleTest.waitForExplicitFinish(); 31 32 // Array of info-bundles about each legacy event to be tested: 33 var gLegacyEventInfo = [ 34 { 35 legacy_name: "webkitTransitionEnd", 36 modern_name: "transitionend", 37 trigger_event: triggerShortTransition, 38 }, 39 { 40 legacy_name: "webkitAnimationStart", 41 modern_name: "animationstart", 42 trigger_event: triggerShortAnimation, 43 }, 44 { 45 legacy_name: "webkitAnimationEnd", 46 modern_name: "animationend", 47 trigger_event: triggerShortAnimation, 48 }, 49 { 50 legacy_name: "webkitAnimationIteration", 51 modern_name: "animationiteration", 52 trigger_event: triggerAnimationIteration, 53 } 54 ]; 55 56 // EVENT-TRIGGERING FUNCTIONS 57 // -------------------------- 58 // This function triggers a very short (1ms long) transition, which will cause 59 // events to fire for the transition ending. 60 function triggerShortTransition(node) { 61 node.style.transition = "1ms color linear" ; 62 node.style.color = "purple"; 63 // Flush style, so that the above assignment value actually takes effect 64 // in the computed style, so that a transition will get triggered when it 65 // changes. 66 window.getComputedStyle(node).color; 67 node.style.color = "teal"; 68 } 69 70 // This function triggers a very short (1ms long) animation, which will cause 71 // events to fire for the animation beginning & ending. 72 function triggerShortAnimation(node) { 73 node.style.animation = "anim1 1ms linear"; 74 } 75 76 // This function triggers a very short (10ms long) animation with many 77 // iterations, which will cause a start event followed by an iteration event 78 // on each subsequent tick, to fire. 79 // 80 // NOTE: We need the many iterations since if an animation frame coincides 81 // with the animation starting or ending we dispatch only the start or end 82 // event and not the iteration event. 83 function triggerAnimationIteration(node) { 84 node.style.animation = "anim1 10ms linear 20000"; 85 } 86 87 // GENERAL UTILITY FUNCTIONS 88 // ------------------------- 89 // Creates a new div and appends it as a child of the specified parentNode, or 90 // (if no parent is specified) as a child of the element with ID 'display'. 91 function createChildDiv(parentNode) { 92 if (!parentNode) { 93 parentNode = document.getElementById("display"); 94 if (!parentNode) { 95 ok(false, "no 'display' element to append to"); 96 } 97 } 98 var div = document.createElement("div"); 99 parentNode.appendChild(div); 100 return div; 101 } 102 103 // Returns an event-handler function, which (when invoked) simply checks that 104 // the event's type matches what's expected. If a callback is passed in, then 105 // the event-handler will invoke that callback as well. 106 function createHandlerWithTypeCheck(expectedEventType, extraHandlerLogic) { 107 var handler = function(e) { 108 is(e.type, expectedEventType, 109 "When an event handler for '" + expectedEventType + "' is invoked, " + 110 "the event's type field should be '" + expectedEventType + "'."); 111 if (extraHandlerLogic) { 112 extraHandlerLogic(e); 113 } 114 } 115 return handler; 116 } 117 118 // TEST FUNCTIONS 119 // -------------- 120 // These functions expect to be passed an entry from gEventInfo, and they 121 // return a Promise which performs the test & resolves when it's complete. 122 // The function names all begin with "mp", which stands for "make promise". 123 // So e.g. "mpTestLegacyEventSent" means "make a promise to test that the 124 // legacy event is sent". 125 126 // Tests that the legacy event type is sent, when only a legacy handler is 127 // registered. 128 function mpTestLegacyEventSent(eventInfo) { 129 return new Promise( 130 function(resolve, reject) { 131 // Create a node & register an event-handler for the legacy event: 132 var div = createChildDiv(); 133 134 var handler = createHandlerWithTypeCheck(eventInfo.legacy_name, 135 function() { 136 // When event-handler is done, clean up & resolve: 137 div.remove(); 138 resolve(); 139 }); 140 div.addEventListener(eventInfo.legacy_name, handler); 141 142 // Trigger the event: 143 eventInfo.trigger_event(div); 144 } 145 ); 146 } 147 148 // Test that the modern event type (and only the modern event type) is fired, 149 // when listeners of both modern & legacy types are registered. The legacy 150 // listener should not be invoked. 151 function mpTestModernBeatsLegacy(eventInfo) { 152 return new Promise( 153 function(resolve, reject) { 154 var div = createChildDiv(); 155 156 var legacyHandler = function(e) { 157 reject("Handler for legacy event '" + eventInfo.legacy_name + 158 "' should not be invoked when there's a handler registered " + 159 "for both modern & legacy event type on the same node"); 160 }; 161 162 var modernHandler = createHandlerWithTypeCheck(eventInfo.modern_name, 163 function() { 164 // Indicate that the test has passed (we invoked the modern handler): 165 ok(true, "Handler for modern event '" + eventInfo.modern_name + 166 "' should be invoked when there's a handler registered for " + 167 "both modern & legacy event type on the same node"); 168 // When event-handler is done, clean up & resolve: 169 div.remove(); 170 resolve(); 171 }); 172 173 div.addEventListener(eventInfo.legacy_name, legacyHandler); 174 div.addEventListener(eventInfo.modern_name, modernHandler); 175 eventInfo.trigger_event(div); 176 } 177 ); 178 } 179 180 // Test that an event which bubbles may fire listeners of different flavors 181 // (modern vs. legacy) at each bubbling level, depending on what's registered 182 // at that level. 183 function mpTestDiffListenersEventBubbling(eventInfo) { 184 return new Promise( 185 function(resolve, reject) { 186 var grandparent = createChildDiv(); 187 var parent = createChildDiv(grandparent); 188 var target = createChildDiv(parent); 189 var didEventFireOnTarget = false; 190 var didEventFireOnParent = false; 191 var eventSentToTarget; 192 193 target.addEventListener(eventInfo.modern_name, 194 createHandlerWithTypeCheck(eventInfo.modern_name, function(e) { 195 ok(e.bubbles, "Expecting event to bubble"); 196 eventSentToTarget = e; 197 didEventFireOnTarget = true; 198 })); 199 200 parent.addEventListener(eventInfo.legacy_name, 201 createHandlerWithTypeCheck(eventInfo.legacy_name, function(e) { 202 is(e, eventSentToTarget, 203 "Same event object should bubble, despite difference in type"); 204 didEventFireOnParent = true; 205 })); 206 207 grandparent.addEventListener(eventInfo.modern_name, 208 createHandlerWithTypeCheck(eventInfo.modern_name, function(e) { 209 ok(didEventFireOnTarget, 210 "Event should have fired on child"); 211 ok(didEventFireOnParent, 212 "Event should have fired on parent"); 213 is(e, eventSentToTarget, 214 "Same event object should bubble, despite difference in type"); 215 // Clean up. 216 grandparent.remove(); 217 resolve(); 218 })); 219 220 eventInfo.trigger_event(target); 221 } 222 ); 223 } 224 225 // Test that an event in the capture phase may fire listeners of different 226 // flavors (modern vs. legacy) at each level, depending on what's registered 227 // at that level. 228 function mpTestDiffListenersEventCapturing(eventInfo) { 229 return new Promise( 230 function(resolve, reject) { 231 var grandparent = createChildDiv(); 232 var parent = createChildDiv(grandparent); 233 var target = createChildDiv(parent); 234 var didEventFireOnTarget = false; 235 var didEventFireOnParent = false; 236 var didEventFireOnGrandparent = false; 237 var eventSentToGrandparent; 238 239 grandparent.addEventListener(eventInfo.modern_name, 240 createHandlerWithTypeCheck(eventInfo.modern_name, function(e) { 241 eventSentToGrandparent = e; 242 didEventFireOnGrandparent = true; 243 }), true); 244 245 parent.addEventListener(eventInfo.legacy_name, 246 createHandlerWithTypeCheck(eventInfo.legacy_name, function(e) { 247 is(e.eventPhase, Event.CAPTURING_PHASE, 248 "event should be in capturing phase"); 249 is(e, eventSentToGrandparent, 250 "Same event object should capture, despite difference in type"); 251 ok(didEventFireOnGrandparent, 252 "Event should have fired on grandparent"); 253 didEventFireOnParent = true; 254 }), true); 255 256 target.addEventListener(eventInfo.modern_name, 257 createHandlerWithTypeCheck(eventInfo.modern_name, function(e) { 258 is(e.eventPhase, Event.AT_TARGET, 259 "event should be at target phase"); 260 is(e, eventSentToGrandparent, 261 "Same event object should capture, despite difference in type"); 262 ok(didEventFireOnParent, 263 "Event should have fired on parent"); 264 // Clean up. 265 grandparent.remove(); 266 resolve(); 267 }), true); 268 269 eventInfo.trigger_event(target); 270 } 271 ); 272 } 273 274 // MAIN FUNCTION: Kick off the tests. 275 function main() { 276 Promise.resolve().then(function() { 277 return Promise.all(gLegacyEventInfo.map(mpTestLegacyEventSent)) 278 }).then(function() { 279 return Promise.all(gLegacyEventInfo.map(mpTestModernBeatsLegacy)); 280 }).then(function() { 281 return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventCapturing)); 282 }).then(function() { 283 return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventBubbling)); 284 }).then(function() { 285 SimpleTest.finish(); 286 }).catch(function(reason) { 287 ok(false, "Test failed: " + reason); 288 SimpleTest.finish(); 289 }); 290 } 291 292 main(); 293 294 </script> 295 </pre> 296 </body> 297 </html>