Touch adjustment does not target shadow DOM elements
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Jun 2012 00:41:28 +0000 (00:41 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Jun 2012 00:41:28 +0000 (00:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=89556

Source/WebCore:

The position of internal shadow-DOM nodes were not being considered
when determining the snap position when TOUCH_ADJUSTMENT is enabled
for fine tuning the position of synthetic mouse events.  This
restriction results in not being able to select the calendar picker
when input type=date, or to clear the search field for input
type=search.

Patch by Kevin Ellis <kevers@chromium.org> on 2012-06-26
Reviewed by Antonio Gomes.

Test: touchadjustment/nested-shadow-node.html

* page/EventHandler.cpp:
(WebCore::EventHandler::bestClickableNodeForTouchPoint):

LayoutTests:

Cannot open calendar picker for input type=date using a touch tap
gesture if TOUCH_ADJUSTMENT is enabled. When touch adjustment is
enabled, the position of a touch point is snapped to the center of an
element when generating synthetic mouse events.  The position of shadow
nodes was not being considered when determining the snap position.
This test verifies that touch adjustment now considers shadow-DOM
when calculating the snap position.

Patch by Kevin Ellis <kevers@chromium.org> on 2012-06-26
Reviewed by Antonio Gomes.

* touchadjustment/nested-shadow-node-expected.txt: Added.
* touchadjustment/nested-shadow-node.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/touchadjustment/nested-shadow-node-expected.txt [new file with mode: 0644]
LayoutTests/touchadjustment/nested-shadow-node.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/EventHandler.cpp

index 5d2c131..6e39859 100644 (file)
@@ -1,3 +1,21 @@
+2012-06-26  Kevin Ellis  <kevers@chromium.org>
+
+        Touch adjustment does not target shadow DOM elements
+        https://bugs.webkit.org/show_bug.cgi?id=89556
+
+        Cannot open calendar picker for input type=date using a touch tap
+        gesture if TOUCH_ADJUSTMENT is enabled. When touch adjustment is 
+        enabled, the position of a touch point is snapped to the center of an
+        element when generating synthetic mouse events.  The position of shadow
+        nodes was not being considered when determining the snap position. 
+        This test verifies that touch adjustment now considers shadow-DOM
+        when calculating the snap position. 
+
+        Reviewed by Antonio Gomes.
+
+        * touchadjustment/nested-shadow-node-expected.txt: Added.
+        * touchadjustment/nested-shadow-node.html: Added.
+
 2012-06-26  Ryosuke Niwa  <rniwa@webkit.org>
 
         Stop calling node() and deprecatedEditingOffset() in comparePositions
             These make sure the alignment used in the first row of the span is correct when things like
             the height of the cell and the height of the span are specified.
 
-
 2012-06-18  Hayato Ito  <hayato@chromium.org>
 
         Event dispatcher should use InsertionPoint::hasDistribution instead of InsertinPoint::isActive in re-targeting.
diff --git a/LayoutTests/touchadjustment/nested-shadow-node-expected.txt b/LayoutTests/touchadjustment/nested-shadow-node-expected.txt
new file mode 100644 (file)
index 0000000..66b4bb1
--- /dev/null
@@ -0,0 +1,24 @@
+Test the case where a clickable target contains a shadow-DOM element. The adjusted point should snap to the location of the shadow-DOM element if close enough to the original touch position.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS adjustedNode.id is element.id
+PASS adjustedPoint.x is within 10 of 88
+PASS adjustedPoint.y is within 10 of 28
+PASS adjustedNode.id is element.id
+PASS adjustedPoint.x is within 10 of 88
+PASS adjustedPoint.y is within 10 of 28
+PASS adjustedNode.id is element.id
+PASS adjustedPoint.x is within 10 of 88
+PASS adjustedPoint.y is within 10 of 28
+PASS adjustedNode.id is element.id
+PASS adjustedPoint.x is within 10 of 88
+PASS adjustedPoint.y is within 10 of 28
+PASS adjustedNode.id is element.id
+PASS adjustedPoint.x is within 1 of 58
+PASS adjustedPoint.y is within 1 of 58
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/touchadjustment/nested-shadow-node.html b/LayoutTests/touchadjustment/nested-shadow-node.html
new file mode 100644 (file)
index 0000000..ef54c94
--- /dev/null
@@ -0,0 +1,103 @@
+<html>
+<head>
+<style>
+    #targetDiv {
+        background: #00f;
+        height: 100px;
+        position: relative;
+        width: 100px;
+    }
+</style>
+<script src="../fast/js/resources/js-test-pre.js"></script>
+
+</head>
+
+<body onload="runTests()">
+
+<div id="targetDiv">
+</div>
+
+<p id='description'></p>
+<div id='console'></div>
+
+<script>
+    var element;
+    var adjustedNode;
+    var adjustedPoint;
+
+    function findAbsolutePosition(node) {
+        var pos = {left: 0, top: 0};
+        do {
+            pos.left += node.offsetLeft;
+            pos.top += node.offsetTop;
+        } while (node = node.offsetParent);
+        return pos;
+    }
+
+    function addShadowDOM() {
+        var targetDiv = document.getElementById("targetDiv");
+        var root = internals.ensureShadowRoot(targetDiv);
+        var shadowDiv = document.createElement("div");
+        shadowDiv.style.width = "20px";
+        shadowDiv.style.height = "20px";
+        shadowDiv.style.background = "#ff0";
+        shadowDiv.style.position = "absolute";
+        shadowDiv.style.right = "10px";
+        shadowDiv.style.top = "10px";
+        root.appendChild(shadowDiv);
+    }
+
+    function runTouchTests() {
+        element = document.getElementById("targetDiv");
+        element.addEventListener('click', function() {}, false);
+        document.addEventListener('click', function() {}, false);
+
+        var pos = findAbsolutePosition(element);
+        var x = pos.left;
+        var y = pos.top;
+        var width = element.clientWidth;
+        var height = element.clientHeight;
+        var midX = x + width / 2;
+        var midY = y + height / 2;
+        var border = 10;
+        var targetRadius = 10;
+        var padding = 30;
+        var targetX = x + width - targetRadius - border;
+        var targetY = y + targetRadius + border;
+        var offset = 2;
+
+        // Test touches that are just outside the bounds of the shadow-DOM.  The adjusted point should be pulled within the bounds of the shadow-DOM node.
+        testTouch(targetX + targetRadius + offset, targetY, padding, targetX, targetY, targetRadius);
+        testTouch(targetX - targetRadius - offset, targetY, padding, targetX, targetY, targetRadius);
+        testTouch(targetX, targetY + targetRadius + offset, padding, targetX, targetY, targetRadius);
+        testTouch(targetX, targetY - targetRadius - offset, padding, targetX, targetY, targetRadius);
+        
+        // A touch in the center of targetDiv is sufficient distance from the shadow-DOM element that the position should not snap.
+        testTouch(midX, midY, padding, midX, midY, 1);
+    }
+
+    function testTouch(touchX, touchY, padding, adjustedX, adjustedY, tolerance) {
+        var left = touchX - padding / 2;
+        var top = touchY - padding / 2;
+        adjustedNode = internals.touchNodeAdjustedToBestClickableNode(left, top, padding, padding, document);
+        shouldBe('adjustedNode.id', 'element.id');
+        adjustedPoint = internals.touchPositionAdjustedToBestClickableNode(left, top, padding, padding, document);
+        shouldBeCloseTo('adjustedPoint.x', adjustedX, tolerance);
+        shouldBeCloseTo('adjustedPoint.y', adjustedY, tolerance);
+    }
+
+    function runTests()
+    {
+        if (window.testRunner && window.internals && internals.touchNodeAdjustedToBestClickableNode && internals.ensureShadowRoot) {
+            description('Test the case where a clickable target contains a shadow-DOM element.  The adjusted point should snap to the location of the shadow-DOM element if close enough to the original touch position.')
+            addShadowDOM();
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+            runTouchTests();
+            isSuccessfullyParsed();
+            testRunner.notifyDone();
+        }
+    }
+</script>
+</body>
+</html>
index 5e6a003..ea2016c 100755 (executable)
@@ -1,3 +1,22 @@
+2012-06-26  Kevin Ellis  <kevers@chromium.org>
+
+        Touch adjustment does not target shadow DOM elements
+        https://bugs.webkit.org/show_bug.cgi?id=89556
+
+        The position of internal shadow-DOM nodes were not being considered
+        when determining the snap position when TOUCH_ADJUSTMENT is enabled 
+        for fine tuning the position of synthetic mouse events.  This 
+        restriction results in not being able to select the calendar picker
+        when input type=date, or to clear the search field for input 
+        type=search.
+
+        Reviewed by Antonio Gomes.
+
+        Test: touchadjustment/nested-shadow-node.html
+
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::bestClickableNodeForTouchPoint):
+
 2012-06-26  Ryosuke Niwa  <rniwa@webkit.org>
 
         Stop calling node() and deprecatedEditingOffset() in comparePositions
