Index: third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp |
diff --git a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp |
index 9ed7c72d32a89996c6a5310e3624e0244119abb8..d26df1fe523da7d7444da8e06cd35d18feb16988 100644 |
--- a/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp |
+++ b/third_party/WebKit/Source/core/layout/svg/LayoutSVGResourceClipper.cpp |
@@ -22,20 +22,73 @@ |
#include "core/layout/svg/LayoutSVGResourceClipper.h" |
-#include "core/SVGNames.h" |
#include "core/dom/ElementTraversal.h" |
#include "core/layout/HitTestResult.h" |
#include "core/layout/svg/SVGLayoutSupport.h" |
#include "core/paint/PaintInfo.h" |
#include "core/svg/SVGGeometryElement.h" |
#include "core/svg/SVGUseElement.h" |
-#include "platform/RuntimeEnabledFeatures.h" |
#include "platform/graphics/paint/SkPictureBuilder.h" |
#include "third_party/skia/include/core/SkPicture.h" |
#include "third_party/skia/include/pathops/SkPathOps.h" |
namespace blink { |
+namespace { |
+ |
+bool requiresMask(const SVGElement& childElement) { |
+ // TODO(fs): This needs to special-case <use> in a way similar to |
+ // contributesToClip. (crbug.com/604677) |
+ LayoutObject* layoutObject = childElement.layoutObject(); |
+ DCHECK(layoutObject); |
+ // Only basic shapes or paths are supported for direct clipping. We need to |
+ // fallback to masking for texts. |
+ if (layoutObject->isSVGText()) |
+ return true; |
+ // Current shape in clip-path gets clipped too. Fallback to masking. |
+ return layoutObject->styleRef().clipPath(); |
+} |
+ |
+bool contributesToClip(const SVGGraphicsElement& element) { |
+ const LayoutObject* layoutObject = element.layoutObject(); |
+ if (!layoutObject) |
+ return false; |
+ const ComputedStyle& style = layoutObject->styleRef(); |
+ if (style.display() == EDisplay::None || |
+ style.visibility() != EVisibility::Visible) |
+ return false; |
+ // Only shapes, paths and texts are allowed for clipping. |
+ return layoutObject->isSVGShape() || layoutObject->isSVGText(); |
+} |
+ |
+bool contributesToClip(const SVGElement& element) { |
+ // <use> within <clipPath> have a restricted content model. |
+ // (https://drafts.fxtf.org/css-masking-1/#ClipPathElement) |
+ if (isSVGUseElement(element)) { |
+ const LayoutObject* useLayoutObject = element.layoutObject(); |
+ if (!useLayoutObject || |
+ useLayoutObject->styleRef().display() == EDisplay::None) |
+ return false; |
+ const SVGGraphicsElement* clippingElement = |
+ toSVGUseElement(element).visibleTargetGraphicsElementForClipping(); |
+ if (!clippingElement) |
+ return false; |
+ return contributesToClip(*clippingElement); |
+ } |
+ if (!element.isSVGGraphicsElement()) |
+ return false; |
+ return contributesToClip(toSVGGraphicsElement(element)); |
+} |
+ |
+void pathFromElement(const SVGElement& element, Path& clipPath) { |
+ if (isSVGGeometryElement(element)) |
+ toSVGGeometryElement(element).toClipPath(clipPath); |
+ else if (isSVGUseElement(element)) |
+ toSVGUseElement(element).toClipPath(clipPath); |
+} |
+ |
+} // namespace |
+ |
LayoutSVGResourceClipper::LayoutSVGResourceClipper(SVGClipPathElement* node) |
: LayoutSVGResourceContainer(node), m_inClipExpansion(false) {} |
@@ -72,40 +125,19 @@ bool LayoutSVGResourceClipper::calculateClipContentPathIfNeeded() { |
bool usingBuilder = false; |
SkOpBuilder clipPathBuilder; |
- for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); |
- childElement; |
- childElement = Traversal<SVGElement>::nextSibling(*childElement)) { |
- LayoutObject* childLayoutObject = childElement->layoutObject(); |
- if (!childLayoutObject) |
- continue; |
- // Only shapes or paths are supported for direct clipping. We need to |
- // fallback to masking for texts. |
- if (childLayoutObject->isSVGText()) { |
- m_clipContentPath.clear(); |
- return false; |
- } |
- if (!childElement->isSVGGraphicsElement()) |
- continue; |
- |
- const ComputedStyle* style = childLayoutObject->style(); |
- if (!style || style->display() == EDisplay::None || |
- (style->visibility() != EVisibility::Visible && |
- !isSVGUseElement(*childElement))) |
+ for (const SVGElement& childElement : |
+ Traversal<SVGElement>::childrenOf(*element())) { |
+ if (!contributesToClip(childElement)) |
continue; |
- // Current shape in clip-path gets clipped too. Fallback to masking. |
- if (style->clipPath()) { |
+ if (requiresMask(childElement)) { |
m_clipContentPath.clear(); |
return false; |
} |
// First clip shape. |
if (m_clipContentPath.isEmpty()) { |
- if (isSVGGeometryElement(childElement)) |
- toSVGGeometryElement(childElement)->toClipPath(m_clipContentPath); |
- else if (isSVGUseElement(childElement)) |
- toSVGUseElement(childElement)->toClipPath(m_clipContentPath); |
- |
+ pathFromElement(childElement, m_clipContentPath); |
continue; |
} |
@@ -125,10 +157,7 @@ bool LayoutSVGResourceClipper::calculateClipContentPathIfNeeded() { |
} |
Path subPath; |
- if (isSVGGeometryElement(childElement)) |
- toSVGGeometryElement(childElement)->toClipPath(subPath); |
- else if (isSVGUseElement(childElement)) |
- toSVGUseElement(childElement)->toClipPath(subPath); |
+ pathFromElement(childElement, subPath); |
clipPathBuilder.add(subPath.getSkPath(), kUnion_SkPathOp); |
} |
@@ -177,49 +206,23 @@ sk_sp<const SkPicture> LayoutSVGResourceClipper::createContentPicture() { |
FloatRect bounds = strokeBoundingBox(); |
SkPictureBuilder pictureBuilder(bounds, nullptr, nullptr); |
- |
- for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); |
- childElement; |
- childElement = Traversal<SVGElement>::nextSibling(*childElement)) { |
- LayoutObject* layoutObject = childElement->layoutObject(); |
- if (!layoutObject) |
- continue; |
- |
- const ComputedStyle* style = layoutObject->style(); |
- if (!style || style->display() == EDisplay::None || |
- (style->visibility() != EVisibility::Visible && |
- !isSVGUseElement(*childElement))) |
+ // Switch to a paint behavior where all children of this <clipPath> will be |
+ // laid out using special constraints: |
+ // - fill-opacity/stroke-opacity/opacity set to 1 |
+ // - masker/filter not applied when laying out the children |
+ // - fill is set to the initial fill paint server (solid, black) |
+ // - stroke is set to the initial stroke paint server (none) |
+ PaintInfo info(pictureBuilder.context(), LayoutRect::infiniteIntRect(), |
+ PaintPhaseForeground, GlobalPaintNormalPhase, |
+ PaintLayerPaintingRenderingClipPathAsMask); |
+ |
+ for (const SVGElement& childElement : |
+ Traversal<SVGElement>::childrenOf(*element())) { |
+ if (!contributesToClip(childElement)) |
continue; |
- |
- bool isUseElement = isSVGUseElement(*childElement); |
- if (isUseElement) { |
- const SVGGraphicsElement* clippingElement = |
- toSVGUseElement(*childElement) |
- .visibleTargetGraphicsElementForClipping(); |
- if (!clippingElement) |
- continue; |
- |
- layoutObject = clippingElement->layoutObject(); |
- if (!layoutObject) |
- continue; |
- } |
- |
- // Only shapes, paths and texts are allowed for clipping. |
- if (!layoutObject->isSVGShape() && !layoutObject->isSVGText()) |
- continue; |
- |
- if (isUseElement) |
- layoutObject = childElement->layoutObject(); |
- |
- // Switch to a paint behavior where all children of this <clipPath> will be |
- // laid out using special constraints: |
- // - fill-opacity/stroke-opacity/opacity set to 1 |
- // - masker/filter not applied when laying out the children |
- // - fill is set to the initial fill paint server (solid, black) |
- // - stroke is set to the initial stroke paint server (none) |
- PaintInfo info(pictureBuilder.context(), LayoutRect::infiniteIntRect(), |
- PaintPhaseForeground, GlobalPaintNormalPhase, |
- PaintLayerPaintingRenderingClipPathAsMask); |
+ // Use the LayoutObject of the direct child even if it is a <use>. In that |
+ // case, we will paint the targeted element indirectly. |
+ const LayoutObject* layoutObject = childElement.layoutObject(); |
layoutObject->paint(info, IntPoint()); |
} |
@@ -230,25 +233,11 @@ sk_sp<const SkPicture> LayoutSVGResourceClipper::createContentPicture() { |
void LayoutSVGResourceClipper::calculateLocalClipBounds() { |
// This is a rough heuristic to appraise the clip size and doesn't consider |
// clip on clip. |
- for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); |
- childElement; |
- childElement = Traversal<SVGElement>::nextSibling(*childElement)) { |
- LayoutObject* layoutObject = childElement->layoutObject(); |
- if (!layoutObject) |
- continue; |
- if (!layoutObject->isSVGShape() && !layoutObject->isSVGText() && |
- !isSVGUseElement(*childElement)) |
- continue; |
- const ComputedStyle* style = layoutObject->style(); |
- if (!style || style->display() == EDisplay::None || |
- (style->visibility() != EVisibility::Visible && |
- !isSVGUseElement(*childElement))) |
+ for (const SVGElement& childElement : |
+ Traversal<SVGElement>::childrenOf(*element())) { |
+ if (!contributesToClip(childElement)) |
continue; |
- if (isSVGUseElement(*childElement) && |
- !toSVGUseElement(*childElement) |
- .visibleTargetGraphicsElementForClipping()) |
- continue; |
- |
+ const LayoutObject* layoutObject = childElement.layoutObject(); |
m_localClipBounds.unite(layoutObject->localToSVGParentTransform().mapRect( |
layoutObject->visualRectInLocalSVGCoordinates())); |
} |
@@ -277,21 +266,16 @@ bool LayoutSVGResourceClipper::hitTestClipContent( |
point = animatedLocalTransform.inverse().mapPoint(point); |
- for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); |
- childElement; |
- childElement = Traversal<SVGElement>::nextSibling(*childElement)) { |
- LayoutObject* layoutObject = childElement->layoutObject(); |
- if (!layoutObject) |
- continue; |
- if (!layoutObject->isSVGShape() && !layoutObject->isSVGText() && |
- !isSVGUseElement(*childElement)) |
+ for (const SVGElement& childElement : |
+ Traversal<SVGElement>::childrenOf(*element())) { |
+ if (!contributesToClip(childElement)) |
continue; |
IntPoint hitPoint; |
HitTestResult result(HitTestRequest::SVGClipContent, hitPoint); |
+ LayoutObject* layoutObject = childElement.layoutObject(); |
if (layoutObject->nodeAtFloatPoint(result, point, HitTestForeground)) |
return true; |
} |
- |
return false; |
} |