Changeset 60761 in webkit


Ignore:
Timestamp:
Jun 6, 2010, 5:26:45 AM (14 years ago)
Author:
krit@webkit.org
Message:

2010-06-06 Dirk Schulze <krit@webkit.org>

Reviewed by Nikolas Zimmermann.

hit testing does not respect clip paths
https://bugs.webkit.org/show_bug.cgi?id=15162

Test: svg/dynamic-updates/SVGClipPath-influences-hitTesting.html


Added a check, if a float point is not only in the shape/object boundaries
but also is not in the clipped away area of a clipPath.

  • rendering/HitTestRequest.h: (WebCore::HitTestRequest::): (WebCore::HitTestRequest::svgClipContent):
  • rendering/RenderPath.cpp: (WebCore::RenderPath::fillContains): (WebCore::RenderPath::nodeAtFloatPoint):
  • rendering/RenderPath.h:
  • rendering/RenderSVGContainer.cpp: (WebCore::RenderSVGContainer::nodeAtFloatPoint):
  • rendering/RenderSVGImage.cpp: (WebCore::RenderSVGImage::nodeAtFloatPoint):
  • rendering/RenderSVGResourceClipper.cpp: (WebCore::RenderSVGResourceClipper::hitTestClipContent):
  • rendering/RenderSVGResourceClipper.h:
  • rendering/RenderSVGText.cpp: (WebCore::RenderSVGText::nodeAtFloatPoint):
  • rendering/SVGRenderSupport.cpp: (WebCore::pointInClippingArea):
  • rendering/SVGRenderSupport.h:

2010-06-06 Dirk Schulze <krit@webkit.org>

Reviewed by Nikolas Zimmermann.

hit testing does not respect clip paths
https://bugs.webkit.org/show_bug.cgi?id=15162


Two tests were incorrect, because of the assumption, that clipPath doesn't
influence hit testing. They needed an update.
The new test checks, that clipped areas don't throw a hit.

  • svg/dynamic-updates/SVGClipPath-influences-hitTesting-expected.txt: Added.
  • svg/dynamic-updates/SVGClipPath-influences-hitTesting.html: Added.
  • svg/dynamic-updates/SVGClipPathElement-dom-clipPathUnits-attr-expected.txt:
  • svg/dynamic-updates/SVGClipPathElement-svgdom-clipPathUnits-prop-expected.txt:
  • svg/dynamic-updates/script-tests/SVGClipPath-influences-hitTesting.js: Added. (executeBackgroundTest): (executeTest):
  • svg/dynamic-updates/script-tests/SVGClipPathElement-dom-clipPathUnits-attr.js: (shouldBeEqualToString.executeTest):
  • svg/dynamic-updates/script-tests/SVGClipPathElement-svgdom-clipPathUnits-prop.js: (executeTest):