index d785834..367ab04 100644 (file)
@@ -2483,11 +2483,19 @@ bool EventHandler::bestClickableNodeForTouchPoint(const IntPoint& touchCenter, c
 {
     HitTestRequest::HitTestRequestType hitType = HitTestRequest::ReadOnly | HitTestRequest::Active;
     IntPoint hitTestPoint = m_frame->view()->windowToContents(touchCenter);
-    HitTestResult result = hitTestResultAtPoint(hitTestPoint, /*allowShadowContent*/ false, /*ignoreClipping*/ false, DontHitTestScrollbars, hitType, touchRadius);
+    HitTestResult result = hitTestResultAtPoint(hitTestPoint, /*allowShadowContent*/ true, /*ignoreClipping*/ false, DontHitTestScrollbars, hitType, touchRadius);
 
     IntRect touchRect(touchCenter - touchRadius, touchRadius + touchRadius);
     RefPtr<StaticHashSetNodeList> nodeList = StaticHashSetNodeList::adopt(result.rectBasedTestResult());
-    return findBestClickableCandidate(targetNode, targetPoint, touchCenter, touchRect, *nodeList.get());
+
+    // FIXME: Should be able to handle targetNode being a shadow DOM node to avoid performing uncessary hit tests
+    // in the case where further processing on the node is required. Returning the shadow ancestor prevents a
+    // regression in touchadjustment/html-label.html. Some refinement is required to testing/internals to
+    // handle targetNode being a shadow DOM node. 
+    bool success = findBestClickableCandidate(targetNode, targetPoint, touchCenter, touchRect, *nodeList.get());
+    if (success && targetNode)
+        targetNode = targetNode->shadowAncestorNode();
+    return success;
 }
 
 bool EventHandler::bestZoomableAreaForTouchPoint(const IntPoint& touchCenter, const IntSize& touchRadius, IntRect& targetArea, Node*& targetNode)