Title: [145870] trunk
Revision
145870
Author
commit-qu...@webkit.org
Date
2013-03-14 20:37:06 -0700 (Thu, 14 Mar 2013)

Log Message

Clickable area is incorrect for elements with border-radius
https://bugs.webkit.org/show_bug.cgi?id=95373

Patch by Xidorn Quan <quanxunz...@gmail.com> on 2013-03-14
Reviewed by Simon Fraser.

Source/WebCore:

As RenderBlock doesn't see rounded rect which comes from border-radius
in nodeAtPoint, the rounded corner seems to have an 'invisible' square
box which catches hits. So we added check on whether hitTestPoint also
intersects the rounded rect when hasBorderRadius is set.

This patch is based on Takashi Sakamoto's work in
https://bugs.webkit.org/show_bug.cgi?id=95373

Test: fast/borders/border-radius-position.html

* platform/graphics/FloatQuad.cpp:
(WebCore):
(WebCore::lineIntersectsCircle):
(WebCore::FloatQuad::intersectsCircle):
(WebCore::FloatQuad::intersectsEllipse):
* platform/graphics/FloatQuad.h:
(FloatQuad):
* platform/graphics/RoundedRect.cpp:
(WebCore::RoundedRect::intersectsQuad):
(WebCore):
* platform/graphics/RoundedRect.h:
(RoundedRect):
* rendering/HitTestLocation.cpp:
(WebCore::HitTestLocation::intersects):
(WebCore):
* rendering/HitTestLocation.h:
(HitTestLocation):
* rendering/RenderBlock.cpp:
(WebCore::RenderBlock::nodeAtPoint):

LayoutTests:

This test is based on Takashi Sakamoto's work in
https://bugs.webkit.org/show_bug.cgi?id=95373

* fast/borders/border-radius-position-expected.txt: Added.
* fast/borders/border-radius-position.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (145869 => 145870)


--- trunk/LayoutTests/ChangeLog	2013-03-15 03:30:26 UTC (rev 145869)
+++ trunk/LayoutTests/ChangeLog	2013-03-15 03:37:06 UTC (rev 145870)
@@ -1,3 +1,16 @@
+2013-03-14  Xidorn Quan  <quanxunz...@gmail.com>
+
+        Clickable area is incorrect for elements with border-radius
+        https://bugs.webkit.org/show_bug.cgi?id=95373
+
+        Reviewed by Simon Fraser.
+
+        This test is based on Takashi Sakamoto's work in
+        https://bugs.webkit.org/show_bug.cgi?id=95373
+
+        * fast/borders/border-radius-position-expected.txt: Added.
+        * fast/borders/border-radius-position.html: Added.
+
 2013-03-14  Andreas Kling  <akl...@apple.com>
 
         REGRESSION(r145169): [Mac][WK2] http/tests/security/cross-frame-access-put.html fails.

Added: trunk/LayoutTests/fast/borders/border-radius-position-expected.txt (0 => 145870)


--- trunk/LayoutTests/fast/borders/border-radius-position-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/borders/border-radius-position-expected.txt	2013-03-15 03:37:06 UTC (rev 145870)
@@ -0,0 +1,17 @@
+Test for bug 95373: https://bugs.webkit.org/show_bug.cgi?id=95373 (border-radius) when border-radius:50% there is still 'invisible' square box
+
+Click outerBox
+Click outerBox
+Click innerBox
+Click outerBox
+Click innerBox
+Click outerBox
+Click outerBox
+Click outerBox
+Click innerBox
+Click innerBox
+Click outerBox
+Click outerBox
+Click innerBox
+Click innerBox
+

Added: trunk/LayoutTests/fast/borders/border-radius-position.html (0 => 145870)


