gecko-dev/dom/events/test/test_legacy_event.html

305 строки
11 KiB
HTML

<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1236979
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1236979 (events that have legacy alternative versions)</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style>
@keyframes anim1 {
0% { margin-left: 0px }
100% { margin-left: 100px }
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1236979">Mozilla Bug 1236979</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 1236979 **/
'use strict';
SimpleTest.waitForExplicitFinish();
// Array of info-bundles about each legacy event to be tested:
var gLegacyEventInfo = [
{
legacy_name: "webkitTransitionEnd",
modern_name: "transitionend",
trigger_event: triggerShortTransition,
},
{
legacy_name: "webkitAnimationStart",
modern_name: "animationstart",
trigger_event: triggerShortAnimation,
},
{
legacy_name: "webkitAnimationEnd",
modern_name: "animationend",
trigger_event: triggerShortAnimation,
},
{
legacy_name: "webkitAnimationIteration",
modern_name: "animationiteration",
trigger_event: triggerAnimationIteration,
}
];
// EVENT-TRIGGERING FUNCTIONS
// --------------------------
// This function triggers a very short (1ms long) transition, which will cause
// events to fire for the transition ending.
function triggerShortTransition(node) {
node.style.transition = "1ms color linear" ;
node.style.color = "purple";
// Flush style, so that the above assignment value actually takes effect
// in the computed style, so that a transition will get triggered when it
// changes.
window.getComputedStyle(node, "").color;
node.style.color = "teal";
}
// This function triggers a very short (1ms long) animation, which will cause
// events to fire for the animation beginning & ending.
function triggerShortAnimation(node) {
node.style.animation = "anim1 1ms linear";
}
// This function triggers a long animation with two iterations, which is
// *nearly* at the end of its first iteration. It will hit the end of that
// iteration (firing an event) almost immediately, 1ms in the future.
//
// NOTE: It's important that this animation have a *long* duration. If it were
// short (e.g. 1ms duration), then we might jump past all its iterations in
// a single refresh-driver tick. And if that were to happens, we'd *never* fire
// any animationiteration events -- the CSS Animations spec says this event
// must not be fired "...when an animationend event would fire at the same time"
// (which would be the case in this example with a 1ms duration). So, to make
// sure our event does fire, we use a long duration and a nearly-as-long
// negative delay. This ensures we hit the end of the first iteration right
// away, and that we don't risk hitting the end of the second iteration at the
// same time.
function triggerAnimationIteration(node) {
node.style.animation = "anim1 300s -299.999s linear 2";
}
// GENERAL UTILITY FUNCTIONS
// -------------------------
// Creates a new div and appends it as a child of the specified parentNode, or
// (if no parent is specified) as a child of the element with ID 'display'.
function createChildDiv(parentNode) {
if (!parentNode) {
parentNode = document.getElementById("display");
if (!parentNode) {
ok(false, "no 'display' element to append to");
}
}
var div = document.createElement("div");
parentNode.appendChild(div);
return div;
}
// Returns an event-handler function, which (when invoked) simply checks that
// the event's type matches what's expected. If a callback is passed in, then
// the event-handler will invoke that callback as well.
function createHandlerWithTypeCheck(expectedEventType, extraHandlerLogic) {
var handler = function(e) {
is(e.type, expectedEventType,
"When an event handler for '" + expectedEventType + "' is invoked, " +
"the event's type field should be '" + expectedEventType + "'.");
if (extraHandlerLogic) {
extraHandlerLogic(e);
}
}
return handler;
}
// TEST FUNCTIONS
// --------------
// These functions expect to be passed an entry from gEventInfo, and they
// return a Promise which performs the test & resolves when it's complete.
// The function names all begin with "mp", which stands for "make promise".
// So e.g. "mpTestLegacyEventSent" means "make a promise to test that the
// legacy event is sent".
// Tests that the legacy event type is sent, when only a legacy handler is
// registered.
function mpTestLegacyEventSent(eventInfo) {
return new Promise(
function(resolve, reject) {
// Create a node & register an event-handler for the legacy event:
var div = createChildDiv();
var handler = createHandlerWithTypeCheck(eventInfo.legacy_name,
function() {
// When event-handler is done, clean up & resolve:
div.parentNode.removeChild(div);
resolve();
});
div.addEventListener(eventInfo.legacy_name, handler);
// Trigger the event:
eventInfo.trigger_event(div);
}
);
}
// Test that the modern event type (and only the modern event type) is fired,
// when listeners of both modern & legacy types are registered. The legacy
// listener should not be invoked.
function mpTestModernBeatsLegacy(eventInfo) {
return new Promise(
function(resolve, reject) {
var div = createChildDiv();
var legacyHandler = function(e) {
reject("Handler for legacy event '" + eventInfo.legacy_name +
"' should not be invoked when there's a handler registered " +
"for both modern & legacy event type on the same node");
};
var modernHandler = createHandlerWithTypeCheck(eventInfo.modern_name,
function() {
// Indicate that the test has passed (we invoked the modern handler):
ok(true, "Handler for modern event '" + eventInfo.modern_name +
"' should be invoked when there's a handler registered for " +
"both modern & legacy event type on the same node");
// When event-handler is done, clean up & resolve:
div.parentNode.removeChild(div);
resolve();
});
div.addEventListener(eventInfo.legacy_name, legacyHandler);
div.addEventListener(eventInfo.modern_name, modernHandler);
eventInfo.trigger_event(div);
}
);
}
// Test that an event which bubbles may fire listeners of different flavors
// (modern vs. legacy) at each bubbling level, depending on what's registered
// at that level.
function mpTestDiffListenersEventBubbling(eventInfo) {
return new Promise(
function(resolve, reject) {
var grandparent = createChildDiv();
var parent = createChildDiv(grandparent);
var target = createChildDiv(parent);
var didEventFireOnTarget = false;
var didEventFireOnParent = false;
var eventSentToTarget;
target.addEventListener(eventInfo.modern_name,
createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
ok(e.bubbles, "Expecting event to bubble");
eventSentToTarget = e;
didEventFireOnTarget = true;
}));
parent.addEventListener(eventInfo.legacy_name,
createHandlerWithTypeCheck(eventInfo.legacy_name, function(e) {
is(e, eventSentToTarget,
"Same event object should bubble, despite difference in type");
didEventFireOnParent = true;
}));
grandparent.addEventListener(eventInfo.modern_name,
createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
ok(didEventFireOnTarget,
"Event should have fired on child");
ok(didEventFireOnParent,
"Event should have fired on parent");
is(e, eventSentToTarget,
"Same event object should bubble, despite difference in type");
// Clean up.
grandparent.parentNode.removeChild(grandparent);
resolve();
}));
eventInfo.trigger_event(target);
}
);
}
// Test that an event in the capture phase may fire listeners of different
// flavors (modern vs. legacy) at each level, depending on what's registered
// at that level.
function mpTestDiffListenersEventCapturing(eventInfo) {
return new Promise(
function(resolve, reject) {
var grandparent = createChildDiv();
var parent = createChildDiv(grandparent);
var target = createChildDiv(parent);
var didEventFireOnTarget = false;
var didEventFireOnParent = false;
var didEventFireOnGrandparent = false;
var eventSentToGrandparent;
grandparent.addEventListener(eventInfo.modern_name,
createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
eventSentToGrandparent = e;
didEventFireOnGrandparent = true;
}), true);
parent.addEventListener(eventInfo.legacy_name,
createHandlerWithTypeCheck(eventInfo.legacy_name, function(e) {
is(e.eventPhase, Event.CAPTURING_PHASE,
"event should be in capturing phase");
is(e, eventSentToGrandparent,
"Same event object should capture, despite difference in type");
ok(didEventFireOnGrandparent,
"Event should have fired on grandparent");
didEventFireOnParent = true;
}), true);
target.addEventListener(eventInfo.modern_name,
createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
is(e.eventPhase, Event.AT_TARGET,
"event should be at target phase");
is(e, eventSentToGrandparent,
"Same event object should capture, despite difference in type");
ok(didEventFireOnParent,
"Event should have fired on parent");
// Clean up.
grandparent.parentNode.removeChild(grandparent);
resolve();
}), true);
eventInfo.trigger_event(target);
}
);
}
// MAIN FUNCTION: Kick off the tests.
function main() {
Promise.resolve().then(function() {
return Promise.all(gLegacyEventInfo.map(mpTestLegacyEventSent))
}).then(function() {
return Promise.all(gLegacyEventInfo.map(mpTestModernBeatsLegacy));
}).then(function() {
return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventCapturing));
}).then(function() {
return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventBubbling));
}).then(function() {
SimpleTest.finish();
}).catch(function(reason) {
ok(false, "Test failed: " + reason);
SimpleTest.finish();
});
}
main();
</script>
</pre>
</body>
</html>