LayoutTests/ChangeLog

 12010-12-21 Jonathan Dixon <joth@chromium.org>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 TouchEvents does not support multi-touch on a page with multiple touch targets
 6 https://bugs.webkit.org/show_bug.cgi?id=51258
 7
 8 * fast/events/touch/multi-touch-grouped-targets-expected.txt: Added.
 9 * fast/events/touch/multi-touch-grouped-targets.html: Added.
 10 * fast/events/touch/script-tests/multi-touch-grouped-targets.js: Added.
 11 (declareTouchHandler.return.shouldBe):
 12 (declareTouchHandler):
 13 (touchEndHandler):
 14 Added new test that ensures both test targets receive events when hit by multiple
 15 touch points from the same platform event.
 16
 17 * fast/events/touch/script-tests/touch-target-limited.js:
 18 (declareTouchStart.return.touchStartHandler):
 19 (declareTouchStart):
 20 (declareTouchMove.return.touchMoveHandler):
 21 (declareTouchMove):
 22 * fast/events/touch/script-tests/touch-target.js:
 23 (declareTouchStart.return.touchStartHandler):
 24 (declareTouchStart):
 25 (declareTouchMove.return.touchMoveHandler):
 26 (declareTouchMove):
 27 * fast/events/touch/touch-target-expected.txt:
 28 * fast/events/touch/touch-target-limited-expected.txt:
 29 Updated & rebaselined existing tests to reflect new expectations.
 30
1312010-12-09 Ryosuke Niwa <rniwa@webkit.org>
232
333 Reviewed by Ojan Vafai.

LayoutTests/fast/events/touch/multi-touch-grouped-targets-expected.txt

 1Tests that the an event is sent for every touch listener, and target touches contains all the points for that target
 2
 3On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 4
 5
 6
 7PASS event.touches.length is 3
 8PASS event.changedTouches.length is 3
 9PASS event.touches.length is 3
 10PASS event.changedTouches.length is 3
 11PASS event.touches.length is 3
 12PASS event.changedTouches.length is 3
 13PASS event.touches.length is 3
 14PASS event.changedTouches.length is 3
 15PASS event.type is "touchend"
 16PASS event.touches.length is 0
 17PASS event.targetTouches.length is 0
 18PASS event.changedTouches.length is 3
 19PASS event.type is "touchend"
 20PASS event.touches.length is 0
 21PASS event.targetTouches.length is 0
 22PASS event.changedTouches.length is 3
 23PASS successfullyParsed is true
 24
 25TEST COMPLETE
 26

LayoutTests/fast/events/touch/multi-touch-grouped-targets.html

 1<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
 2<html>
 3<head>
 4<link rel="stylesheet" href="../../js/resources/js-test-style.css">
 5<script src="../../js/resources/js-test-pre.js"></script>
 6<script src="../../js/resources/js-test-post-function.js"></script>
 7<!--
 8 Touch tests that involve the ontouchstart, ontouchmove, ontouchend or ontouchcancel callbacks
 9 should be written in an asynchronous fashion so they can be run on mobile platforms like Android.
 10 This template will generate an asynchronous style test by using the js-test-post-function script.
 11 You will need to invoke isSuccessfullyParsed() in your test script when the test completes.
 12-->
 13</head>
 14<body>
 15<p id="description"></p>
 16<div id="console"></div>
 17<script src="script-tests/multi-touch-grouped-targets.js"></script>
 18</body>
 19</html>

