[Shadow] Implements event re-targeting for Touch Events.
authorhayato@chromium.org <hayato@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Mar 2013 05:16:16 +0000 (05:16 +0000)
committerhayato@chromium.org <hayato@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Mar 2013 05:16:16 +0000 (05:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=107800

Reviewed by Dimitri Glazkov.

Source/WebCore:

So far, Touch Events have been intentionally disabled in shadow
tree since it allows users to access internal nodes in shadow tree
by accessing Touch Event's information.

This patch re-targets all individual touch point's target in
TouchList (touches, targetTouches and changedTouches) owned by a Touch Event
before the Touch Event is dispatched so that we won't break an
upper boundary of shadow DOM.

Touch Events re-targeting is similar to that of Mouse Events
re-targeting. The difference is that a Touch Event can have more
than one related target because of multiple touches. We must
re-target all individual touch points which can be referred from
the Touch Event.

Now, Touch Events can be fired in shadow tree. I've removed the
existing limitation.

Test: fast/dom/shadow/touch-event-retargeting.html

* dom/EventContext.cpp:
(WebCore::EventContext::isTouchEventContext):
(WebCore):
(WebCore::TouchEventContext::TouchEventContext):
(WebCore::TouchEventContext::~TouchEventContext):
(WebCore::TouchEventContext::handleLocalEvents):
(WebCore::TouchEventContext::isTouchEventContext):
(WebCore::TouchEventContext::checkReachability):  Used for assertion of reachability for each Touch point.
* dom/EventContext.h:
(WebCore):
(EventContext):
(TouchEventContext): Introduced to hold necessary information for re-targeting Touch event.
(WebCore::TouchEventContext::touches):
(WebCore::TouchEventContext::targetTouches):
(WebCore::TouchEventContext::changedTouches):
(WebCore::EventContext::isReachable):
* dom/EventRetargeter.cpp:
(WebCore::EventRetargeter::calculateEventPath):
(WebCore::EventRetargeter::adjustForTouchEvent):
(WebCore):
(WebCore::EventRetargeter::adjustTouchList):
(WebCore::EventRetargeter::adjustForRelatedTarget):
(WebCore::EventRetargeter::calculateAdjustedNodes): Updated so that this can be used for Touch point re-targeting.
(WebCore::EventRetargeter::buildRelatedNodeMap):
* dom/EventRetargeter.h:
(WebCore):
(EventRetargeter):
* dom/Node.cpp:
(WebCore::Node::dispatchEvent):
(WebCore):
(WebCore::Node::dispatchTouchEvent):
* dom/Node.h:
(WebCore):
(Node):
* dom/Touch.cpp:
(WebCore::Touch::Touch):
(WebCore):
(WebCore::Touch::cloneWithNewTarget):
* dom/Touch.h:
(Touch):
* dom/TouchEvent.cpp:
(WebCore::TouchEventDispatchMediator::create):
(WebCore):
(WebCore::TouchEventDispatchMediator::TouchEventDispatchMediator):
(WebCore::TouchEventDispatchMediator::event):
(WebCore::TouchEventDispatchMediator::dispatchEvent):
* dom/TouchEvent.h:
(TouchEvent):
(WebCore::TouchEvent::setTouches):
(WebCore::TouchEvent::setTargetTouches):
(WebCore::TouchEvent::setChangedTouches):
(TouchEventDispatchMediator):
(WebCore):
(WebCore::toTouchEvent):
* dom/TouchList.cpp:
(WebCore::TouchList::item):
(WebCore):
* dom/TouchList.h:
(TouchList):
* page/EventHandler.cpp:
(WebCore::EventHandler::hitTestResultInFrame): Removed the limitation. Adjusting is no longer done here.
(WebCore::EventHandler::handleTouchEvent):

LayoutTests:

* fast/dom/shadow/resources/event-dispatching.js:
(recordEvent):
(dumpTouchList):
(sortDispatchedEvent):
(showSandboxTree):
* fast/dom/shadow/shadow-dom-event-dispatching-distributed-text-node-expected.txt:
* fast/dom/shadow/shadow-dom-event-dispatching-distributed-text-node.html:
* fast/dom/shadow/shadow-dom-event-dispatching-text-node-in-shadow-root-expected.txt:
* fast/dom/shadow/touch-event-retargeting-expected.txt: Added.
* fast/dom/shadow/touch-event-retargeting.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@144877 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/shadow/resources/event-dispatching.js
LayoutTests/fast/dom/shadow/shadow-dom-event-dispatching-distributed-text-node-expected.txt
LayoutTests/fast/dom/shadow/shadow-dom-event-dispatching-distributed-text-node.html
LayoutTests/fast/dom/shadow/shadow-dom-event-dispatching-text-node-in-shadow-root-expected.txt
LayoutTests/fast/dom/shadow/touch-event-retargeting-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/shadow/touch-event-retargeting.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/dom/EventContext.cpp
Source/WebCore/dom/EventContext.h
Source/WebCore/dom/EventRetargeter.cpp
Source/WebCore/dom/EventRetargeter.h
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/Node.h
Source/WebCore/dom/Touch.cpp
Source/WebCore/dom/Touch.h
Source/WebCore/dom/TouchEvent.cpp
Source/WebCore/dom/TouchEvent.h
Source/WebCore/dom/TouchList.cpp
Source/WebCore/dom/TouchList.h
Source/WebCore/page/EventHandler.cpp

index 86c9c3d..439fbb6 100644 (file)
@@ -1,3 +1,21 @@
+2013-03-05  Hayato Ito  <hayato@chromium.org>
+
+        [Shadow] Implements event re-targeting for Touch Events.
+        https://bugs.webkit.org/show_bug.cgi?id=107800
+
+        Reviewed by Dimitri Glazkov.
+
+        * fast/dom/shadow/resources/event-dispatching.js:
+        (recordEvent):
+        (dumpTouchList):
+        (sortDispatchedEvent):
+        (showSandboxTree):
+        * fast/dom/shadow/shadow-dom-event-dispatching-distributed-text-node-expected.txt:
+        * fast/dom/shadow/shadow-dom-event-dispatching-distributed-text-node.html:
+        * fast/dom/shadow/shadow-dom-event-dispatching-text-node-in-shadow-root-expected.txt:
+        * fast/dom/shadow/touch-event-retargeting-expected.txt: Added.
+        * fast/dom/shadow/touch-event-retargeting.html: Added.
+
 2013-03-05  Filip Pizlo  <fpizlo@apple.com>
 
         Add test coverage for DCE and type checks.
index bed3f06..a3b782f 100644 (file)
@@ -68,9 +68,9 @@ function dispatchedEvent(eventType)
 
 function recordEvent(event)
 {
-    var eventType = event.type
+    var eventType = event.type;
     if (!eventRecords[eventType]) {
-        eventRecords[eventType] = []
+        eventRecords[eventType] = [];
     }
     var eventString = '';
     if (event.currentTarget)
@@ -79,6 +79,12 @@ function recordEvent(event)
         eventString += ' (target: ' + event.target.id + ')';
     if (event.relatedTarget)
         eventString += ' (related: ' + event.relatedTarget.id + ')';
+    if (event.touches)
+        eventString += ' (touches: ' + dumpTouchList(event.touches) + ')';
+    if (event.targetTouches)
+        eventString += ' (targetTouches: ' + dumpTouchList(event.targetTouches) + ')';
+    if (event.changedTouches)
+        eventString += ' (changedTouches: ' + dumpTouchList(event.changedTouches) + ')';
     if (event.eventPhase == 1)
         eventString += '(capturing phase)';
     if (event.target && event.currentTarget && event.target.id == event.currentTarget.id)
@@ -96,6 +102,22 @@ function dumpNode(node)
     return output;
 }
 
+function dumpTouchList(touches) {
+    var ids = [];
+    for (var i = 0; i < touches.length; ++i) {
+        if (touches.item(i).target && touches.item(i).target.id)
+            ids.push(touches.item(i).target.id);
+    }
+    ids.sort();
+    var result = '';
+    for (i = 0; i < ids.length; ++i) {
+         result += ids[i];
+         if (i != ids.length - 1)
+             result += ', ';
+    }
+    return result;
+}
+
 function dumpComposedShadowTree(node, indent)
 {
     indent = indent || "";
@@ -130,6 +152,11 @@ function debugDispatchedEvent(eventType)
         debug('    ' + events[i])
 }
 
+function sortDispatchedEvent(eventType)
+{
+    dispatchedEvent(eventType).sort();
+}
+
 function moveMouse(oldElementId, newElementId)
 {
     clearEventRecords();
@@ -149,4 +176,3 @@ function showSandboxTree()
     sandbox.offsetLeft;
     debug('\n\nComposed Shadow Tree will be:\n' + dumpComposedShadowTree(sandbox));
 }
-
index 9a07640..3297f2f 100644 (file)
@@ -21,17 +21,17 @@ Moving mouse from a distributed text node to top
   mouseover
      @top (target: top) (related: shadow-host)
 
-  touchstart
-     @content (target: content)
-     @shadow-root (target: content)
-     @shadow-host (target: shadow-host)
-     @top (target: shadow-host)
-
   mousewheel
      @content (target: content)
      @shadow-root (target: content)
      @shadow-host (target: shadow-host)
      @top (target: shadow-host)
+
+  touchstart
+     @content (target: content) (touches: content) (targetTouches: content) (changedTouches: content)
+     @shadow-root (target: content) (touches: content) (targetTouches: content) (changedTouches: content)
+     @shadow-host (target: shadow-host) (touches: shadow-host) (targetTouches: shadow-host) (changedTouches: shadow-host)
+     @top (target: shadow-host) (touches: shadow-host) (targetTouches: shadow-host) (changedTouches: shadow-host)
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 275ad87..e22121d 100644 (file)
         debugDispatchedEvent('mouseout');
         debugDispatchedEvent('mouseover');
 
-        touchLocation(host);
-        debugDispatchedEvent('touchstart');
-
+        clearEventRecords();
         scrollMouseWheel(host);
         debugDispatchedEvent('mousewheel');
+
+        clearEventRecords();
+        touchLocation(host);
+        debugDispatchedEvent('touchstart');
     </script>
     <script src="../../js/resources/js-test-post.js"></script>
 </body>
 </html>
-
index 773ce9b..1cee0b0 100644 (file)
@@ -22,9 +22,9 @@ Moving mouse from a direct child text node of the shadow root to top
      @top (target: top) (related: shadow-host)
 
   touchstart
-     @shadow-root (target: shadow-root)
-     @shadow-host (target: shadow-host)
-     @top (target: shadow-host)
+     @shadow-root (target: shadow-root) (touches: shadow-root) (targetTouches: shadow-root) (changedTouches: shadow-root)
+     @shadow-host (target: shadow-host) (touches: shadow-host) (targetTouches: shadow-host) (changedTouches: shadow-host)
+     @top (target: shadow-host) (touches: shadow-host) (targetTouches: shadow-host) (changedTouches: shadow-host)
 
   mousewheel
      @shadow-root (target: shadow-root)
diff --git a/LayoutTests/fast/dom/shadow/touch-event-retargeting-expected.txt b/LayoutTests/fast/dom/shadow/touch-event-retargeting-expected.txt
new file mode 100644 (file)
index 0000000..fc392c3
--- /dev/null
@@ -0,0 +1,19 @@
+Tests to ensure that event dispatching behaves as the Shadow DOM spec describes.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+  touchstart
+     @div1 (target: div1) (touches: div1, div2, div3) (targetTouches: div1) (changedTouches: div1, div2, div3)
+     @div2 (target: div2) (touches: div1, div2, div3) (targetTouches: div2) (changedTouches: div1, div2, div3)
+     @div2-shadow-root-child (target: div2-shadow-root-child) (touches: div1, div2-shadow-root-child, div3) (targetTouches: div2-shadow-root-child) (changedTouches: div1, div2-shadow-root-child, div3)
+     @div3 (target: div3) (touches: div1, div2, div3) (targetTouches: div3) (changedTouches: div1, div2, div3)
+     @div3-shadow-root-child (target: div3-shadow-root-child) (touches: div1, div2, div3-shadow-root-child) (targetTouches: div3-shadow-root-child) (changedTouches: div1, div2, div3-shadow-root-child)
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Touch event retargeting.
+
+foo
+
diff --git a/LayoutTests/fast/dom/shadow/touch-event-retargeting.html b/LayoutTests/fast/dom/shadow/touch-event-retargeting.html
new file mode 100644 (file)
index 0000000..84912be
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="../../js/resources/js-test-pre.js"></script>
+    <script src="resources/shadow-dom.js"></script>
+    <script src="resources/event-dispatching.js"></script>
+</head>
+<body>
+<p>Touch event retargeting.</p>
+<div id="sandbox">
+</div>
+<pre id="console"></pre>
+
+<script>
+sandbox.appendChild(
+    createDOM('div', {'id': 'container'},
+              createDOM('div', {'id': 'div1'},
+                        document.createTextNode('foo')),
+              createDOM('div', {'id': 'div2'},
+                        createShadowRoot(
+                            createDOM('div', {},
+                                      document.createTextNode('foo')),
+                            createDOM('div', {'id': 'div2-shadow-root-child'},
+                                      document.createTextNode('foo')),
+                            createDOM('div', {},
+                                      document.createTextNode('foo')))),
+              createDOM('div', {'id': 'div3'},
+                        createShadowRoot(
+                            createDOM('div', {},
+                                      document.createTextNode('foo')),
+                            createDOM('div', {'id': 'div3-shadow-root-child'},
+                                      document.createTextNode('foo')),
+                            createDOM('div', {},
+                                      document.createTextNode('foo'))))));
+
+sandbox.offsetLeft;
+
+function addTouchPoint(target)
+{
+    eventSender.addTouchPoint(target.offsetLeft + target.offsetWidth / 2, target.offsetTop + target.offsetHeight / 2);
+}
+
+function sendTouchEvent(elements)
+{
+    eventSender.clearTouchPoints();
+    for (var i = 0; i < elements.length; ++i)
+        addTouchPoint(getNodeInShadowTreeStack(elements[i]));
+    eventSender.touchStart();
+    eventSender.touchEnd();
+}
+
+addEventListeners(['div1', 'div2', 'div3',
+                   'div2/div2-shadow-root-child', 'div3/div3-shadow-root-child']);
+sendTouchEvent(['div1', 'div2/div2-shadow-root-child', 'div3/div3-shadow-root-child']);
+sortDispatchedEvent('touchstart');
+debugDispatchedEvent('touchstart');
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</body>
+</html>
index 0d8bbd3..3fa038c 100644 (file)
@@ -1,3 +1,93 @@
+2013-03-05  Hayato Ito  <hayato@chromium.org>
+
+        [Shadow] Implements event re-targeting for Touch Events.
+        https://bugs.webkit.org/show_bug.cgi?id=107800
+
+        Reviewed by Dimitri Glazkov.
+
+        So far, Touch Events have been intentionally disabled in shadow
+        tree since it allows users to access internal nodes in shadow tree
+        by accessing Touch Event's information.
+
+        This patch re-targets all individual touch point's target in
+        TouchList (touches, targetTouches and changedTouches) owned by a Touch Event
+        before the Touch Event is dispatched so that we won't break an
+        upper boundary of shadow DOM.
+
+        Touch Events re-targeting is similar to that of Mouse Events
+        re-targeting. The difference is that a Touch Event can have more
+        than one related target because of multiple touches. We must
+        re-target all individual touch points which can be referred from
+        the Touch Event.
+
+        Now, Touch Events can be fired in shadow tree. I've removed the
+        existing limitation.
+
+        Test: fast/dom/shadow/touch-event-retargeting.html
+
+        * dom/EventContext.cpp:
+        (WebCore::EventContext::isTouchEventContext):
+        (WebCore):
+        (WebCore::TouchEventContext::TouchEventContext):
+        (WebCore::TouchEventContext::~TouchEventContext):
+        (WebCore::TouchEventContext::handleLocalEvents):
+        (WebCore::TouchEventContext::isTouchEventContext):
+        (WebCore::TouchEventContext::checkReachability):  Used for assertion of reachability for each Touch point.
+        * dom/EventContext.h:
+        (WebCore):
+        (EventContext):
+        (TouchEventContext): Introduced to hold necessary information for re-targeting Touch event.
+        (WebCore::TouchEventContext::touches):
+        (WebCore::TouchEventContext::targetTouches):
+        (WebCore::TouchEventContext::changedTouches):
+        (WebCore::EventContext::isReachable):
+        * dom/EventRetargeter.cpp:
+        (WebCore::EventRetargeter::calculateEventPath):
+        (WebCore::EventRetargeter::adjustForTouchEvent):
+        (WebCore):
+        (WebCore::EventRetargeter::adjustTouchList):
+        (WebCore::EventRetargeter::adjustForRelatedTarget):
+        (WebCore::EventRetargeter::calculateAdjustedNodes): Updated so that this can be used for Touch point re-targeting.
+        (WebCore::EventRetargeter::buildRelatedNodeMap):
+        * dom/EventRetargeter.h:
+        (WebCore):
+        (EventRetargeter):
+        * dom/Node.cpp:
+        (WebCore::Node::dispatchEvent):
+        (WebCore):
+        (WebCore::Node::dispatchTouchEvent):
+        * dom/Node.h:
+        (WebCore):
+        (Node):
+        * dom/Touch.cpp:
+        (WebCore::Touch::Touch):
+        (WebCore):
+        (WebCore::Touch::cloneWithNewTarget):
+        * dom/Touch.h:
+        (Touch):
+        * dom/TouchEvent.cpp:
+        (WebCore::TouchEventDispatchMediator::create):
+        (WebCore):
+        (WebCore::TouchEventDispatchMediator::TouchEventDispatchMediator):
+        (WebCore::TouchEventDispatchMediator::event):
+        (WebCore::TouchEventDispatchMediator::dispatchEvent):
+        * dom/TouchEvent.h:
+        (TouchEvent):
+        (WebCore::TouchEvent::setTouches):
+        (WebCore::TouchEvent::setTargetTouches):
+        (WebCore::TouchEvent::setChangedTouches):
+        (TouchEventDispatchMediator):
+        (WebCore):
+        (WebCore::toTouchEvent):
+        * dom/TouchList.cpp:
+        (WebCore::TouchList::item):
+        (WebCore):
+        * dom/TouchList.h:
+        (TouchList):
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::hitTestResultInFrame): Removed the limitation. Adjusting is no longer done here.
+        (WebCore::EventHandler::handleTouchEvent):
+
 2013-03-05  Hajime Morrita  <morrita@google.com>
 
         [Custom Elements][V8] Custom Element doesn't need its own WrapperTypeInfo
