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
38namespace WebCore {
39
40SVGResources::SVGResources()
41    : m_linkedResource(0)
42{
43}
44
45static 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
94static 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
107static 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
128static 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
141static inline String targetReferenceFromResource(SVGElement& element)
142{
143    String target;
144    if (isSVGPatternElement(element))
145        target = toSVGPatternElement(element).href();
146    else if (isSVGGradientElement(element))
147        target = toSVGGradientElement(element).href();
148#if ENABLE(FILTERS)
149    else if (isSVGFilterElement(element))
150        target = toSVGFilterElement(element).href();
151#endif
152    else
153        ASSERT_NOT_REACHED();
154
155    return SVGURIReference::fragmentIdentifierFromIRIString(target, element.document());
156}
157
158static inline RenderSVGResourceContainer* paintingResourceFromSVGPaint(Document& document, const SVGPaint::SVGPaintType& paintType, const String& paintUri, AtomicString& id, bool& hasPendingResource)
159{
160    if (paintType != SVGPaint::SVG_PAINTTYPE_URI && paintType != SVGPaint::SVG_PAINTTYPE_URI_RGBCOLOR)
161        return 0;
162
163    id = SVGURIReference::fragmentIdentifierFromIRIString(paintUri, document);
164    RenderSVGResourceContainer* container = getRenderSVGResourceContainerById(document, id);
165    if (!container) {
166        hasPendingResource = true;
167        return 0;
168    }
169
170    RenderSVGResourceType resourceType = container->resourceType();
171    if (resourceType != PatternResourceType && resourceType != LinearGradientResourceType && resourceType != RadialGradientResourceType)
172        return 0;
173
174    return container;
175}
176
177static inline void registerPendingResource(SVGDocumentExtensions* extensions, const AtomicString& id, SVGElement& element)
178{
179    extensions->addPendingResource(id, &element);
180}
181
182bool SVGResources::buildCachedResources(const RenderElement& renderer, const SVGRenderStyle& svgStyle)
183{
184    ASSERT(renderer.element());
185    ASSERT_WITH_SECURITY_IMPLICATION(renderer.element()->isSVGElement());
186
187    if (!renderer.element())
188        return false;
189
190    auto& element = toSVGElement(*renderer.element());
191
192    Document& document = element.document();
193
194    SVGDocumentExtensions* extensions = document.accessSVGExtensions();
195    ASSERT(extensions);
196
197    const AtomicString& tagName = element.localName();
198    if (tagName.isNull())
199        return false;
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 ENABLE(FILTERS)
212        if (svgStyle.hasFilter()) {
213            AtomicString id(svgStyle.filterResource());
214            if (setFilter(getRenderSVGResourceById<RenderSVGResourceFilter>(document, id)))
215                foundResources = true;
216            else
217                registerPendingResource(extensions, id, element);
218        }
219#endif
220
221        if (svgStyle.hasMasker()) {
222            AtomicString id(svgStyle.maskerResource());
223            if (setMasker(getRenderSVGResourceById<RenderSVGResourceMasker>(document, id)))
224                foundResources = true;
225            else
226                registerPendingResource(extensions, id, element);
227        }
228    }
229
230    if (markerTags().contains(tagName) && svgStyle.hasMarkers()) {
231        AtomicString markerStartId(svgStyle.markerStartResource());
232        if (setMarkerStart(getRenderSVGResourceById<RenderSVGResourceMarker>(document, markerStartId)))
233            foundResources = true;
234        else
235            registerPendingResource(extensions, markerStartId, element);
236
237        AtomicString markerMidId(svgStyle.markerMidResource());
238        if (setMarkerMid(getRenderSVGResourceById<RenderSVGResourceMarker>(document, markerMidId)))
239            foundResources = true;
240        else
241            registerPendingResource(extensions, markerMidId, element);
242
243        AtomicString markerEndId(svgStyle.markerEndResource());
244        if (setMarkerEnd(getRenderSVGResourceById<RenderSVGResourceMarker>(document, markerEndId)))
245            foundResources = true;
246        else
247            registerPendingResource(extensions, markerEndId, element);
248    }
249
250    if (fillAndStrokeTags().contains(tagName)) {
251        if (svgStyle.hasFill()) {
252            bool hasPendingResource = false;
253            AtomicString id;
254            if (setFill(paintingResourceFromSVGPaint(document, svgStyle.fillPaintType(), svgStyle.fillPaintUri(), id, hasPendingResource)))
255                foundResources = true;
256            else if (hasPendingResource)
257                registerPendingResource(extensions, id, element);
258        }
259
260        if (svgStyle.hasStroke()) {
261            bool hasPendingResource = false;
262            AtomicString id;
263            if (setStroke(paintingResourceFromSVGPaint(document, svgStyle.strokePaintType(), svgStyle.strokePaintUri(), id, hasPendingResource)))
264                foundResources = true;
265            else if (hasPendingResource)
266                registerPendingResource(extensions, id, element);
267        }
268    }
269
270    if (chainableResourceTags().contains(tagName)) {
271        AtomicString id(targetReferenceFromResource(element));
272        if (setLinkedResource(getRenderSVGResourceContainerById(document, id)))
273            foundResources = true;
274        else
275            registerPendingResource(extensions, id, element);
276    }
277
278    return foundResources;
279}
280
281void SVGResources::removeClientFromCache(RenderElement& renderer, bool markForInvalidation) const
282{
283    if (!m_clipperFilterMaskerData && !m_markerData && !m_fillStrokeData && !m_linkedResource)
284        return;
285
286    if (m_linkedResource) {
287        ASSERT(!m_clipperFilterMaskerData);
288        ASSERT(!m_markerData);
289        ASSERT(!m_fillStrokeData);
290        m_linkedResource->removeClientFromCache(renderer, markForInvalidation);
291        return;
292    }
293
294    if (m_clipperFilterMaskerData) {
295        if (m_clipperFilterMaskerData->clipper)
296            m_clipperFilterMaskerData->clipper->removeClientFromCache(renderer, markForInvalidation);
297#if ENABLE(FILTERS)
298        if (m_clipperFilterMaskerData->filter)
299            m_clipperFilterMaskerData->filter->removeClientFromCache(renderer, markForInvalidation);
300#endif
301        if (m_clipperFilterMaskerData->masker)
302            m_clipperFilterMaskerData->masker->removeClientFromCache(renderer, markForInvalidation);
303    }
304
305    if (m_markerData) {
306        if (m_markerData->markerStart)
307            m_markerData->markerStart->removeClientFromCache(renderer, markForInvalidation);
308        if (m_markerData->markerMid)
309            m_markerData->markerMid->removeClientFromCache(renderer, markForInvalidation);
310        if (m_markerData->markerEnd)
311            m_markerData->markerEnd->removeClientFromCache(renderer, markForInvalidation);
312    }
313
314    if (m_fillStrokeData) {
315        if (m_fillStrokeData->fill)
316            m_fillStrokeData->fill->removeClientFromCache(renderer, markForInvalidation);
317        if (m_fillStrokeData->stroke)
318            m_fillStrokeData->stroke->removeClientFromCache(renderer, markForInvalidation);
319    }
320}
321
322void SVGResources::resourceDestroyed(RenderSVGResourceContainer& resource)
323{
324    if (!m_clipperFilterMaskerData && !m_markerData && !m_fillStrokeData && !m_linkedResource)
325        return;
326
327    if (m_linkedResource == &resource) {
328        ASSERT(!m_clipperFilterMaskerData);
329        ASSERT(!m_markerData);
330        ASSERT(!m_fillStrokeData);
331        m_linkedResource->removeAllClientsFromCache();
332        m_linkedResource = 0;
333        return;
334    }
335
336    switch (resource.resourceType()) {
337    case MaskerResourceType:
338        if (!m_clipperFilterMaskerData)
339            break;
340        if (m_clipperFilterMaskerData->masker == &resource) {
341            m_clipperFilterMaskerData->masker->removeAllClientsFromCache();
342            m_clipperFilterMaskerData->masker = 0;
343        }
344        break;
345    case MarkerResourceType:
346        if (!m_markerData)
347            break;
348        if (m_markerData->markerStart == &resource) {
349            m_markerData->markerStart->removeAllClientsFromCache();
350            m_markerData->markerStart = 0;
351        }
352        if (m_markerData->markerMid == &resource) {
353            m_markerData->markerMid->removeAllClientsFromCache();
354            m_markerData->markerMid = 0;
355        }
356        if (m_markerData->markerEnd == &resource) {
357            m_markerData->markerEnd->removeAllClientsFromCache();
358            m_markerData->markerEnd = 0;
359        }
360        break;
361    case PatternResourceType:
362    case LinearGradientResourceType:
363    case RadialGradientResourceType:
364        if (!m_fillStrokeData)
365            break;
366        if (m_fillStrokeData->fill == &resource) {
367            m_fillStrokeData->fill->removeAllClientsFromCache();
368            m_fillStrokeData->fill = 0;
369        }
370        if (m_fillStrokeData->stroke == &resource) {
371            m_fillStrokeData->stroke->removeAllClientsFromCache();
372            m_fillStrokeData->stroke = 0;
373        }
374        break;
375    case FilterResourceType:
376#if ENABLE(FILTERS)
377        if (!m_clipperFilterMaskerData)
378            break;
379        if (m_clipperFilterMaskerData->filter == &resource) {
380            m_clipperFilterMaskerData->filter->removeAllClientsFromCache();
381            m_clipperFilterMaskerData->filter = 0;
382        }
383#else
384        ASSERT_NOT_REACHED();
385#endif
386        break;
387    case ClipperResourceType:
388        if (!m_clipperFilterMaskerData)
389            break;
390        if (m_clipperFilterMaskerData->clipper == &resource) {
391            m_clipperFilterMaskerData->clipper->removeAllClientsFromCache();
392            m_clipperFilterMaskerData->clipper = 0;
393        }
394        break;
395    case SolidColorResourceType:
396        ASSERT_NOT_REACHED();
397    }
398}
399
400void SVGResources::buildSetOfResources(HashSet<RenderSVGResourceContainer*>& set)
401{
402    if (!m_clipperFilterMaskerData && !m_markerData && !m_fillStrokeData && !m_linkedResource)
403        return;
404
405    if (m_linkedResource) {
406        ASSERT(!m_clipperFilterMaskerData);
407        ASSERT(!m_markerData);
408        ASSERT(!m_fillStrokeData);
409        set.add(m_linkedResource);
410        return;
411    }
412
413    if (m_clipperFilterMaskerData) {
414        if (m_clipperFilterMaskerData->clipper)
415            set.add(m_clipperFilterMaskerData->clipper);
416#if ENABLE(FILTERS)
417        if (m_clipperFilterMaskerData->filter)
418            set.add(m_clipperFilterMaskerData->filter);
419#endif
420        if (m_clipperFilterMaskerData->masker)
421            set.add(m_clipperFilterMaskerData->masker);
422    }
423
424    if (m_markerData) {
425        if (m_markerData->markerStart)
426            set.add(m_markerData->markerStart);
427        if (m_markerData->markerMid)
428            set.add(m_markerData->markerMid);
429        if (m_markerData->markerEnd)
430            set.add(m_markerData->markerEnd);
431    }
432
433    if (m_fillStrokeData) {
434        if (m_fillStrokeData->fill)
435            set.add(m_fillStrokeData->fill);
436        if (m_fillStrokeData->stroke)
437            set.add(m_fillStrokeData->stroke);
438    }
439}
440
441bool SVGResources::setClipper(RenderSVGResourceClipper* clipper)
442{
443    if (!clipper)
444        return false;
445
446    ASSERT(clipper->resourceType() == ClipperResourceType);
447
448    if (!m_clipperFilterMaskerData)
449        m_clipperFilterMaskerData = std::make_unique<ClipperFilterMaskerData>();
450
451    m_clipperFilterMaskerData->clipper = clipper;
452    return true;
453}
454
455void SVGResources::resetClipper()
456{
457    ASSERT(m_clipperFilterMaskerData);
458    ASSERT(m_clipperFilterMaskerData->clipper);
459    m_clipperFilterMaskerData->clipper = 0;
460}
461
462#if ENABLE(FILTERS)
463bool SVGResources::setFilter(RenderSVGResourceFilter* filter)
464{
465    if (!filter)
466        return false;
467
468    ASSERT(filter->resourceType() == FilterResourceType);
469
470    if (!m_clipperFilterMaskerData)
471        m_clipperFilterMaskerData = std::make_unique<ClipperFilterMaskerData>();
472
473    m_clipperFilterMaskerData->filter = filter;
474    return true;
475}
476
477void SVGResources::resetFilter()
478{
479    ASSERT(m_clipperFilterMaskerData);
480    ASSERT(m_clipperFilterMaskerData->filter);
481    m_clipperFilterMaskerData->filter = 0;
482}
483#endif
484
485bool SVGResources::setMarkerStart(RenderSVGResourceMarker* markerStart)
486{
487    if (!markerStart)
488        return false;
489
490    ASSERT(markerStart->resourceType() == MarkerResourceType);
491
492    if (!m_markerData)
493        m_markerData = std::make_unique<MarkerData>();
494
495    m_markerData->markerStart = markerStart;
496    return true;
497}
498
499void SVGResources::resetMarkerStart()
500{
501    ASSERT(m_markerData);
502    ASSERT(m_markerData->markerStart);
503    m_markerData->markerStart = 0;
504}
505
506bool SVGResources::setMarkerMid(RenderSVGResourceMarker* markerMid)
507{
508    if (!markerMid)
509        return false;
510
511    ASSERT(markerMid->resourceType() == MarkerResourceType);
512
513    if (!m_markerData)
514        m_markerData = std::make_unique<MarkerData>();
515
516    m_markerData->markerMid = markerMid;
517    return true;
518}
519
520void SVGResources::resetMarkerMid()
521{
522    ASSERT(m_markerData);
523    ASSERT(m_markerData->markerMid);
524    m_markerData->markerMid = 0;
525}
526
527bool SVGResources::setMarkerEnd(RenderSVGResourceMarker* markerEnd)
528{
529    if (!markerEnd)
530        return false;
531
532    ASSERT(markerEnd->resourceType() == MarkerResourceType);
533
534    if (!m_markerData)
535        m_markerData = std::make_unique<MarkerData>();
536
537    m_markerData->markerEnd = markerEnd;
538    return true;
539}
540
541void SVGResources::resetMarkerEnd()
542{
543    ASSERT(m_markerData);
544    ASSERT(m_markerData->markerEnd);
545    m_markerData->markerEnd = 0;
546}
547
548bool SVGResources::setMasker(RenderSVGResourceMasker* masker)
549{
550    if (!masker)
551        return false;
552
553    ASSERT(masker->resourceType() == MaskerResourceType);
554
555    if (!m_clipperFilterMaskerData)
556        m_clipperFilterMaskerData = std::make_unique<ClipperFilterMaskerData>();
557
558    m_clipperFilterMaskerData->masker = masker;
559    return true;
560}
561
562void SVGResources::resetMasker()
563{
564    ASSERT(m_clipperFilterMaskerData);
565    ASSERT(m_clipperFilterMaskerData->masker);
566    m_clipperFilterMaskerData->masker = 0;
567}
568
569bool SVGResources::setFill(RenderSVGResourceContainer* fill)
570{
571    if (!fill)
572        return false;
573
574    ASSERT(fill->resourceType() == PatternResourceType
575           || fill->resourceType() == LinearGradientResourceType
576           || fill->resourceType() == RadialGradientResourceType);
577
578    if (!m_fillStrokeData)
579        m_fillStrokeData = std::make_unique<FillStrokeData>();
580
581    m_fillStrokeData->fill = fill;
582    return true;
583}
584
585void SVGResources::resetFill()
586{
587    ASSERT(m_fillStrokeData);
588    ASSERT(m_fillStrokeData->fill);
589    m_fillStrokeData->fill = 0;
590}
591
592bool SVGResources::setStroke(RenderSVGResourceContainer* stroke)
593{
594    if (!stroke)
595        return false;
596
597    ASSERT(stroke->resourceType() == PatternResourceType
598           || stroke->resourceType() == LinearGradientResourceType
599           || stroke->resourceType() == RadialGradientResourceType);
600
601    if (!m_fillStrokeData)
602        m_fillStrokeData = std::make_unique<FillStrokeData>();
603
604    m_fillStrokeData->stroke = stroke;
605    return true;
606}
607
608void SVGResources::resetStroke()
609{
610    ASSERT(m_fillStrokeData);
611    ASSERT(m_fillStrokeData->stroke);
612    m_fillStrokeData->stroke = 0;
613}
614
615bool SVGResources::setLinkedResource(RenderSVGResourceContainer* linkedResource)
616{
617    if (!linkedResource)
618        return false;
619
620    m_linkedResource = linkedResource;
621    return true;
622}
623
624void SVGResources::resetLinkedResource()
625{
626    ASSERT(m_linkedResource);
627    m_linkedResource = 0;
628}
629
630#ifndef NDEBUG
631void SVGResources::dump(const RenderObject* object)
632{
633    ASSERT(object);
634    ASSERT(object->node());
635
636    fprintf(stderr, "-> this=%p, SVGResources(renderer=%p, node=%p)\n", this, object, object->node());
637    fprintf(stderr, " | DOM Tree:\n");
638    object->node()->showTreeForThis();
639
640    fprintf(stderr, "\n | List of resources:\n");
641    if (m_clipperFilterMaskerData) {
642        if (RenderSVGResourceClipper* clipper = m_clipperFilterMaskerData->clipper)
643            fprintf(stderr, " |-> Clipper    : %p (node=%p)\n", clipper, &clipper->clipPathElement());
644#if ENABLE(FILTERS)
645        if (RenderSVGResourceFilter* filter = m_clipperFilterMaskerData->filter)
646            fprintf(stderr, " |-> Filter     : %p (node=%p)\n", filter, &filter->filterElement());
647#endif
648        if (RenderSVGResourceMasker* masker = m_clipperFilterMaskerData->masker)
649            fprintf(stderr, " |-> Masker     : %p (node=%p)\n", masker, &masker->maskElement());
650    }
651
652    if (m_markerData) {
653        if (RenderSVGResourceMarker* markerStart = m_markerData->markerStart)
654            fprintf(stderr, " |-> MarkerStart: %p (node=%p)\n", markerStart, &markerStart->markerElement());
655        if (RenderSVGResourceMarker* markerMid = m_markerData->markerMid)
656            fprintf(stderr, " |-> MarkerMid  : %p (node=%p)\n", markerMid, &markerMid->markerElement());
657        if (RenderSVGResourceMarker* markerEnd = m_markerData->markerEnd)
658            fprintf(stderr, " |-> MarkerEnd  : %p (node=%p)\n", markerEnd, &markerEnd->markerElement());
659    }
660
661    if (m_fillStrokeData) {
662        if (RenderSVGResourceContainer* fill = m_fillStrokeData->fill)
663            fprintf(stderr, " |-> Fill       : %p (node=%p)\n", fill, &fill->element());
664        if (RenderSVGResourceContainer* stroke = m_fillStrokeData->stroke)
665            fprintf(stderr, " |-> Stroke     : %p (node=%p)\n", stroke, &stroke->element());
666    }
667
668    if (m_linkedResource)
669        fprintf(stderr, " |-> xlink:href : %p (node=%p)\n", m_linkedResource, &m_linkedResource->element());
670}
671#endif
672
673}
674