Location:
trunk
Files:
3 added
18 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r60759 r60761  
     12010-06-06  Dirk Schulze  <krit@webkit.org>
     2
     3        Reviewed by Nikolas Zimmermann.
     4
     5        hit testing does not respect clip paths
     6        https://bugs.webkit.org/show_bug.cgi?id=15162
     7       
     8        Two tests were incorrect, because of the assumption, that clipPath doesn't
     9        influence hit testing. They needed an update.
     10        The new test checks, that clipped areas don't throw a hit.
     11
     12        * svg/dynamic-updates/SVGClipPath-influences-hitTesting-expected.txt: Added.
     13        * svg/dynamic-updates/SVGClipPath-influences-hitTesting.html: Added.
     14        * svg/dynamic-updates/SVGClipPathElement-dom-clipPathUnits-attr-expected.txt:
     15        * svg/dynamic-updates/SVGClipPathElement-svgdom-clipPathUnits-prop-expected.txt:
     16        * svg/dynamic-updates/script-tests/SVGClipPath-influences-hitTesting.js: Added.
     17        (executeBackgroundTest):
     18        (executeTest):
     19        * svg/dynamic-updates/script-tests/SVGClipPathElement-dom-clipPathUnits-attr.js:
     20        (shouldBeEqualToString.executeTest):
     21        * svg/dynamic-updates/script-tests/SVGClipPathElement-svgdom-clipPathUnits-prop.js:
     22        (executeTest):
     23
    1242010-06-06  Kent Tamura  <tkent@chromium.org>
    225
  • trunk/LayoutTests/svg/dynamic-updates/SVGClipPathElement-dom-clipPathUnits-attr-expected.txt

    r30635 r60761  
    66
    77
     8PASS clipPathElement.getAttribute('clipPathUnits') is "userSpaceOnUse"
    89PASS clipPathElement.getAttribute('clipPathUnits') is "objectBoundingBox"
    9 PASS clipPathElement.getAttribute('clipPathUnits') is "userSpaceOnUse"
    1010PASS successfullyParsed is true
    1111
  • trunk/LayoutTests/svg/dynamic-updates/SVGClipPathElement-svgdom-clipPathUnits-prop-expected.txt

    r30635 r60761  
    66
    77
     8PASS clipPathElement.clipPathUnits.baseVal is SVGUnitTypes.SVG_UNIT_TYPE_USERSPACEONUSE
    89PASS clipPathElement.clipPathUnits.baseVal is SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
    9 PASS clipPathElement.clipPathUnits.baseVal is SVGUnitTypes.SVG_UNIT_TYPE_USERSPACEONUSE
    1010PASS successfullyParsed is true
    1111
  • trunk/LayoutTests/svg/dynamic-updates/script-tests/SVGClipPathElement-dom-clipPathUnits-attr.js

    r48554 r60761  
    1010var clipPathElement = createSVGElement("clipPath");
    1111clipPathElement.setAttribute("id", "clipper");
    12 clipPathElement.setAttribute("clipPathUnits", "objectBoundingBox");
     12clipPathElement.setAttribute("clipPathUnits", "userSpaceOnUse");
    1313
    14 var circleElement = createSVGElement("circle");
    15 circleElement.setAttribute("cx", "150");
    16 circleElement.setAttribute("cy", "150");
    17 circleElement.setAttribute("r", "150");
    18 clipPathElement.appendChild(circleElement);
     14var rectElementA = createSVGElement("rect");
     15rectElementA.setAttribute("width", "10");
     16rectElementA.setAttribute("height", "10");
     17clipPathElement.appendChild(rectElementA);
    1918
    2019defsElement.appendChild(clipPathElement);
    2120
    22 var rectElement = createSVGElement("rect");
    23 rectElement.setAttribute("width", "300");
    24 rectElement.setAttribute("height", "300");
    25 rectElement.setAttribute("fill", "green");
    26 rectElement.setAttribute("clip-path", "url(#clipper)");
    27 rootSVGElement.appendChild(rectElement);
     21var rectElementB = createSVGElement("rect");
     22rectElementB.setAttribute("width", "100");
     23rectElementB.setAttribute("height", "100");
     24rectElementB.setAttribute("fill", "green");
     25rectElementB.setAttribute("clip-path", "url(#clipper)");
     26rootSVGElement.appendChild(rectElementB);
    2827
    29 shouldBeEqualToString("clipPathElement.getAttribute('clipPathUnits')", "objectBoundingBox")
     28shouldBeEqualToString("clipPathElement.getAttribute('clipPathUnits')", "userSpaceOnUse")
    3029
    3130function executeTest() {
    32     clipPathElement.setAttribute("clipPathUnits", "userSpaceOnUse");
    33     shouldBeEqualToString("clipPathElement.getAttribute('clipPathUnits')", "userSpaceOnUse");
     31    clipPathElement.setAttribute("clipPathUnits", "objectBoundingBox");
     32    shouldBeEqualToString("clipPathElement.getAttribute('clipPathUnits')", "objectBoundingBox");
    3433
    3534    completeTest();
    3635}
    3736
    38 startTest(rectElement, 150, 150);
     37startTest(rectElementB, 5, 5);
    3938
    4039var successfullyParsed = true;
  • trunk/LayoutTests/svg/dynamic-updates/script-tests/SVGClipPathElement-svgdom-clipPathUnits-prop.js

    r48554 r60761  
    1010var clipPathElement = createSVGElement("clipPath");
    1111clipPathElement.setAttribute("id", "clipper");
    12 clipPathElement.setAttribute("clipPathUnits", "objectBoundingBox");
     12clipPathElement.setAttribute("clipPathUnits", "userSpaceOnUse");
    1313
    14 var circleElement = createSVGElement("circle");
    15 circleElement.setAttribute("cx", "150");
    16 circleElement.setAttribute("cy", "150");
    17 circleElement.setAttribute("r", "150");
    18 clipPathElement.appendChild(circleElement);
     14var rectElementA = createSVGElement("rect");
     15rectElementA.setAttribute("width", "10");
     16rectElementA.setAttribute("height", "10");
     17clipPathElement.appendChild(rectElementA);
    1918
    20 defsElement.appendChild(clipPathElement);;
     19defsElement.appendChild(clipPathElement);
    2120
    22 var rectElement = createSVGElement("rect");
    23 rectElement.setAttribute("width", "300");
    24 rectElement.setAttribute("height", "300");
    25 rectElement.setAttribute("fill", "green");
    26 rectElement.setAttribute("clip-path", "url(#clipper)");
    27 rootSVGElement.appendChild(rectElement);
     21var rectElementB = createSVGElement("rect");
     22rectElementB.setAttribute("width", "100");
     23rectElementB.setAttribute("height", "100");
     24rectElementB.setAttribute("fill", "green");
     25rectElementB.setAttribute("clip-path", "url(#clipper)");
     26rootSVGElement.appendChild(rectElementB);
    2827
    29 shouldBe("clipPathElement.clipPathUnits.baseVal", "SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX");
     28shouldBe("clipPathElement.clipPathUnits.baseVal", "SVGUnitTypes.SVG_UNIT_TYPE_USERSPACEONUSE");
    3029
    3130function executeTest() {
    32     clipPathElement.clipPathUnits.baseVal = SVGUnitTypes.SVG_UNIT_TYPE_USERSPACEONUSE;
    33     shouldBe("clipPathElement.clipPathUnits.baseVal", "SVGUnitTypes.SVG_UNIT_TYPE_USERSPACEONUSE");
     31    clipPathElement.clipPathUnits.baseVal = SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX;
     32    shouldBe("clipPathElement.clipPathUnits.baseVal", "SVGUnitTypes.SVG_UNIT_TYPE_OBJECTBOUNDINGBOX");
    3433
    3534    completeTest();
    3635}
    3736
    38 startTest(rectElement, 150, 150);
     37startTest(rectElementB, 5, 5);
    3938
    4039var successfullyParsed = true;
  • trunk/WebCore/ChangeLog

    r60760 r60761  
     12010-06-06  Dirk Schulze  <krit@webkit.org>
     2
     3        Reviewed by Nikolas Zimmermann.
     4
     5        hit testing does not respect clip paths
     6        https://bugs.webkit.org/show_bug.cgi?id=15162
     7
     8        Test: svg/dynamic-updates/SVGClipPath-influences-hitTesting.html
     9       
     10        Added a check, if a float point is not only in the shape/object boundaries
     11        but also is not in the clipped away area of a clipPath.
     12
     13        * rendering/HitTestRequest.h:
     14        (WebCore::HitTestRequest::):
     15        (WebCore::HitTestRequest::svgClipContent):
     16        * rendering/RenderPath.cpp:
     17        (WebCore::RenderPath::fillContains):
     18        (WebCore::RenderPath::nodeAtFloatPoint):
     19        * rendering/RenderPath.h:
     20        * rendering/RenderSVGContainer.cpp:
     21        (WebCore::RenderSVGContainer::nodeAtFloatPoint):
     22        * rendering/RenderSVGImage.cpp:
     23        (WebCore::RenderSVGImage::nodeAtFloatPoint):
     24        * rendering/RenderSVGResourceClipper.cpp:
     25        (WebCore::RenderSVGResourceClipper::hitTestClipContent):
     26        * rendering/RenderSVGResourceClipper.h:
     27        * rendering/RenderSVGText.cpp:
     28        (WebCore::RenderSVGText::nodeAtFloatPoint):
     29        * rendering/SVGRenderSupport.cpp:
     30        (WebCore::pointInClippingArea):
     31        * rendering/SVGRenderSupport.h:
     32
    1332010-06-06  Dirk Schulze  <krit@webkit.org>
    234
  • trunk/WebCore/rendering/HitTestRequest.h

    r50583 r60761  
    2828public:
    2929    enum RequestType {
    30         ReadOnly = 0x1,
    31         Active = 0x2,
    32         MouseMove = 0x4,
    33         MouseUp = 0x8,
    34         IgnoreClipping = 0x10
     30        ReadOnly = 1 << 1,
     31        Active = 1 << 2,
     32        MouseMove = 1 << 3,
     33        MouseUp = 1 << 4,
     34        IgnoreClipping = 1 << 5,
     35        SVGClipContent = 1 << 6
    3536    };
    3637
     
    4546    bool mouseUp() const { return m_requestType & MouseUp; }
    4647    bool ignoreClipping() const { return m_requestType & IgnoreClipping; }
     48    bool svgClipContent() const { return m_requestType & SVGClipContent; }
    4749
    4850private:
  • trunk/WebCore/rendering/PointerEventsHitRules.cpp

    r50583 r60761  
    2323namespace WebCore {
    2424
    25 PointerEventsHitRules::PointerEventsHitRules(EHitTesting hitTesting, EPointerEvents pointerEvents)
     25PointerEventsHitRules::PointerEventsHitRules(EHitTesting hitTesting, const HitTestRequest& request, EPointerEvents pointerEvents)
    2626    : requireVisible(false)
    2727    , requireFill(false)
     
    3030    , canHitFill(false)
    3131{
     32    if (request.svgClipContent())
     33        pointerEvents = PE_FILL;
     34
    3235    if (hitTesting == SVG_PATH_HITTESTING) {
    3336        switch (pointerEvents)
  • trunk/WebCore/rendering/PointerEventsHitRules.h

    r50583 r60761  
    2121#define PointerEventsHitRules_h
    2222
     23#include "HitTestRequest.h"
    2324#include "RenderStyleConstants.h"
    2425
     
    3334    };
    3435
    35     PointerEventsHitRules(EHitTesting, EPointerEvents);
     36    PointerEventsHitRules(EHitTesting, const HitTestRequest&, EPointerEvents);
    3637
    3738    bool requireVisible;
  • trunk/WebCore/rendering/RenderPath.cpp

    r60541 r60761  
    3131#include "FloatQuad.h"
    3232#include "GraphicsContext.h"
     33#include "HitTestRequest.h"
    3334#include "PointerEventsHitRules.h"
    3435#include "RenderSVGContainer.h"
     
    7273}
    7374
    74 bool RenderPath::fillContains(const FloatPoint& point, bool requiresFill) const
     75bool RenderPath::fillContains(const FloatPoint& point, bool requiresFill, WindRule fillRule) const
    7576{
    7677    if (!m_fillBoundingBox.contains(point))
     
    8081        return false;
    8182
    82     return m_path.contains(point, style()->svgStyle()->fillRule());
     83    return m_path.contains(point, fillRule);
    8384}
    8485
     
    196197}
    197198
    198 bool RenderPath::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
     199bool RenderPath::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    199200{
    200201    // We only draw in the forground phase, so we only hit-test then.
     
    204205    FloatPoint localPoint = m_localTransform.inverse().mapPoint(pointInParent);
    205206
    206     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, style()->pointerEvents());
    207 
     207    if (!pointInClippingArea(this, localPoint))
     208        return false;
     209
     210    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, request, style()->pointerEvents());
    208211    bool isVisible = (style()->visibility() == VISIBLE);
    209212    if (isVisible || !hitRules.requireVisible) {
    210         if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
    211             || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill))) {
     213        const SVGRenderStyle* svgStyle = style()->svgStyle();
     214        WindRule fillRule = svgStyle->fillRule();
     215        if (request.svgClipContent())
     216            fillRule = svgStyle->clipRule();
     217        if ((hitRules.canHitStroke && (svgStyle->hasStroke() || !hitRules.requireStroke) && strokeContains(localPoint, hitRules.requireStroke))
     218            || (hitRules.canHitFill && (svgStyle->hasFill() || !hitRules.requireFill) && fillContains(localPoint, hitRules.requireFill, fillRule))) {
    212219            updateHitTestResult(result, roundedIntPoint(localPoint));
    213220            return true;
    214221        }
    215222    }
    216 
    217223    return false;
    218224}
  • trunk/WebCore/rendering/RenderPath.h

    r58570 r60761  
    4848private:
    4949    // Hit-detection seperated for the fill and the stroke
    50     bool fillContains(const FloatPoint&, bool requiresFill = true) const;
     50    bool fillContains(const FloatPoint&, bool requiresFill = true, WindRule fillRule = RULE_NONZERO) const;
    5151    bool strokeContains(const FloatPoint&, bool requiresStroke = true) const;
    5252
  • trunk/WebCore/rendering/RenderSVGContainer.cpp

    r60541 r60761  
    160160    FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
    161161
     162    if (!pointInClippingArea(this, localPoint))
     163        return false;
     164               
    162165    for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
    163166        if (child->nodeAtFloatPoint(request, result, localPoint, hitTestAction)) {
  • trunk/WebCore/rendering/RenderSVGImage.cpp

    r60541 r60761  
    113113}
    114114
    115 bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest&, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
     115bool RenderSVGImage::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    116116{
    117117    // We only draw in the forground phase, so we only hit-test then.
     
    119119        return false;
    120120
    121     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, style()->pointerEvents());
    122    
     121    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, request, style()->pointerEvents());
    123122    bool isVisible = (style()->visibility() == VISIBLE);
    124123    if (isVisible || !hitRules.requireVisible) {
    125124        FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
     125           
     126        if (!pointInClippingArea(this, localPoint))
     127            return false;
    126128
    127129        if (hitRules.canHitFill) {
  • trunk/WebCore/rendering/RenderSVGResourceClipper.cpp

    r60541 r60761  
    2828#include "FloatRect.h"
    2929#include "GraphicsContext.h"
     30#include "HitTestRequest.h"
     31#include "HitTestResult.h"
    3032#include "ImageBuffer.h"
    3133#include "IntRect.h"
     
    267269}
    268270
     271bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundingBox, const FloatPoint& nodeAtPoint)
     272{
     273    FloatPoint point = nodeAtPoint;
     274    if (!pointInClippingArea(this, point))
     275        return false;
     276
     277    if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
     278        AffineTransform transform;
     279        transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
     280        transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
     281        point = transform.inverse().mapPoint(point);
     282    }
     283
     284    for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
     285        RenderObject* renderer = childNode->renderer();
     286        if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
     287            continue;
     288        if (!renderer->isRenderPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer())
     289            continue;
     290        IntPoint hitPoint;
     291        HitTestResult result(hitPoint);
     292        if (renderer->nodeAtFloatPoint(HitTestRequest(HitTestRequest::SVGClipContent), result, point, HitTestForeground))
     293            return true;
     294    }
     295
     296    return false;
     297}
     298
    269299FloatRect RenderSVGResourceClipper::resourceBoundingBox(RenderObject* object)
    270300{
  • trunk/WebCore/rendering/RenderSVGResourceClipper.h

    r60541 r60761  
    5555
    5656    virtual RenderSVGResourceType resourceType() const { return ClipperResourceType; }
     57   
     58    bool hitTestClipContent(const FloatRect&, const FloatPoint&);
    5759
    5860    SVGUnitTypes::SVGUnitType clipPathUnits() const { return toUnitType(static_cast<SVGClipPathElement*>(node())->clipPathUnits()); }
  • trunk/WebCore/rendering/RenderSVGText.cpp

    r60541 r60761  
    3434#include "FloatQuad.h"
    3535#include "GraphicsContext.h"
     36#include "HitTestRequest.h"
    3637#include "PointerEventsHitRules.h"
    3738#include "RenderLayer.h"
     
    99100bool RenderSVGText::nodeAtFloatPoint(const HitTestRequest& request, HitTestResult& result, const FloatPoint& pointInParent, HitTestAction hitTestAction)
    100101{
    101     PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, style()->pointerEvents());
     102    PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, request, style()->pointerEvents());
    102103    bool isVisible = (style()->visibility() == VISIBLE);
    103104    if (isVisible || !hitRules.requireVisible) {
     
    105106            || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill))) {
    106107            FloatPoint localPoint = localToParentTransform().inverse().mapPoint(pointInParent);
     108
     109            if (!pointInClippingArea(this, localPoint))
     110                return false;       
     111
    107112            return RenderBlock::nodeAtPoint(request, result, (int)localPoint.x(), (int)localPoint.y(), 0, 0, hitTestAction);
    108113        }
  • trunk/WebCore/rendering/SVGRenderSupport.cpp

    r60689 r60761  
    315315}
    316316
     317bool pointInClippingArea(const RenderObject* object, const FloatPoint& point)
     318{
     319    ASSERT(object);
     320    ASSERT(object->style());
     321
     322    Document* document = object->document();
     323    ASSERT(document);
     324
     325    const SVGRenderStyle* svgStyle = object->style()->svgStyle();
     326    ASSERT(svgStyle);
     327
     328    // We just take clippers into account to determine if a point is on the node. The Specification may
     329    // change later and we also need to check maskers.
     330    if (svgStyle->hasClipper()) {
     331        if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(document, svgStyle->clipperResource()))
     332            return clipper->hitTestClipContent(object->objectBoundingBox(), point);
     333    }
     334
     335    return true;
     336}
     337
    317338void deregisterFromResources(RenderObject* object)
    318339{
  • trunk/WebCore/rendering/SVGRenderSupport.h

    r60689 r60761  
    8080void renderSubtreeToImage(ImageBuffer*, RenderObject*);
    8181
     82bool pointInClippingArea(const RenderObject*, const FloatPoint&);
     83
    8284void deregisterFromResources(RenderObject*);
    8385void clampImageBufferSizeToViewport(FrameView*, IntSize& imageBufferSize);
Note: See TracChangeset for help on using the changeset viewer.