--- trunk/LayoutTests/fast/borders/border-radius-position.html	                        (rev 0)
+++ trunk/LayoutTests/fast/borders/border-radius-position.html	2013-03-15 03:37:06 UTC (rev 145870)
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+div#innerBox {
+    width: 200px;
+    height: 200px;
+    padding: 0px;
+    border-top-left-radius: 100px 80px;
+    border-top-right-radius: 50px 25px;
+    border-bottom-left-radius: 50px 70px;
+    border-bottom-right-radius: 70px 30px;
+    background-color: green;
+}
+
+div#outerBox {
+    width: 200px;
+    height: 200px;
+    margin: 0px;
+    padding: 100px;
+    background-color: lightgreen;
+}
+</style>
+<script>
+if (window.testRunner)
+    testRunner.dumpAsText();
+
+function log(message) {
+    var console = document.getElementById('console');
+    console.innerHTML += message + "\n";
+}
+
+function runTest() {
+    var outerBox = document.getElementById('outerBox');
+    outerBox.addEventListener('click', function(event) {
+        log("Click outerBox");
+    }, false);
+    var innerBox = document.getElementById('innerBox');
+    innerBox.addEventListener('click', function(event) {
+        log("Click innerBox");
+        event.stopPropagation();
+    }, false);
+    if (window.testRunner) {
+        var x = innerBox.offsetLeft;
+        var y = innerBox.offsetTop;
+        // top-left
+        eventSender.mouseMoveTo(x, y);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 25, y + 20);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 30, y + 24);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        // top-right
+        eventSender.mouseMoveTo(x + 200, y);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 200 - 14, y + 8);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 200 - 8, y + 4);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 200 - 4, y + 2);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        // bottom-left
+        eventSender.mouseMoveTo(x, y + 180);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 14, y + 166);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 20, y + 160);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        // bottom-right
+        eventSender.mouseMoveTo(x + 199, y + 200);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 199, y + 180);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 199, y + 175);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+        eventSender.mouseMoveTo(x + 199, y + 170);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+    }
+}
+</script>
+</head>
+<body _onload_="runTest()">
+<div id="outerBox">
+  <div id="innerBox">
+  </div>
+</div>
+<p>Test for <i>bug 95373</i>: <a href="" (border-radius) when border-radius:50% there is still 'invisible' square box
+</p>
+<pre id="console">
+</pre>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (145869 => 145870)


--- trunk/Source/WebCore/ChangeLog	2013-03-15 03:30:26 UTC (rev 145869)
+++ trunk/Source/WebCore/ChangeLog	2013-03-15 03:37:06 UTC (rev 145870)
@@ -1,3 +1,40 @@
+2013-03-14  Xidorn Quan  <quanxunz...@gmail.com>
+
+        Clickable area is incorrect for elements with border-radius
+        https://bugs.webkit.org/show_bug.cgi?id=95373
+
+        Reviewed by Simon Fraser.
+
+        As RenderBlock doesn't see rounded rect which comes from border-radius
+        in nodeAtPoint, the rounded corner seems to have an 'invisible' square
+        box which catches hits. So we added check on whether hitTestPoint also
+        intersects the rounded rect when hasBorderRadius is set.
+
+        This patch is based on Takashi Sakamoto's work in
+        https://bugs.webkit.org/show_bug.cgi?id=95373
+
+        Test: fast/borders/border-radius-position.html
+
+        * platform/graphics/FloatQuad.cpp:
+        (WebCore):
+        (WebCore::lineIntersectsCircle):
+        (WebCore::FloatQuad::intersectsCircle):
+        (WebCore::FloatQuad::intersectsEllipse):
+        * platform/graphics/FloatQuad.h:
+        (FloatQuad):
+        * platform/graphics/RoundedRect.cpp:
+        (WebCore::RoundedRect::intersectsQuad):
+        (WebCore):
+        * platform/graphics/RoundedRect.h:
+        (RoundedRect):
+        * rendering/HitTestLocation.cpp:
+        (WebCore::HitTestLocation::intersects):
+        (WebCore):
+        * rendering/HitTestLocation.h:
+        (HitTestLocation):
+        * rendering/RenderBlock.cpp:
+        (WebCore::RenderBlock::nodeAtPoint):
+
 2013-03-14  Chris Fleizach  <cfleiz...@apple.com>
 
         AX: Crash when removing aria-menu item from DOM

Modified: trunk/Source/WebCore/platform/graphics/FloatQuad.cpp (145869 => 145870)


--- trunk/Source/WebCore/platform/graphics/FloatQuad.cpp	2013-03-15 03:30:26 UTC (rev 145869)
+++ trunk/Source/WebCore/platform/graphics/FloatQuad.cpp	2013-03-15 03:37:06 UTC (rev 145870)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2008 Apple Inc. All rights reserved.
  * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
+ * Copyright (C) 2013 Xidorn Quan (quanxunz...@gmail.com)
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -177,6 +178,55 @@
     return true;
 }
 
