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