Fix hit testing for divs with a hierarchy of css transformed and non-transformed...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Dec 2013 10:43:52 +0000 (10:43 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 17 Dec 2013 10:43:52 +0000 (10:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=124777

Patch by Mihai Maerean <mmaerean@adobe.com> on 2013-12-17
Reviewed by Darin Adler.

Source/WebCore:

After bug #124647, the hit test will still behave incorrectly for transformed divs with non
transformed siblings that are all inside a transformed element (tested by the
hover-rotated-with-children-negative-z.html layout test).

The fix is to not take zOffset into account during hit-testing when child layers are in the
same 3D rendering context. Only when preserve3d is true, should hit-testing compute the
zOffset of the layers with transformations and, when two layers overlap, to return the layer
with the highest zOffset.

The patch includes the work of a.renevier from https://codereview.chromium.org/79943002/

Tests: transforms/3d/hit-testing/hover-rotated-with-children-negative-z.html
       transforms/3d/hit-testing/negative-zoffset-hit-test.html
       transforms/3d/hit-testing/overlapping-layers-hit-test.html

* rendering/RenderLayer.cpp:
(WebCore::computeZOffset):
(WebCore::RenderLayer::hitTestLayer):

LayoutTests:

* transforms/3d/hit-testing/hover-rotated-with-children-negative-z.html: Added.
* transforms/3d/hit-testing/hover-rotated-with-children-negative-z-expected.txt: Added.
* transforms/3d/hit-testing/negative-zoffset-hit-test.html: Added.
* transforms/3d/hit-testing/negative-zoffset-hit-test-expected.txt: Added.
* transforms/3d/hit-testing/overlapping-layers-hit-test.html: Added.
* transforms/3d/hit-testing/overlapping-layers-hit-test-expected.txt: Added.

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

LayoutTests/ChangeLog
LayoutTests/transforms/3d/hit-testing/hover-rotated-with-children-negative-z-expected.txt [new file with mode: 0644]
LayoutTests/transforms/3d/hit-testing/hover-rotated-with-children-negative-z.html [new file with mode: 0644]
LayoutTests/transforms/3d/hit-testing/negative-zoffset-hit-test-expected.txt [new file with mode: 0644]
LayoutTests/transforms/3d/hit-testing/negative-zoffset-hit-test.html [new file with mode: 0644]
LayoutTests/transforms/3d/hit-testing/overlapping-layers-hit-test-expected.txt [new file with mode: 0644]
LayoutTests/transforms/3d/hit-testing/overlapping-layers-hit-test.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderLayer.cpp

index ef4d595..23552df 100644 (file)
@@ -1,3 +1,17 @@
+2013-12-17  Mihai Maerean  <mmaerean@adobe.com>
+
+        Fix hit testing for divs with a hierarchy of css transformed and non-transformed elements
+        https://bugs.webkit.org/show_bug.cgi?id=124777
+
+        Reviewed by Darin Adler.
+
+        * transforms/3d/hit-testing/hover-rotated-with-children-negative-z.html: Added.
+        * transforms/3d/hit-testing/hover-rotated-with-children-negative-z-expected.txt: Added.
+        * transforms/3d/hit-testing/negative-zoffset-hit-test.html: Added.
+        * transforms/3d/hit-testing/negative-zoffset-hit-test-expected.txt: Added.
+        * transforms/3d/hit-testing/overlapping-layers-hit-test.html: Added.
+        * transforms/3d/hit-testing/overlapping-layers-hit-test-expected.txt: Added.
+
 2013-12-16  Thiago de Barros Lacerda  <thiago.lacerda@openbossa.org>
 
         Checking RTCPeerConnection signalingState before setting local/remoteDescription
diff --git a/LayoutTests/transforms/3d/hit-testing/hover-rotated-with-children-negative-z-expected.txt b/LayoutTests/transforms/3d/hit-testing/hover-rotated-with-children-negative-z-expected.txt
new file mode 100644 (file)
index 0000000..97a5c01
--- /dev/null
@@ -0,0 +1,13 @@
+transformed
+child 1
+non transformed child
+child 2
+Test passes if the hover state of a transformed div is activated even if there's a non transformed element behind it.
+
+Element at 70, 70 has id "transformed": PASS
+Element at 630, 130 has id "transformed": PASS
+Element at 40, 130 has id "transformed": PASS
+Element at 620, 270 has id "transformed": PASS
+Element at 130, 100 has id "child1": PASS
+Element at 200, 110 has id "child2": PASS
+
diff --git a/LayoutTests/transforms/3d/hit-testing/hover-rotated-with-children-negative-z.html b/LayoutTests/transforms/3d/hit-testing/hover-rotated-with-children-negative-z.html
new file mode 100644 (file)
index 0000000..7cf2065
--- /dev/null
@@ -0,0 +1,65 @@
+<html>
+       <head>
+               <title>Test - Bug #124777: Fix hover area for divs with a hierarchy of css transformed and non-transformed elements</title>
+               <style>
+                       #transformed {
+                               -webkit-transform: translateY(50px) rotateX(20deg) rotateZ(10deg);
+                               border: solid 5px rgba(128,128,128,0.5);
+                               padding: 10px;
+                               margin: 15px;
+                       }
+                       #transformed, #parent {
+                               width: 600px;
+                               height: 150px;
+                       }
+                       #child1 {
+                               -webkit-transform: rotateX(45deg) rotateZ(45deg) translateZ(-500px) translateX(-300px) translateY(-180px);
+                               border: solid 5px rgba(128,128,128,0.5);
+                               background-color: Lime;
+                       }
+                       #child2 {
+                               -webkit-transform: translateZ(-500px) rotateX(-45deg) rotateZ(-45deg) translateX(50px) translateY(-130px);
+                               border: solid 5px rgba(128,128,128,0.5);
+                               background-color: Cyan;
+                       }
+                       html {
+                               font-size: 16px;
+                               line-height: 16px;
+                               font-family: Verdana;
+                       }
+                       #parent:hover, #transformed:hover, #child1:hover, #child2:hover {
+                               box-shadow: 0px 0px 0px 10px rgba(0,128,0,0.5);
+                       }
+                       #parent {
+                               outline: dotted 1px #888;
+                       }
+               </style>
+
+               <script src="resources/hit-test-utils.js"></script>
+               <script>
+                       const hitTestData = [
+                               { 'point': [70, 70], 'target' : 'transformed' },
+                               { 'point': [630, 130], 'target' : 'transformed' },
+                               { 'point': [40, 130], 'target' : 'transformed' },
+                               { 'point': [620, 270], 'target' : 'transformed' },
+                               { 'point': [130, 100], 'target' : 'child1' },
+                               { 'point': [200, 110], 'target' : 'child2' }
+                       ];
+
+                       window.addEventListener('load', runTest, false);
+                 </script>
+       </head>
+       <body id="body">
+               <div id="parent">
+                       <div id="transformed">transformed
+                               <div id="child1">child 1</div>
+                               <div id="nonTransformedChild">non transformed child</div>
+                               <div id="child2">child 2</div>
+                       </div>
+               </div>
+
+               <p>Test passes if the hover state of a transformed div is activated even if there's a non transformed element behind it.</p>
+
+               <div id="results"></div>
+       </body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/transforms/3d/hit-testing/negative-zoffset-hit-test-expected.txt b/LayoutTests/transforms/3d/hit-testing/negative-zoffset-hit-test-expected.txt
new file mode 100644 (file)
index 0000000..aa0062b
--- /dev/null
@@ -0,0 +1,9 @@
+Element at 50, 50 has id "container": PASS
+Element at 60, 60 has id "container": PASS
+Element at 70, 70 has id "target": PASS
+Element at 80, 80 has id "target": PASS
+Element at 90, 90 has id "target": PASS
+Element at 100, 100 has id "target": PASS
+Element at 150, 150 has id "target": PASS
+Element at 180, 180 has id "container": PASS
+
diff --git a/LayoutTests/transforms/3d/hit-testing/negative-zoffset-hit-test.html b/LayoutTests/transforms/3d/hit-testing/negative-zoffset-hit-test.html
new file mode 100644 (file)
index 0000000..927dae0
--- /dev/null
@@ -0,0 +1,54 @@
+<html>
+<head>
+       <title>Test - Bug #124777: Fix hover area for divs with a hierarchy of css transformed and non-transformed elements</title>
+       <style type="text/css">
+               #container
+               {
+                       position: absolute;
+                       height: 200px;
+                       width:200px;
+                       top: 0;
+                       left: 0;
+                       background-color: gray;
+               }
+
+               /* target contain points with negative z-offsets */
+               #target
+               {
+                       position: relative;
+                       height: 160px;
+                       width: 160px;
+                       margin: 20px;
+                       background-color: #DDD;
+                       -webkit-transform: rotate3d(0, 1, 0, -45deg);
+                       -webkit-transform-origin: right;
+               }
+               #results {
+                               margin-top: 210px;
+               }
+       </style>
+       <script src="resources/hit-test-utils.js"></script>
+       <script>
+                       const hitTestData = [
+                               { 'point': [50, 50], 'target' : 'container' },
+                               { 'point': [60, 60], 'target' : 'container' },
+                               { 'point': [70, 70], 'target' : 'target' },
+                               { 'point': [80, 80], 'target' : 'target' },
+                               { 'point': [90, 90], 'target' : 'target' },
+                               { 'point': [100, 100], 'target' : 'target' },
+                               { 'point': [150, 150], 'target' : 'target' },
+                               { 'point': [180, 180], 'target' : 'container' },
+                       ];
+                       window.addEventListener('load', runTest, false);
+       </script>
+</head>
+<body>
+
+       <div id="container" class="container">
+               <div id="target">
+               </div>
+       </div>
+       <div id="results"></div>
+
+</body>
+</html>
diff --git a/LayoutTests/transforms/3d/hit-testing/overlapping-layers-hit-test-expected.txt b/LayoutTests/transforms/3d/hit-testing/overlapping-layers-hit-test-expected.txt
new file mode 100644 (file)
index 0000000..3f3a229
--- /dev/null
@@ -0,0 +1,15 @@
+Element at 10, 100 has id "container-nopreserve": PASS
+Element at 20, 100 has id "target2": PASS
+Element at 80, 100 has id "target1": PASS
+Element at 100, 100 has id "target1": PASS
+Element at 120, 100 has id "target1": PASS
+Element at 180, 100 has id "target1": PASS
+Element at 190, 100 has id "container-nopreserve": PASS
+Element at 10, 250 has id "container-preserve": PASS
+Element at 20, 250 has id "target4": PASS
+Element at 80, 250 has id "target4": PASS
+Element at 100, 250 has id "target4": PASS
+Element at 120, 250 has id "target3": PASS
+Element at 180, 250 has id "target3": PASS
+Element at 190, 250 has id "container-preserve": PASS
+
diff --git a/LayoutTests/transforms/3d/hit-testing/overlapping-layers-hit-test.html b/LayoutTests/transforms/3d/hit-testing/overlapping-layers-hit-test.html
new file mode 100644 (file)
index 0000000..b3092c8
--- /dev/null
@@ -0,0 +1,106 @@
+<html>
+<head>
+       <title>Test - Bug #124777: Fix hover area for divs with a hierarchy of css transformed and non-transformed elements</title>
+       <style type="text/css">
+               .container
+               {
+                       position: absolute;
+                       height: 200px;
+                       width:200px;
+                       background-color: gray;
+               }
+
+               .nopreserve {
+                       top: 0;
+                       left: 0;
+               }
+
+               .preserve {
+                       top: 250px;
+                       left: 0;
+                       -webkit-transform-style: preserve-3d;
+                       -moz-transform-style: preserve-3d;
+               }
+
+               .box {
+                               width: 100%;
+                               position: absolute;
+                               height: 100%;
+                               top: 0;
+               }
+
+               .red {
+                       background-color: red;
+                       -webkit-transform: perspective( 600px ) rotateY( 45deg );
+                       -moz-transform: perspective( 600px ) rotateY( 45deg );
+                       transform: perspective( 600px ) rotateY( 45deg );
+               }
+
+               .blue {
+                       background-color: blue;
+                       -webkit-transform: perspective( 600px ) rotateY( -45deg );
+                       -moz-transform: perspective( 600px ) rotateY( -45deg );
+                       transform: perspective( 600px ) rotateY( -45deg );
+               }
+
+               /* target1 contain points with negative z-offsets */
+               #target
+               {
+                       position: relative;
+                       height: 160px;
+                       width: 160px;
+                       margin: 20px;
+                       background-color: #DDD;
+                       -webkit-transform: rotate3d(0, 1, 0, -45deg);
+                       -webkit-transform-origin: right;
+               }
+
+               #results {
+                       margin-top: 460px;
+               }
+       </style>
+       <script src="resources/hit-test-utils.js"></script>
+       <script>
+               const hitTestData = [
+
+                       { 'point': [10, 100], 'target' : 'container-nopreserve' },
+                       { 'point': [20, 100], 'target' : 'target2' },
+                       { 'point': [80, 100], 'target' : 'target1' },
+                       { 'point': [100, 100], 'target' : 'target1' },
+                       { 'point': [120, 100], 'target' : 'target1' },
+                       { 'point': [180, 100], 'target' : 'target1' },
+                       { 'point': [190, 100], 'target' : 'container-nopreserve' },
+
+                       { 'point': [10, 250], 'target' : 'container-preserve' },
+                       { 'point': [20, 250], 'target' : 'target4' },
+                       { 'point': [80, 250], 'target' : 'target4' },
+                       { 'point': [100, 250], 'target' : 'target4' },
+                       { 'point': [120, 250], 'target' : 'target3' },
+                       { 'point': [180, 250], 'target' : 'target3' },
+                       { 'point': [190, 250], 'target' : 'container-preserve' },
+
+               ];
+
+               window.addEventListener('load', runTest, false);
+       </script>
+</head>
+<body>
+
+       <div id="container-nopreserve" class="container nopreserve">
+               <div id="target2" class="box red">
+               </div>
+               <div id="target1" class="box blue">
+               </div>
+       </div>
+
+       <div id="container-preserve" class="container preserve">
+               <div id="target3" class="box blue">
+               </div>
+               <div id="target4" class="box red">
+               </div>
+       </div>
+
+       <div id="results"></div>
+
+</body>
+</html>
index edace6d..1c64042 100644 (file)
@@ -1,3 +1,29 @@
+2013-12-17  Mihai Maerean  <mmaerean@adobe.com>
+
+        Fix hit testing for divs with a hierarchy of css transformed and non-transformed elements
+        https://bugs.webkit.org/show_bug.cgi?id=124777
+
+        Reviewed by Darin Adler.
+
+        After bug #124647, the hit test will still behave incorrectly for transformed divs with non
+        transformed siblings that are all inside a transformed element (tested by the
+        hover-rotated-with-children-negative-z.html layout test).
+
+        The fix is to not take zOffset into account during hit-testing when child layers are in the
+        same 3D rendering context. Only when preserve3d is true, should hit-testing compute the
+        zOffset of the layers with transformations and, when two layers overlap, to return the layer
+        with the highest zOffset.
+
+        The patch includes the work of a.renevier from https://codereview.chromium.org/79943002/
+
+        Tests: transforms/3d/hit-testing/hover-rotated-with-children-negative-z.html
+               transforms/3d/hit-testing/negative-zoffset-hit-test.html
+               transforms/3d/hit-testing/overlapping-layers-hit-test.html
+
+        * rendering/RenderLayer.cpp:
+        (WebCore::computeZOffset):
+        (WebCore::RenderLayer::hitTestLayer):
+
 2013-12-17  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         Remove a few more guards mistakenly added in r160367