+// Tests whether the line is contained by or intersected with the circle.
+static inline bool lineIntersectsCircle(const FloatPoint& center, float radius, const FloatPoint& p0, const FloatPoint& p1)
+{
+    float x0 = p0.x() - center.x(), y0 = p0.y() - center.y();
+    float x1 = p1.x() - center.x(), y1 = p1.y() - center.y();
+    float radius2 = radius * radius;
+    if ((x0 * x0 + y0 * y0) <= radius2 || (x1 * x1 + y1 * y1) <= radius2)
+        return true;
+    if (p0 == p1)
+        return false;
+
+    float a = y0 - y1;
+    float b = x1 - x0;
+    float c = x0 * y1 - x1 * y0;
+    float distance2 = c * c / (a * a + b * b);
+    // If distance between the center point and the line > the radius,
+    // the line doesn't cross (or is contained by) the ellipse.
+    if (distance2 > radius2)
+        return false;
+
+    // The nearest point on the line is between p0 and p1?
+    float x = - a * c / (a * a + b * b);
+    float y = - b * c / (a * a + b * b);
+    return (((x0 <= x && x <= x1) || (x0 >= x && x >= x1))
+        && ((y0 <= y && y <= y1) || (y1 <= y && y <= y0)));
+}
+
+bool FloatQuad::intersectsCircle(const FloatPoint& center, float radius) const
+{
+    return containsPoint(center) // The circle may be totally contained by the quad.
+        || lineIntersectsCircle(center, radius, m_p1, m_p2)
+        || lineIntersectsCircle(center, radius, m_p2, m_p3)
+        || lineIntersectsCircle(center, radius, m_p3, m_p4)
+        || lineIntersectsCircle(center, radius, m_p4, m_p1);
+}
+
+bool FloatQuad::intersectsEllipse(const FloatPoint& center, const FloatSize& radii) const
+{
+    // Transform the ellipse to an origin-centered circle whose radius is the product of major radius and minor radius.
+    // Here we apply the same transformation to the quad.
+    FloatQuad transformedQuad(*this);
+    transformedQuad.move(-center.x(), -center.y());
+    transformedQuad.scale(radii.height(), radii.width());
+
+    FloatPoint originPoint;
+    return transformedQuad.intersectsCircle(originPoint, radii.height() * radii.width());
+
+}
+
 bool FloatQuad::isCounterclockwise() const
 {
     // Return if the two first vectors are turning clockwise. If the quad is convex then all following vectors will turn the same way.

Modified: trunk/Source/WebCore/platform/graphics/FloatQuad.h (145869 => 145870)


--- trunk/Source/WebCore/platform/graphics/FloatQuad.h	2013-03-15 03:30:26 UTC (rev 145869)
+++ trunk/Source/WebCore/platform/graphics/FloatQuad.h	2013-03-15 03:37:06 UTC (rev 145870)
@@ -92,6 +92,11 @@
     // This only works for convex quads.
     bool intersectsRect(const FloatRect&) const;
 
+    // Test whether any part of the circle/ellipse intersects with this quad.
+    // Note that these two functions only work for convex quads.
+    bool intersectsCircle(const FloatPoint& center, float radius) const;
+    bool intersectsEllipse(const FloatPoint& center, const FloatSize& radii) const;
+
     // The center of the quad. If the quad is the result of a affine-transformed rectangle this is the same as the original center transformed.
     FloatPoint center() const
     {

Modified: trunk/Source/WebCore/platform/graphics/RoundedRect.cpp (145869 => 145870)


--- trunk/Source/WebCore/platform/graphics/RoundedRect.cpp	2013-03-15 03:30:26 UTC (rev 145869)
+++ trunk/Source/WebCore/platform/graphics/RoundedRect.cpp	2013-03-15 03:37:06 UTC (rev 145870)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2013 Xidorn Quan (quanxunz...@gmail.com)
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -181,4 +182,57 @@
     m_radii.scale(widthRatio < heightRatio ? widthRatio : heightRatio);
 }
 
+bool RoundedRect::intersectsQuad(const FloatQuad& quad) const
+{
+    FloatRect rect(m_rect);
+    if (!quad.intersectsRect(rect))
+        return false;
+
+    const IntSize& topLeft = m_radii.topLeft();
+    if (!topLeft.isEmpty()) {
+        FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height());
+        if (quad.intersectsRect(rect)) {
+            FloatPoint center(m_rect.x() + topLeft.width(), m_rect.y() + topLeft.height());
+            FloatSize size(topLeft.width(), topLeft.height());
+            if (!quad.intersectsEllipse(center, size))
+                return false;
+        }
+    }
+
+    const IntSize& topRight = m_radii.topRight();
+    if (!topRight.isEmpty()) {
+        FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), topRight.width(), topRight.height());
+        if (quad.intersectsRect(rect)) {
+            FloatPoint center(m_rect.maxX() - topRight.width(), m_rect.y() + topRight.height());
+            FloatSize size(topRight.width(), topRight.height());
+            if (!quad.intersectsEllipse(center, size))
+                return false;
+        }
+    }
+
+    const IntSize& bottomLeft = m_radii.bottomLeft();
+    if (!bottomLeft.isEmpty()) {
+        FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), bottomLeft.width(), bottomLeft.height());
+        if (quad.intersectsRect(rect)) {
+            FloatPoint center(m_rect.x() + bottomLeft.width(), m_rect.maxY() - bottomLeft.height());
+            FloatSize size(bottomLeft.width(), bottomLeft.height());
+            if (!quad.intersectsEllipse(center, size))
+                return false;
+        }
+    }
+
+    const IntSize& bottomRight = m_radii.bottomRight();
+    if (!bottomRight.isEmpty()) {
+        FloatRect rect(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height(), bottomRight.width(), bottomRight.height());
+        if (quad.intersectsRect(rect)) {
+            FloatPoint center(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height());
+            FloatSize size(bottomRight.width(), bottomRight.height());
+            if (!quad.intersectsEllipse(center, size))
+                return false;
+        }
+    }
+
+    return true;
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/RoundedRect.h (145869 => 145870)


