summaryrefslogtreecommitdiffstats
path: root/dom/events/test/test_legacy_event.html
blob: d772be10663e19ac36e5b488f3122d9d4db25c3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
<!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>