Fix the issue that Web Audio test case fails on PR3.
[framework/web/webkit-efl.git] / Source / WebCore / rendering / RenderTreeAsText.cpp
1 /*
2  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "RenderTreeAsText.h"
28
29 #include "Document.h"
30 #include "FlowThreadController.h"
31 #include "Frame.h"
32 #include "FrameSelection.h"
33 #include "FrameView.h"
34 #include "HTMLElement.h"
35 #include "HTMLNames.h"
36 #include "InlineTextBox.h"
37 #include "PrintContext.h"
38 #include "RenderBR.h"
39 #include "RenderDetailsMarker.h"
40 #include "RenderFileUploadControl.h"
41 #include "RenderInline.h"
42 #include "RenderLayer.h"
43 #include "RenderListItem.h"
44 #include "RenderListMarker.h"
45 #include "RenderNamedFlowThread.h"
46 #include "RenderPart.h"
47 #include "RenderRegion.h"
48 #include "RenderTableCell.h"
49 #include "RenderView.h"
50 #include "RenderWidget.h"
51 #include "StylePropertySet.h"
52 #include <wtf/HexNumber.h>
53 #include <wtf/UnusedParam.h>
54 #include <wtf/Vector.h>
55 #include <wtf/unicode/CharacterNames.h>
56
57 #if ENABLE(SVG)
58 #include "RenderSVGContainer.h"
59 #include "RenderSVGGradientStop.h"
60 #include "RenderSVGImage.h"
61 #include "RenderSVGInlineText.h"
62 #include "RenderSVGPath.h"
63 #include "RenderSVGRoot.h"
64 #include "RenderSVGText.h"
65 #include "SVGRenderTreeAsText.h"
66 #endif
67
68 #if USE(ACCELERATED_COMPOSITING)
69 #include "RenderLayerBacking.h"
70 #endif
71
72 #if PLATFORM(QT)
73 #include <QVariant>
74 #endif
75
76 namespace WebCore {
77
78 using namespace HTMLNames;
79
80 static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const LayoutRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior = RenderAsTextBehaviorNormal);
81
82 static inline bool hasFractions(double val)
83 {
84     static const double s_epsilon = 0.0001;
85     int ival = static_cast<int>(val);
86     double dval = static_cast<double>(ival);
87     return fabs(val - dval) > s_epsilon;
88 }
89
90 String formatNumberRespectingIntegers(double value)
91 {
92     if (!hasFractions(value))
93         return String::number(static_cast<int>(value));
94     return String::number(value, ShouldRoundDecimalPlaces, 2);
95 }
96
97 TextStream& operator<<(TextStream& ts, const IntRect& r)
98 {
99     return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
100 }
101
102 TextStream& operator<<(TextStream& ts, const IntPoint& p)
103 {
104     return ts << "(" << p.x() << "," << p.y() << ")";
105 }
106
107 TextStream& operator<<(TextStream& ts, const FractionalLayoutPoint& p)
108 {
109     // FIXME: These should be printed as floats. Keeping them ints for consistency with pervious test expectations.
110     return ts << "(" << p.x().toInt() << "," << p.y().toInt() << ")";
111 }
112
113 TextStream& operator<<(TextStream& ts, const FloatPoint& p)
114 {
115     ts << "(" << formatNumberRespectingIntegers(p.x());
116     ts << "," << formatNumberRespectingIntegers(p.y());
117     ts << ")";
118     return ts;
119 }
120
121 TextStream& operator<<(TextStream& ts, const FloatSize& s)
122 {
123     ts << "width=" << formatNumberRespectingIntegers(s.width());
124     ts << " height=" << formatNumberRespectingIntegers(s.height());
125     return ts;
126 }
127
128 void writeIndent(TextStream& ts, int indent)
129 {
130     for (int i = 0; i != indent; ++i)
131         ts << "  ";
132 }
133
134 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
135 {
136     switch (borderStyle) {
137         case BNONE:
138             ts << "none";
139             break;
140         case BHIDDEN:
141             ts << "hidden";
142             break;
143         case INSET:
144             ts << "inset";
145             break;
146         case GROOVE:
147             ts << "groove";
148             break;
149         case RIDGE:
150             ts << "ridge";
151             break;
152         case OUTSET:
153             ts << "outset";
154             break;
155         case DOTTED:
156             ts << "dotted";
157             break;
158         case DASHED:
159             ts << "dashed";
160             break;
161         case SOLID:
162             ts << "solid";
163             break;
164         case DOUBLE:
165             ts << "double";
166             break;
167     }
168
169     ts << " ";
170 }
171
172 static String getTagName(Node* n)
173 {
174     if (n->isDocumentNode())
175         return "";
176     if (n->nodeType() == Node::COMMENT_NODE)
177         return "COMMENT";
178     return n->nodeName();
179 }
180
181 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
182 {
183     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
184         return false;
185
186     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
187     if (elem->getAttribute(classAttr) != "Apple-style-span")
188         return false;
189
190     if (!node->hasChildNodes())
191         return true;
192
193     const StylePropertySet* inlineStyleDecl = elem->inlineStyle();
194     return (!inlineStyleDecl || inlineStyleDecl->isEmpty());
195 }
196
197 String quoteAndEscapeNonPrintables(const String& s)
198 {
199     StringBuilder result;
200     result.append('"');
201     for (unsigned i = 0; i != s.length(); ++i) {
202         UChar c = s[i];
203         if (c == '\\') {
204             result.append('\\');
205             result.append('\\');
206         } else if (c == '"') {
207             result.append('\\');
208             result.append('"');
209         } else if (c == '\n' || c == noBreakSpace)
210             result.append(' ');
211         else {
212             if (c >= 0x20 && c < 0x7F)
213                 result.append(c);
214             else {
215                 result.append('\\');
216                 result.append('x');
217                 result.append('{');
218                 appendUnsignedAsHex(c, result); 
219                 result.append('}');
220             }
221         }
222     }
223     result.append('"');
224     return result.toString();
225 }
226
227 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
228 {
229     ts << o.renderName();
230
231     if (behavior & RenderAsTextShowAddresses)
232         ts << " " << static_cast<const void*>(&o);
233
234     if (o.style() && o.style()->zIndex())
235         ts << " zI: " << o.style()->zIndex();
236
237     if (o.node()) {
238         String tagName = getTagName(o.node());
239         if (!tagName.isEmpty()) {
240             ts << " {" << tagName << "}";
241             // flag empty or unstyled AppleStyleSpan because we never
242             // want to leave them in the DOM
243             if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
244                 ts << " *empty or unstyled AppleStyleSpan*";
245         }
246     }
247     
248     RenderBlock* cb = o.containingBlock();
249     bool adjustForTableCells = cb ? cb->isTableCell() : false;
250
251     LayoutRect r;
252     if (o.isText()) {
253         // FIXME: Would be better to dump the bounding box x and y rather than the first run's x and y, but that would involve updating
254         // many test results.
255         const RenderText& text = *toRenderText(&o);
256         IntRect linesBox = text.linesBoundingBox();
257         r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
258         if (adjustForTableCells && !text.firstTextBox())
259             adjustForTableCells = false;
260     } else if (o.isRenderInline()) {
261         // FIXME: Would be better not to just dump 0, 0 as the x and y here.
262         const RenderInline& inlineFlow = *toRenderInline(&o);
263         r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
264         adjustForTableCells = false;
265     } else if (o.isTableCell()) {
266         // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect.  We'd like
267         // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
268         // captured by the results.
269         const RenderTableCell& cell = *toRenderTableCell(&o);
270         r = LayoutRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
271     } else if (o.isBox())
272         r = toRenderBox(&o)->frameRect();
273
274     // FIXME: Temporary in order to ensure compatibility with existing layout test results.
275     if (adjustForTableCells)
276         r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
277
278     // FIXME: Convert layout test results to report sub-pixel values, in the meantime using enclosingIntRect
279     // for consistency with old results.
280     ts << " " << enclosingIntRect(r);
281
282     if (!(o.isText() && !o.isBR())) {
283         if (o.isFileUploadControl())
284             ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
285
286         if (o.parent()) {
287             Color color = o.style()->visitedDependentColor(CSSPropertyColor);
288             if (o.parent()->style()->visitedDependentColor(CSSPropertyColor) != color)
289                 ts << " [color=" << color.nameForRenderTreeAsText() << "]";
290
291             // Do not dump invalid or transparent backgrounds, since that is the default.
292             Color backgroundColor = o.style()->visitedDependentColor(CSSPropertyBackgroundColor);
293             if (o.parent()->style()->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor
294                 && backgroundColor.isValid() && backgroundColor.rgb())
295                 ts << " [bgcolor=" << backgroundColor.nameForRenderTreeAsText() << "]";
296             
297             Color textFillColor = o.style()->visitedDependentColor(CSSPropertyWebkitTextFillColor);
298             if (o.parent()->style()->visitedDependentColor(CSSPropertyWebkitTextFillColor) != textFillColor
299                 && textFillColor.isValid() && textFillColor != color && textFillColor.rgb())
300                 ts << " [textFillColor=" << textFillColor.nameForRenderTreeAsText() << "]";
301
302             Color textStrokeColor = o.style()->visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
303             if (o.parent()->style()->visitedDependentColor(CSSPropertyWebkitTextStrokeColor) != textStrokeColor
304                 && textStrokeColor.isValid() && textStrokeColor != color && textStrokeColor.rgb())
305                 ts << " [textStrokeColor=" << textStrokeColor.nameForRenderTreeAsText() << "]";
306
307             if (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth() && o.style()->textStrokeWidth() > 0)
308                 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
309         }
310
311         if (!o.isBoxModelObject())
312             return;
313
314         const RenderBoxModelObject& box = *toRenderBoxModelObject(&o);
315         if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
316             ts << " [border:";
317
318             BorderValue prevBorder = o.style()->borderTop();
319             if (!box.borderTop())
320                 ts << " none";
321             else {
322                 ts << " (" << box.borderTop() << "px ";
323                 printBorderStyle(ts, o.style()->borderTopStyle());
324                 Color col = o.style()->borderTopColor();
325                 if (!col.isValid())
326                     col = o.style()->color();
327                 ts << col.nameForRenderTreeAsText() << ")";
328             }
329
330             if (o.style()->borderRight() != prevBorder) {
331                 prevBorder = o.style()->borderRight();
332                 if (!box.borderRight())
333                     ts << " none";
334                 else {
335                     ts << " (" << box.borderRight() << "px ";
336                     printBorderStyle(ts, o.style()->borderRightStyle());
337                     Color col = o.style()->borderRightColor();
338                     if (!col.isValid())
339                         col = o.style()->color();
340                     ts << col.nameForRenderTreeAsText() << ")";
341                 }
342             }
343
344             if (o.style()->borderBottom() != prevBorder) {
345                 prevBorder = box.style()->borderBottom();
346                 if (!box.borderBottom())
347                     ts << " none";
348                 else {
349                     ts << " (" << box.borderBottom() << "px ";
350                     printBorderStyle(ts, o.style()->borderBottomStyle());
351                     Color col = o.style()->borderBottomColor();
352                     if (!col.isValid())
353                         col = o.style()->color();
354                     ts << col.nameForRenderTreeAsText() << ")";
355                 }
356             }
357
358             if (o.style()->borderLeft() != prevBorder) {
359                 prevBorder = o.style()->borderLeft();
360                 if (!box.borderLeft())
361                     ts << " none";
362                 else {
363                     ts << " (" << box.borderLeft() << "px ";
364                     printBorderStyle(ts, o.style()->borderLeftStyle());
365                     Color col = o.style()->borderLeftColor();
366                     if (!col.isValid())
367                         col = o.style()->color();
368                     ts << col.nameForRenderTreeAsText() << ")";
369                 }
370             }
371
372             ts << "]";
373         }
374
375 #if ENABLE(MATHML)
376         // We want to show any layout padding, both CSS padding and intrinsic padding, so we can't just check o.style()->hasPadding().
377         if (o.isRenderMathMLBlock() && (box.paddingTop() || box.paddingRight() || box.paddingBottom() || box.paddingLeft())) {
378             ts << " [";
379             LayoutUnit cssTop = box.computedCSSPaddingTop();
380             LayoutUnit cssRight = box.computedCSSPaddingRight();
381             LayoutUnit cssBottom = box.computedCSSPaddingBottom();
382             LayoutUnit cssLeft = box.computedCSSPaddingLeft();
383             if (box.paddingTop() != cssTop || box.paddingRight() != cssRight || box.paddingBottom() != cssBottom || box.paddingLeft() != cssLeft) {
384                 ts << "intrinsic ";
385                 if (cssTop || cssRight || cssBottom || cssLeft)
386                     ts << "+ CSS ";
387             }
388             ts << "padding: " << roundToInt(box.paddingTop()) << " " << roundToInt(box.paddingRight()) << " " << roundToInt(box.paddingBottom()) << " " << roundToInt(box.paddingLeft()) << "]";
389         }
390 #endif
391     }
392
393     if (o.isTableCell()) {
394         const RenderTableCell& c = *toRenderTableCell(&o);
395         ts << " [r=" << c.rowIndex() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
396     }
397
398 #if ENABLE(DETAILS_ELEMENT)
399     if (o.isDetailsMarker()) {
400         ts << ": ";
401         switch (toRenderDetailsMarker(&o)->orientation()) {
402         case RenderDetailsMarker::Left:
403             ts << "left";
404             break;
405         case RenderDetailsMarker::Right:
406             ts << "right";
407             break;
408         case RenderDetailsMarker::Up:
409             ts << "up";
410             break;
411         case RenderDetailsMarker::Down:
412             ts << "down";
413             break;
414         }
415     }
416 #endif
417
418     if (o.isListMarker()) {
419         String text = toRenderListMarker(&o)->text();
420         if (!text.isEmpty()) {
421             if (text.length() != 1)
422                 text = quoteAndEscapeNonPrintables(text);
423             else {
424                 switch (text[0]) {
425                     case bullet:
426                         text = "bullet";
427                         break;
428                     case blackSquare:
429                         text = "black square";
430                         break;
431                     case whiteBullet:
432                         text = "white bullet";
433                         break;
434                     default:
435                         text = quoteAndEscapeNonPrintables(text);
436                 }
437             }
438             ts << ": " << text;
439         }
440     }
441     
442     if (behavior & RenderAsTextShowIDAndClass) {
443         if (Node* node = o.node()) {
444             if (node->hasID())
445                 ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\"";
446
447             if (node->hasClass()) {
448                 StyledElement* styledElement = static_cast<StyledElement*>(node);
449                 String classes;
450                 for (size_t i = 0; i < styledElement->classNames().size(); ++i) {
451                     if (i > 0)
452                         classes += " ";
453                     classes += styledElement->classNames()[i];
454                 }
455                 ts << " class=\"" + classes + "\"";
456             }
457         }
458     }
459     
460     if (behavior & RenderAsTextShowLayoutState) {
461         bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
462         if (needsLayout)
463             ts << " (needs layout:";
464         
465         bool havePrevious = false;
466         if (o.selfNeedsLayout()) {
467             ts << " self";
468             havePrevious = true;
469         }
470
471         if (o.needsPositionedMovementLayout()) {
472             if (havePrevious)
473                 ts << ",";
474             havePrevious = true;
475             ts << " positioned movement";
476         }
477
478         if (o.normalChildNeedsLayout()) {
479             if (havePrevious)
480                 ts << ",";
481             havePrevious = true;
482             ts << " child";
483         }
484
485         if (o.posChildNeedsLayout()) {
486             if (havePrevious)
487                 ts << ",";
488             ts << " positioned child";
489         }
490
491         if (needsLayout)
492             ts << ")";
493     }
494
495 #if PLATFORM(QT)
496     // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget
497     // is invisible the QWidget should be invisible too.
498     if (o.isRenderPart()) {
499         const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o));
500         if (part->widget() && part->widget()->platformWidget()) {
501             QObject* wid = part->widget()->platformWidget();
502
503             ts << " [QT: ";
504             ts << "geometry: {" << wid->property("geometry").toRect() << "} ";
505             ts << "isHidden: " << !wid->property("isVisible").toBool() << " ";
506             ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " ";
507             ts << "isParentVisible: " << part->widget()->isParentVisible() << " ] ";
508         }
509     }
510 #endif
511 }
512
513 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
514 {
515     // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
516     // to detect any changes caused by the conversion to floating point. :(
517     int x = run.x();
518     int y = run.y();
519     int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
520
521     // FIXME: Table cell adjustment is temporary until results can be updated.
522     if (o.containingBlock()->isTableCell())
523         y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
524         
525     ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
526     if (!run.isLeftToRightDirection() || run.dirOverride()) {
527         ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
528         if (run.dirOverride())
529             ts << " override";
530     }
531     ts << ": "
532         << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
533     if (run.hasHyphen())
534         ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
535     ts << "\n";
536 }
537
538 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
539 {
540 #if ENABLE(SVG)
541     if (o.isSVGShape()) {
542         write(ts, *toRenderSVGShape(&o), indent);
543         return;
544     }
545     if (o.isSVGGradientStop()) {
546         writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent);
547         return;
548     }
549     if (o.isSVGResourceContainer()) {
550         writeSVGResourceContainer(ts, o, indent);
551         return;
552     }
553     if (o.isSVGContainer()) {
554         writeSVGContainer(ts, o, indent);
555         return;
556     }
557     if (o.isSVGRoot()) {
558         write(ts, *toRenderSVGRoot(&o), indent);
559         return;
560     }
561     if (o.isSVGText()) {
562         writeSVGText(ts, *toRenderSVGText(&o), indent);
563         return;
564     }
565     if (o.isSVGInlineText()) {
566         writeSVGInlineText(ts, *toRenderSVGInlineText(&o), indent);
567         return;
568     }
569     if (o.isSVGImage()) {
570         writeSVGImage(ts, *toRenderSVGImage(&o), indent);
571         return;
572     }
573 #endif
574
575     writeIndent(ts, indent);
576
577     RenderTreeAsText::writeRenderObject(ts, o, behavior);
578     ts << "\n";
579
580     if (o.isText() && !o.isBR()) {
581         const RenderText& text = *toRenderText(&o);
582         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
583             writeIndent(ts, indent + 1);
584             writeTextRun(ts, text, *box);
585         }
586     }
587
588     for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
589         if (child->hasLayer())
590             continue;
591         write(ts, *child, indent + 1, behavior);
592     }
593
594     if (o.isWidget()) {
595         Widget* widget = toRenderWidget(&o)->widget();
596         if (widget && widget->isFrameView()) {
597             FrameView* view = static_cast<FrameView*>(widget);
598             RenderView* root = view->frame()->contentRenderer();
599             if (root) {
600                 view->layout();
601                 RenderLayer* l = root->layer();
602                 if (l)
603                     writeLayers(ts, l, l, l->rect(), indent + 1, behavior);
604             }
605         }
606     }
607 }
608
609 enum LayerPaintPhase {
610     LayerPaintPhaseAll = 0,
611     LayerPaintPhaseBackground = -1,
612     LayerPaintPhaseForeground = 1
613 };
614
615 static void write(TextStream& ts, RenderLayer& l,
616                   const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect, const LayoutRect& clipRect, const LayoutRect& outlineClipRect,
617                   LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
618 {
619     IntRect adjustedLayoutBounds = pixelSnappedIntRect(layerBounds);
620     IntRect adjustedBackgroundClipRect = pixelSnappedIntRect(backgroundClipRect);
621     IntRect adjustedClipRect = pixelSnappedIntRect(clipRect);
622     IntRect adjustedOutlineClipRect = pixelSnappedIntRect(outlineClipRect);
623
624     writeIndent(ts, indent);
625
626     ts << "layer ";
627     
628     if (behavior & RenderAsTextShowAddresses)
629         ts << static_cast<const void*>(&l) << " ";
630       
631     ts << adjustedLayoutBounds;
632
633     if (!adjustedLayoutBounds.isEmpty()) {
634         if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
635             ts << " backgroundClip " << adjustedBackgroundClipRect;
636         if (!adjustedClipRect.contains(adjustedLayoutBounds))
637             ts << " clip " << adjustedClipRect;
638         if (!adjustedOutlineClipRect.contains(adjustedLayoutBounds))
639             ts << " outlineClip " << adjustedOutlineClipRect;
640     }
641
642     if (l.renderer()->hasOverflowClip()) {
643         if (l.scrollXOffset())
644             ts << " scrollX " << l.scrollXOffset();
645         if (l.scrollYOffset())
646             ts << " scrollY " << l.scrollYOffset();
647         if (l.renderBox() && l.renderBox()->pixelSnappedClientWidth() != l.scrollWidth())
648             ts << " scrollWidth " << l.scrollWidth();
649         if (l.renderBox() && l.renderBox()->pixelSnappedClientHeight() != l.scrollHeight())
650             ts << " scrollHeight " << l.scrollHeight();
651     }
652
653     if (paintPhase == LayerPaintPhaseBackground)
654         ts << " layerType: background only";
655     else if (paintPhase == LayerPaintPhaseForeground)
656         ts << " layerType: foreground only";
657     
658 #if USE(ACCELERATED_COMPOSITING)
659     if (behavior & RenderAsTextShowCompositedLayers) {
660         if (l.isComposited())
661             ts << " (composited, bounds=" << l.backing()->compositedBounds() << ", drawsContent=" << l.backing()->graphicsLayer()->drawsContent() << ", paints into ancestor=" << l.backing()->paintsIntoCompositedAncestor() << ")";
662     }
663 #else
664     UNUSED_PARAM(behavior);
665 #endif
666     
667     ts << "\n";
668
669     if (paintPhase != LayerPaintPhaseBackground)
670         write(ts, *l.renderer(), indent + 1, behavior);
671 }
672
673 static void writeRenderNamedFlowThreads(TextStream& ts, RenderView* renderView, const RenderLayer* rootLayer,
674                         const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
675 {
676     if (!renderView->hasRenderNamedFlowThreads())
677         return;
678
679     const RenderNamedFlowThreadList* list = renderView->flowThreadController()->renderNamedFlowThreadList();
680
681     writeIndent(ts, indent);
682     ts << "Flow Threads\n";
683
684     for (RenderNamedFlowThreadList::const_iterator iter = list->begin(); iter != list->end(); ++iter) {
685         const RenderNamedFlowThread* renderFlowThread = *iter;
686
687         writeIndent(ts, indent + 1);
688         ts << "Thread with flow-name '" << renderFlowThread->flowThreadName() << "'\n";
689
690         RenderLayer* layer = renderFlowThread->layer();
691         writeLayers(ts, rootLayer, layer, paintRect, indent + 2, behavior);
692
693         // Display the render regions attached to this flow thread
694         const RenderRegionList& flowThreadRegionList = renderFlowThread->renderRegionList();
695         if (!flowThreadRegionList.isEmpty()) {
696             writeIndent(ts, indent + 1);
697             ts << "Regions for flow '"<< renderFlowThread->flowThreadName() << "'\n";
698             for (RenderRegionList::const_iterator itRR = flowThreadRegionList.begin(); itRR != flowThreadRegionList.end(); ++itRR) {
699                 RenderRegion* renderRegion = *itRR;
700                 writeIndent(ts, indent + 2);
701                 ts << "RenderRegion";
702                 if (renderRegion->node()) {
703                     String tagName = getTagName(renderRegion->node());
704                     if (!tagName.isEmpty())
705                         ts << " {" << tagName << "}";
706                     if (renderRegion->node()->isElementNode() && renderRegion->node()->hasID()) {
707                         Element* element = static_cast<Element*>(renderRegion->node());
708                         ts << " #" << element->idForStyleResolution();
709                     }
710                     if (renderRegion->hasCustomRegionStyle())
711                         ts << " region style: 1";
712                 }
713                 if (!renderRegion->isValid())
714                     ts << " invalid";
715                 ts << "\n";
716             }
717         }
718     }
719 }
720
721 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
722                         const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
723 {
724     // FIXME: Apply overflow to the root layer to not break every test.  Complete hack.  Sigh.
725     LayoutRect paintDirtyRect(paintRect);
726     if (rootLayer == l) {
727         paintDirtyRect.setWidth(max<LayoutUnit>(paintDirtyRect.width(), rootLayer->renderBox()->layoutOverflowRect().maxX()));
728         paintDirtyRect.setHeight(max<LayoutUnit>(paintDirtyRect.height(), rootLayer->renderBox()->layoutOverflowRect().maxY()));
729         l->setSize(l->size().expandedTo(pixelSnappedIntSize(l->renderBox()->maxLayoutOverflow(), LayoutPoint(0, 0))));
730     }
731     
732     // Calculate the clip rects we should use.
733     LayoutRect layerBounds;
734     ClipRect damageRect, clipRectToApply, outlineRect;
735     l->calculateRects(rootLayer, 0, TemporaryClipRects, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
736
737     // Ensure our lists are up-to-date.
738     l->updateLayerListsIfNeeded();
739
740     bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer);
741     Vector<RenderLayer*>* negList = l->negZOrderList();
742     bool paintsBackgroundSeparately = negList && negList->size() > 0;
743     if (shouldPaint && paintsBackgroundSeparately)
744         write(ts, *l, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), LayerPaintPhaseBackground, indent, behavior);
745
746     if (negList) {
747         int currIndent = indent;
748         if (behavior & RenderAsTextShowLayerNesting) {
749             writeIndent(ts, indent);
750             ts << " negative z-order list(" << negList->size() << ")\n";
751             ++currIndent;
752         }
753         for (unsigned i = 0; i != negList->size(); ++i)
754             writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior);
755     }
756
757     if (shouldPaint)
758         write(ts, *l, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
759
760     if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) {
761         int currIndent = indent;
762         if (behavior & RenderAsTextShowLayerNesting) {
763             writeIndent(ts, indent);
764             ts << " normal flow list(" << normalFlowList->size() << ")\n";
765             ++currIndent;
766         }
767         for (unsigned i = 0; i != normalFlowList->size(); ++i)
768             writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior);
769     }
770
771     if (Vector<RenderLayer*>* posList = l->posZOrderList()) {
772         int currIndent = indent;
773         if (behavior & RenderAsTextShowLayerNesting) {
774             writeIndent(ts, indent);
775             ts << " positive z-order list(" << posList->size() << ")\n";
776             ++currIndent;
777         }
778         for (unsigned i = 0; i != posList->size(); ++i)
779             writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior);
780     }
781     
782     // Altough the RenderFlowThread requires a layer, it is not collected by its parent,
783     // so we have to treat it as a special case.
784     if (l->renderer()->isRenderView()) {
785         RenderView* renderView = toRenderView(l->renderer());
786         writeRenderNamedFlowThreads(ts, renderView, rootLayer, paintDirtyRect, indent, behavior);
787     }
788 }
789
790 static String nodePosition(Node* node)
791 {
792     String result;
793
794     Element* body = node->document()->body();
795     Node* parent;
796     for (Node* n = node; n; n = parent) {
797         parent = n->parentOrHostNode();
798         if (n != node)
799             result += " of ";
800         if (parent) {
801             if (body && n == body) {
802                 // We don't care what offset body may be in the document.
803                 result += "body";
804                 break;
805             }
806             if (n->isShadowRoot())
807                 result += "{" + getTagName(n) + "}";
808             else
809                 result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
810         } else
811             result += "document";
812     }
813
814     return result;
815 }
816
817 static void writeSelection(TextStream& ts, const RenderObject* o)
818 {
819     Node* n = o->node();
820     if (!n || !n->isDocumentNode())
821         return;
822
823     Document* doc = static_cast<Document*>(n);
824     Frame* frame = doc->frame();
825     if (!frame)
826         return;
827
828     VisibleSelection selection = frame->selection()->selection();
829     if (selection.isCaret()) {
830         ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode());
831         if (selection.affinity() == UPSTREAM)
832             ts << " (upstream affinity)";
833         ts << "\n";
834     } else if (selection.isRange())
835         ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n"
836            << "selection end:   position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n";
837 }
838
839 static String externalRepresentation(RenderBox* renderer, RenderAsTextBehavior behavior)
840 {
841     TextStream ts;
842     if (!renderer->hasLayer())
843         return ts.release();
844         
845     RenderLayer* layer = renderer->layer();
846     writeLayers(ts, layer, layer, layer->rect(), 0, behavior);
847     writeSelection(ts, renderer);
848     return ts.release();
849 }
850
851 String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
852 {
853     RenderObject* renderer = frame->contentRenderer();
854     if (!renderer || !renderer->isBox())
855         return String();
856
857     PrintContext printContext(frame);
858     if (behavior & RenderAsTextPrintingMode)
859         printContext.begin(toRenderBox(renderer)->width());
860     if (!(behavior & RenderAsTextDontUpdateLayout))
861         frame->document()->updateLayout();
862
863     return externalRepresentation(toRenderBox(renderer), behavior);
864 }
865
866 String externalRepresentation(Element* element, RenderAsTextBehavior behavior)
867 {
868     RenderObject* renderer = element->renderer();
869     if (!renderer || !renderer->isBox())
870         return String();
871     // Doesn't support printing mode.
872     ASSERT(!(behavior & RenderAsTextPrintingMode));
873     if (!(behavior & RenderAsTextDontUpdateLayout) && element->document())
874         element->document()->updateLayout();
875     
876     return externalRepresentation(toRenderBox(renderer), behavior | RenderAsTextShowAllLayers);
877 }
878
879 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
880 {
881     for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) {
882         if (child->isCounter()) {
883             if (!isFirstCounter)
884                 stream << " ";
885             isFirstCounter = false;
886             String str(toRenderText(child)->text());
887             stream << str;
888         }
889     }
890 }
891
892 String counterValueForElement(Element* element)
893 {
894     // Make sure the element is not freed during the layout.
895     RefPtr<Element> elementRef(element);
896     element->document()->updateLayout();
897     TextStream stream;
898     bool isFirstCounter = true;
899     // The counter renderers should be children of :before or :after pseudo-elements.
900     if (RenderObject* renderer = element->renderer()) {
901         if (RenderObject* pseudoElement = renderer->beforePseudoElementRenderer())
902             writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
903         if (RenderObject* pseudoElement = renderer->afterPseudoElementRenderer())
904             writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
905     }
906     return stream.release();
907 }
908
909 String markerTextForListItem(Element* element)
910 {
911     // Make sure the element is not freed during the layout.
912     RefPtr<Element> elementRef(element);
913     element->document()->updateLayout();
914
915     RenderObject* renderer = element->renderer();
916     if (!renderer || !renderer->isListItem())
917         return String();
918
919     return toRenderListItem(renderer)->markerText();
920 }
921
922 } // namespace WebCore