<!DOCTYPE HTML> <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=508906 --> <head> <title>Test for Bug 603008</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=508906">Mozilla Bug 603008</a> <p id="display"></p> <div id="content" style="display: none"> </div> <pre id="test"> <script class="testbody" type="application/javascript;version=1.8"> /** Test for Bug 306008 - Touch* Events **/ let tests = [], testTarget, parent; let touch = { id: 0, point: {x: 0, y: 0}, radius: {x: 0, y: 0}, rotation: 0, force: 0.5, target: null } function nextTest() { if (tests.length) SimpleTest.executeSoon(tests.shift()); } function random() { return Math.floor(Math.random() * 100); } function checkEvent(aFakeEvent) { return function(aEvent) { is(aFakeEvent.ctrlKey, aEvent.ctrlKey, "Correct ctrlKey"); is(aFakeEvent.altKey, aEvent.altKey, "Correct altKey"); is(aFakeEvent.shiftKey, aEvent.shiftKey, "Correct shiftKey"); is(aFakeEvent.metaKey, aEvent.metaKey, "Correct metaKey"); checkTouches(aFakeEvent.touches, aEvent.touches); checkTouches(aFakeEvent.targetTouches, aEvent.targetTouches); checkTouches(aFakeEvent.changedTouches, aEvent.changedTouches); } } function checkTouches(aTouches1, aTouches2) { is(aTouches1.length, aTouches2.length, "Correct touches length"); for (var i = 0; i < aTouches1.length; i++) { checkTouch(aTouches1[i], aTouches2[i]); } } function checkTouch(aFakeTouch, aTouch) { is(aFakeTouch.identifier, aTouch.identifier, "Touch has correct identifier"); is(aFakeTouch.target, aTouch.target, "Touch has correct target"); is(aFakeTouch.page.x, aTouch.pageX, "Touch has correct pageX"); is(aFakeTouch.page.y, aTouch.pageY, "Touch has correct pageY"); is(aFakeTouch.page.x + Math.round(window.mozInnerScreenX), aTouch.screenX, "Touch has correct screenX"); is(aFakeTouch.page.y + Math.round(window.mozInnerScreenY), aTouch.screenY, "Touch has correct screenY"); is(aFakeTouch.page.x, aTouch.clientX, "Touch has correct clientX"); is(aFakeTouch.page.y, aTouch.clientY, "Touch has correct clientY"); is(aFakeTouch.radius.x, aTouch.radiusX, "Touch has correct radiusX"); is(aFakeTouch.radius.y, aTouch.radiusY, "Touch has correct radiusY"); is(aFakeTouch.rotationAngle, aTouch.rotationAngle, "Touch has correct rotationAngle"); is(aFakeTouch.force, aTouch.force, "Touch has correct force"); } function sendTouchEvent(windowUtils, aType, aEvent, aModifiers) { var ids = [], xs=[], ys=[], rxs = [], rys = [], rotations = [], forces = []; for (var touchType of ["touches", "changedTouches", "targetTouches"]) { for (var i = 0; i < aEvent[touchType].length; i++) { if (ids.indexOf(aEvent[touchType][i].identifier) == -1) { ids.push(aEvent[touchType][i].identifier); xs.push(aEvent[touchType][i].page.x); ys.push(aEvent[touchType][i].page.y); rxs.push(aEvent[touchType][i].radius.x); rys.push(aEvent[touchType][i].radius.y); rotations.push(aEvent[touchType][i].rotationAngle); forces.push(aEvent[touchType][i].force); } } } return windowUtils.sendTouchEvent(aType, ids, xs, ys, rxs, rys, rotations, forces, ids.length, aModifiers, 0); } function touchEvent(aOptions) { if (!aOptions) { aOptions = {}; } this.ctrlKey = aOptions.ctrlKey || false; this.altKey = aOptions.altKey || false; this.shiftKey = aOptions.shiftKey || false; this.metaKey = aOptions.metaKey || false; this.touches = aOptions.touches || []; this.targetTouches = aOptions.targetTouches || []; this.changedTouches = aOptions.changedTouches || []; } function testtouch(aOptions) { if (!aOptions) aOptions = {}; this.identifier = aOptions.identifier || 0; this.target = aOptions.target || 0; this.page = aOptions.page || {x: 0, y: 0}; this.radius = aOptions.radius || {x: 0, y: 0}; this.rotationAngle = aOptions.rotationAngle || 0; this.force = aOptions.force || 1; } function testSingleTouch(name) { let cwu = SpecialPowers.getDOMWindowUtils(window); let target = document.getElementById("testTarget"); let target2 = document.getElementById("testTarget2"); let bcr = target.getBoundingClientRect(); let bcr2 = target2.getBoundingClientRect(); let touch1 = new testtouch({ page: {x: Math.round(bcr.left + bcr.width/2), y: Math.round(bcr.top + bcr.height/2)}, target: target }); let event = new touchEvent({ touches: [touch1], targetTouches: [touch1], changedTouches: [touch1] }); // test touchstart event fires correctly var checkFunction = checkEvent(event); window.addEventListener("touchstart", checkFunction, false); sendTouchEvent(cwu, "touchstart", event, 0); window.removeEventListener("touchstart", checkFunction, false); // test touchmove event fires correctly event.touches[0].page.x -= 1; event.targetTouches[0].page.x -= 1; event.changedTouches[0].page.x -= 1; checkFunction = checkEvent(event); window.addEventListener("touchmove", checkFunction, false); sendTouchEvent(cwu, "touchmove", event, 0); window.removeEventListener("touchmove", checkFunction, false); // test touchend event fires correctly event.touches = []; event.targetTouches = []; checkFunction = checkEvent(event); window.addEventListener("touchend", checkFunction, false); sendTouchEvent(cwu, "touchend", event, 0); window.removeEventListener("touchend", checkFunction, false); nextTest(); } function testSingleTouch2(name) { // firing a touchstart that includes only one touch will evict any touches in the queue with touchend messages let cwu = SpecialPowers.getDOMWindowUtils(window); let target = document.getElementById("testTarget"); let target2 = document.getElementById("testTarget2"); let bcr = target.getBoundingClientRect(); let bcr2 = target2.getBoundingClientRect(); let touch1 = new testtouch({ identifier: 0, page: {x: Math.round(bcr.left + bcr.width/2), y: Math.round(bcr.top + bcr.height/2)}, target: target }); let event1 = new touchEvent({ touches: [touch1], targetTouches: [touch1], changedTouches: [touch1] }); let touch2 = new testtouch({ identifier: 1, page: {x: Math.round(bcr2.left + bcr2.width/2), y: Math.round(bcr2.top + bcr2.height/2)}, target: target2 }); let event2 = new touchEvent({ touches: [touch2], targetTouches: [touch2], changedTouches: [touch2] }); // test touchstart event fires correctly var checkFunction1 = checkEvent(event1); window.addEventListener("touchstart", checkFunction1, false); sendTouchEvent(cwu, "touchstart", event1, 0); window.removeEventListener("touchstart", checkFunction1, false); event1.touches = []; event1.targetTouches = []; checkFunction1 = checkEvent(event1); var checkFunction2 = checkEvent(event2); window.addEventListener("touchend", checkFunction1, false); window.addEventListener("touchstart", checkFunction2, false); sendTouchEvent(cwu, "touchstart", event2, 0); window.removeEventListener("touchend", checkFunction1, false); window.removeEventListener("touchstart", checkFunction2, false); sendTouchEvent(cwu, "touchstart", event1, 0); nextTest(); } function testMultiTouch(name) { let cwu = SpecialPowers.getDOMWindowUtils(window); let target1 = document.getElementById("testTarget"); let target2 = document.getElementById("testTarget2"); let bcr = target1.getBoundingClientRect(); let bcr2 = target2.getBoundingClientRect(); let touch1 = new testtouch({ identifier: 0, page: {x: Math.round(bcr.left + bcr.width/2), y: Math.round(bcr.top + bcr.height/2)}, target: target1 }); let touch2 = new testtouch({ identifier: 1, page: {x: Math.round(bcr2.left + bcr2.width/2), y: Math.round(bcr2.top + bcr2.height/2)}, target: target2 }); let event = new touchEvent({ touches: [touch1], targetTouches: [touch1], changedTouches: [touch1] }); // test touchstart event fires correctly var checkFunction = checkEvent(event); window.addEventListener("touchstart", checkFunction, false); sendTouchEvent(cwu, "touchstart", event, 0); window.removeEventListener("touchstart", checkFunction, false); event.touches.push(touch2); event.targetTouches = [touch2]; event.changedTouches = [touch2]; window.addEventListener("touchstart", checkFunction, false); sendTouchEvent(cwu, "touchstart", event, 0); window.removeEventListener("touchstart", checkFunction, false); // test moving one touch point event.touches[0].page.x -= 1; event.targetTouches = [event.touches[0]]; event.changedTouches = [event.touches[0]]; window.addEventListener("touchmove", checkFunction, false); sendTouchEvent(cwu, "touchmove", event, 0); window.removeEventListener("touchmove", checkFunction, false); // test moving both touch points -- two touchmove events should fire, one on each target event.touches[0].page.x -= 1; event.touches[1].page.x -= 1; event.targetTouches = event.touches; event.changedTouches = event.touches; var touchMoveEvents = 0; var checkFunction2 = function(aEvent) { is(event.ctrlKey, aEvent.ctrlKey, "Correct ctrlKey"); is(event.altKey, aEvent.altKey, "Correct altKey"); is(event.shiftKey, aEvent.shiftKey, "Correct shiftKey"); is(event.metaKey, aEvent.metaKey, "Correct metaKey"); checkTouches(event.touches, aEvent.touches); checkTouches(event.changedTouches, aEvent.changedTouches); if (aEvent.targetTouches[0].target == target1) { checkTouches([event.touches[0]], aEvent.targetTouches); } else if (aEvent.targetTouches[0].target == target2) { checkTouches([event.touches[1]], aEvent.targetTouches); } else ok(false, "Event target is incorrect: " + event.targetTouches[0].target.nodeName + "#" + event.targetTouches[0].target.id); touchMoveEvents++; }; window.addEventListener("touchmove", checkFunction2, false); sendTouchEvent(cwu, "touchmove", event, 0); ok(touchMoveEvents, 2, "Correct number of touchmove events fired"); window.removeEventListener("touchmove", checkFunction2, false); // test removing just one finger var expected = new touchEvent({ touches: [touch2], targetTouches: [], changedTouches: [touch1] }); checkFunction = checkEvent(expected); event.touches = []; event.targetTouches = []; event.changedTouches = [touch1]; // test removing the other finger window.addEventListener("touchend", checkFunction, false); sendTouchEvent(cwu, "touchend", event, 0); window.removeEventListener("touchend", checkFunction, false); event.touches = []; event.targetTouches = []; event.changedTouches = [touch2]; checkFunction = checkEvent(event); window.addEventListener("touchend", checkFunction, false); sendTouchEvent(cwu, "touchend", event, 0); window.removeEventListener("touchend", checkFunction, false); nextTest(); } function testTouchChanged() { let cwu = SpecialPowers.getDOMWindowUtils(window); let target1 = document.getElementById("testTarget"); let bcr = target1.getBoundingClientRect(); let touch1 = new testtouch({ identifier: 0, page: {x: Math.round(bcr.left + bcr.width/2), y: Math.round(bcr.top + bcr.height/2)}, target: target1 }); let event = new touchEvent({ touches: [touch1], targetTouches: [touch1], changedTouches: [touch1] }); var checkFunction = checkEvent(event); sendTouchEvent(cwu, "touchstart", event, 0); var moveEvents = 0; function onMove(aEvent) { moveEvents++; } window.addEventListener("touchmove", onMove, false); // the first touchmove should always fire an event sendTouchEvent(cwu, "touchmove", event, 0); // changing nothing should not fire a touchmove event sendTouchEvent(cwu, "touchmove", event, 0); // test moving x event.touches[0].page.x -= 1; sendTouchEvent(cwu, "touchmove", event, 0); // test moving y event.touches[0].page.y -= 1; sendTouchEvent(cwu, "touchmove", event, 0); // test changing y radius event.touches[0].radius.y += 1; sendTouchEvent(cwu, "touchmove", event, 0); // test changing x radius event.touches[0].radius.x += 1; sendTouchEvent(cwu, "touchmove", event, 0); // test changing rotationAngle event.touches[0].rotationAngle += 1; sendTouchEvent(cwu, "touchmove", event, 0); // test changing force event.touches[0].force += 1; sendTouchEvent(cwu, "touchmove", event, 0); // changing nothing again sendTouchEvent(cwu, "touchmove", event, 0); is(moveEvents, 7, "Six move events fired"); window.removeEventListener("touchmove", onMove, false); sendTouchEvent(cwu, "touchend", event, 0); nextTest(); } function testPreventDefault() { let cwu = SpecialPowers.getDOMWindowUtils(window); let target = document.getElementById("testTarget"); let target2 = document.getElementById("testTarget2"); let bcr = target.getBoundingClientRect(); let bcr2 = target2.getBoundingClientRect(); let touch1 = new testtouch({ page: {x: bcr.left + bcr.width/2, y: bcr.top + bcr.height/2}, target: target }); let event = new touchEvent({ touches: [touch1], targetTouches: [touch1], changedTouches: [touch1] }); let preventFunction = function(aEvent) { aEvent.preventDefault(); } let tests = [ [{ name: "touchstart", prevent: false }, { name: "touchmove", prevent: false }, { name: "touchmove", prevent: false }, { name: "touchend", prevent: false }], [{ name: "touchstart", prevent: true, doPrevent: true }, { name: "touchmove", prevent: false }, { name: "touchmove", prevent: false }, { name: "touchend", prevent: false }], [{ name: "touchstart", prevent: false }, { name: "touchmove", prevent: true, doPrevent: true }, { name: "touchmove", prevent: false }, { name: "touchend", prevent: false }], [{ name: "touchstart", prevent: false }, { name: "touchmove", prevent: false }, { name: "touchmove", prevent: false, doPrevent: true }, { name: "touchend", prevent: false }], [{ name: "touchstart", prevent: false }, { name: "touchmove", prevent: false }, { name: "touchmove", prevent: false }, { name: "touchend", prevent: true, doPrevent: true }] ]; var dotest = function(aTest) { if (aTest.doPrevent) { target.addEventListener(aTest.name, preventFunction, false); } if (aTest.name == "touchmove") { touch1.page.x++; event.touches[0] = touch1; } is(sendTouchEvent(cwu, aTest.name, event, 0), aTest.prevent, "Got correct status"); if (aTest.doPrevent) target.removeEventListener(aTest.name, preventFunction, false); } for (var i = 0; i < tests.length; i++) { for (var j = 0; j < tests[i].length; j++) { dotest(tests[i][j]); } } nextTest(); } function testRemovingElement() { let cwu = SpecialPowers.getDOMWindowUtils(window); let target = document.getElementById("testTarget"); let bcr = document.getElementById("testTarget").getBoundingClientRect(); let touch1 = new testtouch({ page: {x: bcr.left + bcr.width/2, y: bcr.top + bcr.height/2}, }); let e = new touchEvent({ touches: [touch1], targetTouches: [touch1], changedTouches: [touch1] }); var touchEvents = 0; var removeTarget = function(aEvent) { aEvent.target.parentNode.removeChild(aEvent.target); }; var checkTarget = function(aEvent) { is(aEvent.target, target, "Event has correct target"); touchEvents++; }; target.addEventListener("touchstart", removeTarget, false); target.addEventListener("touchmove", checkTarget, false); target.addEventListener("touchend", checkTarget, false); sendTouchEvent(cwu, "touchstart", e, 0); e.touches[0].page.x++; sendTouchEvent(cwu, "touchmove", e, 0); sendTouchEvent(cwu, "touchend", e, 0); target.removeEventListener("touchstart", removeTarget, false); target.removeEventListener("touchmove", checkTarget, false); target.removeEventListener("touchend", checkTarget, false); is(touchEvents, 2, "Check target was called twice"); nextTest(); } function testNAC() { let cwu = SpecialPowers.getDOMWindowUtils(window); let target = document.getElementById("testTarget3"); let bcr = target.getBoundingClientRect(); let touch1 = new testtouch({ page: {x: Math.round(bcr.left + bcr.width/2), y: Math.round(bcr.top + bcr.height/2)}, target: target }); let event = new touchEvent({ touches: [touch1], targetTouches: [touch1], changedTouches: [touch1] }); // test touchstart event fires correctly var checkFunction = checkEvent(event); window.addEventListener("touchstart", checkFunction, false); sendTouchEvent(cwu, "touchstart", event, 0); window.removeEventListener("touchstart", checkFunction, false); sendTouchEvent(cwu, "touchend", event, 0); nextTest(); } function doTest() { tests.push(testSingleTouch); tests.push(testSingleTouch2); tests.push(testMultiTouch); tests.push(testPreventDefault); tests.push(testTouchChanged); tests.push(testRemovingElement); tests.push(testNAC); tests.push(function() { SimpleTest.finish(); }); nextTest(); } SimpleTest.waitForExplicitFinish(); addLoadEvent(doTest); </script> </pre> <div id="parent"> <span id="testTarget" style="padding: 5px; border: 1px solid black;">testTarget</span> <span id="testTarget2" style="padding: 5px; border: 1px solid blue;">testTarget</span> <input type="text" id="testTarget3"> </div> </body> </html>