bool LayoutSVGResourceClipper::calculateClipContentPathIfNeeded()
{
    if (!m_clipContentPath.isEmpty())
        return true;

    // If the current clip-path gets clipped itself, we have to fallback to masking.
    if (style()->svgStyle().hasClipper())
        return false;

    unsigned opCount = 0;
    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() == NONE || style->visibility() != VISIBLE)
            continue;

        // Current shape in clip-path gets clipped too. Fallback to masking.
        if (style->svgStyle().hasClipper()) {
            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);

            continue;
        }

        // Multiple shapes require PathOps. In some degenerate cases PathOps can exhibit quadratic
        // behavior, so we cap the number of ops to a reasonable count.
        const unsigned kMaxOps = 42;
        if (!RuntimeEnabledFeatures::pathOpsSVGClippingEnabled() || ++opCount > kMaxOps) {
            m_clipContentPath.clear();
            return false;
        }

        // Second clip shape => start using the builder.
        if (!usingBuilder) {
            clipPathBuilder.add(m_clipContentPath.skPath(), kUnion_SkPathOp);
            usingBuilder = true;
        }

        Path subPath;
        if (isSVGGeometryElement(childElement))
            toSVGGeometryElement(childElement)->toClipPath(subPath);
        else if (isSVGUseElement(childElement))
            toSVGUseElement(childElement)->toClipPath(subPath);

        clipPathBuilder.add(subPath.skPath(), kUnion_SkPathOp);
    }

    if (usingBuilder) {
        SkPath resolvedPath;
        clipPathBuilder.resolve(&resolvedPath);
        m_clipContentPath = resolvedPath;
    }

    return true;
}
bool RenderSVGResourceClipper::tryPathOnlyClipping(GraphicsContext* context,
    const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox) {
    // If the current clip-path gets clipped itself, we have to fallback to masking.
    if (!style()->svgStyle().clipperResource().isEmpty())
        return false;
    WindRule clipRule = RULE_NONZERO;
    Path clipPath = Path();

    for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
        RenderObject* renderer = childElement->renderer();
        if (!renderer)
            continue;
        // Only shapes or paths are supported for direct clipping. We need to fallback to masking for texts.
        if (renderer->isSVGText())
            return false;
        if (!childElement->isSVGGraphicsElement())
            continue;
        SVGGraphicsElement* styled = toSVGGraphicsElement(childElement);
        RenderStyle* style = renderer->style();
        if (!style || style->display() == NONE || style->visibility() != VISIBLE)
             continue;
        const SVGRenderStyle& svgStyle = style->svgStyle();
        // Current shape in clip-path gets clipped too. Fallback to masking.
        if (!svgStyle.clipperResource().isEmpty())
            return false;

        if (clipPath.isEmpty()) {
            // First clip shape.
            styled->toClipPath(clipPath);
            clipRule = svgStyle.clipRule();
            clipPath.setWindRule(clipRule);
            continue;
        }

        if (RuntimeEnabledFeatures::pathOpsSVGClippingEnabled()) {
            // Attempt to generate a combined clip path, fall back to masking if not possible.
            Path subPath;
            styled->toClipPath(subPath);
            subPath.setWindRule(svgStyle.clipRule());
            if (!clipPath.unionPath(subPath))
                return false;
        } else {
            return false;
        }
    }
    // Only one visible shape/path was found. Directly continue clipping and transform the content to userspace if necessary.
    if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
        AffineTransform transform;
        transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
        transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
        clipPath.transform(transform);
    }

    // Transform path by animatedLocalTransform.
    clipPath.transform(animatedLocalTransform);

    // The SVG specification wants us to clip everything, if clip-path doesn't have a child.
    if (clipPath.isEmpty())
        clipPath.addRect(FloatRect());
    context->clipPath(clipPath, clipRule);
    return true;
}