index 2897c7d..56543a7 100644 (file)
@@ -33,6 +33,8 @@
 #include "FocusEvent.h"
 #include "MouseEvent.h"
 #include "Node.h"
+#include "TouchEvent.h"
+#include "TouchList.h"
 
 namespace WebCore {
 
@@ -61,6 +63,11 @@ bool EventContext::isMouseOrFocusEventContext() const
     return false;
 }
 
+bool EventContext::isTouchEventContext() const
+{
+    return false;
+}
+
 MouseOrFocusEventContext::MouseOrFocusEventContext(PassRefPtr<Node> node, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target)
     : EventContext(node, currentTarget, target)
     , m_relatedTarget(0)
@@ -86,4 +93,47 @@ bool MouseOrFocusEventContext::isMouseOrFocusEventContext() const
     return true;
 }
 
+#if ENABLE(TOUCH_EVENTS)
+TouchEventContext::TouchEventContext(PassRefPtr<Node> node, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target)
+    : EventContext(node, currentTarget, target)
+    , m_touches(TouchList::create())
+    , m_targetTouches(TouchList::create())
+    , m_changedTouches(TouchList::create())
+{
+}
+
+TouchEventContext::~TouchEventContext()
+{
+}
+
+void TouchEventContext::handleLocalEvents(Event* event) const
+{
+#ifndef NDEBUG
+    checkReachability(m_touches.get());
+    checkReachability(m_targetTouches.get());
+    checkReachability(m_changedTouches.get());
+#endif
+    ASSERT(event->isTouchEvent());
+    TouchEvent* touchEvent = toTouchEvent(event);
+    touchEvent->setTouches(m_touches);
+    touchEvent->setTargetTouches(m_targetTouches);
+    touchEvent->setChangedTouches(m_changedTouches);
+    EventContext::handleLocalEvents(event);
+}
+
+bool TouchEventContext::isTouchEventContext() const
+{
+    return true;
+}
+
+#ifndef NDEBUG
+void TouchEventContext::checkReachability(TouchList* touchList) const
+{
+    for (size_t i = 0; i < touchList->length(); ++i)
+        ASSERT(isReachable(touchList->item(i)->target()->toNode()));
+}
+#endif
+
+#endif // ENABLE(TOUCH_EVENTS)
+
 }