LayoutTests/fast/events/touch/script-tests/multi-touch-grouped-targets.js

 1var targetsDiv = document.createElement("div");
 2targetsDiv.id = "targetsDiv";
 3
 4var div1 = document.createElement("div");
 5div1.id = "targetA";
 6div1.style.width = "100px";
 7div1.style.height = "100px";
 8div1.style.backgroundColor = "blue";
 9
 10var div2 = document.createElement("div");
 11div2.id = "targetB";
 12div2.style.width = "100px";
 13div2.style.height = "100px";
 14div2.style.backgroundColor = "green";
 15
 16document.body.insertBefore(targetsDiv, document.getElementById('console'));
 17targetsDiv.appendChild(div1);
 18targetsDiv.appendChild(document.createElement('br'));
 19targetsDiv.appendChild(div2);
 20
 21function declareTouchHandler(div_id, expectedTargetTouches)
 22{
 23 return function ()
 24 {
 25 // Do not use the parameters (div_id, expectedTargetTouches) in shouldBe.. calls, as
 26 // the order of event dispatch is not deterministic across executions and reordering
 27 // ofthe PASS debug output details would cause the test to be unreliable.
 28 shouldBe('event.touches.length', '3');
 29 if (event.targetTouches.length != expectedTargetTouches)
 30 testFailed('Wrong targetTouch length: ' + event.targetTouches.length + ' vs ' + expectedTargetTouches);
 31 for (var i = 0; i < event.targetTouches.length; i++)
 32 {
 33 if (event.targetTouches[i].target.id != div_id)
 34 testFailed('Incorrect targetTouch ID: ' + event.targetTouches[i].target.id + ' vs ' + div_id);
 35 }
 36 shouldBe('event.changedTouches.length', '3');
 37 }
 38}
 39
 40var endCount = 0;
 41function touchEndHandler()
 42{
 43 shouldBeEqualToString('event.type', 'touchend');
 44
 45 shouldBe('event.touches.length', '0');
 46 shouldBe('event.targetTouches.length', '0');
 47 shouldBe('event.changedTouches.length', '3');
 48
 49 if (++endCount == 2)
 50 {
 51 successfullyParsed = true;
 52 layoutTestController.notifyDone();
 53 isSuccessfullyParsed();
 54 }
 55}
 56
 57div1.addEventListener("touchstart", declareTouchHandler('targetA', '2'), false);
 58div1.addEventListener("touchmove", declareTouchHandler('targetA', '2'), false);
 59div1.addEventListener("touchend", touchEndHandler, false);
 60
 61div2.addEventListener("touchstart", declareTouchHandler('targetB', '1'), false);
 62div2.addEventListener("touchmove", declareTouchHandler('targetB', '1'), false);
 63div2.addEventListener("touchend", touchEndHandler, false);
 64
 65description("Tests that the an event is sent for every touch listener, and target touches contains all the points for that target");
 66
 67if (window.layoutTestController) {
 68 layoutTestController.waitUntilDone();
 69}
 70
 71if (window.eventSender) {
 72 eventSender.clearTouchPoints();
 73 eventSender.addTouchPoint(50, 150);
 74 eventSender.addTouchPoint(50, 250);
 75 eventSender.addTouchPoint(50, 150);
 76 eventSender.touchStart();
 77
 78 eventSender.updateTouchPoint(0, 200, 150);
 79 eventSender.updateTouchPoint(1, 300, 250);
 80 eventSender.updateTouchPoint(2, 400, 150);
 81 eventSender.touchMove();
 82
 83 eventSender.releaseTouchPoint(0);
 84 eventSender.releaseTouchPoint(1);
 85 eventSender.releaseTouchPoint(2);
 86 eventSender.touchEnd();
 87} else
 88 debug('This test requires DRT.');
 89

LayoutTests/fast/events/touch/script-tests/touch-target-limited.js

@@div2.style.width = "100px";
1313div2.style.height = "100px";
1414div2.style.backgroundColor = "green";
1515
16 var touchStartCount = 0;
17 var touchMoveCount = 0;
18 
1916document.body.insertBefore(targetsDiv, document.getElementById('console'));
2017targetsDiv.appendChild(div1);
2118targetsDiv.appendChild(document.createElement('br'));
2219targetsDiv.appendChild(div2);
2320
24 function touchStartHandler()
 21function declareTouchStart()