--- trunk/Source/WebCore/platform/graphics/RoundedRect.h	2013-03-15 03:30:26 UTC (rev 145869)
+++ trunk/Source/WebCore/platform/graphics/RoundedRect.h	2013-03-15 03:37:06 UTC (rev 145870)
@@ -27,6 +27,7 @@
 #ifndef RoundedRect_h
 #define RoundedRect_h
 
+#include "FloatQuad.h"
 #include "IntRect.h"
 
 namespace WebCore {
@@ -96,6 +97,10 @@
     bool isRenderable() const;
     void adjustRadii();
 
+    // Tests whether the quad intersects any part of this rounded rectangle.
+    // This only works for convex quads.
+    bool intersectsQuad(const FloatQuad&) const;
+
 private:
     IntRect m_rect;
     Radii m_radii;

Modified: trunk/Source/WebCore/rendering/HitTestLocation.cpp (145869 => 145870)


--- trunk/Source/WebCore/rendering/HitTestLocation.cpp	2013-03-15 03:30:26 UTC (rev 145869)
+++ trunk/Source/WebCore/rendering/HitTestLocation.cpp	2013-03-15 03:37:06 UTC (rev 145870)
@@ -180,6 +180,11 @@
     return intersectsRect(rect);
 }
 
+bool HitTestLocation::intersects(const RoundedRect& rect) const
+{
+    return rect.intersectsQuad(m_transformedRect);
+}
+
 IntRect HitTestLocation::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
 {
     IntPoint actualPoint(flooredIntPoint(point));

Modified: trunk/Source/WebCore/rendering/HitTestLocation.h (145869 => 145870)


--- trunk/Source/WebCore/rendering/HitTestLocation.h	2013-03-15 03:30:26 UTC (rev 145869)
+++ trunk/Source/WebCore/rendering/HitTestLocation.h	2013-03-15 03:37:06 UTC (rev 145870)
@@ -26,6 +26,7 @@
 #include "FloatRect.h"
 #include "HitTestRequest.h"
 #include "LayoutRect.h"
+#include "RoundedRect.h"
 #include "TextDirection.h"
 #include <wtf/Forward.h>
 #include <wtf/ListHashSet.h>
@@ -78,6 +79,7 @@
 
     bool intersects(const LayoutRect&) const;
     bool intersects(const FloatRect&) const;
+    bool intersects(const RoundedRect&) const;
 
     const FloatPoint& transformedPoint() const { return m_transformedPoint; }
     const FloatQuad& transformedRect() const { return m_transformedRect; }

Modified: trunk/Source/WebCore/rendering/RenderBlock.cpp (145869 => 145870)


--- trunk/Source/WebCore/rendering/RenderBlock.cpp	2013-03-15 03:30:26 UTC (rev 145869)
+++ trunk/Source/WebCore/rendering/RenderBlock.cpp	2013-03-15 03:37:06 UTC (rev 145870)
@@ -4894,6 +4894,13 @@
         LayoutRect overflowBox = visualOverflowRect();
         flipForWritingMode(overflowBox);
         overflowBox.moveBy(adjustedLocation);
+        if (style()->hasBorderRadius()) {
+            LayoutRect borderRect = borderBoxRect();
+            borderRect.moveBy(adjustedLocation);
+            RoundedRect border = style()->getRoundedBorderFor(borderRect, view());
+            if (!locationInContainer.intersects(border))
+                return false;
+        }
         if (!locationInContainer.intersects(overflowBox))
             return false;
     }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to