зеркало из https://github.com/mozilla/gecko-dev.git
305 строки
11 KiB
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>
|