Changeset 98361 in webkit


Ignore:
Timestamp:
Oct 25, 2011 10:54:11 AM (12 years ago)
Author:
Simon Fraser
Message:

REGRESSION (r88580): Cursor fails to change to pointer on embedded Google maps popups
https://bugs.webkit.org/show_bug.cgi?id=62797

Source/WebCore:

Reviewed by Chris Marrin.

When hit testing through transformed layers, RenderLayer would simply use
the composited bounds of the layer as the localHitTestRect for hit testing sublayers.
However, this broke hit testing on pages that have a composited, non-transformed layer
which falls outside the bounds of its parent, composited-transformed layer, like Google
Maps.

Fix by mapping the hitTestRect through transforms. However this is tricky, because
mapping a rect into the coordinate system of a layer can result in invalid rectangles
when point projection results in a negative w component. Fix TransformationMatrix::projectPoint()
to detect this case and replace X and Y with large values, and add boundsOfProjectedQuad(), which
maps rectangles with possibly-infinite location or bounds into rects which are representable
in an IntRect.

Tests: transforms/3d/hit-testing/composited-hit-test.html

transforms/3d/hit-testing/rotated-hit-test-with-child.html
transforms/3d/hit-testing/rotated-hit-test2.html

  • platform/graphics/transforms/TransformationMatrix.cpp:

(WebCore::TransformationMatrix::projectPoint):
(WebCore::TransformationMatrix::projectQuad):
(WebCore::clampEdgeValue):
(WebCore::TransformationMatrix::boundsOfProjectedQuad):

  • platform/graphics/transforms/TransformationMatrix.h:
  • rendering/HitTestingTransformState.cpp:

(WebCore::HitTestingTransformState::boundsOfMappedQuad):

  • rendering/HitTestingTransformState.h:
  • rendering/RenderLayer.cpp:

(WebCore::RenderLayer::hitTestLayer):

LayoutTests:

Reviewed by Chris Marrin.

New tests for hit testing through various configurations of 3D transforms.

  • platform/mac/transforms/3d/hit-testing/rotated-hit-test-expected.png: Removed.
  • platform/mac/transforms/3d/hit-testing/rotated-hit-test-expected.txt: Removed.
  • transforms/3d/hit-testing/composited-hit-test-expected.txt: Added.
  • transforms/3d/hit-testing/composited-hit-test.html: Added.
  • transforms/3d/hit-testing/resources/hit-test-utils.js: Added.
  • transforms/3d/hit-testing/rotated-hit-test-expected.txt: Added.
  • transforms/3d/hit-testing/rotated-hit-test-with-child-expected.txt: Added.
  • transforms/3d/hit-testing/rotated-hit-test-with-child.html: Added.
  • transforms/3d/hit-testing/rotated-hit-test.html:
  • transforms/3d/hit-testing/rotated-hit-test2-expected.txt: Added.
  • transforms/3d/hit-testing/rotated-hit-test2.html: Added.
