|
1 <html xmlns="http://www.w3.org/1999/xhtml"> |
|
2 <!-- |
|
3 https://bugzilla.mozilla.org/show_bug.cgi?id=572270 |
|
4 --> |
|
5 <head> |
|
6 <title>Test TimeEvents dispatching</title> |
|
7 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> |
|
8 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> |
|
9 </head> |
|
10 <body> |
|
11 <a target="_blank" |
|
12 href="https://bugzilla.mozilla.org/show_bug.cgi?id=572270">Mozilla Bug |
|
13 572270</a> |
|
14 <p id="display"></p> |
|
15 <div id="content" style="display: none"> |
|
16 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100px" height="100px"> |
|
17 <g font-size="10px"> |
|
18 <circle cx="0" cy="0" r="15" fill="blue" id="circle" |
|
19 onbegin="parentHandler(evt)" onrepeat="parentHandler(evt)" |
|
20 onend="parentHandler(evt)"> |
|
21 <animate attributeName="cy" from="0" to="100" dur="60s" begin="2s" |
|
22 id="anim" repeatCount="2" |
|
23 onbegin="handleOnBegin(evt)" onrepeat="handleOnRepeat(evt)" |
|
24 onend="handleOnEnd(evt)"/> |
|
25 </circle> |
|
26 </g> |
|
27 </svg> |
|
28 </div> |
|
29 <pre id="test"> |
|
30 <script class="testbody" type="text/javascript"> |
|
31 <![CDATA[ |
|
32 /** Test SMIL TimeEvents dispatching **/ |
|
33 |
|
34 /* Global Variables */ |
|
35 const gTimeoutDur = 5000; // Time until we give up waiting for events in ms |
|
36 var gSvg = document.getElementById("svg"); |
|
37 var gAnim = document.getElementById('anim'); |
|
38 var gCircle = document.getElementById('circle'); |
|
39 var gExpectedEvents = new Array(); |
|
40 var gTimeoutID; |
|
41 var gTestStages = |
|
42 [ testPlaybackBegin, |
|
43 testPlaybackRepeat, |
|
44 testPlaybackEnd, |
|
45 testForwardsSeekToMid, |
|
46 testForwardsSeekToNextInterval, |
|
47 testForwardsSeekPastEnd, |
|
48 testBackwardsSeekToMid, |
|
49 testBackwardsSeekToStart, |
|
50 testCreateEvent, |
|
51 testRegistration |
|
52 ]; |
|
53 |
|
54 SimpleTest.waitForExplicitFinish(); |
|
55 |
|
56 function continueTest() |
|
57 { |
|
58 if (gTestStages.length == 0) { |
|
59 SimpleTest.finish(); |
|
60 return; |
|
61 } |
|
62 gTestStages.shift()(); |
|
63 } |
|
64 |
|
65 function testPlaybackBegin() |
|
66 { |
|
67 // Test events are dispatched through normal playback |
|
68 gSvg.pauseAnimations(); |
|
69 gSvg.setCurrentTime(1.99); |
|
70 gExpectedEvents.push("beginEvent", "beginEvent"); // Two registered handlers |
|
71 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
72 gSvg.unpauseAnimations(); |
|
73 } |
|
74 |
|
75 function testPlaybackRepeat() |
|
76 { |
|
77 gSvg.pauseAnimations(); |
|
78 gSvg.setCurrentTime(61.99); |
|
79 gExpectedEvents.push(["repeatEvent", 1], ["repeatEvent", 1]); |
|
80 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
81 gSvg.unpauseAnimations(); |
|
82 } |
|
83 |
|
84 function testPlaybackEnd() |
|
85 { |
|
86 gSvg.pauseAnimations(); |
|
87 gSvg.setCurrentTime(121.99); |
|
88 gExpectedEvents.push("endEvent", "endEvent"); |
|
89 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
90 gSvg.unpauseAnimations(); |
|
91 } |
|
92 |
|
93 function testForwardsSeekToMid() |
|
94 { |
|
95 gSvg.pauseAnimations(); |
|
96 // Set animation parameters to something that repeats a lot |
|
97 gSvg.setCurrentTime(0); |
|
98 gAnim.setAttribute('begin', '2s; 102s'); |
|
99 gAnim.setAttribute('dur', '15s'); |
|
100 gAnim.setAttribute('repeatCount', '6'); |
|
101 gSvg.setCurrentTime(46.99); |
|
102 gExpectedEvents.push("beginEvent", "beginEvent", |
|
103 ["repeatEvent", 3], ["repeatEvent", 3]); |
|
104 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
105 gSvg.unpauseAnimations(); |
|
106 } |
|
107 |
|
108 function testForwardsSeekToNextInterval() |
|
109 { |
|
110 // Skip to next interval -- we shouldn't get any additional begin or end |
|
111 // events in between |
|
112 gSvg.pauseAnimations(); |
|
113 gSvg.setCurrentTime(131.99); |
|
114 gExpectedEvents.push(["repeatEvent", 2], ["repeatEvent", 2]); |
|
115 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
116 gSvg.unpauseAnimations(); |
|
117 } |
|
118 |
|
119 function testForwardsSeekPastEnd() |
|
120 { |
|
121 gSvg.pauseAnimations(); |
|
122 gSvg.setCurrentTime(200); |
|
123 gExpectedEvents.push("endEvent", "endEvent"); |
|
124 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
125 gSvg.unpauseAnimations(); |
|
126 } |
|
127 |
|
128 function testBackwardsSeekToMid() |
|
129 { |
|
130 gSvg.pauseAnimations(); |
|
131 gSvg.setCurrentTime(31.99); |
|
132 gExpectedEvents.push("beginEvent", "beginEvent", |
|
133 ["repeatEvent", 2], ["repeatEvent", 2]); |
|
134 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
135 gSvg.unpauseAnimations(); |
|
136 } |
|
137 |
|
138 function testBackwardsSeekToStart() |
|
139 { |
|
140 gSvg.pauseAnimations(); |
|
141 gExpectedEvents.push("endEvent", "endEvent"); |
|
142 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
143 gSvg.setCurrentTime(0); |
|
144 } |
|
145 |
|
146 function testCreateEvent() |
|
147 { |
|
148 var evt; |
|
149 try { |
|
150 evt = document.createEvent("TimeEvents"); |
|
151 } catch (e) { |
|
152 ok(false, "Failed to create TimeEvent via script: " + e); |
|
153 return; |
|
154 } |
|
155 evt.initTimeEvent("repeatEvent", null, 3); |
|
156 is(evt.type, "repeatEvent", "Unexpected type for user-generated event"); |
|
157 is(evt.detail, 3, "Unexpected detail for user-generated event"); |
|
158 is(evt.target, null, "Unexpected event target"); |
|
159 is(evt.currentTarget, null, "Unexpected event current target"); |
|
160 is(evt.eventPhase, evt.NONE); |
|
161 is(evt.bubbles, false, "Event should not bubble"); |
|
162 is(evt.cancelable, false, "Event should not be cancelable"); |
|
163 is(evt.view, null, "Event view should be null"); |
|
164 |
|
165 // Prior to dispatch we should be able to change the event type |
|
166 evt.initTimeEvent("beginEvent", document.defaultView, 0); |
|
167 is(evt.type, "beginEvent", "Failed to update event type before dispatch"); |
|
168 is(evt.detail, 0, "Failed to update event detail before dispatch"); |
|
169 is(evt.view, document.defaultView, "Event view should be set"); |
|
170 |
|
171 // But not directly as it's readonly |
|
172 try { |
|
173 evt.type = "endEvent"; |
|
174 } catch(e) { } |
|
175 is(evt.type, "beginEvent", "Event type should be readonly"); |
|
176 |
|
177 // Likewise the detail field should be readonly |
|
178 try { |
|
179 evt.detail = "8"; |
|
180 } catch(e) { } |
|
181 is(evt.detail, 0, "Event detail should be readonly"); |
|
182 |
|
183 // Dispatch |
|
184 gExpectedEvents.push("beginEvent", "beginEvent"); |
|
185 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
186 gAnim.dispatchEvent(evt); |
|
187 } |
|
188 |
|
189 function testRegistration() |
|
190 { |
|
191 gSvg.pauseAnimations(); |
|
192 // Reset animation to something simple |
|
193 gSvg.setCurrentTime(0); |
|
194 gAnim.setAttribute('begin', '2s'); |
|
195 gAnim.setAttribute('dur', '50s'); |
|
196 |
|
197 // Remove attribute handler |
|
198 gAnim.removeAttribute('onbegin'); |
|
199 |
|
200 // Add bogus handlers |
|
201 gAnim.setAttribute('onbeginElement', 'handleOnBegin(evt)'); |
|
202 gAnim.addEventListener("begin", handleOnBegin, false); |
|
203 gAnim.addEventListener("onbegin", handleOnBegin, false); |
|
204 |
|
205 // We should now have just one legitimate listener: the one registered to |
|
206 // handle 'beginElement' |
|
207 gSvg.setCurrentTime(1.99); |
|
208 gExpectedEvents.push("beginEvent"); |
|
209 gTimeoutID = setTimeout(timeoutFail, gTimeoutDur); |
|
210 gSvg.unpauseAnimations(); |
|
211 } |
|
212 |
|
213 function handleOnBegin(evt) |
|
214 { |
|
215 is(evt.type, "beginEvent", "Expected begin event but got " + evt.type); |
|
216 checkExpectedEvent(evt); |
|
217 } |
|
218 |
|
219 function handleOnRepeat(evt) |
|
220 { |
|
221 is(evt.type, "repeatEvent", "Expected repeat event but got " + evt.type); |
|
222 checkExpectedEvent(evt); |
|
223 } |
|
224 |
|
225 function handleOnEnd(evt) |
|
226 { |
|
227 is(evt.type, "endEvent", "Expected end event but got " + evt.type); |
|
228 checkExpectedEvent(evt); |
|
229 } |
|
230 |
|
231 function sanityCheckEvent(evt) |
|
232 { |
|
233 is(evt.target, gAnim, "Unexpected event target"); |
|
234 is(evt.currentTarget, gAnim, "Unexpected event current target"); |
|
235 is(evt.eventPhase, evt.AT_TARGET); |
|
236 is(evt.bubbles, false, "Event should not bubble"); |
|
237 is(evt.cancelable, false, "Event should not be cancelable"); |
|
238 // Currently we set event timestamps to 0 which DOM 2 allows. This isn't |
|
239 // correct since SMIL uses this field to avoid synchronisation slew but first |
|
240 // we need to fix bug 323039 and bug 77992 which involve storing timestamps as |
|
241 // 64-bit integers and deciding whether those timestamps should be related to |
|
242 // the epoch or system start. |
|
243 is(evt.timeStamp, 0, "Event timeStamp should be 0"); |
|
244 ok(evt.view !== null, "Event view not set"); |
|
245 } |
|
246 |
|
247 function checkExpectedEvent(evt) |
|
248 { |
|
249 sanityCheckEvent(evt); |
|
250 ok(gExpectedEvents.length > 0, "Unexpected event: " + evt.type); |
|
251 if (gExpectedEvents.length == 0) return; |
|
252 |
|
253 var expected = gExpectedEvents.shift(); |
|
254 if (typeof expected == 'string') { |
|
255 is(evt.type, expected, "Unexpected event type"); |
|
256 is(evt.detail, 0, "Unexpected event detail (repeat iteration)"); |
|
257 } else { |
|
258 is(evt.type, expected[0], "Unexpected event type"); |
|
259 is(evt.detail, expected[1], "Unexpected event detail (repeat iteration)"); |
|
260 } |
|
261 if (gExpectedEvents.length == 0) { |
|
262 clearTimeout(gTimeoutID); |
|
263 continueTest(); |
|
264 } |
|
265 } |
|
266 |
|
267 function timeoutFail() |
|
268 { |
|
269 ok(false, "Timed out waiting for events: " + gExpectedEvents.join(', ')); |
|
270 SimpleTest.finish(); // No point continuing |
|
271 } |
|
272 |
|
273 function parentHandler(evt) |
|
274 { |
|
275 ok(false, "Handler on parent got called but event shouldn't bubble."); |
|
276 } |
|
277 |
|
278 window.addEventListener("load", continueTest, false); |
|
279 |
|
280 // Register event handlers *in addition* to the handlers already added via the |
|
281 // "onbegin", "onend", "onrepeat" attributes on the <animate> and <circle> |
|
282 // elements. This is to test that both types of registration work. |
|
283 gAnim.addEventListener("beginEvent", handleOnBegin, false); |
|
284 gAnim.addEventListener("repeatEvent", handleOnRepeat, false); |
|
285 gAnim.addEventListener("endEvent", handleOnEnd, false); |
|
286 gCircle.addEventListener("beginEvent", parentHandler, false); |
|
287 ]]> |
|
288 </script> |
|
289 </pre> |
|
290 </body> |
|
291 </html> |