Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / rendering / svg / SVGRenderTreeAsText.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2007, 2009 Apple Inc. All rights reserved.
3  *           (C) 2005 Rob Buis <buis@kde.org>
4  *           (C) 2006 Alexander Kellett <lypanov@kde.org>
5  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30
31 #include "core/rendering/svg/SVGRenderTreeAsText.h"
32
33 #include "SVGNames.h"
34 #include "core/rendering/InlineTextBox.h"
35 #include "core/rendering/RenderTreeAsText.h"
36 #include "core/rendering/svg/RenderSVGGradientStop.h"
37 #include "core/rendering/svg/RenderSVGImage.h"
38 #include "core/rendering/svg/RenderSVGInlineText.h"
39 #include "core/rendering/svg/RenderSVGResourceClipper.h"
40 #include "core/rendering/svg/RenderSVGResourceFilter.h"
41 #include "core/rendering/svg/RenderSVGResourceLinearGradient.h"
42 #include "core/rendering/svg/RenderSVGResourceMarker.h"
43 #include "core/rendering/svg/RenderSVGResourceMasker.h"
44 #include "core/rendering/svg/RenderSVGResourcePattern.h"
45 #include "core/rendering/svg/RenderSVGResourceRadialGradient.h"
46 #include "core/rendering/svg/RenderSVGResourceSolidColor.h"
47 #include "core/rendering/svg/RenderSVGRoot.h"
48 #include "core/rendering/svg/RenderSVGShape.h"
49 #include "core/rendering/svg/RenderSVGText.h"
50 #include "core/rendering/svg/SVGInlineTextBox.h"
51 #include "core/rendering/svg/SVGRootInlineBox.h"
52 #include "core/svg/LinearGradientAttributes.h"
53 #include "core/svg/PatternAttributes.h"
54 #include "core/svg/RadialGradientAttributes.h"
55 #include "core/svg/SVGCircleElement.h"
56 #include "core/svg/SVGEllipseElement.h"
57 #include "core/svg/SVGLineElement.h"
58 #include "core/svg/SVGLinearGradientElement.h"
59 #include "core/svg/SVGPathElement.h"
60 #include "core/svg/SVGPathUtilities.h"
61 #include "core/svg/SVGPatternElement.h"
62 #include "core/svg/SVGPointList.h"
63 #include "core/svg/SVGPolyElement.h"
64 #include "core/svg/SVGRadialGradientElement.h"
65 #include "core/svg/SVGRectElement.h"
66 #include "core/svg/SVGStopElement.h"
67 #include "platform/graphics/GraphicsTypes.h"
68
69 #include <math.h>
70 #include <memory>
71
72 namespace WebCore {
73
74 /** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d"
75  * Can be used in cases where you don't know which item in the list is the first
76  * one to be printed, but still want to avoid strings like ", b, c".
77  */
78 class TextStreamSeparator {
79 public:
80     TextStreamSeparator(const String& s)
81         : m_separator(s)
82         , m_needToSeparate(false)
83     {
84     }
85
86 private:
87     friend TextStream& operator<<(TextStream&, TextStreamSeparator&);
88
89     String m_separator;
90     bool m_needToSeparate;
91 };
92
93 TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep)
94 {
95     if (sep.m_needToSeparate)
96         ts << sep.m_separator;
97     else
98         sep.m_needToSeparate = true;
99     return ts;
100 }
101
102 template<typename ValueType>
103 static void writeNameValuePair(TextStream& ts, const char* name, ValueType value)
104 {
105     ts << " [" << name << "=" << value << "]";
106 }
107
108 template<typename ValueType>
109 static void writeNameAndQuotedValue(TextStream& ts, const char* name, ValueType value)
110 {
111     ts << " [" << name << "=\"" << value << "\"]";
112 }
113
114 static void writeIfNotEmpty(TextStream& ts, const char* name, const String& value)
115 {
116     if (!value.isEmpty())
117         writeNameValuePair(ts, name, value);
118 }
119
120 template<typename ValueType>
121 static void writeIfNotDefault(TextStream& ts, const char* name, ValueType value, ValueType defaultValue)
122 {
123     if (value != defaultValue)
124         writeNameValuePair(ts, name, value);
125 }
126
127 TextStream& operator<<(TextStream& ts, const AffineTransform& transform)
128 {
129     if (transform.isIdentity())
130         ts << "identity";
131     else
132         ts << "{m=(("
133            << transform.a() << "," << transform.b()
134            << ")("
135            << transform.c() << "," << transform.d()
136            << ")) t=("
137            << transform.e() << "," << transform.f()
138            << ")}";
139
140     return ts;
141 }
142
143 static TextStream& operator<<(TextStream& ts, const WindRule rule)
144 {
145     switch (rule) {
146     case RULE_NONZERO:
147         ts << "NON-ZERO";
148         break;
149     case RULE_EVENODD:
150         ts << "EVEN-ODD";
151         break;
152     }
153
154     return ts;
155 }
156
157 static TextStream& operator<<(TextStream& ts, const SVGUnitTypes::SVGUnitType& unitType)
158 {
159     ts << SVGPropertyTraits<SVGUnitTypes::SVGUnitType>::toString(unitType);
160     return ts;
161 }
162
163 static TextStream& operator<<(TextStream& ts, const SVGMarkerUnitsType& markerUnit)
164 {
165     ts << SVGPropertyTraits<SVGMarkerUnitsType>::toString(markerUnit);
166     return ts;
167 }
168
169 TextStream& operator<<(TextStream& ts, const Color& c)
170 {
171     return ts << c.nameForRenderTreeAsText();
172 }
173
174 // FIXME: Maybe this should be in KCanvasRenderingStyle.cpp
175 static TextStream& operator<<(TextStream& ts, const DashArray& a)
176 {
177     ts << "{";
178     DashArray::const_iterator end = a.end();
179     for (DashArray::const_iterator it = a.begin(); it != end; ++it) {
180         if (it != a.begin())
181             ts << ", ";
182         ts << *it;
183     }
184     ts << "}";
185     return ts;
186 }
187
188 // FIXME: Maybe this should be in GraphicsTypes.cpp
189 static TextStream& operator<<(TextStream& ts, LineCap style)
190 {
191     switch (style) {
192     case ButtCap:
193         ts << "BUTT";
194         break;
195     case RoundCap:
196         ts << "ROUND";
197         break;
198     case SquareCap:
199         ts << "SQUARE";
200         break;
201     }
202     return ts;
203 }
204
205 // FIXME: Maybe this should be in GraphicsTypes.cpp
206 static TextStream& operator<<(TextStream& ts, LineJoin style)
207 {
208     switch (style) {
209     case MiterJoin:
210         ts << "MITER";
211         break;
212     case RoundJoin:
213         ts << "ROUND";
214         break;
215     case BevelJoin:
216         ts << "BEVEL";
217         break;
218     }
219     return ts;
220 }
221
222 static TextStream& operator<<(TextStream& ts, const SVGSpreadMethodType& type)
223 {
224     ts << SVGPropertyTraits<SVGSpreadMethodType>::toString(type).upper();
225     return ts;
226 }
227
228 static void writeSVGPaintingResource(TextStream& ts, RenderSVGResource* resource)
229 {
230     if (resource->resourceType() == SolidColorResourceType) {
231         ts << "[type=SOLID] [color=" << static_cast<RenderSVGResourceSolidColor*>(resource)->color() << "]";
232         return;
233     }
234
235     // All other resources derive from RenderSVGResourceContainer
236     RenderSVGResourceContainer* container = static_cast<RenderSVGResourceContainer*>(resource);
237     SVGElement* element = container->element();
238     ASSERT(element);
239
240     if (resource->resourceType() == PatternResourceType)
241         ts << "[type=PATTERN]";
242     else if (resource->resourceType() == LinearGradientResourceType)
243         ts << "[type=LINEAR-GRADIENT]";
244     else if (resource->resourceType() == RadialGradientResourceType)
245         ts << "[type=RADIAL-GRADIENT]";
246
247     ts << " [id=\"" << element->getIdAttribute() << "\"]";
248 }
249
250 static void writeStyle(TextStream& ts, const RenderObject& object)
251 {
252     const RenderStyle* style = object.style();
253     const SVGRenderStyle* svgStyle = style->svgStyle();
254
255     if (!object.localTransform().isIdentity())
256         writeNameValuePair(ts, "transform", object.localTransform());
257     writeIfNotDefault(ts, "image rendering", style->imageRendering(), RenderStyle::initialImageRendering());
258     writeIfNotDefault(ts, "opacity", style->opacity(), RenderStyle::initialOpacity());
259     if (object.isSVGShape()) {
260         const RenderSVGShape& shape = static_cast<const RenderSVGShape&>(object);
261         ASSERT(shape.element());
262
263         bool hasFallback;
264         if (RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), hasFallback)) {
265             TextStreamSeparator s(" ");
266             ts << " [stroke={" << s;
267             writeSVGPaintingResource(ts, strokePaintingResource);
268
269             SVGLengthContext lengthContext(shape.element());
270             double dashOffset = svgStyle->strokeDashOffset()->value(lengthContext);
271             double strokeWidth = svgStyle->strokeWidth()->value(lengthContext);
272             RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray();
273
274             DashArray dashArray;
275             SVGLengthList::ConstIterator it = dashes->begin();
276             SVGLengthList::ConstIterator itEnd = dashes->end();
277             for (; it != itEnd; ++it)
278                 dashArray.append(it->value(lengthContext));
279
280             writeIfNotDefault(ts, "opacity", svgStyle->strokeOpacity(), 1.0f);
281             writeIfNotDefault(ts, "stroke width", strokeWidth, 1.0);
282             writeIfNotDefault(ts, "miter limit", svgStyle->strokeMiterLimit(), 4.0f);
283             writeIfNotDefault(ts, "line cap", svgStyle->capStyle(), ButtCap);
284             writeIfNotDefault(ts, "line join", svgStyle->joinStyle(), MiterJoin);
285             writeIfNotDefault(ts, "dash offset", dashOffset, 0.0);
286             if (!dashArray.isEmpty())
287                 writeNameValuePair(ts, "dash array", dashArray);
288
289             ts << "}]";
290         }
291
292         if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(const_cast<RenderSVGShape*>(&shape), shape.style(), hasFallback)) {
293             TextStreamSeparator s(" ");
294             ts << " [fill={" << s;
295             writeSVGPaintingResource(ts, fillPaintingResource);
296
297             writeIfNotDefault(ts, "opacity", svgStyle->fillOpacity(), 1.0f);
298             writeIfNotDefault(ts, "fill rule", svgStyle->fillRule(), RULE_NONZERO);
299             ts << "}]";
300         }
301         writeIfNotDefault(ts, "clip rule", svgStyle->clipRule(), RULE_NONZERO);
302     }
303
304     writeIfNotEmpty(ts, "start marker", svgStyle->markerStartResource());
305     writeIfNotEmpty(ts, "middle marker", svgStyle->markerMidResource());
306     writeIfNotEmpty(ts, "end marker", svgStyle->markerEndResource());
307 }
308
309 static TextStream& writePositionAndStyle(TextStream& ts, const RenderObject& object)
310 {
311     ts << " " << enclosingIntRect(const_cast<RenderObject&>(object).absoluteClippedOverflowRect());
312     writeStyle(ts, object);
313     return ts;
314 }
315
316 static TextStream& operator<<(TextStream& ts, const RenderSVGShape& shape)
317 {
318     writePositionAndStyle(ts, shape);
319
320     SVGElement* svgElement = shape.element();
321     SVGLengthContext lengthContext(svgElement);
322
323     if (svgElement->hasTagName(SVGNames::rectTag)) {
324         SVGRectElement* element = toSVGRectElement(svgElement);
325         writeNameValuePair(ts, "x", element->x()->currentValue()->value(lengthContext));
326         writeNameValuePair(ts, "y", element->y()->currentValue()->value(lengthContext));
327         writeNameValuePair(ts, "width", element->width()->currentValue()->value(lengthContext));
328         writeNameValuePair(ts, "height", element->height()->currentValue()->value(lengthContext));
329     } else if (svgElement->hasTagName(SVGNames::lineTag)) {
330         SVGLineElement* element = toSVGLineElement(svgElement);
331         writeNameValuePair(ts, "x1", element->x1()->currentValue()->value(lengthContext));
332         writeNameValuePair(ts, "y1", element->y1()->currentValue()->value(lengthContext));
333         writeNameValuePair(ts, "x2", element->x2()->currentValue()->value(lengthContext));
334         writeNameValuePair(ts, "y2", element->y2()->currentValue()->value(lengthContext));
335     } else if (svgElement->hasTagName(SVGNames::ellipseTag)) {
336         SVGEllipseElement* element = toSVGEllipseElement(svgElement);
337         writeNameValuePair(ts, "cx", element->cx()->currentValue()->value(lengthContext));
338         writeNameValuePair(ts, "cy", element->cy()->currentValue()->value(lengthContext));
339         writeNameValuePair(ts, "rx", element->rx()->currentValue()->value(lengthContext));
340         writeNameValuePair(ts, "ry", element->ry()->currentValue()->value(lengthContext));
341     } else if (svgElement->hasTagName(SVGNames::circleTag)) {
342         SVGCircleElement* element = toSVGCircleElement(svgElement);
343         writeNameValuePair(ts, "cx", element->cx()->currentValue()->value(lengthContext));
344         writeNameValuePair(ts, "cy", element->cy()->currentValue()->value(lengthContext));
345         writeNameValuePair(ts, "r", element->r()->currentValue()->value(lengthContext));
346     } else if (svgElement->hasTagName(SVGNames::polygonTag) || svgElement->hasTagName(SVGNames::polylineTag)) {
347         writeNameAndQuotedValue(ts, "points", toSVGPolyElement(svgElement)->points()->currentValue()->valueAsString());
348     } else if (svgElement->hasTagName(SVGNames::pathTag)) {
349         String pathString;
350         // FIXME: We should switch to UnalteredParsing here - this will affect the path dumping output of dozens of tests.
351         buildStringFromByteStream(toSVGPathElement(svgElement)->pathByteStream(), pathString, NormalizedParsing);
352         writeNameAndQuotedValue(ts, "data", pathString);
353     } else
354         ASSERT_NOT_REACHED();
355     return ts;
356 }
357
358 static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root)
359 {
360     return writePositionAndStyle(ts, root);
361 }
362
363 static void writeRenderSVGTextBox(TextStream& ts, const RenderSVGText& text)
364 {
365     SVGRootInlineBox* box = toSVGRootInlineBox(text.firstRootBox());
366     if (!box)
367         return;
368
369     ts << " " << enclosingIntRect(FloatRect(text.location(), FloatSize(box->logicalWidth(), box->logicalHeight())));
370
371     // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
372     ts << " contains 1 chunk(s)";
373
374     if (text.parent() && (text.parent()->style()->visitedDependentColor(CSSPropertyColor) != text.style()->visitedDependentColor(CSSPropertyColor)))
375         writeNameValuePair(ts, "color", text.resolveColor(CSSPropertyColor).nameForRenderTreeAsText());
376 }
377
378 static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent)
379 {
380     Vector<SVGTextFragment>& fragments = textBox->textFragments();
381     if (fragments.isEmpty())
382         return;
383
384     RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer());
385     ASSERT(textRenderer);
386
387     const SVGRenderStyle* svgStyle = textRenderer->style()->svgStyle();
388     String text = textBox->textRenderer()->text();
389
390     unsigned fragmentsSize = fragments.size();
391     for (unsigned i = 0; i < fragmentsSize; ++i) {
392         SVGTextFragment& fragment = fragments.at(i);
393         writeIndent(ts, indent + 1);
394
395         unsigned startOffset = fragment.characterOffset;
396         unsigned endOffset = fragment.characterOffset + fragment.length;
397
398         // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now.
399         ts << "chunk 1 ";
400         ETextAnchor anchor = svgStyle->textAnchor();
401         bool isVerticalText = svgStyle->isVerticalWritingMode();
402         if (anchor == TA_MIDDLE) {
403             ts << "(middle anchor";
404             if (isVerticalText)
405                 ts << ", vertical";
406             ts << ") ";
407         } else if (anchor == TA_END) {
408             ts << "(end anchor";
409             if (isVerticalText)
410                 ts << ", vertical";
411             ts << ") ";
412         } else if (isVerticalText)
413             ts << "(vertical) ";
414         startOffset -= textBox->start();
415         endOffset -= textBox->start();
416         // </hack>
417
418         ts << "text run " << i + 1 << " at (" << fragment.x << "," << fragment.y << ")";
419         ts << " startOffset " << startOffset << " endOffset " << endOffset;
420         if (isVerticalText)
421             ts << " height " << fragment.height;
422         else
423             ts << " width " << fragment.width;
424
425         if (!textBox->isLeftToRightDirection() || textBox->dirOverride()) {
426             ts << (textBox->isLeftToRightDirection() ? " LTR" : " RTL");
427             if (textBox->dirOverride())
428                 ts << " override";
429         }
430
431         ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.characterOffset, fragment.length)) << "\n";
432     }
433 }
434
435 static inline void writeSVGInlineTextBoxes(TextStream& ts, const RenderText& text, int indent)
436 {
437     for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
438         if (!box->isSVGInlineTextBox())
439             continue;
440
441         writeSVGInlineTextBox(ts, toSVGInlineTextBox(box), indent);
442     }
443 }
444
445 static void writeStandardPrefix(TextStream& ts, const RenderObject& object, int indent)
446 {
447     writeIndent(ts, indent);
448     ts << object.renderName();
449
450     if (object.node())
451         ts << " {" << object.node()->nodeName() << "}";
452 }
453
454 static void writeChildren(TextStream& ts, const RenderObject& object, int indent)
455 {
456     for (RenderObject* child = object.firstChild(); child; child = child->nextSibling())
457         write(ts, *child, indent + 1);
458 }
459
460 static inline void writeCommonGradientProperties(TextStream& ts, SVGSpreadMethodType spreadMethod, const AffineTransform& gradientTransform, SVGUnitTypes::SVGUnitType gradientUnits)
461 {
462     writeNameValuePair(ts, "gradientUnits", gradientUnits);
463
464     if (spreadMethod != SVGSpreadMethodPad)
465         ts << " [spreadMethod=" << spreadMethod << "]";
466
467     if (!gradientTransform.isIdentity())
468         ts << " [gradientTransform=" << gradientTransform << "]";
469 }
470
471 void writeSVGResourceContainer(TextStream& ts, const RenderObject& object, int indent)
472 {
473     writeStandardPrefix(ts, object, indent);
474
475     Element* element = toElement(object.node());
476     const AtomicString& id = element->getIdAttribute();
477     writeNameAndQuotedValue(ts, "id", id);
478
479     RenderSVGResourceContainer* resource = toRenderSVGResourceContainer(const_cast<RenderObject*>(&object));
480     ASSERT(resource);
481
482     if (resource->resourceType() == MaskerResourceType) {
483         RenderSVGResourceMasker* masker = toRenderSVGResourceMasker(resource);
484         writeNameValuePair(ts, "maskUnits", masker->maskUnits());
485         writeNameValuePair(ts, "maskContentUnits", masker->maskContentUnits());
486         ts << "\n";
487     } else if (resource->resourceType() == FilterResourceType) {
488         RenderSVGResourceFilter* filter = toRenderSVGResourceFilter(resource);
489         writeNameValuePair(ts, "filterUnits", filter->filterUnits());
490         writeNameValuePair(ts, "primitiveUnits", filter->primitiveUnits());
491         ts << "\n";
492         // Creating a placeholder filter which is passed to the builder.
493         FloatRect dummyRect;
494         IntRect dummyIntRect;
495         RefPtr<SVGFilter> dummyFilter = SVGFilter::create(AffineTransform(), dummyIntRect, dummyRect, dummyRect, true);
496         if (RefPtr<SVGFilterBuilder> builder = filter->buildPrimitives(dummyFilter.get())) {
497             if (FilterEffect* lastEffect = builder->lastEffect())
498                 lastEffect->externalRepresentation(ts, indent + 1);
499         }
500     } else if (resource->resourceType() == ClipperResourceType) {
501         writeNameValuePair(ts, "clipPathUnits", toRenderSVGResourceClipper(resource)->clipPathUnits());
502         ts << "\n";
503     } else if (resource->resourceType() == MarkerResourceType) {
504         RenderSVGResourceMarker* marker = toRenderSVGResourceMarker(resource);
505         writeNameValuePair(ts, "markerUnits", marker->markerUnits());
506         ts << " [ref at " << marker->referencePoint() << "]";
507         ts << " [angle=";
508         if (marker->angle() == -1)
509             ts << "auto" << "]\n";
510         else
511             ts << marker->angle() << "]\n";
512     } else if (resource->resourceType() == PatternResourceType) {
513         RenderSVGResourcePattern* pattern = static_cast<RenderSVGResourcePattern*>(resource);
514
515         // Dump final results that are used for rendering. No use in asking SVGPatternElement for its patternUnits(), as it may
516         // link to other patterns using xlink:href, we need to build the full inheritance chain, aka. collectPatternProperties()
517         PatternAttributes attributes;
518         toSVGPatternElement(pattern->element())->collectPatternAttributes(attributes);
519
520         writeNameValuePair(ts, "patternUnits", attributes.patternUnits());
521         writeNameValuePair(ts, "patternContentUnits", attributes.patternContentUnits());
522
523         AffineTransform transform = attributes.patternTransform();
524         if (!transform.isIdentity())
525             ts << " [patternTransform=" << transform << "]";
526         ts << "\n";
527     } else if (resource->resourceType() == LinearGradientResourceType) {
528         RenderSVGResourceLinearGradient* gradient = static_cast<RenderSVGResourceLinearGradient*>(resource);
529
530         // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
531         // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
532         LinearGradientAttributes attributes;
533         toSVGLinearGradientElement(gradient->element())->collectGradientAttributes(attributes);
534         writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits());
535
536         ts << " [start=" << gradient->startPoint(attributes) << "] [end=" << gradient->endPoint(attributes) << "]\n";
537     }  else if (resource->resourceType() == RadialGradientResourceType) {
538         RenderSVGResourceRadialGradient* gradient = toRenderSVGResourceRadialGradient(resource);
539
540         // Dump final results that are used for rendering. No use in asking SVGGradientElement for its gradientUnits(), as it may
541         // link to other gradients using xlink:href, we need to build the full inheritance chain, aka. collectGradientProperties()
542         RadialGradientAttributes attributes;
543         toSVGRadialGradientElement(gradient->element())->collectGradientAttributes(attributes);
544         writeCommonGradientProperties(ts, attributes.spreadMethod(), attributes.gradientTransform(), attributes.gradientUnits());
545
546         FloatPoint focalPoint = gradient->focalPoint(attributes);
547         FloatPoint centerPoint = gradient->centerPoint(attributes);
548         float radius = gradient->radius(attributes);
549         float focalRadius = gradient->focalRadius(attributes);
550
551         ts << " [center=" << centerPoint << "] [focal=" << focalPoint << "] [radius=" << radius << "] [focalRadius=" << focalRadius << "]\n";
552     } else
553         ts << "\n";
554     writeChildren(ts, object, indent);
555 }
556
557 void writeSVGContainer(TextStream& ts, const RenderObject& container, int indent)
558 {
559     // Currently RenderSVGResourceFilterPrimitive has no meaningful output.
560     if (container.isSVGResourceFilterPrimitive())
561         return;
562     writeStandardPrefix(ts, container, indent);
563     writePositionAndStyle(ts, container);
564     ts << "\n";
565     writeResources(ts, container, indent);
566     writeChildren(ts, container, indent);
567 }
568
569 void write(TextStream& ts, const RenderSVGRoot& root, int indent)
570 {
571     writeStandardPrefix(ts, root, indent);
572     ts << root << "\n";
573     writeChildren(ts, root, indent);
574 }
575
576 void writeSVGText(TextStream& ts, const RenderSVGText& text, int indent)
577 {
578     writeStandardPrefix(ts, text, indent);
579     writeRenderSVGTextBox(ts, text);
580     ts << "\n";
581     writeResources(ts, text, indent);
582     writeChildren(ts, text, indent);
583 }
584
585 void writeSVGInlineText(TextStream& ts, const RenderSVGInlineText& text, int indent)
586 {
587     writeStandardPrefix(ts, text, indent);
588     ts << " " << enclosingIntRect(FloatRect(text.firstRunOrigin(), text.floatLinesBoundingBox().size())) << "\n";
589     writeResources(ts, text, indent);
590     writeSVGInlineTextBoxes(ts, text, indent);
591 }
592
593 void writeSVGImage(TextStream& ts, const RenderSVGImage& image, int indent)
594 {
595     writeStandardPrefix(ts, image, indent);
596     writePositionAndStyle(ts, image);
597     ts << "\n";
598     writeResources(ts, image, indent);
599 }
600
601 void write(TextStream& ts, const RenderSVGShape& shape, int indent)
602 {
603     writeStandardPrefix(ts, shape, indent);
604     ts << shape << "\n";
605     writeResources(ts, shape, indent);
606 }
607
608 void writeSVGGradientStop(TextStream& ts, const RenderSVGGradientStop& stop, int indent)
609 {
610     writeStandardPrefix(ts, stop, indent);
611
612     SVGStopElement* stopElement = toSVGStopElement(stop.node());
613     ASSERT(stopElement);
614
615     RenderStyle* style = stop.style();
616     if (!style)
617         return;
618
619     ts << " [offset=" << stopElement->offset()->currentValue()->value() << "] [color=" << stopElement->stopColorIncludingOpacity() << "]\n";
620 }
621
622 void writeResources(TextStream& ts, const RenderObject& object, int indent)
623 {
624     const RenderStyle* style = object.style();
625     const SVGRenderStyle* svgStyle = style->svgStyle();
626
627     // FIXME: We want to use SVGResourcesCache to determine which resources are present, instead of quering the resource <-> id cache.
628     // For now leave the DRT output as is, but later on we should change this so cycles are properly ignored in the DRT output.
629     RenderObject& renderer = const_cast<RenderObject&>(object);
630     if (!svgStyle->maskerResource().isEmpty()) {
631         if (RenderSVGResourceMasker* masker = getRenderSVGResourceById<RenderSVGResourceMasker>(object.document(), svgStyle->maskerResource())) {
632             writeIndent(ts, indent);
633             ts << " ";
634             writeNameAndQuotedValue(ts, "masker", svgStyle->maskerResource());
635             ts << " ";
636             writeStandardPrefix(ts, *masker, 0);
637             ts << " " << masker->resourceBoundingBox(&renderer) << "\n";
638         }
639     }
640     if (!svgStyle->clipperResource().isEmpty()) {
641         if (RenderSVGResourceClipper* clipper = getRenderSVGResourceById<RenderSVGResourceClipper>(object.document(), svgStyle->clipperResource())) {
642             writeIndent(ts, indent);
643             ts << " ";
644             writeNameAndQuotedValue(ts, "clipPath", svgStyle->clipperResource());
645             ts << " ";
646             writeStandardPrefix(ts, *clipper, 0);
647             ts << " " << clipper->resourceBoundingBox(&renderer) << "\n";
648         }
649     }
650     if (!svgStyle->filterResource().isEmpty()) {
651         if (RenderSVGResourceFilter* filter = getRenderSVGResourceById<RenderSVGResourceFilter>(object.document(), svgStyle->filterResource())) {
652             writeIndent(ts, indent);
653             ts << " ";
654             writeNameAndQuotedValue(ts, "filter", svgStyle->filterResource());
655             ts << " ";
656             writeStandardPrefix(ts, *filter, 0);
657             ts << " " << filter->resourceBoundingBox(&renderer) << "\n";
658         }
659     }
660 }
661
662 } // namespace WebCore