Location:
trunk
Files:
9 added
2 deleted
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r98359 r98361  
     12011-10-25  Simon Fraser  <simon.fraser@apple.com>
     2
     3        REGRESSION (r88580): Cursor fails to change to pointer on embedded Google maps popups
     4        https://bugs.webkit.org/show_bug.cgi?id=62797
     5
     6        Reviewed by Chris Marrin.
     7       
     8        New tests for hit testing through various configurations of 3D transforms.
     9
     10        * platform/mac/transforms/3d/hit-testing/rotated-hit-test-expected.png: Removed.
     11        * platform/mac/transforms/3d/hit-testing/rotated-hit-test-expected.txt: Removed.
     12        * transforms/3d/hit-testing/composited-hit-test-expected.txt: Added.
     13        * transforms/3d/hit-testing/composited-hit-test.html: Added.
     14        * transforms/3d/hit-testing/resources/hit-test-utils.js: Added.
     15        * transforms/3d/hit-testing/rotated-hit-test-expected.txt: Added.
     16        * transforms/3d/hit-testing/rotated-hit-test-with-child-expected.txt: Added.
     17        * transforms/3d/hit-testing/rotated-hit-test-with-child.html: Added.
     18        * transforms/3d/hit-testing/rotated-hit-test.html:
     19        * transforms/3d/hit-testing/rotated-hit-test2-expected.txt: Added.
     20        * transforms/3d/hit-testing/rotated-hit-test2.html: Added.
     21
    1222011-10-24  Jer Noble  <jer.noble@apple.com>
    223
  • trunk/LayoutTests/transforms/3d/hit-testing/rotated-hit-test.html

    r41952 r98361  
    11<html>
    22<head>
    3   <title>Hit testing of almost edge-on elements</title>
    4   <style type="text/css" media="screen">
    5  
    6   div:hover {
    7     outline: 2px solid orange;
    8   }
     3  <style type="text/css">
     4    body {
     5      margin: 0;
     6    }
    97    #container {
    108      width: 200px;
    119      height: 200px;
     10      margin: 50px;
    1211      border: 1px solid black;
    13       -webkit-perspective: 500;
     12      -webkit-perspective: 500px;
    1413    }
    1514   
    1615    .box {
    17       display: inline-block;
     16      position: absolute;
    1817      width: 200px;
    1918      height: 200px;
    2019      background-color: gray;
     20      opacity: 0.75;
     21    }
     22   
     23    #left {
    2124      -webkit-transform-origin: 10% 50%;
    2225      -webkit-transform: rotateY(90deg);
    2326    }
     27
     28    #top {
     29      -webkit-transform-origin: 50% 10%;
     30      -webkit-transform: rotateX(-90deg);
     31    }
     32
     33    #right {
     34      -webkit-transform-origin: 90% 50%;
     35      -webkit-transform: rotateY(-90deg);
     36    }
     37
     38    #bottom {
     39      -webkit-transform-origin: 50% 90%;
     40      -webkit-transform: rotateX(90deg);
     41    }
     42
    2443    .box:hover {
    2544      background-color: orange;
    2645    }
    2746  </style>
    28   <script type="text/javascript" charset="utf-8">
    29     function log(s)
    30     {
    31       var results = document.getElementById('results');
    32       results.innerHTML += s + '<br>';
    33     }
    34 
    35     function runTest()
    36     {
    37       var firstHit = document.elementFromPoint(40, 150);
    38      
    39       var box1 = document.getElementById('box1');
    40       var container = document.getElementById('container');
    41 
    42       if (firstHit == box1)
    43         log('Found box1: PASS');
    44       else
    45         log('Found ' + firstHit.id + ' : FAIL');
    46     }
    47    
     47  <script src="resources/hit-test-utils.js"></script>
     48  <script>
     49      const hitTestData = [
     50        { 'point': [85, 100], 'target' : 'left' },
     51        { 'point': [155, 80], 'target' : 'top' },
     52        { 'point': [230, 80], 'target' : 'right' },
     53        { 'point': [155, 220], 'target' : 'bottom' },
     54        { 'point': [60, 60], 'target' : 'container' },
     55        { 'point': [100, 100], 'target' : 'container' },
     56        { 'point': [240, 240], 'target' : 'container' },
     57        { 'point': [200, 200], 'target' : 'container' },
     58      ];
     59      window.addEventListener('load', runTest, false);
    4860  </script>
    4961</head>
    50 <body onload="runTest()">
     62<body>
    5163
    5264<div id="container">
    53   <div class="box" id="box1"></div>
     65  <div class="box" id="left"></div>
     66  <div class="box" id="top"></div>
     67  <div class="box" id="right"></div>
     68  <div class="box" id="bottom"></div>
    5469</div>
    5570
    56 <div id="results">
    57 </div>
    58 
     71<div id="results"></div>
    5972</body>
    6073</html>
  • trunk/Source/WebCore/ChangeLog

    r98360 r98361  
     12011-10-25  Simon Fraser  <simon.fraser@apple.com>
     2
     3        REGRESSION (r88580): Cursor fails to change to pointer on embedded Google maps popups
     4        https://bugs.webkit.org/show_bug.cgi?id=62797
     5
     6        Reviewed by Chris Marrin.
     7       
     8        When hit testing through transformed layers, RenderLayer would simply use
     9        the composited bounds of the layer as the localHitTestRect for hit testing sublayers.
     10        However, this broke hit testing on pages that have a composited, non-transformed layer
     11        which falls outside the bounds of its parent, composited-transformed layer, like Google
     12        Maps.
     13       
     14        Fix by mapping the hitTestRect through transforms. However this is tricky, because
     15        mapping a rect into the coordinate system of a layer can result in invalid rectangles
     16        when point projection results in a negative w component. Fix TransformationMatrix::projectPoint()
     17        to detect this case and replace X and Y with large values, and add boundsOfProjectedQuad(), which
     18        maps rectangles with possibly-infinite location or bounds into rects which are representable
     19        in an IntRect.
     20
     21        Tests: transforms/3d/hit-testing/composited-hit-test.html
     22               transforms/3d/hit-testing/rotated-hit-test-with-child.html
     23               transforms/3d/hit-testing/rotated-hit-test2.html
     24
     25        * platform/graphics/transforms/TransformationMatrix.cpp:
     26        (WebCore::TransformationMatrix::projectPoint):
     27        (WebCore::TransformationMatrix::projectQuad):
     28        (WebCore::clampEdgeValue):
     29        (WebCore::TransformationMatrix::boundsOfProjectedQuad):
     30        * platform/graphics/transforms/TransformationMatrix.h:
     31        * rendering/HitTestingTransformState.cpp:
     32        (WebCore::HitTestingTransformState::boundsOfMappedQuad):
     33        * rendering/HitTestingTransformState.h:
     34        * rendering/RenderLayer.cpp:
     35        (WebCore::RenderLayer::hitTestLayer):
     36
    1372011-10-25  Adrienne Walker  <enne@google.com>
    238
  • trunk/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp

    r95901 r98361  
    3737#include <wtf/MathExtras.h>
    3838
     39using namespace std;
     40
    3941namespace WebCore {
    4042
     
    533535}
    534536
    535 FloatPoint TransformationMatrix::projectPoint(const FloatPoint& p) const
     537FloatPoint TransformationMatrix::projectPoint(const FloatPoint& p, bool* clamped) const
    536538{
    537539    // This is basically raytracing. We have a point in the destination
     
    547549    //
    548550    // d = -dot (Pn', R0) / dot (Pn', Rd)
     551    if (clamped)
     552        *clamped = false;
    549553   
    550554    double x = p.x();
     
    552556    double z = -(m13() * x + m23() * y + m43()) / m33();
    553557
     558    // FIXME: use multVecMatrix()
    554559    double outX = x * m11() + y * m21() + z * m31() + m41();
    555560    double outY = x * m12() + y * m22() + z * m32() + m42();
    556561
    557562    double w = x * m14() + y * m24() + z * m34() + m44();
    558     if (w != 1 && w != 0) {
     563    if (w <= 0) {
     564        outX = copysign(numeric_limits<int>::max(), outX);
     565        outY = copysign(numeric_limits<int>::max(), outY);
     566        if (clamped)
     567            *clamped = true;
     568    } else if (w != 1) {
    559569        outX /= w;
    560570        outY /= w;
     
    571581    projectedQuad.setP3(projectPoint(q.p3()));
    572582    projectedQuad.setP4(projectPoint(q.p4()));
     583   
    573584    return projectedQuad;
     585}
     586
     587static float clampEdgeValue(float f)
     588{
     589    ASSERT(!isnan(f));
     590    return min<float>(max<float>(f, -numeric_limits<int>::max() / 2), numeric_limits<int>::max() / 2);
     591}
     592
     593IntRect TransformationMatrix::clampedBoundsOfProjectedQuad(const FloatQuad& q) const
     594{
     595    FloatRect mappedQuadBounds = projectQuad(q).boundingBox();
     596
     597    float left = clampEdgeValue(floorf(mappedQuadBounds.x()));
     598    float top = clampEdgeValue(floorf(mappedQuadBounds.y()));
     599
     600    float right;
     601    if (isinf(mappedQuadBounds.x()) && isinf(mappedQuadBounds.width()))
     602        right = numeric_limits<int>::max() / 2;
     603    else
     604        right = clampEdgeValue(ceilf(mappedQuadBounds.maxX()));
     605
     606    float bottom;
     607    if (isinf(mappedQuadBounds.y()) && isinf(mappedQuadBounds.height()))
     608        bottom = numeric_limits<int>::max() / 2;
     609    else
     610        bottom = clampEdgeValue(ceilf(mappedQuadBounds.maxY()));
     611   
     612    return IntRect(clampToInteger(left), clampToInteger(top),
     613                   clampToInteger(right - left), clampToInteger(bottom - top));
    574614}
    575615
  • trunk/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h

    r95901 r98361  
    157157    // the local x,y position of the point where that ray intersects
    158158    // with the destination plane.
    159     FloatPoint projectPoint(const FloatPoint&) const;
     159    FloatPoint projectPoint(const FloatPoint&, bool* clamped = 0) const;
    160160    // Projects the four corners of the quad
    161161    FloatQuad projectQuad(const FloatQuad&) const;
     162    // Projects the four corners of the quad and takes a bounding box,
     163    // while sanitizing values created when the w component is negative.
     164    IntRect clampedBoundsOfProjectedQuad(const FloatQuad&) const;
    162165
    163166    double m11() const { return m_matrix[0][0]; }
  • trunk/Source/WebCore/rendering/HitTestingTransformState.cpp

    r95901 r98361  
    7474}
    7575
     76IntRect HitTestingTransformState::boundsOfMappedQuad() const
     77{
     78    return m_accumulatedTransform.inverse().clampedBoundsOfProjectedQuad(m_lastPlanarQuad);
     79}
     80
    7681} // namespace WebCore
  • trunk/Source/WebCore/rendering/HitTestingTransformState.h

    r95901 r98361  
    5959    FloatPoint mappedPoint() const;
    6060    FloatQuad mappedQuad() const;
     61    IntRect boundsOfMappedQuad() const;
    6162    void flatten();
    6263
  • trunk/Source/WebCore/rendering/RenderLayer.cpp

    r98060 r98361  
    31433143        // by our container.
    31443144        LayoutPoint localPoint = roundedLayoutPoint(newTransformState->mappedPoint());
    3145         LayoutRect localHitTestRect;
    3146 #if USE(ACCELERATED_COMPOSITING)
    3147         if (isComposited()) {
    3148             // It doesn't make sense to project hitTestRect into the plane of this layer, so use the same bounds we use for painting.
    3149             localHitTestRect = backing()->compositedBounds();
    3150         } else
    3151 #endif
    3152             localHitTestRect = newTransformState->mappedQuad().enclosingBoundingBox();
     3145        LayoutRect localHitTestRect = newTransformState->boundsOfMappedQuad();
    31533146
    31543147        // Now do a hit test with the root layer shifted to be us.
Note: See TracChangeset for help on using the changeset viewer.