Changeset 145870 in webkit


Ignore:
Timestamp:
Mar 14, 2013 8:37:06 PM (11 years ago)
Author:
commit-queue@webkit.org
Message:

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

Patch by Xidorn Quan <quanxunzhen@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.
Location:
trunk
Files:
2 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r145869 r145870  
     12013-03-14  Xidorn Quan  <quanxunzhen@gmail.com>
     2
     3        Clickable area is incorrect for elements with border-radius
     4        https://bugs.webkit.org/show_bug.cgi?id=95373
     5
     6        Reviewed by Simon Fraser.
     7
     8        This test is based on Takashi Sakamoto's work in
     9        https://bugs.webkit.org/show_bug.cgi?id=95373
     10
     11        * fast/borders/border-radius-position-expected.txt: Added.
     12        * fast/borders/border-radius-position.html: Added.
     13
    1142013-03-14  Andreas Kling  <akling@apple.com>
    215
  • trunk/Source/WebCore/ChangeLog

    r145866 r145870  
     12013-03-14  Xidorn Quan  <quanxunzhen@gmail.com>
     2
     3        Clickable area is incorrect for elements with border-radius
     4        https://bugs.webkit.org/show_bug.cgi?id=95373
     5
     6        Reviewed by Simon Fraser.
     7
     8        As RenderBlock doesn't see rounded rect which comes from border-radius
     9        in nodeAtPoint, the rounded corner seems to have an 'invisible' square
     10        box which catches hits. So we added check on whether hitTestPoint also
     11        intersects the rounded rect when hasBorderRadius is set.
     12
     13        This patch is based on Takashi Sakamoto's work in
     14        https://bugs.webkit.org/show_bug.cgi?id=95373
     15
     16        Test: fast/borders/border-radius-position.html
     17
     18        * platform/graphics/FloatQuad.cpp:
     19        (WebCore):
     20        (WebCore::lineIntersectsCircle):
     21        (WebCore::FloatQuad::intersectsCircle):
     22        (WebCore::FloatQuad::intersectsEllipse):
     23        * platform/graphics/FloatQuad.h:
     24        (FloatQuad):
     25        * platform/graphics/RoundedRect.cpp:
     26        (WebCore::RoundedRect::intersectsQuad):
     27        (WebCore):
     28        * platform/graphics/RoundedRect.h:
     29        (RoundedRect):
     30        * rendering/HitTestLocation.cpp:
     31        (WebCore::HitTestLocation::intersects):
     32        (WebCore):
     33        * rendering/HitTestLocation.h:
     34        (HitTestLocation):
     35        * rendering/RenderBlock.cpp:
     36        (WebCore::RenderBlock::nodeAtPoint):
     37
    1382013-03-14  Chris Fleizach  <cfleizach@apple.com>
    239
  • trunk/Source/WebCore/platform/graphics/FloatQuad.cpp

    r123624 r145870  
    22 * Copyright (C) 2008 Apple Inc. All rights reserved.
    33 * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
     4 * Copyright (C) 2013 Xidorn Quan (quanxunzhen@gmail.com)
    45 *
    56 * Redistribution and use in source and binary forms, with or without
     
    178179}
    179180
     181// Tests whether the line is contained by or intersected with the circle.
     182static inline bool lineIntersectsCircle(const FloatPoint& center, float radius, const FloatPoint& p0, const FloatPoint& p1)
     183{
     184    float x0 = p0.x() - center.x(), y0 = p0.y() - center.y();
     185    float x1 = p1.x() - center.x(), y1 = p1.y() - center.y();
     186    float radius2 = radius * radius;
     187    if ((x0 * x0 + y0 * y0) <= radius2 || (x1 * x1 + y1 * y1) <= radius2)
     188        return true;
     189    if (p0 == p1)
     190        return false;
     191
     192    float a = y0 - y1;
     193    float b = x1 - x0;
     194    float c = x0 * y1 - x1 * y0;
     195    float distance2 = c * c / (a * a + b * b);
     196    // If distance between the center point and the line > the radius,
     197    // the line doesn't cross (or is contained by) the ellipse.
     198    if (distance2 > radius2)
     199        return false;
     200
     201    // The nearest point on the line is between p0 and p1?
     202    float x = - a * c / (a * a + b * b);
     203    float y = - b * c / (a * a + b * b);
     204    return (((x0 <= x && x <= x1) || (x0 >= x && x >= x1))
     205        && ((y0 <= y && y <= y1) || (y1 <= y && y <= y0)));
     206}
     207
     208bool FloatQuad::intersectsCircle(const FloatPoint& center, float radius) const
     209{
     210    return containsPoint(center) // The circle may be totally contained by the quad.
     211        || lineIntersectsCircle(center, radius, m_p1, m_p2)
     212        || lineIntersectsCircle(center, radius, m_p2, m_p3)
     213        || lineIntersectsCircle(center, radius, m_p3, m_p4)
     214        || lineIntersectsCircle(center, radius, m_p4, m_p1);
     215}
     216
     217bool FloatQuad::intersectsEllipse(const FloatPoint& center, const FloatSize& radii) const
     218{
     219    // Transform the ellipse to an origin-centered circle whose radius is the product of major radius and minor radius.
     220    // Here we apply the same transformation to the quad.
     221    FloatQuad transformedQuad(*this);
     222    transformedQuad.move(-center.x(), -center.y());
     223    transformedQuad.scale(radii.height(), radii.width());
     224
     225    FloatPoint originPoint;
     226    return transformedQuad.intersectsCircle(originPoint, radii.height() * radii.width());
     227
     228}
     229
    180230bool FloatQuad::isCounterclockwise() const
    181231{
  • trunk/Source/WebCore/platform/graphics/FloatQuad.h

    r123624 r145870  
    9393    bool intersectsRect(const FloatRect&) const;
    9494
     95    // Test whether any part of the circle/ellipse intersects with this quad.
     96    // Note that these two functions only work for convex quads.
     97    bool intersectsCircle(const FloatPoint& center, float radius) const;
     98    bool intersectsEllipse(const FloatPoint& center, const FloatSize& radii) const;
     99
    95100    // 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.
    96101    FloatPoint center() const
  • trunk/Source/WebCore/platform/graphics/RoundedRect.cpp

    r145474 r145870  
    22 * Copyright (C) 2003, 2006, 2009 Apple Inc. All rights reserved.
    33 * Copyright (C) 2010 Google Inc. All rights reserved.
     4 * Copyright (C) 2013 Xidorn Quan (quanxunzhen@gmail.com)
    45 *
    56 * Redistribution and use in source and binary forms, with or without
     
    182183}
    183184
     185bool RoundedRect::intersectsQuad(const FloatQuad& quad) const
     186{
     187    FloatRect rect(m_rect);
     188    if (!quad.intersectsRect(rect))
     189        return false;
     190
     191    const IntSize& topLeft = m_radii.topLeft();
     192    if (!topLeft.isEmpty()) {
     193        FloatRect rect(m_rect.x(), m_rect.y(), topLeft.width(), topLeft.height());
     194        if (quad.intersectsRect(rect)) {
     195            FloatPoint center(m_rect.x() + topLeft.width(), m_rect.y() + topLeft.height());
     196            FloatSize size(topLeft.width(), topLeft.height());
     197            if (!quad.intersectsEllipse(center, size))
     198                return false;
     199        }
     200    }
     201
     202    const IntSize& topRight = m_radii.topRight();
     203    if (!topRight.isEmpty()) {
     204        FloatRect rect(m_rect.maxX() - topRight.width(), m_rect.y(), topRight.width(), topRight.height());
     205        if (quad.intersectsRect(rect)) {
     206            FloatPoint center(m_rect.maxX() - topRight.width(), m_rect.y() + topRight.height());
     207            FloatSize size(topRight.width(), topRight.height());
     208            if (!quad.intersectsEllipse(center, size))
     209                return false;
     210        }
     211    }
     212
     213    const IntSize& bottomLeft = m_radii.bottomLeft();
     214    if (!bottomLeft.isEmpty()) {
     215        FloatRect rect(m_rect.x(), m_rect.maxY() - bottomLeft.height(), bottomLeft.width(), bottomLeft.height());
     216        if (quad.intersectsRect(rect)) {
     217            FloatPoint center(m_rect.x() + bottomLeft.width(), m_rect.maxY() - bottomLeft.height());
     218            FloatSize size(bottomLeft.width(), bottomLeft.height());
     219            if (!quad.intersectsEllipse(center, size))
     220                return false;
     221        }
     222    }
     223
     224    const IntSize& bottomRight = m_radii.bottomRight();
     225    if (!bottomRight.isEmpty()) {
     226        FloatRect rect(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height(), bottomRight.width(), bottomRight.height());
     227        if (quad.intersectsRect(rect)) {
     228            FloatPoint center(m_rect.maxX() - bottomRight.width(), m_rect.maxY() - bottomRight.height());
     229            FloatSize size(bottomRight.width(), bottomRight.height());
     230            if (!quad.intersectsEllipse(center, size))
     231                return false;
     232        }
     233    }
     234
     235    return true;
     236}
     237
    184238} // namespace WebCore
  • trunk/Source/WebCore/platform/graphics/RoundedRect.h

    r125304 r145870  
    2828#define RoundedRect_h
    2929
     30#include "FloatQuad.h"
    3031#include "IntRect.h"
    3132
     
    9798    void adjustRadii();
    9899
     100    // Tests whether the quad intersects any part of this rounded rectangle.
     101    // This only works for convex quads.
     102    bool intersectsQuad(const FloatQuad&) const;
     103
    99104private:
    100105    IntRect m_rect;
  • trunk/Source/WebCore/rendering/HitTestLocation.cpp

    r144743 r145870  
    181181}
    182182
     183bool HitTestLocation::intersects(const RoundedRect& rect) const
     184{
     185    return rect.intersectsQuad(m_transformedRect);
     186}
     187
    183188IntRect HitTestLocation::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
    184189{
  • trunk/Source/WebCore/rendering/HitTestLocation.h

    r144743 r145870  
    2727#include "HitTestRequest.h"
    2828#include "LayoutRect.h"
     29#include "RoundedRect.h"
    2930#include "TextDirection.h"
    3031#include <wtf/Forward.h>
     
    7980    bool intersects(const LayoutRect&) const;
    8081    bool intersects(const FloatRect&) const;
     82    bool intersects(const RoundedRect&) const;
    8183
    8284    const FloatPoint& transformedPoint() const { return m_transformedPoint; }
  • trunk/Source/WebCore/rendering/RenderBlock.cpp

    r145728 r145870  
    48954895        flipForWritingMode(overflowBox);
    48964896        overflowBox.moveBy(adjustedLocation);
     4897        if (style()->hasBorderRadius()) {
     4898            LayoutRect borderRect = borderBoxRect();
     4899            borderRect.moveBy(adjustedLocation);
     4900            RoundedRect border = style()->getRoundedBorderFor(borderRect, view());
     4901            if (!locationInContainer.intersects(border))
     4902                return false;
     4903        }
    48974904        if (!locationInContainer.intersects(overflowBox))
    48984905            return false;
Note: See TracChangeset for help on using the changeset viewer.