<html xmlns="http://www.w3.org/1999/xhtml"> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=572270 --> <head> <title>Test TimeEvents dispatching</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=572270">Mozilla Bug 572270</a> <p id="display"></p> <div id="content" style="display: none"> <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100px" height="100px"> <g font-size="10px"> <circle cx="0" cy="0" r="15" fill="blue" id="circle" onbegin="parentHandler(evt)" onrepeat="parentHandler(evt)" onend="parentHandler(evt)"> <animate attributeName="cy" from="0" to="100" dur="60s" begin="2s" id="anim" repeatCount="2" onbegin="handleOnBegin(evt)" onrepeat="handleOnRepeat(evt)" onend="handleOnEnd(evt)"/> </circle> </g> </svg> </div> <pre id="test"> <script class="testbody" type="text/javascript"> <![CDATA[ /** Test SMIL TimeEvents dispatching **/ /* Global Variables */ const gTimeoutDur = 60000; // Time until we give up waiting for events in ms var gSvg = document.getElementById("svg"); var gAnim = document.getElementById('anim'); var gCircle = document.getElementById('circle'); var gExpectedEvents = new Array(); var gTimeoutID; var gTestStages = [ testPlaybackBegin, testPlaybackRepeat, testPlaybackEnd, testForwardsSeekToMid, testForwardsSeekToNextInterval, testForwardsSeekPastEnd, testBackwardsSeekToMid, testBackwardsSeekToStart, testCreateEvent, testRegistration ]; SimpleTest.waitForExplicitFinish(); SimpleTest.requestFlakyTimeout("untriaged"); function continueTest() { if (gTestStages.length == 0) { SimpleTest.finish(); return; } gTestStages.shift()(); } function testPlaybackBegin() { // Test events are dispatched through normal playback gSvg.pauseAnimations(); gSvg.setCurrentTime(1.99); gExpectedEvents.push("beginEvent", "beginEvent"); // Two registered handlers gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gSvg.unpauseAnimations(); } function testPlaybackRepeat() { gSvg.pauseAnimations(); gSvg.setCurrentTime(61.99); gExpectedEvents.push(["repeatEvent", 1], ["repeatEvent", 1]); gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gSvg.unpauseAnimations(); } function testPlaybackEnd() { gSvg.pauseAnimations(); gSvg.setCurrentTime(121.99); gExpectedEvents.push("endEvent", "endEvent"); gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gSvg.unpauseAnimations(); } function testForwardsSeekToMid() { gSvg.pauseAnimations(); // Set animation parameters to something that repeats a lot gSvg.setCurrentTime(0); gAnim.setAttribute('begin', '2s; 102s'); gAnim.setAttribute('dur', '15s'); gAnim.setAttribute('repeatCount', '6'); gSvg.setCurrentTime(46.99); gExpectedEvents.push("beginEvent", "beginEvent", ["repeatEvent", 3], ["repeatEvent", 3]); gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gSvg.unpauseAnimations(); } function testForwardsSeekToNextInterval() { // Skip to next interval -- we shouldn't get any additional begin or end // events in between gSvg.pauseAnimations(); gSvg.setCurrentTime(131.99); gExpectedEvents.push(["repeatEvent", 2], ["repeatEvent", 2]); gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gSvg.unpauseAnimations(); } function testForwardsSeekPastEnd() { gSvg.pauseAnimations(); gSvg.setCurrentTime(200); gExpectedEvents.push("endEvent", "endEvent"); gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gSvg.unpauseAnimations(); } function testBackwardsSeekToMid() { gSvg.pauseAnimations(); gSvg.setCurrentTime(31.99); gExpectedEvents.push("beginEvent", "beginEvent", ["repeatEvent", 2], ["repeatEvent", 2]); gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gSvg.unpauseAnimations(); } function testBackwardsSeekToStart() { gSvg.pauseAnimations(); gExpectedEvents.push("endEvent", "endEvent"); gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gSvg.setCurrentTime(0); } function testCreateEvent() { var evt; try { evt = document.createEvent("TimeEvents"); } catch (e) { ok(false, "Failed to create TimeEvent via script: " + e); return; } evt.initTimeEvent("repeatEvent", null, 3); is(evt.type, "repeatEvent", "Unexpected type for user-generated event"); is(evt.detail, 3, "Unexpected detail for user-generated event"); is(evt.target, null, "Unexpected event target"); is(evt.currentTarget, null, "Unexpected event current target"); is(evt.eventPhase, evt.NONE); is(evt.bubbles, false, "Event should not bubble"); is(evt.cancelable, false, "Event should not be cancelable"); is(evt.view, null, "Event view should be null"); // Prior to dispatch we should be able to change the event type evt.initTimeEvent("beginEvent", document.defaultView, 0); is(evt.type, "beginEvent", "Failed to update event type before dispatch"); is(evt.detail, 0, "Failed to update event detail before dispatch"); is(evt.view, document.defaultView, "Event view should be set"); // But not directly as it's readonly try { evt.type = "endEvent"; } catch(e) { } is(evt.type, "beginEvent", "Event type should be readonly"); // Likewise the detail field should be readonly try { evt.detail = "8"; } catch(e) { } is(evt.detail, 0, "Event detail should be readonly"); // Dispatch gExpectedEvents.push("beginEvent", "beginEvent"); gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gAnim.dispatchEvent(evt); } function testRegistration() { gSvg.pauseAnimations(); // Reset animation to something simple gSvg.setCurrentTime(0); gAnim.setAttribute('begin', '2s'); gAnim.setAttribute('dur', '50s'); // Remove attribute handler gAnim.removeAttribute('onbegin'); // Add bogus handlers gAnim.setAttribute('onbeginElement', 'handleOnBegin(evt)'); gAnim.addEventListener("begin", handleOnBegin, false); gAnim.addEventListener("onbegin", handleOnBegin, false); // We should now have just one legitimate listener: the one registered to // handle 'beginElement' gSvg.setCurrentTime(1.99); gExpectedEvents.push("beginEvent"); gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); gSvg.unpauseAnimations(); } function handleOnBegin(evt) { is(evt.type, "beginEvent", "Expected begin event but got " + evt.type); checkExpectedEvent(evt); } function handleOnRepeat(evt) { is(evt.type, "repeatEvent", "Expected repeat event but got " + evt.type); checkExpectedEvent(evt); } function handleOnEnd(evt) { is(evt.type, "endEvent", "Expected end event but got " + evt.type); checkExpectedEvent(evt); } function sanityCheckEvent(evt) { is(evt.target, gAnim, "Unexpected event target"); is(evt.currentTarget, gAnim, "Unexpected event current target"); is(evt.eventPhase, evt.AT_TARGET); is(evt.bubbles, false, "Event should not bubble"); is(evt.cancelable, false, "Event should not be cancelable"); if (SpecialPowers.getBoolPref("dom.event.highrestimestamp.enabled")) { var now = window.performance.now(); ok(evt.timeStamp > 0 && evt.timeStamp < now, "Event timeStamp (" + evt.timeStamp + ") should be > 0 but " + "before the current time (" + now + ")"); } else { is(evt.timeStamp, 0, "Event timeStamp should be 0"); } ok(evt.view !== null, "Event view not set"); } function checkExpectedEvent(evt) { sanityCheckEvent(evt); ok(gExpectedEvents.length > 0, "Unexpected event: " + evt.type); if (gExpectedEvents.length == 0) return; var expected = gExpectedEvents.shift(); if (typeof expected == 'string') { is(evt.type, expected, "Unexpected event type"); is(evt.detail, 0, "Unexpected event detail (repeat iteration)"); } else { is(evt.type, expected[0], "Unexpected event type"); is(evt.detail, expected[1], "Unexpected event detail (repeat iteration)"); } if (gExpectedEvents.length == 0) { clearTimeout(gTimeoutID); continueTest(); } } function timeoutFail() { ok(false, "Timed out waiting for events: " + gExpectedEvents.join(', ')); SimpleTest.finish(); // No point continuing } function parentHandler(evt) { ok(false, "Handler on parent got called but event shouldn't bubble."); } window.addEventListener("load", continueTest, false); // Register event handlers *in addition* to the handlers already added via the // "onbegin", "onend", "onrepeat" attributes on the <animate> and <circle> // elements. This is to test that both types of registration work. gAnim.addEventListener("beginEvent", handleOnBegin, false); gAnim.addEventListener("repeatEvent", handleOnRepeat, false); gAnim.addEventListener("endEvent", handleOnEnd, false); gCircle.addEventListener("beginEvent", parentHandler, false); var expectedEvents = ["begin", "beginEvent", "repeat", "repeatEvent", "end", "endEvent", "SVGZoom", "zoom"]; for (var i = 0; i < expectedEvents.length; ++i) { is((new Event(expectedEvents[i])).type, expectedEvents[i], "Unexpected event type!"); } var timeEvents = ["begin", "repeat", "end"]; var expectedEvents = ["begin", "beginEvent", "repeat", "repeatEvent", "end", "endEvent"]; var d = document.createElement("div"); for (var i = 0; i < timeEvents.length; ++i) { d.addEventListener(timeEvents[i], function(e) { is(e.type, expectedEvents[0], "Got the expected event type."); expectedEvents.shift(); }); // Without "Event" suffix. var e = document.createEvent("timeevent"); e.initEvent(timeEvents[i], true, true); d.dispatchEvent(e); // With "Event" suffix. e = document.createEvent("timeevent"); e.initEvent(timeEvents[i] + "Event", true, true); d.dispatchEvent(e); } is(expectedEvents.length, 0, "Got all the expected events."); expectedEvents = ["zoom", "SVGZoom"]; d.addEventListener("zoom", function(e) { is(e.type, expectedEvents[0]); expectedEvents.shift(); }); var zoomEvent = document.createEvent("svgzoomevent"); zoomEvent.initEvent("zoom", true, true); d.dispatchEvent(zoomEvent); zoomEvent = document.createEvent("svgzoomevent"); zoomEvent.initEvent("SVGZoom", true, true); d.dispatchEvent(zoomEvent); is(expectedEvents.length, 0, "Got all the expected events."); ]]> </script> </pre> </body> </html>