2522{
26  shouldBeEqualToString('event.type', 'touchstart');
27  switch (touchStartCount) {
28  case 0:
29  shouldBeEqualToString('event.touches[0].target.id', div1.id);
30  shouldBeEqualToString('event.touches[1].target.id', div2.id);
31  break;
32  case 1:
33  shouldBeEqualToString('event.touches[0].target.id', div2.id);
34  shouldBeEqualToString('event.touches[1].target.id', div1.id);
35  break;
 23 var touchStartCount = 0;
 24 return function touchStartHandler()
 25 {
 26 shouldBeEqualToString('event.type', 'touchstart');
 27 switch (touchStartCount) {
 28 case 0:
 29 shouldBeEqualToString('event.touches[0].target.id', div1.id);
 30 shouldBeEqualToString('event.touches[1].target.id', div2.id);
 31 break;
 32 case 1:
 33 shouldBeEqualToString('event.touches[0].target.id', div2.id);
 34 shouldBeEqualToString('event.touches[1].target.id', div1.id);
 35 break;
 36 }
 37 shouldBe('event.targetTouches.length', '1');
 38
 39 touchStartCount++;
3640 }
37 
38  touchStartCount++;
3941}
4042
41 function touchMoveHandler()
 43function declareTouchMove(div_id)
4244{
43  shouldBeEqualToString('event.type', 'touchmove');
44  switch (touchMoveCount) {
45  case 0:
46  case 1:
47  shouldBeEqualToString('event.touches[0].target.id', div1.id);
48  shouldBeEqualToString('event.touches[1].target.id', div2.id);
49  break;
50  case 2:
51  shouldBeEqualToString('event.touches[0].target.id', div2.id);
52  shouldBeEqualToString('event.touches[1].target.id', div1.id);
53  break;
54  }
55 
56  if (++touchMoveCount == 3)
 45 var touchMoveCount = 0;
 46 return function touchMoveHandler()
5747 {
58  successfullyParsed = true;
59  layoutTestController.notifyDone();
60  isSuccessfullyParsed();
 48 shouldBeEqualToString('event.type', 'touchmove');
 49 switch (touchMoveCount) {
 50 case 0:
 51 case 1:
 52 shouldBeEqualToString('event.touches[0].target.id', div1.id);
 53 shouldBeEqualToString('event.touches[1].target.id', div2.id);
 54 break;
 55 case 2:
 56 shouldBeEqualToString('event.touches[0].target.id', div2.id);
 57 shouldBeEqualToString('event.touches[1].target.id', div1.id);
 58 break;
 59 }
 60 shouldBe('event.targetTouches.length', '1');
 61
 62 if (++touchMoveCount == 3)
 63 {
 64 successfullyParsed = true;
 65 layoutTestController.notifyDone();
 66 isSuccessfullyParsed();
 67 }
6168 }
6269}
6370
64 div1.addEventListener("touchstart", touchStartHandler, false);
65 div1.addEventListener("touchmove", touchMoveHandler, false);
 71div1.addEventListener("touchstart", declareTouchStart(), false);
 72div1.addEventListener("touchmove", declareTouchMove(), false);
6673
67 div2.addEventListener("touchstart", touchStartHandler, false);
68 div2.addEventListener("touchmove", touchMoveHandler, false);
 74div2.addEventListener("touchstart", declareTouchStart(), false);
 75div2.addEventListener("touchmove", declareTouchMove(), false);
6976
7077description("Tests that the target of touches match the element where the event originated, not where the touch is currently occurring. This is a limited version of test touch-target.html that avoids the situation where one touch point is released while another is maintained.");
7178

LayoutTests/fast/events/touch/script-tests/touch-target.js

@@div2.style.width = "100px";
1313div2.style.height = "100px";
1414div2.style.backgroundColor = "green";
1515
16 var touchStartCount = 0;
17 var touchMoveCount = 0;
18 
1916document.body.insertBefore(targetsDiv, document.getElementById('console'));
2017targetsDiv.appendChild(div1);
2118targetsDiv.appendChild(document.createElement('br'));
2219targetsDiv.appendChild(div2);
2320
24 function touchStartHandler()
 21function declareTouchStart()
2522{
26  shouldBeEqualToString('event.type', 'touchstart');
27  switch (touchStartCount) {
28  case 0:
29  shouldBeEqualToString('event.touches[0].target.id', div1.id);
30  shouldBeEqualToString('event.touches[1].target.id', div2.id);
31  break;
32  case 1:
33  shouldBeEqualToString('event.touches[0].target.id', div2.id);
34  shouldBeEqualToString('event.touches[1].target.id', div1.id);
35  break;
 23 var touchStartCount = 0;
 24 return function touchStartHandler()
 25 {
 26 shouldBeEqualToString('event.type', 'touchstart');
 27 switch (touchStartCount) {
 28 case 0:
 29 shouldBeEqualToString('event.touches[0].target.id', div1.id);
 30 shouldBeEqualToString('event.touches[1].target.id', div2.id);
 31 break;
 32 case 1:
 33 shouldBeEqualToString('event.touches[0].target.id', div2.id);
 34 shouldBeEqualToString('event.touches[1].target.id', div1.id);
 35 break;
 36 }
 37 shouldBe('event.targetTouches.length', '1');
 38
 39 touchStartCount++;
3640 }
37 
38  touchStartCount++;
3941}
4042
41 function touchMoveHandler()
 43function declareTouchMove(div_id)
4244{
43  shouldBeEqualToString('event.type', 'touchmove');
44  switch (touchMoveCount) {
45  case 0:
46  case 1:
47  shouldBeEqualToString('event.touches[0].target.id', div1.id);
48  shouldBeEqualToString('event.touches[1].target.id', div2.id);
49  break;
50  case 2:
51  shouldBeEqualToString('event.touches[0].target.id', div2.id);
52  shouldBeEqualToString('event.touches[1].target.id', div1.id);
53  break;
54  }
55 
56  if (++touchMoveCount == 3)
 45 var touchMoveCount = 0;
 46 return function touchMoveHandler()
5747 {
58  successfullyParsed = true;
59  layoutTestController.notifyDone();
60  isSuccessfullyParsed();
 48 shouldBeEqualToString('event.type', 'touchmove');
 49 switch (touchMoveCount) {
 50 case 0:
 51 case 1:
 52 shouldBeEqualToString('event.touches[0].target.id', div1.id);
 53 shouldBeEqualToString('event.touches[1].target.id', div2.id);
 54 break;
 55 case 2:
 56 shouldBeEqualToString('event.touches[0].target.id', div2.id);
 57 shouldBeEqualToString('event.touches[1].target.id', div1.id);
 58 break;
 59 }
 60 shouldBe('event.targetTouches.length', '1');
 61
 62 if (++touchMoveCount == 3)
 63 {
 64 successfullyParsed = true;
 65 layoutTestController.notifyDone();
 66 isSuccessfullyParsed();
 67 }
6168 }
6269}
6370
64 div1.addEventListener("touchstart", touchStartHandler, false);
65 div1.addEventListener("touchmove", touchMoveHandler, false);
 71div1.addEventListener("touchstart", declareTouchStart(), false);
 72div1.addEventListener("touchmove", declareTouchMove(), false);
6673
67 div2.addEventListener("touchstart", touchStartHandler, false);
68 div2.addEventListener("touchmove", touchMoveHandler, false);
 74div2.addEventListener("touchstart", declareTouchStart(), false);
 75div2.addEventListener("touchmove", declareTouchMove(), false);
6976
7077description("Tests that the target of touches match the element where the event originated, not where the touch is currently occurring.");
7178

LayoutTests/fast/events/touch/touch-target-expected.txt

@@On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
77PASS event.type is "touchstart"
88PASS event.touches[0].target.id is "targetA"
99PASS event.touches[1].target.id is "targetB"
 10PASS event.targetTouches.length is 1
 11PASS event.type is "touchstart"
 12PASS event.touches[0].target.id is "targetA"
 13PASS event.touches[1].target.id is "targetB"
 14PASS event.targetTouches.length is 1
 15PASS event.type is "touchmove"
 16PASS event.touches[0].target.id is "targetA"
 17PASS event.touches[1].target.id is "targetB"
 18PASS event.targetTouches.length is 1
1019PASS event.type is "touchmove"
1120PASS event.touches[0].target.id is "targetA"
1221PASS event.touches[1].target.id is "targetB"
 22PASS event.targetTouches.length is 1
1323PASS event.type is "touchmove"
1424PASS event.touches[0].target.id is "targetA"
1525PASS event.touches[1].target.id is "targetB"
 26PASS event.targetTouches.length is 1
 27PASS event.type is "touchmove"
 28PASS event.touches[0].target.id is "targetA"
 29PASS event.touches[1].target.id is "targetB"
 30PASS event.targetTouches.length is 1
1631PASS event.type is "touchstart"
1732PASS event.touches[0].target.id is "targetB"
1833PASS event.touches[1].target.id is "targetA"
 34PASS event.targetTouches.length is 1
 35PASS event.type is "touchmove"
 36PASS event.touches[0].target.id is "targetB"
 37PASS event.touches[1].target.id is "targetA"
 38PASS event.targetTouches.length is 1
 39PASS successfullyParsed is true
 40
 41TEST COMPLETE
1942PASS event.type is "touchmove"
2043PASS event.touches[0].target.id is "targetB"
2144PASS event.touches[1].target.id is "targetA"
 45PASS event.targetTouches.length is 1
2246PASS successfullyParsed is true
2347
2448TEST COMPLETE

LayoutTests/fast/events/touch/touch-target-limited-expected.txt

@@On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
77PASS event.type is "touchstart"
88PASS event.touches[0].target.id is "targetA"
99PASS event.touches[1].target.id is "targetB"
 10PASS event.targetTouches.length is 1
 11PASS event.type is "touchstart"
 12PASS event.touches[0].target.id is "targetA"
 13PASS event.touches[1].target.id is "targetB"
 14PASS event.targetTouches.length is 1
 15PASS event.type is "touchmove"
 16PASS event.touches[0].target.id is "targetA"
 17PASS event.touches[1].target.id is "targetB"
 18PASS event.targetTouches.length is 1
 19PASS event.type is "touchmove"
 20PASS event.touches[0].target.id is "targetA"
 21PASS event.touches[1].target.id is "targetB"
 22PASS event.targetTouches.length is 1
1023PASS event.type is "touchmove"
1124PASS event.touches[0].target.id is "targetA"
1225PASS event.touches[1].target.id is "targetB"
 26PASS event.targetTouches.length is 1
1327PASS event.type is "touchmove"
1428PASS event.touches[0].target.id is "targetA"
1529PASS event.touches[1].target.id is "targetB"
 30PASS event.targetTouches.length is 1
1631PASS event.type is "touchstart"
1732PASS event.touches[0].target.id is "targetB"
1833PASS event.touches[1].target.id is "targetA"
 34PASS event.targetTouches.length is 1
 35PASS event.type is "touchstart"
 36PASS event.touches[0].target.id is "targetB"
 37PASS event.touches[1].target.id is "targetA"
 38PASS event.targetTouches.length is 1
 39PASS event.type is "touchmove"
 40PASS event.touches[0].target.id is "targetB"
 41PASS event.touches[1].target.id is "targetA"
 42PASS event.targetTouches.length is 1
 43PASS successfullyParsed is true
 44
 45TEST COMPLETE
1946PASS event.type is "touchmove"
2047PASS event.touches[0].target.id is "targetB"
2148PASS event.touches[1].target.id is "targetA"
 49PASS event.targetTouches.length is 1
2250PASS successfullyParsed is true
2351
2452TEST COMPLETE

WebCore/ChangeLog

 12010-12-21 Jonathan Dixon <joth@chromium.org>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 TouchEvents does not support multi-touch on a page with multiple touch targets
 6 https://bugs.webkit.org/show_bug.cgi?id=51258
 7
 8 Test: fast/events/touch/multi-touch-grouped-targets.html
 9
 10 * page/EventHandler.cpp:
 11 (WebCore::eventNameForTouchPointState):
 12 (WebCore::EventHandler::handleTouchEvent):
 13 Redesigned event handler to ensure all event targets involved in a given
 14 multitouch event get the appropriate event(s) fired.
 15
 16 * platform/PlatformTouchPoint.h: Added end-stop marker for State enum.
 17
1182010-12-09 Ryosuke Niwa <rniwa@webkit.org>
219
320 Reviewed by Ojan Vafai.

WebCore/page/EventHandler.cpp

@@void EventHandler::updateLastScrollbarUnderMouse(Scrollbar* scrollbar, bool setL
27862786
27872787#if ENABLE(TOUCH_EVENTS)
27882788
2789 static PassRefPtr<TouchList> assembleTargetTouches(Touch* touchTarget, TouchList* touches)
 2789static const AtomicString& eventNameForTouchPointState(PlatformTouchPoint::State state)
27902790{
2791  RefPtr<TouchList> targetTouches = TouchList::create();
2792 
2793  for (unsigned i = 0; i < touches->length(); ++i) {
2794  if (touches->item(i)->target()->toNode()->isSameNode(touchTarget->target()->toNode()))
2795  targetTouches->append(touches->item(i));
 2791 switch (state) {
 2792 case PlatformTouchPoint::TouchReleased:
 2793 return eventNames().touchendEvent;
 2794 case PlatformTouchPoint::TouchCancelled:
 2795 return eventNames().touchcancelEvent;
 2796 case PlatformTouchPoint::TouchPressed:
 2797 return eventNames().touchstartEvent;
 2798 case PlatformTouchPoint::TouchMoved:
 2799 return eventNames().touchmoveEvent;
 2800 default:
 2801 ASSERT_NOT_REACHED();
 2802 return emptyAtom;
27962803 }
2797 
2798  return targetTouches.release();
27992804}
28002805
28012806bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
28022807{
 2808 // First build up the lists to use for the 'touches', 'targetTouches' and 'changedTouches' attributes
 2809 // in the JS event. See http://www.sitepen.com/blog/2008/07/10/touching-and-gesturing-on-the-iphone/
 2810 // for an overview of how these lists fit together.
 2811
 2812 // Holds the complete set of touches on the screen and will be used as the 'touches' list in the JS event.
28032813 RefPtr<TouchList> touches = TouchList::create();
2804  RefPtr<TouchList> pressedTouches = TouchList::create();
2805  RefPtr<TouchList> releasedTouches = TouchList::create();
2806  RefPtr<TouchList> movedTouches = TouchList::create();
2807  RefPtr<TouchList> cancelTouches = TouchList::create();
 2814
 2815 // A different view on the 'touches' list above, filtered and grouped by event target. Used for the
 2816 // 'targetTouches' list in the JS event.
 2817 typedef HashMap<EventTarget*, RefPtr<TouchList> > TargetTouchesMap;
 2818 TargetTouchesMap touchesByTarget;
 2819
 2820 // Array of touches per state, used to assemble the 'changedTouches' list in the JS event.
 2821 typedef HashSet<RefPtr<EventTarget> > EventTargetSet;
 2822 struct {
 2823 // The touches corresponding to the particular change state this struct instance represents.
 2824 RefPtr<TouchList> m_touches;
 2825 // Set of targets involved in m_touches.
 2826 EventTargetSet m_targets;
 2827 } changedTouches[PlatformTouchPoint::TouchStateEnd];
28082828
28092829 const Vector<PlatformTouchPoint>& points = event.touchPoints();
2810  AtomicString* eventName = 0;
28112830
28122831 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture);
28132832
28142833 for (unsigned i = 0; i < points.size(); ++i) {
28152834 const PlatformTouchPoint& point = points[i];
 2835 PlatformTouchPoint::State pointState = point.state();
28162836 IntPoint pagePoint = documentPointForWindowPoint(m_frame, point.pos());
28172837
28182838 HitTestRequest::HitTestRequestType hitType = HitTestRequest::Active | HitTestRequest::ReadOnly;

@@bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
28212841 // should affect the active state of the current node if necessary,
28222842 // HitTestRequest::Active signifies that the hit test is taking place
28232843 // with the mouse (or finger in this case) being pressed.
2824  switch (point.state()) {
 2844 switch (pointState) {
28252845 case PlatformTouchPoint::TouchPressed:
28262846 hitType = HitTestRequest::Active;
28272847 break;

@@bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
28372857 }
28382858
28392859 HitTestResult result = hitTestResultAtPoint(pagePoint, /*allowShadowContent*/ false, false, DontHitTestScrollbars, hitType);
2840  Node* target = result.innerNode();
 2860 RefPtr<Node> node = result.innerNode();
 2861 ASSERT(node);
28412862
28422863 // Touch events should not go to text nodes
2843  if (target && target->isTextNode())
2844  target = target->parentNode();
 2864 if (node->isTextNode())
 2865 node = node->parentNode();
28452866
2846  Document* doc = target->document();
 2867 Document* doc = node->document();
28472868 if (!doc)
28482869 continue;
28492870 if (!doc->hasListenerType(Document::TOUCH_LISTENER))

@@bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
28602881 // Increment the platform touch id by 1 to avoid storing a key of 0 in the hashmap.
28612882 unsigned touchPointTargetKey = point.id() + 1;
28622883 RefPtr<EventTarget> touchTarget;
2863  if (point.state() == PlatformTouchPoint::TouchPressed) {
2864  m_originatingTouchPointTargets.set(touchPointTargetKey, target);
2865  touchTarget = target;
2866  } else if (point.state() == PlatformTouchPoint::TouchReleased || point.state() == PlatformTouchPoint::TouchCancelled) {
 2884 if (pointState == PlatformTouchPoint::TouchPressed) {
 2885 m_originatingTouchPointTargets.set(touchPointTargetKey, node.get());
 2886 touchTarget = node;
 2887 } else if (pointState == PlatformTouchPoint::TouchReleased || pointState == PlatformTouchPoint::TouchCancelled) {
28672888 // The target should be the original target for this touch, so get it from the hashmap. As it's a release or cancel
28682889 // we also remove it from the map.
28692890 touchTarget = m_originatingTouchPointTargets.take(touchPointTargetKey);

@@bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
28772898 point.screenPos().x(), point.screenPos().y(),
28782899 adjustedPageX, adjustedPageY);
28792900
2880  // touches should contain information about every touch currently on the screen.
2881  if (point.state() != PlatformTouchPoint::TouchReleased)
 2901 // Ensure this target's touch list exists, even if it ends up empty, so it can always be passed to TouchEvent::Create below.
 2902 TargetTouchesMap::iterator targetTouchesIterator = touchesByTarget.find(touchTarget.get());
 2903 if (targetTouchesIterator == touchesByTarget.end())
 2904 targetTouchesIterator = touchesByTarget.set(touchTarget.get(), TouchList::create()).first;
 2905
 2906 // touches and targetTouches should only contain information about touches still on the screen, so if this point is
 2907 // released or cancelled it will only appear in the changedTouches list.
 2908 if (pointState != PlatformTouchPoint::TouchReleased && pointState != PlatformTouchPoint::TouchCancelled) {
28822909 touches->append(touch);
 2910 targetTouchesIterator->second->append(touch);
 2911 }
28832912
28842913 // Now build up the correct list for changedTouches.
28852914 // Note that any touches that are in the TouchStationary state (e.g. if
28862915 // the user had several points touched but did not move them all) should
2887  // only be present in the touches list. They may also be added to the
2888  // targetTouches list later, but should never be in the changedTouches
2889  // list so we do not handle them explicitly here.
 2916 // never be in the changedTouches list so we do not handle them explicitly here.
28902917 // See https://bugs.webkit.org/show_bug.cgi?id=37609 for further discussion
28912918 // about the TouchStationary state.
2892  if (point.state() == PlatformTouchPoint::TouchReleased)
2893  releasedTouches->append(touch);
2894  else if (point.state() == PlatformTouchPoint::TouchCancelled)
2895  cancelTouches->append(touch);
2896  else if (point.state() == PlatformTouchPoint::TouchPressed)
2897  pressedTouches->append(touch);
2898  else if (point.state() == PlatformTouchPoint::TouchMoved)
2899  movedTouches->append(touch);
 2919 if (pointState != PlatformTouchPoint::TouchStationary) {
 2920 ASSERT(pointState < PlatformTouchPoint::TouchStateEnd);
 2921 if (!changedTouches[pointState].m_touches)
 2922 changedTouches[pointState].m_touches = TouchList::create();
 2923 changedTouches[pointState].m_touches->append(touch);
 2924 changedTouches[pointState].m_targets.add(touchTarget);
 2925 }
29002926 }
2901 
29022927 m_touchPressed = touches->length() > 0;
29032928
 2929 // Now iterate the changedTouches list and m_targets within it, sending events to the tagets as required.
29042930 bool defaultPrevented = false;
2905  Touch* changedTouch = 0;
2906  EventTarget* touchEventTarget = 0;
2907 
2908  if (cancelTouches->length() > 0) {
2909  // We dispatch the event to the target of the touch that caused this touch event to be generated, i.e.
2910  // we take it from the list that will be used as the changedTouches property of the event.
2911  // The choice to use the touch at index 0 guarantees that there is a target (as we checked the length
2912  // above). In the case that there are multiple touches in what becomes the changedTouches list, it is
2913  // difficult to say how we should prioritise touches and as such, item 0 is an arbitrary choice.
2914  changedTouch = cancelTouches->item(0);
2915  ASSERT(changedTouch);
2916  touchEventTarget = changedTouch->target();
2917  ASSERT(touchEventTarget);
2918 
2919  eventName = &eventNames().touchcancelEvent;
2920  RefPtr<TouchEvent> cancelEv =
2921  TouchEvent::create(TouchList::create().get(), TouchList::create().get(), cancelTouches.get(),
2922  *eventName, touchEventTarget->toNode()->document()->defaultView(),
2923  0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(),
2924  event.metaKey());
2925  ExceptionCode ec = 0;
2926  touchEventTarget->dispatchEvent(cancelEv.get(), ec);
2927  defaultPrevented |= cancelEv->defaultPrevented();
2928  }
2929 
2930  if (releasedTouches->length() > 0) {
2931  Touch* changedTouch = releasedTouches->item(0);
2932  ASSERT(changedTouch);
2933  touchEventTarget = changedTouch->target();
2934  ASSERT(touchEventTarget);
2935 
2936  RefPtr<TouchList> targetTouches = assembleTargetTouches(changedTouch, touches.get());
2937 
2938  eventName = &eventNames().touchendEvent;
2939  RefPtr<TouchEvent> endEv =
2940  TouchEvent::create(touches.get(), targetTouches.get(), releasedTouches.get(),
2941  *eventName, touchEventTarget->toNode()->document()->defaultView(),
2942  0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(),
2943  event.metaKey());
2944  ExceptionCode ec = 0;
2945  touchEventTarget->dispatchEvent(endEv.get(), ec);
2946  defaultPrevented |= endEv->defaultPrevented();
2947  }
2948  if (pressedTouches->length() > 0) {
2949  Touch* changedTouch = pressedTouches->item(0);
2950  ASSERT(changedTouch);
2951  touchEventTarget = changedTouch->target();
2952  ASSERT(touchEventTarget);
2953 
2954  RefPtr<TouchList> targetTouches = assembleTargetTouches(changedTouch, touches.get());
2955 
2956  eventName = &eventNames().touchstartEvent;
2957  RefPtr<TouchEvent> startEv =
2958  TouchEvent::create(touches.get(), targetTouches.get(), pressedTouches.get(),
2959  *eventName, touchEventTarget->toNode()->document()->defaultView(),
2960  0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(),
2961  event.metaKey());
2962  ExceptionCode ec = 0;
2963  touchEventTarget->dispatchEvent(startEv.get(), ec);
2964  defaultPrevented |= startEv->defaultPrevented();
2965  }
2966 
2967  if (movedTouches->length() > 0) {
2968  Touch* changedTouch = movedTouches->item(0);
2969  ASSERT(changedTouch);
2970  touchEventTarget = changedTouch->target();
2971  ASSERT(touchEventTarget);
2972 
2973  RefPtr<TouchList> targetTouches = assembleTargetTouches(changedTouch, touches.get());
2974 
2975  eventName = &eventNames().touchmoveEvent;
2976  RefPtr<TouchEvent> moveEv =
2977  TouchEvent::create(touches.get(), targetTouches.get(), movedTouches.get(),
2978  *eventName, touchEventTarget->toNode()->document()->defaultView(),
2979  0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(),
2980  event.metaKey());
2981  ExceptionCode ec = 0;
2982  touchEventTarget->dispatchEvent(moveEv.get(), ec);
2983  defaultPrevented |= moveEv->defaultPrevented();
 2931 RefPtr<TouchList> emptyList = TouchList::create();
 2932 for (unsigned state = 0; state != PlatformTouchPoint::TouchStateEnd; ++state) {
 2933 if (!changedTouches[state].m_touches)
 2934 continue;
 2935
 2936 // When sending a touch cancel event, use empty touches and targetTouches lists.
 2937 RefPtr<TouchList>& effectiveTouches(state == PlatformTouchPoint::TouchCancelled ? emptyList : touches);
 2938 const AtomicString& stateName(eventNameForTouchPointState(static_cast<PlatformTouchPoint::State>(state)));
 2939 const EventTargetSet& targetsForState = changedTouches[state].m_targets;
 2940
 2941 for (EventTargetSet::const_iterator it = targetsForState.begin(); it != targetsForState.end(); ++it) {
 2942 EventTarget* touchEventTarget = it->get();
 2943 RefPtr<TouchList> targetTouches(state == PlatformTouchPoint::TouchCancelled ? emptyList : touchesByTarget.get(touchEventTarget));
 2944 ASSERT(targetTouches);
 2945
 2946 RefPtr<TouchEvent> touchEvent =
 2947 TouchEvent::create(effectiveTouches.get(), targetTouches.get(), changedTouches[state].m_touches.get(),
 2948 stateName, touchEventTarget->toNode()->document()->defaultView(),
 2949 0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey());
 2950 ExceptionCode ec = 0;
 2951 touchEventTarget->dispatchEvent(touchEvent.get(), ec);
 2952 defaultPrevented |= touchEvent->defaultPrevented();
 2953 }
29842954 }
29852955
29862956 return defaultPrevented;

WebCore/platform/PlatformTouchPoint.h

@@public:
4040 TouchPressed,
4141 TouchMoved,
4242 TouchStationary,
43  TouchCancelled
 43 TouchCancelled,
 44 TouchStateEnd // Placeholder: must remain the last item.
4445 };
4546
4647#if PLATFORM(QT)