index fe1cf02..c9a447c 100644 (file)
@@ -35,6 +35,9 @@
 namespace WebCore {
 
 class Event;
+#if ENABLE(TOUCH_EVENTS)
+class TouchList;
+#endif
 
 class EventContext {
 public:
@@ -47,11 +50,12 @@ public:
     bool currentTargetSameAsTarget() const { return m_currentTarget.get() == m_target.get(); }
     virtual void handleLocalEvents(Event*) const;
     virtual bool isMouseOrFocusEventContext() const;
+    virtual bool isTouchEventContext() const;
 
 protected:
 #ifndef NDEBUG
     bool isUnreachableNode(EventTarget*);
-    bool isReachable(Node*);
+    bool isReachable(Node*) const;
 #endif
     RefPtr<Node> m_node;
     RefPtr<EventTarget> m_currentTarget;
@@ -73,6 +77,36 @@ private:
     RefPtr<EventTarget> m_relatedTarget;
 };
 
+
+#if ENABLE(TOUCH_EVENTS)
+class TouchEventContext : public EventContext {
+public:
+    TouchEventContext(PassRefPtr<Node>, PassRefPtr<EventTarget> currentTarget, PassRefPtr<EventTarget> target);
+    virtual ~TouchEventContext();
+
+    virtual void handleLocalEvents(Event*) const OVERRIDE;
+    virtual bool isTouchEventContext() const OVERRIDE;
+
+    TouchList* touches() { return m_touches.get(); }
+    TouchList* targetTouches() { return m_targetTouches.get(); }
+    TouchList* changedTouches() { return m_changedTouches.get(); }
+
+private:
+    RefPtr<TouchList> m_touches;
+    RefPtr<TouchList> m_targetTouches;
+    RefPtr<TouchList> m_changedTouches;
+#ifndef NDEBUG
+    void checkReachability(TouchList*) const;
+#endif
+};
+
+inline TouchEventContext* toTouchEventContext(EventContext* eventContext)
+{
+    ASSERT(!eventContext || eventContext->isTouchEventContext());
+    return static_cast<TouchEventContext*>(eventContext);
+}
+#endif // ENABLE(TOUCH_EVENTS)
+
 #ifndef NDEBUG
 inline bool EventContext::isUnreachableNode(EventTarget* target)
 {
@@ -80,7 +114,7 @@ inline bool EventContext::isUnreachableNode(EventTarget* target)
     return target && target->toNode() && !target->toNode()->isSVGElement() && !isReachable(target->toNode());
 }
 
-inline bool EventContext::isReachable(Node* target)
+inline bool EventContext::isReachable(Node* target) const
 {
     ASSERT(target);
     TreeScope* targetScope = target->treeScope();
index 915da47..a8cce45 100644 (file)
@@ -26,6 +26,9 @@
 #include "FocusEvent.h"
 #include "MouseEvent.h"
 #include "ShadowRoot.h"
+#include "Touch.h"
+#include "TouchEvent.h"
+#include "TouchList.h"
 #include "TreeScope.h"
 #include <wtf/PassRefPtr.h>
 #include <wtf/RefPtr.h>
@@ -77,6 +80,9 @@ void EventRetargeter::calculateEventPath(Node* node, Event* event, EventPath& ev
     bool inDocument = node->inDocument();
     bool isSVGElement = node->isSVGElement();
     bool isMouseOrFocusEvent = event->isMouseEvent() || event->isFocusEvent();
+#if ENABLE(TOUCH_EVENTS)
+    bool isTouchEvent = event->isTouchEvent();
+#endif
     Vector<EventTarget*, 32> targetStack;
     for (EventPathWalker walker(node); walker.node(); walker.moveToParent()) {
         Node* node = walker.node();
@@ -86,6 +92,10 @@ void EventRetargeter::calculateEventPath(Node* node, Event* event, EventPath& ev
             targetStack.append(targetStack.last());
         if (isMouseOrFocusEvent)
             eventPath.append(adoptPtr(new MouseOrFocusEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last())));
+#if ENABLE(TOUCH_EVENTS)
+        else if (isTouchEvent)
+            eventPath.append(adoptPtr(new TouchEventContext(node, eventTargetRespectingTargetRules(node), targetStack.last())));
+#endif
         else
             eventPath.append(adoptPtr(new EventContext(node, eventTargetRespectingTargetRules(node), targetStack.last())));
         if (!inDocument)
@@ -111,7 +121,44 @@ void EventRetargeter::adjustForFocusEvent(Node* node, const FocusEvent& focusEve
     adjustForRelatedTarget(node, focusEvent.relatedTarget(), eventPath);
 }
 
-void EventRetargeter::adjustForRelatedTarget(Node* node, EventTarget* relatedTarget, EventPath& eventPath)
+#if ENABLE(TOUCH_EVENTS)
+void EventRetargeter::adjustForTouchEvent(Node* node, const TouchEvent& touchEvent, EventPath& eventPath)
+{
+    size_t eventPathSize = eventPath.size();
+
+    EventPathTouchLists eventPathTouches(eventPathSize);
+    EventPathTouchLists eventPathTargetTouches(eventPathSize);
+    EventPathTouchLists eventPathChangedTouches(eventPathSize);
+
+    for (size_t i = 0; i < eventPathSize; ++i) {
+        ASSERT(eventPath[i]->isTouchEventContext());
+        TouchEventContext* touchEventContext = toTouchEventContext(eventPath[i].get());
+        eventPathTouches[i] = touchEventContext->touches();
+        eventPathTargetTouches[i] = touchEventContext->targetTouches();
+        eventPathChangedTouches[i] = touchEventContext->changedTouches();
+    }
+
+    adjustTouchList(node, *touchEvent.touches(), eventPath, eventPathTouches);
+    adjustTouchList(node, *touchEvent.targetTouches(), eventPath, eventPathTargetTouches);
+    adjustTouchList(node, *touchEvent.changedTouches(), eventPath, eventPathChangedTouches);
+}
+
+void EventRetargeter::adjustTouchList(const Node* node, const TouchList& touchList, const EventPath& eventPath, EventPathTouchLists& eventPathTouchLists)
+{
+    size_t eventPathSize = eventPath.size();
+    ASSERT(eventPathTouchLists.size() == eventPathSize);
+    for (size_t i = 0; i < touchList.length(); ++i) {
+        const Touch& touch = *touchList.item(i);
+        AdjustedNodes adjustedNodes;
+        calculateAdjustedNodes(node, touch.target()->toNode(), DoesNotStopAtBoundary, const_cast<EventPath&>(eventPath), adjustedNodes);
+        ASSERT(adjustedNodes.size() == eventPathSize);
+        for (size_t j = 0; j < eventPathSize; ++j)
+            eventPathTouchLists[j]->append(touch.cloneWithNewTarget(adjustedNodes[j].get()));
+    }
+}
+#endif
+
+void EventRetargeter::adjustForRelatedTarget(const Node* node, EventTarget* relatedTarget, EventPath& eventPath)
 {
     if (!node)
         return;
@@ -121,7 +168,7 @@ void EventRetargeter::adjustForRelatedTarget(Node* node, EventTarget* relatedTar
     if (!relatedNode)
         return;
     AdjustedNodes adjustedNodes;
-    calculateAdjustedNodes(node, relatedNode, eventPath, adjustedNodes);
+    calculateAdjustedNodes(node, relatedNode, StopAtBoundaryIfNeeded, eventPath, adjustedNodes);
     ASSERT(adjustedNodes.size() <= eventPath.size());
     for (size_t i = 0; i < adjustedNodes.size(); ++i) {
         ASSERT(eventPath[i]->isMouseOrFocusEventContext());
@@ -130,7 +177,7 @@ void EventRetargeter::adjustForRelatedTarget(Node* node, EventTarget* relatedTar
     }
 }
 
-void EventRetargeter::calculateAdjustedNodes(Node* node, Node* relatedNode, EventPath& eventPath, AdjustedNodes& adjustedNodes)
+void EventRetargeter::calculateAdjustedNodes(const Node* node, const Node* relatedNode, EventWithRelatedTargetDispatchBehavior eventWithRelatedTargetDispatchBehavior, EventPath& eventPath, AdjustedNodes& adjustedNodes)
 {
     RelatedNodeMap relatedNodeMap;
     buildRelatedNodeMap(relatedNode, relatedNodeMap);
@@ -150,6 +197,8 @@ void EventRetargeter::calculateAdjustedNodes(Node* node, Node* relatedNode, Even
             adjustedNodes.append(adjustedNode);
         }
         lastTreeScope = scope;
+        if (eventWithRelatedTargetDispatchBehavior == DoesNotStopAtBoundary)
+            continue;
         if (targetIsIdenticalToToRelatedTarget) {
             if (node->treeScope()->rootNode() == (*iter)->node()) {
                 eventPath.shrink(iter + 1 - eventPath.begin());
@@ -164,7 +213,7 @@ void EventRetargeter::calculateAdjustedNodes(Node* node, Node* relatedNode, Even
     }
 }
 
-void EventRetargeter::buildRelatedNodeMap(Node* relatedNode, RelatedNodeMap& relatedNodeMap)
+void EventRetargeter::buildRelatedNodeMap(const Node* relatedNode, RelatedNodeMap& relatedNodeMap)
 {
     Vector<Node*, 32> relatedNodeStack;
     TreeScope* lastTreeScope = 0;
index c711a96..f2ea059 100644 (file)
@@ -39,6 +39,9 @@ class EventTarget;
 class FocusEvent;
 class MouseEvent;
 class Node;
+#if ENABLE(TOUCH_EVENTS)
+class TouchEvent;
+#endif
 class TreeScope;
 
 enum EventDispatchBehavior {
@@ -51,16 +54,26 @@ public:
     static void calculateEventPath(Node*, Event*, EventPath&);
     static void adjustForMouseEvent(Node*, const MouseEvent&, EventPath&);
     static void adjustForFocusEvent(Node*, const FocusEvent&, EventPath&);
+#if ENABLE(TOUCH_EVENTS)
+    typedef Vector<RefPtr<TouchList> > EventPathTouchLists;
+    static void adjustForTouchEvent(Node*, const TouchEvent&, EventPath&);
+#endif
     static EventTarget* eventTargetRespectingTargetRules(Node* referenceNode);
 
 private:
     typedef Vector<RefPtr<Node> > AdjustedNodes;
     typedef HashMap<TreeScope*, Node*> RelatedNodeMap;
-
-    static void adjustForRelatedTarget(Node*, EventTarget* relatedTarget, EventPath&);
-    static void calculateAdjustedNodes(Node*, Node* relatedNode, EventPath&, AdjustedNodes&);
-    static void buildRelatedNodeMap(Node*, RelatedNodeMap&);
+    enum EventWithRelatedTargetDispatchBehavior {
+        StopAtBoundaryIfNeeded,
+        DoesNotStopAtBoundary
+    };
+    static void adjustForRelatedTarget(const Node*, EventTarget* relatedTarget, EventPath&);
+    static void calculateAdjustedNodes(const Node*, const Node* relatedNode, EventWithRelatedTargetDispatchBehavior, EventPath&, AdjustedNodes&);
+    static void buildRelatedNodeMap(const Node*, RelatedNodeMap&);
     static Node* findRelatedNode(TreeScope*, RelatedNodeMap&);
+#if ENABLE(TOUCH_EVENTS)
+    static void adjustTouchList(const Node*, const TouchList&, const EventPath&, EventPathTouchLists&);
+#endif
 };
 
 inline EventTarget* EventRetargeter::eventTargetRespectingTargetRules(Node* referenceNode)
index dc11c97..9af7366 100644 (file)
 #include "TemplateContentDocumentFragment.h"
 #include "Text.h"
 #include "TextEvent.h"
+#include "TouchEvent.h"
 #include "TreeScopeAdopter.h"
 #include "UIEvent.h"
 #include "UIEventWithKeyState.h"
@@ -2328,6 +2329,10 @@ bool Node::dispatchEvent(PassRefPtr<Event> event)
 {
     if (event->isMouseEvent())
         return EventDispatcher::dispatchEvent(this, MouseEventDispatchMediator::create(adoptRef(toMouseEvent(event.leakRef())), MouseEventDispatchMediator::SyntheticMouseEvent));
+#if ENABLE(TOUCH_EVENTS)
+    if (event->isTouchEvent())
+        return dispatchTouchEvent(adoptRef(toTouchEvent(event.leakRef())));
+#endif
     return EventDispatcher::dispatchEvent(this, EventDispatchMediator::create(event));
 }
 
@@ -2388,6 +2393,13 @@ bool Node::dispatchGestureEvent(const PlatformGestureEvent& event)
 }
 #endif
 
+#if ENABLE(TOUCH_EVENTS)
+bool Node::dispatchTouchEvent(PassRefPtr<TouchEvent> event)
+{
+    return EventDispatcher::dispatchEvent(this, TouchEventDispatchMediator::create(event));
+}
+#endif
+
 void Node::dispatchSimulatedClick(Event* underlyingEvent, SimulatedClickMouseEventOptions eventOptions, SimulatedClickVisualOptions visualOptions)
 {
     EventDispatcher::dispatchSimulatedClick(this, underlyingEvent, eventOptions, visualOptions);
index 710c1c6..86d1c69 100644 (file)
@@ -92,6 +92,10 @@ class TagNodeList;
 class PlatformGestureEvent;
 #endif
 
+#if ENABLE(TOUCH_EVENTS)
+class TouchEvent;
+#endif
+
 #if ENABLE(MICRODATA)
 class HTMLPropertiesCollection;
 class PropertyNodeList;
@@ -637,6 +641,10 @@ public:
 #if ENABLE(GESTURE_EVENTS)
     bool dispatchGestureEvent(const PlatformGestureEvent&);
 #endif
+#if ENABLE(TOUCH_EVENTS)
+    bool dispatchTouchEvent(PassRefPtr<TouchEvent>);
+#endif
+
     void dispatchSimulatedClick(Event* underlyingEvent, SimulatedClickMouseEventOptions = SendNoEvents, SimulatedClickVisualOptions = ShowPressedLook);
     bool dispatchBeforeLoadEvent(const String& sourceURL);
 
index 7f362fc..5ebb791 100644 (file)
@@ -75,6 +75,28 @@ Touch::Touch(Frame* frame, EventTarget* target, unsigned identifier, int screenX
     m_absoluteLocation = roundedLayoutPoint(FloatPoint(x, y));
 }
 
+Touch::Touch(EventTarget* target, unsigned identifier, int clientX, int clientY, int screenX, int screenY, int pageX, int pageY, int radiusX, int radiusY, float rotationAngle, float force, LayoutPoint absoluteLocation)
+    : m_target(target)
+    , m_identifier(identifier)
+    , m_clientX(clientX)
+    , m_clientY(clientY)
+    , m_screenX(screenX)
+    , m_screenY(screenY)
+    , m_pageX(pageX)
+    , m_pageY(pageY)
+    , m_radiusX(radiusX)
+    , m_radiusY(radiusY)
+    , m_rotationAngle(rotationAngle)
+    , m_force(force)
+    , m_absoluteLocation(absoluteLocation)
+{
+}
+
+PassRefPtr<Touch> Touch::cloneWithNewTarget(EventTarget* eventTarget) const
+{
+    return adoptRef(new Touch(eventTarget, m_identifier, m_clientX, m_clientY, m_screenX, m_screenY, m_pageX, m_pageY, m_radiusX, m_radiusY, m_rotationAngle, m_force, m_absoluteLocation));
+}
+
 } // namespace WebCore
 
 #endif
index 66825f5..b8979a6 100644 (file)
@@ -61,12 +61,17 @@ public:
     float webkitRotationAngle() const { return m_rotationAngle; }
     float webkitForce() const { return m_force; }
     const LayoutPoint& absoluteLocation() const { return m_absoluteLocation; }
+    PassRefPtr<Touch> cloneWithNewTarget(EventTarget*) const;
 
 private:
     Touch(Frame* frame, EventTarget* target, unsigned identifier,
             int screenX, int screenY, int pageX, int pageY,
             int radiusX, int radiusY, float rotationAngle, float force);
 
+    Touch(EventTarget*, unsigned identifier, int clientX, int clientY,
+        int screenX, int screenY, int pageX, int pageY,
+        int radiusX, int radiusY, float rotationAngle, float force, LayoutPoint absoluteLocation);
+
     RefPtr<EventTarget> m_target;
     unsigned m_identifier;
     int m_clientX;
index a58f1dc..238916a 100644 (file)
@@ -30,7 +30,9 @@
 
 #include "TouchEvent.h"
 
+#include "EventDispatcher.h"
 #include "EventNames.h"
+#include "EventRetargeter.h"
 #include "TouchList.h"
 
 namespace WebCore {
@@ -99,6 +101,27 @@ bool TouchEvent::isTouchEvent() const
     return true;
 }
 
+PassRefPtr<TouchEventDispatchMediator> TouchEventDispatchMediator::create(PassRefPtr<TouchEvent> touchEvent)
+{
+    return adoptRef(new TouchEventDispatchMediator(touchEvent));
+}
+
+TouchEventDispatchMediator::TouchEventDispatchMediator(PassRefPtr<TouchEvent> touchEvent)
+    : EventDispatchMediator(touchEvent)
+{
+}
+
+TouchEvent* TouchEventDispatchMediator::event() const
+{
+    return static_cast<TouchEvent*>(EventDispatchMediator::event());
+}
+
+bool TouchEventDispatchMediator::dispatchEvent(EventDispatcher* dispatcher) const
+{
+    EventRetargeter::adjustForTouchEvent(dispatcher->node(), *event(), dispatcher->eventPath());
+    return dispatcher->dispatch();
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(TOUCH_EVENTS)
index b2e5b2e..6c139a8 100644 (file)
 
 #if ENABLE(TOUCH_EVENTS)
 
+#include "EventDispatchMediator.h"
 #include "MouseRelatedEvent.h"
+#include "TouchList.h"
 
 namespace WebCore {
 
-class TouchList;
-
 class TouchEvent : public MouseRelatedEvent {
 public:
     virtual ~TouchEvent();
@@ -63,6 +63,11 @@ public:
     TouchList* touches() const { return m_touches.get(); }
     TouchList* targetTouches() const { return m_targetTouches.get(); }
     TouchList* changedTouches() const { return m_changedTouches.get(); }
+
+    void setTouches(PassRefPtr<TouchList> touches) { m_touches = touches; }
+    void setTargetTouches(PassRefPtr<TouchList> targetTouches) { m_targetTouches = targetTouches; }
+    void setChangedTouches(PassRefPtr<TouchList> changedTouches) { m_changedTouches = changedTouches; }
+
 #if PLATFORM(BLACKBERRY)
     void setDoubleTap(bool doubleTap) { m_doubleTap = doubleTap; }
     bool isDoubleTap() const { return m_doubleTap; }
@@ -90,6 +95,22 @@ private:
 #endif
 };
 
+class TouchEventDispatchMediator : public EventDispatchMediator {
+public:
+    static PassRefPtr<TouchEventDispatchMediator> create(PassRefPtr<TouchEvent>);
+
+private:
+    explicit TouchEventDispatchMediator(PassRefPtr<TouchEvent>);
+    TouchEvent* event() const;
+    virtual bool dispatchEvent(EventDispatcher*) const OVERRIDE;
+};
+
+inline TouchEvent* toTouchEvent(Event* event)
+{
+    ASSERT(event && event->isTouchEvent());
+    return static_cast<TouchEvent*>(event);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(TOUCH_EVENTS)
index 4167e42..9add228 100644 (file)
@@ -38,6 +38,11 @@ Touch* TouchList::item(unsigned index)
     return m_values[index].get();
 }
 
+const Touch* TouchList::item(unsigned index) const
+{
+    return const_cast<TouchList*>(this)->item(index);
+}
+
 } // namespace WebCore
 
 #endif
index 2546955..639b73c 100644 (file)
@@ -44,6 +44,7 @@ public:
     unsigned length() const { return m_values.size(); }
 
     Touch* item(unsigned);
+    const Touch* item(unsigned) const;
 
     void append(const PassRefPtr<Touch> touch) { m_values.append(touch); }
 
index 436bb35..4e041f4 100644 (file)
@@ -3775,7 +3775,6 @@ HitTestResult EventHandler::hitTestResultInFrame(Frame* frame, const LayoutPoint
             return result;
     }
     frame->contentRenderer()->hitTest(HitTestRequest(hitType), result);
-    result.setToNonShadowAncestor();
     return result;
 }
 
@@ -3926,10 +3925,7 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
         int adjustedPageX = lroundf(pagePoint.x() / scaleFactor);
         int adjustedPageY = lroundf(pagePoint.y() / scaleFactor);
 
-        // FIXME: Instead of taking shadow ancestor, event retargetting algorithm should run.
-        // https://bugs.webkit.org/show_bug.cgi?id=107800
-        EventTarget* adjustedTouchTarget = doc->ancestorInThisScope(touchTarget.get()->toNode());
-        RefPtr<Touch> touch = Touch::create(targetFrame, adjustedTouchTarget, point.id(),
+        RefPtr<Touch> touch = Touch::create(targetFrame, touchTarget.get(), point.id(),
                                             point.screenPos().x(), point.screenPos().y(),
                                             adjustedPageX, adjustedPageY,
                                             point.radiusX(), point.radiusY(), point.rotationAngle(), point.force());
@@ -3986,7 +3982,7 @@ bool EventHandler::handleTouchEvent(const PlatformTouchEvent& event)
                 TouchEvent::create(effectiveTouches.get(), targetTouches.get(), changedTouches[state].m_touches.get(),
                                    stateName, touchEventTarget->toNode()->document()->defaultView(),
                                    0, 0, 0, 0, event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey());
-            touchEventTarget->dispatchEvent(touchEvent.get(), IGNORE_EXCEPTION);
+            touchEventTarget->toNode()->dispatchTouchEvent(touchEvent.get());
             swallowedEvent = swallowedEvent || touchEvent->defaultPrevented() || touchEvent->defaultHandled();
         }
     }