Index: Source/core/svg/SVGSVGElement.cpp |
diff --git a/Source/core/svg/SVGSVGElement.cpp b/Source/core/svg/SVGSVGElement.cpp |
index af5a33658f00b7a24f4ceec4348df4e23b319193..83a336bc12f24d66406e358beb9265a31466d3e0 100644 |
--- a/Source/core/svg/SVGSVGElement.cpp |
+++ b/Source/core/svg/SVGSVGElement.cpp |
@@ -2,6 +2,7 @@ |
* Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org> |
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Rob Buis <buis@kde.org> |
* Copyright (C) 2007 Apple Inc. All rights reserved. |
+ * Copyright (C) 2014 Google, Inc. |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Library General Public |
@@ -329,49 +330,114 @@ void SVGSVGElement::svgAttributeChanged(const QualifiedName& attrName) |
SVGGraphicsElement::svgAttributeChanged(attrName); |
} |
-PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, SVGElement* referenceElement, CollectIntersectionOrEnclosure collect) const |
+// FloatRect::intersects does not consider horizontal or vertical lines (because of isEmpty()). |
+static bool intersectsAllowingEmpty(const FloatRect& r1, const FloatRect& r2) |
+{ |
+ if (r1.width() < 0 || r1.height() < 0 || r2.width() < 0 || r2.height() < 0) |
+ return false; |
+ |
+ return r1.x() < r2.maxX() && r2.x() < r1.maxX() |
+ && r1.y() < r2.maxY() && r2.y() < r1.maxY(); |
+} |
+ |
+// One of the element types that can cause graphics to be drawn onto the target canvas. |
+// Specifically: circle, ellipse, image, line, path, polygon, polyline, rect, text and use. |
+static bool isIntersectionOrEnclosureTarget(RenderObject* renderer) |
+{ |
+ return renderer->isSVGShape() |
+ || renderer->isSVGText() |
+ || renderer->isSVGImage() |
+ || renderer->node()->hasTagName(SVGNames::useTag); |
+} |
+ |
+bool SVGSVGElement::checkIntersectionOrEnclosure(const SVGElement& element, const FloatRect& rect, |
+ CheckIntersectionOrEnclosure mode) const |
+{ |
+ RenderObject* renderer = element.renderer(); |
+ ASSERT(!renderer || renderer->style()); |
+ if (!renderer || renderer->style()->pointerEvents() == PE_NONE) |
+ return false; |
+ |
+ if (!isIntersectionOrEnclosureTarget(renderer)) |
+ return false; |
+ |
+ AffineTransform ctm = toSVGGraphicsElement(element).computeCTM(AncestorScope, DisallowStyleUpdate, this); |
+ FloatRect mappedRepaintRect = ctm.mapRect(renderer->repaintRectInLocalCoordinates()); |
+ |
+ bool result = false; |
+ switch (mode) { |
+ case CheckIntersection: |
+ result = intersectsAllowingEmpty(rect, mappedRepaintRect); |
+ break; |
+ case CheckEnclosure: |
+ result = rect.contains(mappedRepaintRect); |
+ break; |
+ default: |
+ ASSERT_NOT_REACHED(); |
+ break; |
+ } |
+ |
+ return result; |
+} |
+ |
+PassRefPtr<NodeList> SVGSVGElement::collectIntersectionOrEnclosureList(const FloatRect& rect, |
+ SVGElement* referenceElement, CheckIntersectionOrEnclosure mode) const |
{ |
Vector<RefPtr<Node> > nodes; |
- Element* element = ElementTraversal::next(*(referenceElement ? referenceElement : this)); |
- while (element) { |
- if (element->isSVGElement()) { |
- SVGElement* svgElement = toSVGElement(element); |
- if (collect == CollectIntersectionList) { |
- if (RenderSVGModelObject::checkIntersection(svgElement->renderer(), rect)) |
- nodes.append(element); |
- } else { |
- if (RenderSVGModelObject::checkEnclosure(svgElement->renderer(), rect)) |
- nodes.append(element); |
- } |
+ |
+ const SVGElement* root = this; |
+ if (referenceElement) { |
+ // Only the common subtree needs to be traversed. |
+ if (contains(referenceElement)) { |
+ root = referenceElement; |
+ } else if (!isDescendantOf(referenceElement)) { |
+ // No common subtree. |
+ return StaticNodeList::adopt(nodes); |
} |
+ } |
+ |
+ for (Element* element = ElementTraversal::firstWithin(*root); element; |
+ element = ElementTraversal::next(*element, root)) { |
- element = ElementTraversal::next(*element, referenceElement ? referenceElement : this); |
+ if (!WebCore::isSVGGraphicsElement(*element)) |
+ continue; |
+ |
+ SVGElement* svgElement = toSVGElement(element); |
+ if (checkIntersectionOrEnclosure(*svgElement, rect, mode)) |
+ nodes.append(element); |
} |
+ |
return StaticNodeList::adopt(nodes); |
} |
-PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> passRect, SVGElement* referenceElement) const |
+PassRefPtr<NodeList> SVGSVGElement::getIntersectionList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const |
{ |
- RefPtr<SVGRectTearOff> rect = passRect; |
- return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CollectIntersectionList); |
+ document().updateLayoutIgnorePendingStylesheets(); |
+ |
+ return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckIntersection); |
} |
-PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> passRect, SVGElement* referenceElement) const |
+PassRefPtr<NodeList> SVGSVGElement::getEnclosureList(PassRefPtr<SVGRectTearOff> rect, SVGElement* referenceElement) const |
{ |
- RefPtr<SVGRectTearOff> rect = passRect; |
- return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CollectEnclosureList); |
+ document().updateLayoutIgnorePendingStylesheets(); |
+ |
+ return collectIntersectionOrEnclosureList(rect->target()->value(), referenceElement, CheckEnclosure); |
} |
-bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> passRect) const |
+bool SVGSVGElement::checkIntersection(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const |
{ |
- RefPtr<SVGRectTearOff> rect = passRect; |
- return RenderSVGModelObject::checkIntersection(element->renderer(), rect->target()->value()); |
+ ASSERT(element); |
+ document().updateLayoutIgnorePendingStylesheets(); |
+ |
+ return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckIntersection); |
} |
-bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> passRect) const |
+bool SVGSVGElement::checkEnclosure(SVGElement* element, PassRefPtr<SVGRectTearOff> rect) const |
{ |
- RefPtr<SVGRectTearOff> rect = passRect; |
- return RenderSVGModelObject::checkEnclosure(element->renderer(), rect->target()->value()); |
+ ASSERT(element); |
+ document().updateLayoutIgnorePendingStylesheets(); |
+ |
+ return checkIntersectionOrEnclosure(*element, rect->target()->value(), CheckEnclosure); |
} |
void SVGSVGElement::deselectAll() |