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