1/*
2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "core/rendering/svg/SVGResources.h"
22
23#include "core/SVGNames.h"
24#include "core/rendering/style/SVGRenderStyle.h"
25#include "core/rendering/svg/RenderSVGResourceClipper.h"
26#include "core/rendering/svg/RenderSVGResourceFilter.h"
27#include "core/rendering/svg/RenderSVGResourceMarker.h"
28#include "core/rendering/svg/RenderSVGResourceMasker.h"
29#include "core/svg/SVGFilterElement.h"
30#include "core/svg/SVGGradientElement.h"
31#include "core/svg/SVGPatternElement.h"
32#include "core/svg/SVGURIReference.h"
33
34#ifndef NDEBUG
35#include <stdio.h>
36#endif
37
38namespace blink {
39
40using namespace SVGNames;
41
42SVGResources::SVGResources()
43    : m_linkedResource(0)
44{
45}
46
47static HashSet<AtomicString>& clipperFilterMaskerTags()
48{
49    DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
50    if (s_tagList.isEmpty()) {
51        // "container elements": http://www.w3.org/TR/SVG11/intro.html#TermContainerElement
52        // "graphics elements" : http://www.w3.org/TR/SVG11/intro.html#TermGraphicsElement
53        s_tagList.add(aTag.localName());
54        s_tagList.add(circleTag.localName());
55        s_tagList.add(ellipseTag.localName());
56#if ENABLE(SVG_FONTS)
57        s_tagList.add(glyphTag.localName());
58#endif
59        s_tagList.add(gTag.localName());
60        s_tagList.add(imageTag.localName());
61        s_tagList.add(lineTag.localName());
62        s_tagList.add(markerTag.localName());
63        s_tagList.add(maskTag.localName());
64#if ENABLE(SVG_FONTS)
65        s_tagList.add(missing_glyphTag.localName());
66#endif
67        s_tagList.add(pathTag.localName());
68        s_tagList.add(polygonTag.localName());
69        s_tagList.add(polylineTag.localName());
70        s_tagList.add(rectTag.localName());
71        s_tagList.add(svgTag.localName());
72        s_tagList.add(textTag.localName());
73        s_tagList.add(useTag.localName());
74
75        // Not listed in the definitions is the clipPath element, the SVG spec says though:
76        // The "clipPath" element or any of its children can specify property "clip-path".
77        // So we have to add clipPathTag here, otherwhise clip-path on clipPath will fail.
78        // (Already mailed SVG WG, waiting for a solution)
79        s_tagList.add(clipPathTag.localName());
80
81        // Not listed in the definitions are the text content elements, though filter/clipper/masker on tspan/text/.. is allowed.
82        // (Already mailed SVG WG, waiting for a solution)
83#if ENABLE(SVG_FONTS)
84        s_tagList.add(altGlyphTag.localName());
85#endif
86        s_tagList.add(textPathTag.localName());
87        s_tagList.add(tspanTag.localName());
88
89        // Not listed in the definitions is the foreignObject element, but clip-path
90        // is a supported attribute.
91        s_tagList.add(foreignObjectTag.localName());
92
93        // Elements that we ignore, as it doesn't make any sense.
94        // defs, pattern, switch (FIXME: Mail SVG WG about these)
95        // symbol (is converted to a svg element, when referenced by use, we can safely ignore it.)
96    }
97
98    return s_tagList;
99}
100
101bool SVGResources::supportsMarkers(const SVGElement& element)
102{
103    DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
104    if (s_tagList.isEmpty()) {
105        s_tagList.add(lineTag.localName());
106        s_tagList.add(pathTag.localName());
107        s_tagList.add(polygonTag.localName());
108        s_tagList.add(polylineTag.localName());
109    }
110
111    return s_tagList.contains(element.localName());
112}
113
114static HashSet<AtomicString>& fillAndStrokeTags()
115{
116    DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
117    if (s_tagList.isEmpty()) {
118#if ENABLE(SVG_FONTS)
119        s_tagList.add(altGlyphTag.localName());
120#endif
121        s_tagList.add(circleTag.localName());
122        s_tagList.add(ellipseTag.localName());
123        s_tagList.add(lineTag.localName());
124        s_tagList.add(pathTag.localName());
125        s_tagList.add(polygonTag.localName());
126        s_tagList.add(polylineTag.localName());
127        s_tagList.add(rectTag.localName());
128        s_tagList.add(textTag.localName());
129        s_tagList.add(textPathTag.localName());
130        s_tagList.add(tspanTag.localName());
131    }
132
133    return s_tagList;
134}
135
136static HashSet<AtomicString>& chainableResourceTags()
137{
138    DEFINE_STATIC_LOCAL(HashSet<AtomicString>, s_tagList, ());
139    if (s_tagList.isEmpty()) {
140        s_tagList.add(linearGradientTag.localName());
141        s_tagList.add(filterTag.localName());
142        s_tagList.add(patternTag.localName());
143        s_tagList.add(radialGradientTag.localName());
144    }
145
146    return s_tagList;
147}
148
149static inline AtomicString targetReferenceFromResource(SVGElement& element)
150{
151    String target;
152    if (isSVGPatternElement(element))
153        target = toSVGPatternElement(element).href()->currentValue()->value();
154    else if (isSVGGradientElement(element))
155        target = toSVGGradientElement(element).href()->currentValue()->value();
156    else if (isSVGFilterElement(element))
157        target = toSVGFilterElement(element).href()->currentValue()->value();
158    else
159        ASSERT_NOT_REACHED();
160
161    return SVGURIReference::fragmentIdentifierFromIRIString(target, element.treeScope());
162}
163
164static inline bool svgPaintTypeHasURL(SVGPaintType paintType)
165{
166    switch (paintType) {
167    case SVG_PAINTTYPE_URI_NONE:
168    case SVG_PAINTTYPE_URI_CURRENTCOLOR:
169    case SVG_PAINTTYPE_URI_RGBCOLOR:
170    case SVG_PAINTTYPE_URI:
171        return true;
172    default:
173        break;
174    }
175    return false;
176}
177
178static inline RenderSVGResourceContainer* paintingResourceFromSVGPaint(TreeScope& treeScope, const SVGPaintType& paintType, const String& paintUri, AtomicString& id, bool& hasPendingResource)
179{
180    if (!svgPaintTypeHasURL(paintType))
181        return 0;
182
183    id = SVGURIReference::fragmentIdentifierFromIRIString(paintUri, treeScope);
184    RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(treeScope, id);
185    if (!container) {
186        hasPendingResource = true;
187        return 0;
188    }
189
190    RenderSVGResourceType resourceType = container->resourceType();
191    if (resourceType != PatternResourceType && resourceType != LinearGradientResourceType && resourceType != RadialGradientResourceType)
192        return 0;
193
194    return container;
195}
196
197static inline void registerPendingResource(SVGDocumentExtensions& extensions, const AtomicString& id, SVGElement* element)
198{
199    ASSERT(element);
200    extensions.addPendingResource(id, element);
201}
202
203bool SVGResources::hasResourceData() const
204{
205    return !m_clipperFilterMaskerData
206        && !m_markerData
207        && !m_fillStrokeData
208        && !m_linkedResource;
209}
210
211static inline SVGResources* ensureResources(OwnPtr<SVGResources>& resources)
212{
213    if (!resources)
214        resources = adoptPtr(new SVGResources);
215
216    return resources.get();
217}
218
219PassOwnPtr<SVGResources> SVGResources::buildResources(const RenderObject* object, const SVGRenderStyle& style)
220{
221    ASSERT(object);
222
223    Node* node = object->node();
224    ASSERT(node);
225    ASSERT_WITH_SECURITY_IMPLICATION(node->isSVGElement());
226
227    SVGElement* element = toSVGElement(node);
228    if (!element)
229        return nullptr;
230
231    TreeScope& treeScope = element->treeScope();
232
233    SVGDocumentExtensions& extensions = object->document().accessSVGExtensions();
234
235    const AtomicString& tagName = element->localName();
236    if (tagName.isNull())
237        return nullptr;
238
239    OwnPtr<SVGResources> resources;
240    if (clipperFilterMaskerTags().contains(tagName)) {
241        if (style.hasClipper()) {
242            AtomicString id = style.clipperResource();
243            if (!ensureResources(resources)->setClipper(getRenderSVGResourceById<RenderSVGResourceClipper>(treeScope, id)))
244                registerPendingResource(extensions, id, element);
245        }
246
247        if (style.hasFilter()) {
248            AtomicString id = style.filterResource();
249            if (!ensureResources(resources)->setFilter(getRenderSVGResourceById<RenderSVGResourceFilter>(treeScope, id)))
250                registerPendingResource(extensions, id, element);
251        }
252
253        if (style.hasMasker()) {
254            AtomicString id = style.maskerResource();
255            if (!ensureResources(resources)->setMasker(getRenderSVGResourceById<RenderSVGResourceMasker>(treeScope, id)))
256                registerPendingResource(extensions, id, element);
257        }
258    }
259
260    if (style.hasMarkers() && supportsMarkers(*element)) {
261        const AtomicString& markerStartId = style.markerStartResource();
262        if (!ensureResources(resources)->setMarkerStart(getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, markerStartId)))
263            registerPendingResource(extensions, markerStartId, element);
264
265        const AtomicString& markerMidId = style.markerMidResource();
266        if (!ensureResources(resources)->setMarkerMid(getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, markerMidId)))
267            registerPendingResource(extensions, markerMidId, element);
268
269        const AtomicString& markerEndId = style.markerEndResource();
270        if (!ensureResources(resources)->setMarkerEnd(getRenderSVGResourceById<RenderSVGResourceMarker>(treeScope, style.markerEndResource())))
271            registerPendingResource(extensions, markerEndId, element);
272    }
273
274    if (fillAndStrokeTags().contains(tagName)) {
275        if (style.hasFill()) {
276            bool hasPendingResource = false;
277            AtomicString id;
278            RenderSVGResourceContainer* resource = paintingResourceFromSVGPaint(treeScope, style.fillPaintType(), style.fillPaintUri(), id, hasPendingResource);
279            if (!ensureResources(resources)->setFill(resource) && hasPendingResource) {
280                registerPendingResource(extensions, id, element);
281            }
282        }
283
284        if (style.hasStroke()) {
285            bool hasPendingResource = false;
286            AtomicString id;
287            RenderSVGResourceContainer* resource = paintingResourceFromSVGPaint(treeScope, style.strokePaintType(), style.strokePaintUri(), id, hasPendingResource);
288            if (!ensureResources(resources)->setStroke(resource) && hasPendingResource) {
289                registerPendingResource(extensions, id, element);
290            }
291        }
292    }
293
294    if (chainableResourceTags().contains(tagName)) {
295        AtomicString id = targetReferenceFromResource(*element);
296        if (!ensureResources(resources)->setLinkedResource(getRenderSVGResourceContainerById(treeScope, id)))
297            registerPendingResource(extensions, id, element);
298    }
299
300    return (!resources || resources->hasResourceData()) ? nullptr : resources.release();
301}
302
303void SVGResources::layoutIfNeeded()
304{
305    if (m_clipperFilterMaskerData) {
306        if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
307            clipper->layoutIfNeeded();
308        if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
309            masker->layoutIfNeeded();
310        if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
311            filter->layoutIfNeeded();
312    }
313
314    if (m_markerData) {
315        if (RenderSVGResourceMarker* marker = m_markerData->markerStart)
316            marker->layoutIfNeeded();
317        if (RenderSVGResourceMarker* marker = m_markerData->markerMid)
318            marker->layoutIfNeeded();
319        if (RenderSVGResourceMarker* marker = m_markerData->markerEnd)
320            marker->layoutIfNeeded();
321    }
322
323    if (m_fillStrokeData) {
324        if (RenderSVGResourceContainer* fill = m_fillStrokeData->fill)
325            fill->layoutIfNeeded();
326        if (RenderSVGResourceContainer* stroke = m_fillStrokeData->stroke)
327            stroke->layoutIfNeeded();
328    }
329
330    if (m_linkedResource)
331        m_linkedResource->layoutIfNeeded();
332}
333
334void SVGResources::removeClientFromCache(RenderObject* object, bool markForInvalidation) const
335{
336    if (hasResourceData())
337        return;
338
339    if (m_linkedResource) {
340        ASSERT(!m_clipperFilterMaskerData);
341        ASSERT(!m_markerData);
342        ASSERT(!m_fillStrokeData);
343        m_linkedResource->removeClientFromCache(object, markForInvalidation);
344        return;
345    }
346
347    if (m_clipperFilterMaskerData) {
348        if (m_clipperFilterMaskerData->clipper)
349            m_clipperFilterMaskerData->clipper->removeClientFromCache(object, markForInvalidation);
350        if (m_clipperFilterMaskerData->filter)
351            m_clipperFilterMaskerData->filter->removeClientFromCache(object, markForInvalidation);
352        if (m_clipperFilterMaskerData->masker)
353            m_clipperFilterMaskerData->masker->removeClientFromCache(object, markForInvalidation);
354    }
355
356    if (m_markerData) {
357        if (m_markerData->markerStart)
358            m_markerData->markerStart->removeClientFromCache(object, markForInvalidation);
359        if (m_markerData->markerMid)
360            m_markerData->markerMid->removeClientFromCache(object, markForInvalidation);
361        if (m_markerData->markerEnd)
362            m_markerData->markerEnd->removeClientFromCache(object, markForInvalidation);
363    }
364
365    if (m_fillStrokeData) {
366        if (m_fillStrokeData->fill)
367            m_fillStrokeData->fill->removeClientFromCache(object, markForInvalidation);
368        if (m_fillStrokeData->stroke)
369            m_fillStrokeData->stroke->removeClientFromCache(object, markForInvalidation);
370    }
371}
372
373void SVGResources::resourceDestroyed(RenderSVGResourceContainer* resource)
374{
375    ASSERT(resource);
376    if (hasResourceData())
377        return;
378
379    if (m_linkedResource == resource) {
380        ASSERT(!m_clipperFilterMaskerData);
381        ASSERT(!m_markerData);
382        ASSERT(!m_fillStrokeData);
383        m_linkedResource->removeAllClientsFromCache();
384        m_linkedResource = 0;
385        return;
386    }
387
388    switch (resource->resourceType()) {
389    case MaskerResourceType:
390        if (!m_clipperFilterMaskerData)
391            break;
392        if (m_clipperFilterMaskerData->masker == resource) {
393            m_clipperFilterMaskerData->masker->removeAllClientsFromCache();
394            m_clipperFilterMaskerData->masker = 0;
395        }
396        break;
397    case MarkerResourceType:
398        if (!m_markerData)
399            break;
400        if (m_markerData->markerStart == resource) {
401            m_markerData->markerStart->removeAllClientsFromCache();
402            m_markerData->markerStart = 0;
403        }
404        if (m_markerData->markerMid == resource) {
405            m_markerData->markerMid->removeAllClientsFromCache();
406            m_markerData->markerMid = 0;
407        }
408        if (m_markerData->markerEnd == resource) {
409            m_markerData->markerEnd->removeAllClientsFromCache();
410            m_markerData->markerEnd = 0;
411        }
412        break;
413    case PatternResourceType:
414    case LinearGradientResourceType:
415    case RadialGradientResourceType:
416        if (!m_fillStrokeData)
417            break;
418        if (m_fillStrokeData->fill == resource) {
419            m_fillStrokeData->fill->removeAllClientsFromCache();
420            m_fillStrokeData->fill = 0;
421        }
422        if (m_fillStrokeData->stroke == resource) {
423            m_fillStrokeData->stroke->removeAllClientsFromCache();
424            m_fillStrokeData->stroke = 0;
425        }
426        break;
427    case FilterResourceType:
428        if (!m_clipperFilterMaskerData)
429            break;
430        if (m_clipperFilterMaskerData->filter == resource) {
431            m_clipperFilterMaskerData->filter->removeAllClientsFromCache();
432            m_clipperFilterMaskerData->filter = 0;
433        }
434        break;
435    case ClipperResourceType:
436        if (!m_clipperFilterMaskerData)
437            break;
438        if (m_clipperFilterMaskerData->clipper == resource) {
439            m_clipperFilterMaskerData->clipper->removeAllClientsFromCache();
440            m_clipperFilterMaskerData->clipper = 0;
441        }
442        break;
443    case SolidColorResourceType:
444        ASSERT_NOT_REACHED();
445    }
446}
447
448void SVGResources::buildSetOfResources(HashSet<RenderSVGResourceContainer*>& set)
449{
450    if (hasResourceData())
451        return;
452
453    if (m_linkedResource) {
454        ASSERT(!m_clipperFilterMaskerData);
455        ASSERT(!m_markerData);
456        ASSERT(!m_fillStrokeData);
457        set.add(m_linkedResource);
458        return;
459    }
460
461    if (m_clipperFilterMaskerData) {
462        if (m_clipperFilterMaskerData->clipper)
463            set.add(m_clipperFilterMaskerData->clipper);
464        if (m_clipperFilterMaskerData->filter)
465            set.add(m_clipperFilterMaskerData->filter);
466        if (m_clipperFilterMaskerData->masker)
467            set.add(m_clipperFilterMaskerData->masker);
468    }
469
470    if (m_markerData) {
471        if (m_markerData->markerStart)
472            set.add(m_markerData->markerStart);
473        if (m_markerData->markerMid)
474            set.add(m_markerData->markerMid);
475        if (m_markerData->markerEnd)
476            set.add(m_markerData->markerEnd);
477    }
478
479    if (m_fillStrokeData) {
480        if (m_fillStrokeData->fill)
481            set.add(m_fillStrokeData->fill);
482        if (m_fillStrokeData->stroke)
483            set.add(m_fillStrokeData->stroke);
484    }
485}
486
487bool SVGResources::setClipper(RenderSVGResourceClipper* clipper)
488{
489    if (!clipper)
490        return false;
491
492    ASSERT(clipper->resourceType() == ClipperResourceType);
493
494    if (!m_clipperFilterMaskerData)
495        m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
496
497    m_clipperFilterMaskerData->clipper = clipper;
498    return true;
499}
500
501void SVGResources::resetClipper()
502{
503    ASSERT(m_clipperFilterMaskerData);
504    ASSERT(m_clipperFilterMaskerData->clipper);
505    m_clipperFilterMaskerData->clipper = 0;
506}
507
508bool SVGResources::setFilter(RenderSVGResourceFilter* filter)
509{
510    if (!filter)
511        return false;
512
513    ASSERT(filter->resourceType() == FilterResourceType);
514
515    if (!m_clipperFilterMaskerData)
516        m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
517
518    m_clipperFilterMaskerData->filter = filter;
519    return true;
520}
521
522void SVGResources::resetFilter()
523{
524    ASSERT(m_clipperFilterMaskerData);
525    ASSERT(m_clipperFilterMaskerData->filter);
526    m_clipperFilterMaskerData->filter = 0;
527}
528
529bool SVGResources::setMarkerStart(RenderSVGResourceMarker* markerStart)
530{
531    if (!markerStart)
532        return false;
533
534    ASSERT(markerStart->resourceType() == MarkerResourceType);
535
536    if (!m_markerData)
537        m_markerData = MarkerData::create();
538
539    m_markerData->markerStart = markerStart;
540    return true;
541}
542
543void SVGResources::resetMarkerStart()
544{
545    ASSERT(m_markerData);
546    ASSERT(m_markerData->markerStart);
547    m_markerData->markerStart = 0;
548}
549
550bool SVGResources::setMarkerMid(RenderSVGResourceMarker* markerMid)
551{
552    if (!markerMid)
553        return false;
554
555    ASSERT(markerMid->resourceType() == MarkerResourceType);
556
557    if (!m_markerData)
558        m_markerData = MarkerData::create();
559
560    m_markerData->markerMid = markerMid;
561    return true;
562}
563
564void SVGResources::resetMarkerMid()
565{
566    ASSERT(m_markerData);
567    ASSERT(m_markerData->markerMid);
568    m_markerData->markerMid = 0;
569}
570
571bool SVGResources::setMarkerEnd(RenderSVGResourceMarker* markerEnd)
572{
573    if (!markerEnd)
574        return false;
575
576    ASSERT(markerEnd->resourceType() == MarkerResourceType);
577
578    if (!m_markerData)
579        m_markerData = MarkerData::create();
580
581    m_markerData->markerEnd = markerEnd;
582    return true;
583}
584
585void SVGResources::resetMarkerEnd()
586{
587    ASSERT(m_markerData);
588    ASSERT(m_markerData->markerEnd);
589    m_markerData->markerEnd = 0;
590}
591
592bool SVGResources::setMasker(RenderSVGResourceMasker* masker)
593{
594    if (!masker)
595        return false;
596
597    ASSERT(masker->resourceType() == MaskerResourceType);
598
599    if (!m_clipperFilterMaskerData)
600        m_clipperFilterMaskerData = ClipperFilterMaskerData::create();
601
602    m_clipperFilterMaskerData->masker = masker;
603    return true;
604}
605
606void SVGResources::resetMasker()
607{
608    ASSERT(m_clipperFilterMaskerData);
609    ASSERT(m_clipperFilterMaskerData->masker);
610    m_clipperFilterMaskerData->masker = 0;
611}
612
613bool SVGResources::setFill(RenderSVGResourceContainer* fill)
614{
615    if (!fill)
616        return false;
617
618    ASSERT(fill->resourceType() == PatternResourceType
619           || fill->resourceType() == LinearGradientResourceType
620           || fill->resourceType() == RadialGradientResourceType);
621
622    if (!m_fillStrokeData)
623        m_fillStrokeData = FillStrokeData::create();
624
625    m_fillStrokeData->fill = fill;
626    return true;
627}
628
629void SVGResources::resetFill()
630{
631    ASSERT(m_fillStrokeData);
632    ASSERT(m_fillStrokeData->fill);
633    m_fillStrokeData->fill = 0;
634}
635
636bool SVGResources::setStroke(RenderSVGResourceContainer* stroke)
637{
638    if (!stroke)
639        return false;
640
641    ASSERT(stroke->resourceType() == PatternResourceType
642           || stroke->resourceType() == LinearGradientResourceType
643           || stroke->resourceType() == RadialGradientResourceType);
644
645    if (!m_fillStrokeData)
646        m_fillStrokeData = FillStrokeData::create();
647
648    m_fillStrokeData->stroke = stroke;
649    return true;
650}
651
652void SVGResources::resetStroke()
653{
654    ASSERT(m_fillStrokeData);
655    ASSERT(m_fillStrokeData->stroke);
656    m_fillStrokeData->stroke = 0;
657}
658
659bool SVGResources::setLinkedResource(RenderSVGResourceContainer* linkedResource)
660{
661    if (!linkedResource)
662        return false;
663
664    m_linkedResource = linkedResource;
665    return true;
666}
667
668void SVGResources::resetLinkedResource()
669{
670    ASSERT(m_linkedResource);
671    m_linkedResource = 0;
672}
673
674#ifndef NDEBUG
675void SVGResources::dump(const RenderObject* object)
676{
677    ASSERT(object);
678    ASSERT(object->node());
679
680    fprintf(stderr, "-> this=%p, SVGResources(renderer=%p, node=%p)\n", this, object, object->node());
681    fprintf(stderr, " | DOM Tree:\n");
682    object->node()->showTreeForThis();
683
684    fprintf(stderr, "\n | List of resources:\n");
685    if (m_clipperFilterMaskerData) {
686        if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
687            fprintf(stderr, " |-> Clipper    : %p (node=%p)\n", clipper, clipper->element());
688        if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
689            fprintf(stderr, " |-> Filter     : %p (node=%p)\n", filter, filter->element());
690        if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
691            fprintf(stderr, " |-> Masker     : %p (node=%p)\n", masker, masker->element());
692    }
693
694    if (m_markerData) {
695        if (RenderSVGResourceMarker* markerStart = m_markerData->markerStart)
696            fprintf(stderr, " |-> MarkerStart: %p (node=%p)\n", markerStart, markerStart->element());
697        if (RenderSVGResourceMarker* markerMid = m_markerData->markerMid)
698            fprintf(stderr, " |-> MarkerMid  : %p (node=%p)\n", markerMid, markerMid->element());
699        if (RenderSVGResourceMarker* markerEnd = m_markerData->markerEnd)
700            fprintf(stderr, " |-> MarkerEnd  : %p (node=%p)\n", markerEnd, markerEnd->element());
701    }
702
703    if (m_fillStrokeData) {
704        if (RenderSVGResourceContainer* fill = m_fillStrokeData->fill)
705            fprintf(stderr, " |-> Fill       : %p (node=%p)\n", fill, fill->element());
706        if (RenderSVGResourceContainer* stroke = m_fillStrokeData->stroke)
707            fprintf(stderr, " |-> Stroke     : %p (node=%p)\n", stroke, stroke->element());
708    }
709
710    if (m_linkedResource)
711        fprintf(stderr, " |-> xlink:href : %p (node=%p)\n", m_linkedResource, m_linkedResource->element());
712}
713#endif
714
715}
716