/*
 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include "SVGResources.h"

#if ENABLE(SVG)
#include "FilterOperation.h"
#include "PathOperation.h"
#include "RenderSVGResourceClipper.h"
#include "RenderSVGResourceFilter.h"
#include "RenderSVGResourceMarker.h"
#include "RenderSVGResourceMasker.h"
#include "LegacyRenderSVGRoot.h"
#include "SVGFilterElement.h"
#include "SVGGradientElement.h"
#include "SVGNames.h"
#include "SVGPatternElement.h"
#include "SVGRenderStyle.h"
#include "SVGURIReference.h"
#include "StyleCachedImage.h"
#include <wtf/NeverDestroyed.h>
#include <wtf/SetForScope.h>

#ifndef NDEBUG
#include <stdio.h>
#endif

namespace WebCore {

SVGResources::SVGResources() = default;

static NEVER_INLINE HashSet<AtomicString>& clipperFilterMaskerTags()
{
	static NeverDestroyed<HashSet<AtomicString>> s_tagList;
    if (UNLIKELY(s_tagList->isEmpty())) {
        // "container elements": http://www.w3.org/TR/SVG11/intro.html#TermContainerElement
        // "graphics elements" : http://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement
        s_tagList->add(SVGNames::aTag.localName());
        s_tagList->add(SVGNames::circleTag.localName());
        s_tagList->add(SVGNames::ellipseTag.localName());
        s_tagList->add(SVGNames::glyphTag.localName());
        s_tagList->add(SVGNames::gTag.localName());
        s_tagList->add(SVGNames::imageTag.localName());
        s_tagList->add(SVGNames::lineTag.localName());
        s_tagList->add(SVGNames::markerTag.localName());
        s_tagList->add(SVGNames::maskTag.localName());
        s_tagList->add(SVGNames::missing_glyphTag.localName());
        s_tagList->add(SVGNames::pathTag.localName());
        s_tagList->add(SVGNames::polygonTag.localName());
        s_tagList->add(SVGNames::polylineTag.localName());
        s_tagList->add(SVGNames::rectTag.localName());
        s_tagList->add(SVGNames::svgTag.localName());
        s_tagList->add(SVGNames::switchTag.localName());
        s_tagList->add(SVGNames::textTag.localName());
        s_tagList->add(SVGNames::useTag.localName());

        // Not listed in the definitions is the clipPath element, the SVG spec says though:
        // The "clipPath" element or any of its children can specify property "clip-path".
        // So we have to add clipPathTag here, otherwhise clip-path on clipPath will fail.
        // (Already mailed SVG WG, waiting for a solution)
        s_tagList->add(SVGNames::clipPathTag.localName());

        // Not listed in the definitions are the text content elements, though filter/clipper/masker on tspan/text/.. is allowed.
        // (Already mailed SVG WG, waiting for a solution)
        s_tagList->add(SVGNames::altGlyphTag.localName());
        s_tagList->add(SVGNames::textPathTag.localName());
        s_tagList->add(SVGNames::trefTag.localName());
        s_tagList->add(SVGNames::tspanTag.localName());

        // Not listed in the definitions is the foreignObject element, but clip-path
        // is a supported attribute.
        s_tagList->add(SVGNames::foreignObjectTag.localName());

        // Elements that we ignore, as it doesn't make any sense.
        // defs, pattern (FIXME: Mail SVG WG about these)
        // symbol (is converted to a svg element, when referenced by use, we can safely ignore it.)
    }

    return s_tagList;
}

static HashSet<AtomicString>& markerTags()
{
	static NeverDestroyed<HashSet<AtomicString>> s_tagList;
    if (s_tagList->isEmpty()) {
        s_tagList->add(SVGNames::lineTag.localName());
        s_tagList->add(SVGNames::pathTag.localName());
        s_tagList->add(SVGNames::polygonTag.localName());
        s_tagList->add(SVGNames::polylineTag.localName());
    }

    return s_tagList;
}

static HashSet<AtomicString>& fillAndStrokeTags()
{
	static NeverDestroyed<HashSet<AtomicString>> s_tagList;
    if (s_tagList->isEmpty()) {
        s_tagList->add(SVGNames::altGlyphTag.localName());
        s_tagList->add(SVGNames::circleTag.localName());
        s_tagList->add(SVGNames::ellipseTag.localName());
        s_tagList->add(SVGNames::lineTag.localName());
        s_tagList->add(SVGNames::pathTag.localName());
        s_tagList->add(SVGNames::polygonTag.localName());
        s_tagList->add(SVGNames::polylineTag.localName());
        s_tagList->add(SVGNames::rectTag.localName());
        s_tagList->add(SVGNames::textTag.localName());
        s_tagList->add(SVGNames::textPathTag.localName());
        s_tagList->add(SVGNames::trefTag.localName());
        s_tagList->add(SVGNames::tspanTag.localName());
    }

    return s_tagList;
}

static HashSet<AtomicString>& chainableResourceTags()
{
	static NeverDestroyed<HashSet<AtomicString>> s_tagList;
    if (s_tagList->isEmpty()) {
        s_tagList->add(SVGNames::linearGradientTag.localName());
        s_tagList->add(SVGNames::filterTag.localName());
        s_tagList->add(SVGNames::patternTag.localName());
        s_tagList->add(SVGNames::radialGradientTag.localName());
    }

    return s_tagList;
}

static inline String targetReferenceFromResource(SVGElement& element)
{
    String target;
    if (element.hasTagName(SVGNames::patternTag))
        target = toSVGPatternElement(element).href();
    else if (element.hasTagName(SVGNames::linearGradientTag) || element.hasTagName(SVGNames::radialGradientTag))
        target = toSVGGradientElement(element).href();
#if ENABLE(FILTERS)
    else if (element.hasTagName(SVGNames::filterTag))
        target = toSVGFilterElement(element).href();
#endif
    else
        ASSERT_NOT_REACHED();

    return SVGURIReference::fragmentIdentifierFromIRIString(target, element.document());
}

static inline bool isChainableResource(const SVGElement& element, const SVGElement& linkedResource)
{
    if (element.hasTagName(SVGNames::patternTag))
        return linkedResource.hasTagName(SVGNames::patternTag);

    if (element.hasTagName(SVGNames::linearGradientTag) || element.hasTagName(SVGNames::radialGradientTag))
        return linkedResource.hasTagName(SVGNames::linearGradientTag) || linkedResource.hasTagName(SVGNames::radialGradientTag);

    if (element.hasTagName(SVGNames::filterTag))
        return linkedResource.hasTagName(SVGNames::filterTag);

    ASSERT_NOT_REACHED();
    return false;
}

static inline RenderSVGResourceContainer* paintingResourceFromSVGPaint(TreeScope& treeScope, const SVGPaintType& paintType, const String& paintUri, AtomicString& id, bool& hasPendingResource)
{
    if (paintType != SVG_PAINTTYPE_URI && paintType != SVG_PAINTTYPE_URI_RGBCOLOR)
        return nullptr;

    id = SVGURIReference::fragmentIdentifierFromIRIString(paintUri, treeScope.documentScope());
    RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(treeScope, id);
    if (!container) {
        hasPendingResource = true;
        return nullptr;
    }

    RenderSVGResourceType resourceType = container->resourceType();
    if (resourceType != PatternResourceType && resourceType != LinearGradientResourceType && resourceType != RadialGradientResourceType)
        return nullptr;

    return container;
}

PassOwnPtr<SVGResources> SVGResources::buildCachedResources(const RenderElement& renderer, const RenderStyle* style)
{
    ASSERT(style);
    ASSERT(renderer.element());
    ASSERT_WITH_SECURITY_IMPLICATION(renderer.element()->isSVGElement());

    if (!renderer.element())
        return nullptr;

    SVGElement& element = toSVGElement(*renderer.element());

    auto& treeScope = element.treeScopeForSVGReferences();
    Document& document = treeScope.documentScope();

    const AtomicString& tagName = element.localName();
    if (tagName.isNull())
        return nullptr;

    auto& svgStyle = style->svgStyle();

    auto ensureResources = [](OwnPtr<SVGResources>& resources) -> SVGResources& {
        if (!resources)
            resources = adoptPtr(new SVGResources());
        return *resources;
    };

    OwnPtr<SVGResources> foundResources;
    if (clipperFilterMaskerTags().contains(tagName)) {
        if (style->clipPath() && isReferenceClipPathOperation(*style->clipPath())) {
            // FIXME: -webkit-clip-path should support external resources
            // https://bugs.webkit.org/show_bug.cgi?id=127032
            auto& clipPath = toReferenceClipPathOperation(*style->clipPath());
            AtomicString id(clipPath.fragment());
            if (auto* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(treeScope, id))
				ensureResources(foundResources).setClipper(clipper);
			else
                treeScope.addPendingSVGResource(id, element);
        }

#if ENABLE(FILTERS)
        if (style->hasFilter()) {
            const FilterOperations& filterOperations = style->filter();
            if (filterOperations.size() == 1) {
                const FilterOperation& filterOperation = *filterOperations.at(0);
                if (filterOperation.type() == FilterOperation::REFERENCE) {
                    const ReferenceFilterOperation& referenceFilterOperation = static_cast<const ReferenceFilterOperation&>(filterOperation);
                    AtomicString id = SVGURIReference::fragmentIdentifierFromIRIString(referenceFilterOperation.url(), element.document());
                    if (auto* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(treeScope, id))
                        ensureResources(foundResources).setFilter(filter);
                    else
                        treeScope.addPendingSVGResource(id, element);
                }
            }
        }
#endif

        if (style->hasPositionedMask()) {
            // FIXME: We should support all the values in the CSS mask property, but for now just use the first mask-image if it's a reference.
            auto* maskImage = style->maskImage();
            auto reresolvedURL = maskImage ? maskImage->reresolvedURL(document) : URL();

            if (!reresolvedURL.isEmpty()) {
                auto resourceID = SVGURIReference::fragmentIdentifierFromIRIString(reresolvedURL.string(), document);
                if (auto* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(treeScope, resourceID))
					ensureResources(foundResources).setMasker(masker);
				else
                    treeScope.addPendingSVGResource(resourceID, element);
            }
        }
    }

    if (markerTags().contains(tagName) && svgStyle.hasMarkers()) {
        AtomicString markerStartId(svgStyle.markerStartResource());
        if (auto* marker = getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, markerStartId))
            ensureResources(foundResources).setMarkerStart(marker);
        else
        	treeScope.addPendingSVGResource(markerStartId, element);

        AtomicString markerMidId(svgStyle.markerMidResource());
        if (auto* marker = getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, markerMidId))
            ensureResources(foundResources).setMarkerMid(marker);
        else
        	treeScope.addPendingSVGResource(markerMidId, element);

        AtomicString markerEndId(svgStyle.markerEndResource());
        if (auto* marker = getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, markerEndId))
            ensureResources(foundResources).setMarkerEnd(marker);
        else
        	treeScope.addPendingSVGResource(markerEndId, element);
    }

    if (fillAndStrokeTags().contains(tagName)) {
        if (svgStyle.hasFill()) {
            bool hasPendingResource = false;
            AtomicString id;
            if (auto* fill = paintingResourceFromSVGPaint(treeScope, svgStyle.fillPaintType(), svgStyle.fillPaintUri(), id, hasPendingResource))
            	ensureResources(foundResources).setFill(fill);
            else if (hasPendingResource)
            	treeScope.addPendingSVGResource(id, element);
        }

        if (svgStyle.hasStroke()) {
            bool hasPendingResource = false;
            AtomicString id;
            if (auto* stroke = paintingResourceFromSVGPaint(treeScope, svgStyle.strokePaintType(), svgStyle.strokePaintUri(), id, hasPendingResource))
            	ensureResources(foundResources).setStroke(stroke);
            else if (hasPendingResource)
            	treeScope.addPendingSVGResource(id, element);
        }
    }

    if (chainableResourceTags().contains(tagName)) {
        AtomicString id(targetReferenceFromResource(element));
        auto* linkedResource = getRenderSVGResourceContainerById(treeScope, id);
        if (!linkedResource)
            treeScope.addPendingSVGResource(id, element);
        else if (isChainableResource(element, linkedResource->element()))
            ensureResources(foundResources).setLinkedResource(linkedResource);
    }

    return foundResources.release();
}

void SVGResources::layoutDifferentRootIfNeeded(const RenderElement& resourcesClient)
{
    if (m_inLayoutForDifferentRoot)
        return;

    SetForScope<bool> inLayoutForDifferentRoot(m_inLayoutForDifferentRoot, true);
    const RenderSVGRoot* clientRoot = nullptr;

    auto layoutDifferentRootIfNeeded = [&](RenderSVGResourceContainer* container) {
        if (!container)
            return;

        auto* root = SVGRenderSupport::findTreeRootObject(*container);
        if (root->isInLayout())
            return;

        if (!clientRoot)
            clientRoot = SVGRenderSupport::findTreeRootObject(resourcesClient);

        if (clientRoot == root)
            return;

        container->layoutIfNeeded();
    };

    if (m_clipperFilterMaskerData) {
        layoutDifferentRootIfNeeded(m_clipperFilterMaskerData->clipper.get());
        layoutDifferentRootIfNeeded(m_clipperFilterMaskerData->masker.get());
        layoutDifferentRootIfNeeded(m_clipperFilterMaskerData->filter.get());
    }

    if (m_markerData) {
        layoutDifferentRootIfNeeded(m_markerData->markerStart.get());
        layoutDifferentRootIfNeeded(m_markerData->markerMid.get());
        layoutDifferentRootIfNeeded(m_markerData->markerEnd.get());
    }
}

bool SVGResources::markerReverseStart() const
{
    return m_markerData
        && m_markerData->markerStart
        && m_markerData->markerStart->markerElement().orientType() == SVGMarkerOrientAutoStartReverse;
}

void SVGResources::removeClientFromCache(RenderElement& renderer, bool markForInvalidation) const
{
    if (isEmpty())
        return;

    if (m_linkedResource) {
        ASSERT(!m_clipperFilterMaskerData);
        ASSERT(!m_markerData);
        ASSERT(!m_fillStrokeData);
        m_linkedResource->removeClientFromCache(renderer, markForInvalidation);
        return;
    }

    if (m_clipperFilterMaskerData) {
    	if (auto* clipper = m_clipperFilterMaskerData->clipper.get())
			clipper->removeClientFromCache(renderer, markForInvalidation);
#if ENABLE(FILTERS)
        if (auto* filter = m_clipperFilterMaskerData->filter.get())
            filter->removeClientFromCache(renderer, markForInvalidation);
#endif
        if (auto* masker = m_clipperFilterMaskerData->masker.get())
            masker->removeClientFromCache(renderer, markForInvalidation);
    }

    if (m_markerData) {
    	if (auto* markerStart = m_markerData->markerStart.get())
			markerStart->removeClientFromCache(renderer, markForInvalidation);
		if (auto* markerMid = m_markerData->markerMid.get())
			markerMid->removeClientFromCache(renderer, markForInvalidation);
		if (auto* markerEnd = m_markerData->markerEnd.get())
			markerEnd->removeClientFromCache(renderer, markForInvalidation);
    }

    if (m_fillStrokeData) {
        if (auto* fill = m_fillStrokeData->fill.get())
            fill->removeClientFromCache(renderer, markForInvalidation);
        if (auto* stroke = m_fillStrokeData->stroke.get())
            stroke->removeClientFromCache(renderer, markForInvalidation);
    }
}

bool SVGResources::resourceDestroyed(RenderSVGResourceContainer& resource)
{
    if (isEmpty())
        return false;

    if (m_linkedResource == &resource) {
        ASSERT(!m_clipperFilterMaskerData);
        ASSERT(!m_markerData);
        ASSERT(!m_fillStrokeData);
        m_linkedResource->removeAllClientsFromCache();
        m_linkedResource = nullptr;
        return true;
    }

    bool foundResources = false;
    switch (resource.resourceType()) {
    case MaskerResourceType:
        if (!m_clipperFilterMaskerData)
            break;
        if (m_clipperFilterMaskerData->masker == &resource) {
        	resource.removeAllClientsFromCache();
            m_clipperFilterMaskerData->masker = nullptr;
            foundResources = true;
        }
        break;
    case MarkerResourceType:
        if (!m_markerData)
            break;
        if (m_markerData->markerStart == &resource) {
        	resource.removeAllClientsFromCache();
            m_markerData->markerStart = nullptr;
            foundResources = true;
        }
        if (m_markerData->markerMid == &resource) {
        	resource.removeAllClientsFromCache();
            m_markerData->markerMid = nullptr;
            foundResources = true;
        }
        if (m_markerData->markerEnd == &resource) {
        	resource.removeAllClientsFromCache();
            m_markerData->markerEnd = nullptr;
            foundResources = true;
        }
        break;
    case PatternResourceType:
    case LinearGradientResourceType:
    case RadialGradientResourceType:
        if (!m_fillStrokeData)
            break;
        if (m_fillStrokeData->fill == &resource) {
        	resource.removeAllClientsFromCache();
            m_fillStrokeData->fill = nullptr;
            foundResources = true;
        }
        if (m_fillStrokeData->stroke == &resource) {
        	resource.removeAllClientsFromCache();
            m_fillStrokeData->stroke = nullptr;
            foundResources = true;
        }
        break;
    case FilterResourceType:
#if ENABLE(FILTERS)
        if (!m_clipperFilterMaskerData)
            break;
        if (m_clipperFilterMaskerData->filter == &resource) {
        	resource.removeAllClientsFromCache();
            m_clipperFilterMaskerData->filter = nullptr;
            foundResources = true;
        }
#else
        ASSERT_NOT_REACHED();
#endif
        break;
    case ClipperResourceType:
        if (!m_clipperFilterMaskerData)
            break; 
        if (m_clipperFilterMaskerData->clipper == &resource) {
        	resource.removeAllClientsFromCache();
            m_clipperFilterMaskerData->clipper = nullptr;
            foundResources = true;
        }
        break;
    case SolidColorResourceType:
        ASSERT_NOT_REACHED();
    }
    return foundResources;
}

void SVGResources::buildSetOfResources(WeakHashSet<RenderSVGResourceContainer>& set)
{
    if (isEmpty())
        return;

    if (auto* linkedResource = m_linkedResource.get()) {
        ASSERT(!m_clipperFilterMaskerData);
        ASSERT(!m_markerData);
        ASSERT(!m_fillStrokeData);
        set.add(*linkedResource);
        return;
    }

    if (m_clipperFilterMaskerData) {
    	if (auto* clipper = m_clipperFilterMaskerData->clipper.get())
			set.add(*clipper);
		if (auto* filter = m_clipperFilterMaskerData->filter.get())
			set.add(*filter);
		if (auto* masker = m_clipperFilterMaskerData->masker.get())
			set.add(*masker);
    }

    if (m_markerData) {
        if (auto* markerStart = m_markerData->markerStart.get())
            set.add(*markerStart);
        if (auto* markerMid = m_markerData->markerMid.get())
            set.add(*markerMid);
        if (auto* markerEnd = m_markerData->markerEnd.get())
            set.add(*markerEnd);
    }

    if (m_fillStrokeData) {
        if (auto* fill = m_fillStrokeData->fill.get())
            set.add(*fill);
        if (auto* stroke = m_fillStrokeData->stroke.get())
            set.add(*stroke);
    }
}

bool SVGResources::setClipper(RenderSVGResourceClipper* clipper)
{
    ASSERT(clipper);
    if (!clipper)
        return false;

    ASSERT(clipper->resourceType() == ClipperResourceType);

    if (!m_clipperFilterMaskerData)
        m_clipperFilterMaskerData = ClipperFilterMaskerData::create();

    m_clipperFilterMaskerData->clipper = makeWeakPtr(*clipper);
    return true;
}

void SVGResources::resetClipper()
{
    ASSERT(m_clipperFilterMaskerData);
    ASSERT(m_clipperFilterMaskerData->clipper);
    m_clipperFilterMaskerData->clipper = nullptr;
}

#if ENABLE(FILTERS)
bool SVGResources::setFilter(RenderSVGResourceFilter* filter)
{
    ASSERT(filter);
    if (!filter)
        return false;

    ASSERT(filter->resourceType() == FilterResourceType);

    if (!m_clipperFilterMaskerData)
        m_clipperFilterMaskerData = ClipperFilterMaskerData::create();

    m_clipperFilterMaskerData->filter = makeWeakPtr(*filter);
    return true;
}

void SVGResources::resetFilter()
{
    ASSERT(m_clipperFilterMaskerData);
    ASSERT(m_clipperFilterMaskerData->filter);
    m_clipperFilterMaskerData->filter = nullptr;
}
#endif

bool SVGResources::setMarkerStart(RenderSVGResourceMarker* markerStart)
{
    ASSERT(markerStart);
    if (!markerStart)
        return false;

    ASSERT(markerStart->resourceType() == MarkerResourceType);

    if (!m_markerData)
        m_markerData = MarkerData::create();

    m_markerData->markerStart = makeWeakPtr(*markerStart);
    return true;
}

void SVGResources::resetMarkerStart()
{
    ASSERT(m_markerData);
    ASSERT(m_markerData->markerStart);
    m_markerData->markerStart = nullptr;
}

bool SVGResources::setMarkerMid(RenderSVGResourceMarker* markerMid)
{
    ASSERT(markerMid);
    if (!markerMid)
        return false;

    ASSERT(markerMid->resourceType() == MarkerResourceType);

    if (!m_markerData)
        m_markerData = MarkerData::create();

    m_markerData->markerMid = makeWeakPtr(*markerMid);
    return true;
}

void SVGResources::resetMarkerMid()
{
    ASSERT(m_markerData);
    ASSERT(m_markerData->markerMid);
    m_markerData->markerMid = nullptr;
}

bool SVGResources::setMarkerEnd(RenderSVGResourceMarker* markerEnd)
{
    ASSERT(markerEnd);
    if (!markerEnd)
        return false;

    ASSERT(markerEnd->resourceType() == MarkerResourceType);

    if (!m_markerData)
        m_markerData = MarkerData::create();

    m_markerData->markerEnd = makeWeakPtr(*markerEnd);
    return true;
}

void SVGResources::resetMarkerEnd()
{
    ASSERT(m_markerData);
    ASSERT(m_markerData->markerEnd);
    m_markerData->markerEnd = nullptr;
}

bool SVGResources::setMasker(RenderSVGResourceMasker* masker)
{
    ASSERT(masker);
    if (!masker)
        return false;

    ASSERT(masker->resourceType() == MaskerResourceType);

    if (!m_clipperFilterMaskerData)
        m_clipperFilterMaskerData = ClipperFilterMaskerData::create();

    m_clipperFilterMaskerData->masker = makeWeakPtr(*masker);
    return true;
}

void SVGResources::resetMasker()
{
    ASSERT(m_clipperFilterMaskerData);
    ASSERT(m_clipperFilterMaskerData->masker);
    m_clipperFilterMaskerData->masker = nullptr;
}

bool SVGResources::setFill(RenderSVGResourceContainer* fill)
{
    ASSERT(fill);
    if (!fill)
        return false;

    ASSERT(fill->resourceType() == PatternResourceType
           || fill->resourceType() == LinearGradientResourceType
           || fill->resourceType() == RadialGradientResourceType);

    if (!m_fillStrokeData)
        m_fillStrokeData = FillStrokeData::create();

    m_fillStrokeData->fill = makeWeakPtr(*fill);
    return true;
}

void SVGResources::resetFill()
{
    ASSERT(m_fillStrokeData);
    ASSERT(m_fillStrokeData->fill);
    m_fillStrokeData->fill = nullptr;
}

bool SVGResources::setStroke(RenderSVGResourceContainer* stroke)
{
    ASSERT(stroke);
    if (!stroke)
        return false;

    ASSERT(stroke->resourceType() == PatternResourceType
           || stroke->resourceType() == LinearGradientResourceType
           || stroke->resourceType() == RadialGradientResourceType);

    if (!m_fillStrokeData)
        m_fillStrokeData = FillStrokeData::create();

    m_fillStrokeData->stroke = makeWeakPtr(*stroke);
    return true;
}

void SVGResources::resetStroke()
{
    ASSERT(m_fillStrokeData);
    ASSERT(m_fillStrokeData->stroke);
    m_fillStrokeData->stroke = nullptr;
}

bool SVGResources::setLinkedResource(RenderSVGResourceContainer* linkedResource)
{
    ASSERT(linkedResource);
    if (!linkedResource)
        return false;

    m_linkedResource = makeWeakPtr(*linkedResource);
    return true;
}

void SVGResources::resetLinkedResource()
{
    ASSERT(m_linkedResource);
    m_linkedResource = nullptr;
}

#ifndef NDEBUG
void SVGResources::dump(const RenderObject* object)
{
    ASSERT(object);
    ASSERT(object->node());

    fprintf(stderr, "-> this=%p, SVGResources(renderer=%p, node=%p)\n", this, object, object->node());
    fprintf(stderr, " | DOM Tree:\n");
    object->node()->showTreeForThis();

    fprintf(stderr, "\n | List of resources:\n");
    if (m_clipperFilterMaskerData) {
        if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
            fprintf(stderr, " |-> Clipper    : %p (node=%p)\n", clipper, clipper->node());
#if ENABLE(FILTERS)
        if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
            fprintf(stderr, " |-> Filter     : %p (node=%p)\n", filter, filter->node());
#endif
        if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
            fprintf(stderr, " |-> Masker     : %p (node=%p)\n", masker, masker->node());
    }

    if (m_markerData) {
        if (RenderSVGResourceMarker* markerStart = m_markerData->markerStart)
            fprintf(stderr, " |-> MarkerStart: %p (node=%p)\n", markerStart, markerStart->node());
        if (RenderSVGResourceMarker* markerMid = m_markerData->markerMid)
            fprintf(stderr, " |-> MarkerMid  : %p (node=%p)\n", markerMid, markerMid->node());
        if (RenderSVGResourceMarker* markerEnd = m_markerData->markerEnd)
            fprintf(stderr, " |-> MarkerEnd  : %p (node=%p)\n", markerEnd, markerEnd->node());
    }

    if (m_fillStrokeData) {
        if (RenderSVGResourceContainer* fill = m_fillStrokeData->fill)
            fprintf(stderr, " |-> Fill       : %p (node=%p)\n", fill, fill->node());
        if (RenderSVGResourceContainer* stroke = m_fillStrokeData->stroke)
            fprintf(stderr, " |-> Stroke     : %p (node=%p)\n", stroke, stroke->node());
    }

    if (m_linkedResource)
        fprintf(stderr, " |-> xlink:href : %p (node=%p)\n", m_linkedResource, m_linkedResource->node());
}
#endif

}

#endif