index 36f0ecc..045aa75 100644 (file)
@@ -4709,14 +4709,8 @@ bool RenderLayer::isFlowThreadCollectingGraphicsLayersUnderRegions() const
 static double computeZOffset(const HitTestingTransformState& transformState)
 {
     // We got an affine transform, so no z-offset
-    if (transformState.m_accumulatedTransform.isAffine()) {
-        // Non transformed layers are being hit last, not through or in-between transformed layers.
-        // The paint order says that the divs creating stacking contexts (including transforms) are painted after the
-        // other siblings so they should be hit tested in the reverse order. Also, a rotated div in a non-rotated parent
-        // should be hit in its entire area, not hit its parent's background, even if the z-coordinate is negative where
-        // the mouse is located.
-        return -std::numeric_limits<int>::max();
-    }
+    if (transformState.m_accumulatedTransform.isAffine())
+        return 0;
 
     // Flatten the point into the target plane
     FloatPoint targetPoint = transformState.mappedPoint();
@@ -4920,11 +4914,6 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont
         // Our layers can depth-test with our container, so share the z depth pointer with the container, if it passed one down.
         zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset;
         zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset;
-    } else if (m_has3DTransformedDescendant) {
-        // Flattening layer with 3d children; use a local zOffset pointer to depth-test children and foreground.
-        depthSortDescendants = true;
-        zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset;
-        zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset;
     } else if (zOffset) {
         zOffsetForDescendantsPtr = 0;
         // Container needs us to give back a z offset for the hit layer.