|
1 <!DOCTYPE HTML> |
|
2 <html> |
|
3 <!-- |
|
4 https://bugzilla.mozilla.org/show_bug.cgi?id=970964 |
|
5 --> |
|
6 <head> |
|
7 <title>Test for Bug 970964</title> |
|
8 <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> |
|
9 <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> |
|
10 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> |
|
11 </head> |
|
12 <body> |
|
13 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=970964">Mozilla Bug 970964</a> |
|
14 <p id="display"></p> |
|
15 <div id="content" style="display: none"> |
|
16 |
|
17 </div> |
|
18 <pre id="test"> |
|
19 <script type="application/javascript"> |
|
20 |
|
21 /** Test for Bug 970964 **/ |
|
22 |
|
23 function ok(condition, msg) { |
|
24 parent.ok(condition, msg); |
|
25 } |
|
26 |
|
27 function is(a, b, msg) { |
|
28 parent.is(a, b, msg); |
|
29 } |
|
30 |
|
31 function testtouch(aOptions) { |
|
32 if (!aOptions) |
|
33 aOptions = {}; |
|
34 this.identifier = aOptions.identifier || 0; |
|
35 this.target = aOptions.target || 0; |
|
36 this.page = aOptions.page || {x: 0, y: 0}; |
|
37 this.radius = aOptions.radius || {x: 0, y: 0}; |
|
38 this.rotationAngle = aOptions.rotationAngle || 0; |
|
39 this.force = aOptions.force || 1; |
|
40 } |
|
41 |
|
42 function touchEvent(aOptions) { |
|
43 if (!aOptions) { |
|
44 aOptions = {}; |
|
45 } |
|
46 this.ctrlKey = aOptions.ctrlKey || false; |
|
47 this.altKey = aOptions.altKey || false; |
|
48 this.shiftKey = aOptions.shiftKey || false; |
|
49 this.metaKey = aOptions.metaKey || false; |
|
50 this.touches = aOptions.touches || []; |
|
51 this.targetTouches = aOptions.targetTouches || []; |
|
52 this.changedTouches = aOptions.changedTouches || []; |
|
53 } |
|
54 |
|
55 function sendTouchEvent(windowUtils, aType, aEvent, aModifiers) { |
|
56 var ids = [], xs=[], ys=[], rxs = [], rys = [], |
|
57 rotations = [], forces = []; |
|
58 |
|
59 for (var touchType of ["touches", "changedTouches", "targetTouches"]) { |
|
60 for (var i = 0; i < aEvent[touchType].length; i++) { |
|
61 if (ids.indexOf(aEvent[touchType][i].identifier) == -1) { |
|
62 ids.push(aEvent[touchType][i].identifier); |
|
63 xs.push(aEvent[touchType][i].page.x); |
|
64 ys.push(aEvent[touchType][i].page.y); |
|
65 rxs.push(aEvent[touchType][i].radius.x); |
|
66 rys.push(aEvent[touchType][i].radius.y); |
|
67 rotations.push(aEvent[touchType][i].rotationAngle); |
|
68 forces.push(aEvent[touchType][i].force); |
|
69 } |
|
70 } |
|
71 } |
|
72 return windowUtils.sendTouchEvent(aType, |
|
73 ids, xs, ys, rxs, rys, |
|
74 rotations, forces, |
|
75 ids.length, aModifiers, 0); |
|
76 } |
|
77 |
|
78 function getDefaultArgEvent(eventname) { |
|
79 return new PointerEvent(eventname, { |
|
80 bubbles: true, cancelable: true, view: window, |
|
81 detail: 0, screenX: 0, screenY: 0, clientX: 0, clientY: 0, |
|
82 ctrlKey: false, altKey: false, shiftKey: false, metaKey: false, |
|
83 button: 0, relatedTarget: null, pointerId: 0 |
|
84 }); |
|
85 } |
|
86 |
|
87 function getTouchEventForTarget(target, cwu, id) { |
|
88 var bcr = target.getBoundingClientRect(); |
|
89 var touch = new testtouch({ |
|
90 page: {x: Math.round(bcr.left + bcr.width/2), |
|
91 y: Math.round(bcr.top + bcr.height/2)}, |
|
92 target: target, |
|
93 identifier: id, |
|
94 }); |
|
95 var event = new touchEvent({ |
|
96 touches: [touch], |
|
97 targetTouches: [touch], |
|
98 changedTouches: [touch] |
|
99 }); |
|
100 return event; |
|
101 } |
|
102 |
|
103 function runTests() { |
|
104 var d0 = document.getElementById("d0"); |
|
105 var d1 = document.getElementById("d1"); |
|
106 var d2 = document.getElementById("d2"); |
|
107 var d3 = document.getElementById("d3"); |
|
108 |
|
109 // Test Pointer firing before any mouse/touch original source |
|
110 |
|
111 var mouseDownTriggered = 0; |
|
112 var pointerDownTriggered = 0; |
|
113 var touchDownTriggered = 0; |
|
114 var touchCancelTriggered = 0; |
|
115 var pointerCancelTriggered = 0; |
|
116 |
|
117 d0.onmousedown = function(e) { |
|
118 mouseDownTriggered = 1; |
|
119 is(pointerDownTriggered , 1, "Mouse event must be triggered after pointer event!"); |
|
120 }; |
|
121 d0.ontouchstart = function(e) { |
|
122 touchDownTriggered = 1; |
|
123 is(touchDownTriggered , 1, "Touch event must be triggered after pointer event!"); |
|
124 } |
|
125 d0.ontouchcancel = function(e) { |
|
126 touchCancelTriggered = 1; |
|
127 is(pointerCancelTriggered, 1, "Touch cancel event must be triggered after pointer event!"); |
|
128 } |
|
129 d0.onpointerdown = function(e) { |
|
130 pointerDownTriggered = 1; |
|
131 is(mouseDownTriggered, 0, "Pointer event must be triggered before mouse event!"); |
|
132 is(touchDownTriggered, 0, "Pointer event must be triggered before touch event!"); |
|
133 }; |
|
134 d0.addEventListener("pointercancel", function(ev) { |
|
135 d0.removeEventListener("pointercancel", arguments.callee, false); |
|
136 is(ev.pointerId, 0, "Correct default pointerId"); |
|
137 is(ev.bubbles, true, "bubbles should be true"); |
|
138 is(ev.cancelable, false, "pointercancel cancelable should be false "); |
|
139 pointerCancelTriggered = 1; |
|
140 is(touchCancelTriggered, 0, "Pointer event must be triggered before touch event!"); |
|
141 }, false); |
|
142 |
|
143 // Test pointer event generated from mouse event |
|
144 synthesizeMouse(d1, 3, 3, { type: "mousemove"}); |
|
145 synthesizeMouse(d1, 3, 3, { type: "mousedown"}); |
|
146 synthesizeMouse(d1, 3, 3, { type: "mouseup"}); |
|
147 |
|
148 // Test pointer event generated from touch event |
|
149 pointerDownTriggered = 0; |
|
150 mouseDownTriggered = 0; |
|
151 |
|
152 var cwu = SpecialPowers.getDOMWindowUtils(window); |
|
153 var event1 = getTouchEventForTarget(d1, cwu, 0); |
|
154 sendTouchEvent(cwu, "touchmove", event1, 0); |
|
155 sendTouchEvent(cwu, "touchstart", event1, 0); |
|
156 // Test Touch to Pointer Cancel |
|
157 sendTouchEvent(cwu, "touchcancel", event1, 0); |
|
158 |
|
159 // Check Pointer enter/leave from mouse generated event |
|
160 var mouseEnterTriggered = 0; |
|
161 var pointerEnterTriggered = 0; |
|
162 d2.onpointerenter = function(e) { |
|
163 pointerEnterTriggered = 1; |
|
164 is(e.bubbles, false, "bubbles should be false"); |
|
165 is(e.cancelable, false, "cancelable should be false"); |
|
166 is(mouseEnterTriggered, 0, "Pointer event must be triggered before mouse event!"); |
|
167 }; |
|
168 d2.onmouseenter = function(e) { |
|
169 mouseEnterTriggered = 1; |
|
170 is(pointerEnterTriggered , 1, "Mouse event must be triggered after pointer event!"); |
|
171 }; |
|
172 synthesizeMouse(d2, 3, 3, { type: "mousemove"}); |
|
173 d2.onmouseenter = function(e) {} |
|
174 |
|
175 // Test Multi Pointer enter/leave for pointers generated from Mouse and Touch at the same time |
|
176 // Enter mouse and touch generated pointers to different elements |
|
177 var d1enterCount = 0; |
|
178 var d2enterCount = 0; |
|
179 var d3enterCount = 0; |
|
180 var d1leaveCount = 0; |
|
181 var d2leaveCount = 0; |
|
182 var d3leaveCount = 0; |
|
183 var mousePointerEnterLeaveCount = 0; |
|
184 var touchPointerEnterLeaveCount = 0; |
|
185 |
|
186 var checkPointerType = function(pointerType) { |
|
187 if (pointerType == "mouse") { |
|
188 ++mousePointerEnterLeaveCount; |
|
189 } else if (pointerType == "touch") { |
|
190 ++touchPointerEnterLeaveCount; |
|
191 } |
|
192 }; |
|
193 |
|
194 d1.onpointerenter = function(e) { |
|
195 ++d1enterCount; |
|
196 is(e.bubbles, false, "bubbles should be false"); |
|
197 is(e.cancelable, false, "cancelable should be false"); |
|
198 checkPointerType(e.pointerType); |
|
199 }; |
|
200 d2.onpointerenter = function(e) { |
|
201 ++d2enterCount; |
|
202 is(e.bubbles, false, "bubbles should be false"); |
|
203 is(e.cancelable, false, "cancelable should be false"); |
|
204 checkPointerType(e.pointerType); |
|
205 }; |
|
206 d3.onpointerenter = function(e) { |
|
207 ++d3enterCount; |
|
208 is(e.bubbles, false, "bubbles should be false"); |
|
209 is(e.cancelable, false, "cancelable should be false"); |
|
210 checkPointerType(e.pointerType); |
|
211 }; |
|
212 d1.onpointerleave = function(e) { |
|
213 ++d1leaveCount; |
|
214 is(e.bubbles, false, "bubbles should be false"); |
|
215 is(e.cancelable, false, "cancelable should be false"); |
|
216 checkPointerType(e.pointerType); |
|
217 }; |
|
218 d2.onpointerleave = function(e) { |
|
219 ++d2leaveCount; |
|
220 is(e.bubbles, false, "bubbles should be false"); |
|
221 is(e.cancelable, false, "cancelable should be false"); |
|
222 checkPointerType(e.pointerType); |
|
223 }; |
|
224 d3.onpointerleave = function(e) { |
|
225 ++d3leaveCount; |
|
226 is(e.bubbles, false, "bubbles should be false"); |
|
227 is(e.cancelable, false, "cancelable should be false"); |
|
228 checkPointerType(e.pointerType); |
|
229 }; |
|
230 |
|
231 synthesizeMouse(d1, 3, 3, { type: "mousemove"}); |
|
232 sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d3, cwu, 3), 0); |
|
233 is(touchPointerEnterLeaveCount, 1, "Wrong touch enterLeave count for!"); |
|
234 is(mousePointerEnterLeaveCount, 2, "Wrong mouse enterLeave count for!"); |
|
235 |
|
236 is(d1enterCount, 1, "Wrong enter count for! d1"); |
|
237 is(d2leaveCount, 1, "Wrong leave count for! d2"); |
|
238 is(d3enterCount, 1, "Wrong enter count for! d3"); |
|
239 |
|
240 sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 3), 0); |
|
241 synthesizeMouse(d3, 3, 3, { type: "mousemove"}); |
|
242 is(touchPointerEnterLeaveCount, 3, "Wrong touch enterLeave count for!"); |
|
243 is(mousePointerEnterLeaveCount, 4, "Wrong mouse enterLeave count for!"); |
|
244 |
|
245 is(d3leaveCount, 1, "Wrong leave count for! d3"); |
|
246 is(d1leaveCount, 1, "Wrong leave count for! d1"); |
|
247 is(d1enterCount, 2, "Wrong enter count for! d1"); |
|
248 is(d3enterCount, 2, "Wrong enter count for! d3"); |
|
249 |
|
250 sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d2, cwu, 3), 0); |
|
251 synthesizeMouse(d2, 3, 3, { type: "mousemove"}); |
|
252 is(touchPointerEnterLeaveCount, 5, "Wrong touch enterLeave count for!"); |
|
253 is(mousePointerEnterLeaveCount, 6, "Wrong mouse enterLeave count for!"); |
|
254 |
|
255 is(d1leaveCount, 2, "Wrong leave count for! d1"); |
|
256 is(d2enterCount, 2, "Wrong enter count for! d2"); |
|
257 is(d3leaveCount, 2, "Wrong leave count for! d3"); |
|
258 |
|
259 sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 3), 0); |
|
260 synthesizeMouse(d1, 3, 3, { type: "mousemove"}); |
|
261 is(touchPointerEnterLeaveCount, 7, "Wrong touch enterLeave count for!"); |
|
262 is(mousePointerEnterLeaveCount, 8, "Wrong mouse enterLeave count for!"); |
|
263 |
|
264 is(d2leaveCount, 3, "Wrong leave count for! d2"); |
|
265 is(d1enterCount, 4, "Wrong enter count for! d1"); |
|
266 |
|
267 // Test for pointer buttons when it generated from mousemove event |
|
268 d1.onpointermove = function(e) { |
|
269 is(e.buttons, 0, "Buttons must be 0 on pointer generated from mousemove"); |
|
270 is(e.button, -1, "Button must be -1 on pointer generated from mousemove when no buttons pressed"); |
|
271 is(e.pointerType, "mouse", "Pointer type must be mouse"); |
|
272 }; |
|
273 cwu.sendMouseEvent("mousemove", 4, 4, 0, 0, 0, false, 0, 0); |
|
274 |
|
275 d1.onpointermove = function(e) { |
|
276 is(e.buttons, 1, "Buttons must be 1 on pointer generated from touch event"); |
|
277 is(e.button, 0, "Button must be 0 on pointer generated from touch eventd"); |
|
278 is(e.pointerType, "touch", "Pointer type must be touch"); |
|
279 }; |
|
280 sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 2), 0); |
|
281 |
|
282 // Test for cancel trigger pointerOut (Touch Pointer must be at d1 now) |
|
283 pointerCancelTriggered = 0; |
|
284 var pointerOutTriggeredForCancelEvent = 0; |
|
285 var pointerLeaveTriggeredForCancelEvent = 0; |
|
286 d1.onpointerout = function(e) { |
|
287 if (pointerOutTriggeredForCancelEvent == 0) { |
|
288 is(e.pointerId, 3, "Wrong Pointer type, should be id from Touch event"); |
|
289 is(e.pointerType, "touch", "Wrong Pointer type, should be touch type"); |
|
290 } else { |
|
291 is(e.pointerId, 0, "Wrong Pointer type, should be id from mouse event"); |
|
292 is(e.pointerType, "mouse", "Wrong Pointer type, should be mouse type"); |
|
293 } |
|
294 pointerOutTriggeredForCancelEvent = 1; |
|
295 }; |
|
296 d1.onpointerleave = function(e) { |
|
297 is(pointerOutTriggeredForCancelEvent, 1, "Pointer Out must be dispatched bedore Pointer leave"); |
|
298 if (pointerLeaveTriggeredForCancelEvent == 0) { |
|
299 is(e.pointerId, 3, "Wrong Pointer type, should be id from Touch event"); |
|
300 is(e.pointerType, "touch", "Wrong Pointer type, should be touch type"); |
|
301 } else { |
|
302 is(e.pointerId, 0, "Wrong Pointer type, should be id from mouse event"); |
|
303 is(e.pointerType, "mouse", "Wrong Pointer type, should be mouse type"); |
|
304 } |
|
305 pointerLeaveTriggeredForCancelEvent = 1; |
|
306 } |
|
307 |
|
308 sendTouchEvent(cwu, "touchcancel", getTouchEventForTarget(d1, cwu, 3), 0); |
|
309 is(pointerOutTriggeredForCancelEvent, 1, "Pointer Out not dispatched on PointerCancel"); |
|
310 is(pointerLeaveTriggeredForCancelEvent, 1, "Pointer Leave not dispatched on PointerCancel"); |
|
311 |
|
312 finishTest(); |
|
313 } |
|
314 |
|
315 function finishTest() { |
|
316 // Let window.onerror have a chance to fire |
|
317 setTimeout(function() { |
|
318 setTimeout(function() { |
|
319 window.parent.postMessage("SimpleTest.finish();", "*"); |
|
320 }, 0); |
|
321 }, 0); |
|
322 } |
|
323 |
|
324 window.onload = function () { |
|
325 SpecialPowers.pushPrefEnv({ |
|
326 "set": [ |
|
327 ["dom.w3c_pointer_events.enabled", true], |
|
328 ] |
|
329 }, runTests); |
|
330 } |
|
331 |
|
332 SimpleTest.waitForExplicitFinish(); |
|
333 |
|
334 </script> |
|
335 </pre> |
|
336 <div id="d0"> |
|
337 Test divs -- |
|
338 <div id="d1">t</div><div id="d2">t</div><div id="d3">t</div> |
|
339 -- |
|
340 </div> |
|
341 </body> |
|
342 </html> |