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