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