[CSS Regions]Add helper class for flow threads info in RenderView
[WebKit-https.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 IntRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior behavior = 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 FloatPoint& p)
108 {
109     ts << "(" << formatNumberRespectingIntegers(p.x());
110     ts << "," << formatNumberRespectingIntegers(p.y());
111     ts << ")";
112     return ts;
113 }
114
115 TextStream& operator<<(TextStream& ts, const FloatSize& s)
116 {
117     ts << "width=" << formatNumberRespectingIntegers(s.width());
118     ts << " height=" << formatNumberRespectingIntegers(s.height());
119     return ts;
120 }
121
122 void writeIndent(TextStream& ts, int indent)
123 {
124     for (int i = 0; i != indent; ++i)
125         ts << "  ";
126 }
127
128 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
129 {
130     switch (borderStyle) {
131         case BNONE:
132             ts << "none";
133             break;
134         case BHIDDEN:
135             ts << "hidden";
136             break;
137         case INSET:
138             ts << "inset";
139             break;
140         case GROOVE:
141             ts << "groove";
142             break;
143         case RIDGE:
144             ts << "ridge";
145             break;
146         case OUTSET:
147             ts << "outset";
148             break;
149         case DOTTED:
150             ts << "dotted";
151             break;
152         case DASHED:
153             ts << "dashed";
154             break;
155         case SOLID:
156             ts << "solid";
157             break;
158         case DOUBLE:
159             ts << "double";
160             break;
161     }
162
163     ts << " ";
164 }
165
166 static String getTagName(Node* n)
167 {
168     if (n->isDocumentNode())
169         return "";
170     if (n->nodeType() == Node::COMMENT_NODE)
171         return "COMMENT";
172     return n->nodeName();
173 }
174
175 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
176 {
177     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
178         return false;
179
180     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
181     if (elem->getAttribute(classAttr) != "Apple-style-span")
182         return false;
183
184     if (!node->hasChildNodes())
185         return true;
186
187     const StylePropertySet* inlineStyleDecl = elem->inlineStyle();
188     return (!inlineStyleDecl || inlineStyleDecl->isEmpty());
189 }
190
191 String quoteAndEscapeNonPrintables(const String& s)
192 {
193     StringBuilder result;
194     result.append('"');
195     for (unsigned i = 0; i != s.length(); ++i) {
196         UChar c = s[i];
197         if (c == '\\') {
198             result.append('\\');
199             result.append('\\');
200         } else if (c == '"') {
201             result.append('\\');
202             result.append('"');
203         } else if (c == '\n' || c == noBreakSpace)
204             result.append(' ');
205         else {
206             if (c >= 0x20 && c < 0x7F)
207                 result.append(c);
208             else {
209                 result.append('\\');
210                 result.append('x');
211                 result.append('{');
212                 appendUnsignedAsHex(c, result); 
213                 result.append('}');
214             }
215         }
216     }
217     result.append('"');
218     return result.toString();
219 }
220
221 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
222 {
223     ts << o.renderName();
224
225     if (behavior & RenderAsTextShowAddresses)
226         ts << " " << static_cast<const void*>(&o);
227
228     if (o.style() && o.style()->zIndex())
229         ts << " zI: " << o.style()->zIndex();
230
231     if (o.node()) {
232         String tagName = getTagName(o.node());
233         if (!tagName.isEmpty()) {
234             ts << " {" << tagName << "}";
235             // flag empty or unstyled AppleStyleSpan because we never
236             // want to leave them in the DOM
237             if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
238                 ts << " *empty or unstyled AppleStyleSpan*";
239         }
240     }
241     
242     RenderBlock* cb = o.containingBlock();
243     bool adjustForTableCells = cb ? cb->isTableCell() : false;
244
245     IntRect r;
246     if (o.isText()) {
247         // 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
248         // many test results.
249         const RenderText& text = *toRenderText(&o);
250         IntRect linesBox = text.linesBoundingBox();
251         r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
252         if (adjustForTableCells && !text.firstTextBox())
253             adjustForTableCells = false;
254     } else if (o.isRenderInline()) {
255         // FIXME: Would be better not to just dump 0, 0 as the x and y here.
256         const RenderInline& inlineFlow = *toRenderInline(&o);
257         r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
258         adjustForTableCells = false;
259     } else if (o.isTableCell()) {
260         // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect.  We'd like
261         // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
262         // captured by the results.
263         const RenderTableCell& cell = *toRenderTableCell(&o);
264         r = IntRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
265     } else if (o.isBox())
266         r = toRenderBox(&o)->frameRect();
267
268     // FIXME: Temporary in order to ensure compatibility with existing layout test results.
269     if (adjustForTableCells)
270         r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
271
272     ts << " " << r;
273
274     if (!(o.isText() && !o.isBR())) {
275         if (o.isFileUploadControl())
276             ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
277
278         if (o.parent()) {
279             Color color = o.style()->visitedDependentColor(CSSPropertyColor);
280             if (o.parent()->style()->visitedDependentColor(CSSPropertyColor) != color)
281                 ts << " [color=" << color.nameForRenderTreeAsText() << "]";
282
283             // Do not dump invalid or transparent backgrounds, since that is the default.
284             Color backgroundColor = o.style()->visitedDependentColor(CSSPropertyBackgroundColor);
285             if (o.parent()->style()->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor
286                 && backgroundColor.isValid() && backgroundColor.rgb())
287                 ts << " [bgcolor=" << backgroundColor.nameForRenderTreeAsText() << "]";
288             
289             Color textFillColor = o.style()->visitedDependentColor(CSSPropertyWebkitTextFillColor);
290             if (o.parent()->style()->visitedDependentColor(CSSPropertyWebkitTextFillColor) != textFillColor
291                 && textFillColor.isValid() && textFillColor != color && textFillColor.rgb())
292                 ts << " [textFillColor=" << textFillColor.nameForRenderTreeAsText() << "]";
293
294             Color textStrokeColor = o.style()->visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
295             if (o.parent()->style()->visitedDependentColor(CSSPropertyWebkitTextStrokeColor) != textStrokeColor
296                 && textStrokeColor.isValid() && textStrokeColor != color && textStrokeColor.rgb())
297                 ts << " [textStrokeColor=" << textStrokeColor.nameForRenderTreeAsText() << "]";
298
299             if (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth() && o.style()->textStrokeWidth() > 0)
300                 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
301         }
302
303         if (!o.isBoxModelObject())
304             return;
305
306         const RenderBoxModelObject& box = *toRenderBoxModelObject(&o);
307         if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
308             ts << " [border:";
309
310             BorderValue prevBorder;
311             if (o.style()->borderTop() != prevBorder) {
312                 prevBorder = o.style()->borderTop();
313                 if (!box.borderTop())
314                     ts << " none";
315                 else {
316                     ts << " (" << box.borderTop() << "px ";
317                     printBorderStyle(ts, o.style()->borderTopStyle());
318                     Color col = o.style()->borderTopColor();
319                     if (!col.isValid())
320                         col = o.style()->color();
321                     ts << col.nameForRenderTreeAsText() << ")";
322                 }
323             }
324
325             if (o.style()->borderRight() != prevBorder) {
326                 prevBorder = o.style()->borderRight();
327                 if (!box.borderRight())
328                     ts << " none";
329                 else {
330                     ts << " (" << box.borderRight() << "px ";
331                     printBorderStyle(ts, o.style()->borderRightStyle());
332                     Color col = o.style()->borderRightColor();
333                     if (!col.isValid())
334                         col = o.style()->color();
335                     ts << col.nameForRenderTreeAsText() << ")";
336                 }
337             }
338
339             if (o.style()->borderBottom() != prevBorder) {
340                 prevBorder = box.style()->borderBottom();
341                 if (!box.borderBottom())
342                     ts << " none";
343                 else {
344                     ts << " (" << box.borderBottom() << "px ";
345                     printBorderStyle(ts, o.style()->borderBottomStyle());
346                     Color col = o.style()->borderBottomColor();
347                     if (!col.isValid())
348                         col = o.style()->color();
349                     ts << col.nameForRenderTreeAsText() << ")";
350                 }
351             }
352
353             if (o.style()->borderLeft() != prevBorder) {
354                 prevBorder = o.style()->borderLeft();
355                 if (!box.borderLeft())
356                     ts << " none";
357                 else {
358                     ts << " (" << box.borderLeft() << "px ";
359                     printBorderStyle(ts, o.style()->borderLeftStyle());
360                     Color col = o.style()->borderLeftColor();
361                     if (!col.isValid())
362                         col = o.style()->color();
363                     ts << col.nameForRenderTreeAsText() << ")";
364                 }
365             }
366
367             ts << "]";
368         }
369     }
370
371     if (o.isTableCell()) {
372         const RenderTableCell& c = *toRenderTableCell(&o);
373         ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
374     }
375
376 #if ENABLE(DETAILS)
377     if (o.isDetailsMarker()) {
378         ts << ": ";
379         switch (toRenderDetailsMarker(&o)->orientation()) {
380         case RenderDetailsMarker::Left:
381             ts << "left";
382             break;
383         case RenderDetailsMarker::Right:
384             ts << "right";
385             break;
386         case RenderDetailsMarker::Up:
387             ts << "up";
388             break;
389         case RenderDetailsMarker::Down:
390             ts << "down";
391             break;
392         }
393     }
394 #endif
395
396     if (o.isListMarker()) {
397         String text = toRenderListMarker(&o)->text();
398         if (!text.isEmpty()) {
399             if (text.length() != 1)
400                 text = quoteAndEscapeNonPrintables(text);
401             else {
402                 switch (text[0]) {
403                     case bullet:
404                         text = "bullet";
405                         break;
406                     case blackSquare:
407                         text = "black square";
408                         break;
409                     case whiteBullet:
410                         text = "white bullet";
411                         break;
412                     default:
413                         text = quoteAndEscapeNonPrintables(text);
414                 }
415             }
416             ts << ": " << text;
417         }
418     }
419     
420     if (behavior & RenderAsTextShowIDAndClass) {
421         if (Node* node = o.node()) {
422             if (node->hasID())
423                 ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\"";
424
425             if (node->hasClass()) {
426                 StyledElement* styledElement = static_cast<StyledElement*>(node);
427                 String classes;
428                 for (size_t i = 0; i < styledElement->classNames().size(); ++i) {
429                     if (i > 0)
430                         classes += " ";
431                     classes += styledElement->classNames()[i];
432                 }
433                 ts << " class=\"" + classes + "\"";
434             }
435         }
436     }
437     
438     if (behavior & RenderAsTextShowLayoutState) {
439         bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
440         if (needsLayout)
441             ts << " (needs layout:";
442         
443         bool havePrevious = false;
444         if (o.selfNeedsLayout()) {
445             ts << " self";
446             havePrevious = true;
447         }
448
449         if (o.needsPositionedMovementLayout()) {
450             if (havePrevious)
451                 ts << ",";
452             havePrevious = true;
453             ts << " positioned movement";
454         }
455
456         if (o.normalChildNeedsLayout()) {
457             if (havePrevious)
458                 ts << ",";
459             havePrevious = true;
460             ts << " child";
461         }
462
463         if (o.posChildNeedsLayout()) {
464             if (havePrevious)
465                 ts << ",";
466             ts << " positioned child";
467         }
468
469         if (needsLayout)
470             ts << ")";
471     }
472
473 #if PLATFORM(QT)
474     // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget
475     // is invisible the QWidget should be invisible too.
476     if (o.isRenderPart()) {
477         const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o));
478         if (part->widget() && part->widget()->platformWidget()) {
479             QObject* wid = part->widget()->platformWidget();
480
481             ts << " [QT: ";
482             ts << "geometry: {" << wid->property("geometry").toRect() << "} ";
483             ts << "isHidden: " << !wid->property("isVisible").toBool() << " ";
484             ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " ";
485             ts << "isParentVisible: " << part->widget()->isParentVisible() << " ] ";
486         }
487     }
488 #endif
489 }
490
491 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
492 {
493     // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
494     // to detect any changes caused by the conversion to floating point. :(
495     int x = run.x();
496     int y = run.y();
497     int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
498
499     // FIXME: Table cell adjustment is temporary until results can be updated.
500     if (o.containingBlock()->isTableCell())
501         y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
502         
503     ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
504     if (!run.isLeftToRightDirection() || run.dirOverride()) {
505         ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
506         if (run.dirOverride())
507             ts << " override";
508     }
509     ts << ": "
510         << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
511     if (run.hasHyphen())
512         ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
513     ts << "\n";
514 }
515
516 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
517 {
518 #if ENABLE(SVG)
519     if (o.isSVGShape()) {
520         write(ts, *toRenderSVGShape(&o), indent);
521         return;
522     }
523     if (o.isSVGGradientStop()) {
524         writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent);
525         return;
526     }
527     if (o.isSVGResourceContainer()) {
528         writeSVGResourceContainer(ts, o, indent);
529         return;
530     }
531     if (o.isSVGContainer()) {
532         writeSVGContainer(ts, o, indent);
533         return;
534     }
535     if (o.isSVGRoot()) {
536         write(ts, *toRenderSVGRoot(&o), indent);
537         return;
538     }
539     if (o.isSVGText()) {
540         writeSVGText(ts, *toRenderSVGText(&o), indent);
541         return;
542     }
543     if (o.isSVGInlineText()) {
544         writeSVGInlineText(ts, *toRenderSVGInlineText(&o), indent);
545         return;
546     }
547     if (o.isSVGImage()) {
548         writeSVGImage(ts, *toRenderSVGImage(&o), indent);
549         return;
550     }
551 #endif
552
553     writeIndent(ts, indent);
554
555     RenderTreeAsText::writeRenderObject(ts, o, behavior);
556     ts << "\n";
557
558     if (o.isText() && !o.isBR()) {
559         const RenderText& text = *toRenderText(&o);
560         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
561             writeIndent(ts, indent + 1);
562             writeTextRun(ts, text, *box);
563         }
564     }
565
566     for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
567         if (child->hasLayer())
568             continue;
569         write(ts, *child, indent + 1, behavior);
570     }
571
572     if (o.isWidget()) {
573         Widget* widget = toRenderWidget(&o)->widget();
574         if (widget && widget->isFrameView()) {
575             FrameView* view = static_cast<FrameView*>(widget);
576             RenderView* root = view->frame()->contentRenderer();
577             if (root) {
578                 view->layout();
579                 RenderLayer* l = root->layer();
580                 if (l)
581                     writeLayers(ts, l, l, l->rect(), indent + 1, behavior);
582             }
583         }
584     }
585 }
586
587 enum LayerPaintPhase {
588     LayerPaintPhaseAll = 0,
589     LayerPaintPhaseBackground = -1,
590     LayerPaintPhaseForeground = 1
591 };
592
593 static void write(TextStream& ts, RenderLayer& l,
594                   const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect,
595                   LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
596 {
597     writeIndent(ts, indent);
598
599     ts << "layer ";
600     
601     if (behavior & RenderAsTextShowAddresses)
602         ts << static_cast<const void*>(&l) << " ";
603       
604     ts << layerBounds;
605
606     if (!layerBounds.isEmpty()) {
607         if (!backgroundClipRect.contains(layerBounds))
608             ts << " backgroundClip " << backgroundClipRect;
609         if (!clipRect.contains(layerBounds))
610             ts << " clip " << clipRect;
611         if (!outlineClipRect.contains(layerBounds))
612             ts << " outlineClip " << outlineClipRect;
613     }
614
615     if (l.renderer()->hasOverflowClip()) {
616         if (l.scrollXOffset())
617             ts << " scrollX " << l.scrollXOffset();
618         if (l.scrollYOffset())
619             ts << " scrollY " << l.scrollYOffset();
620         if (l.renderBox() && l.renderBox()->pixelSnappedClientWidth() != l.scrollWidth())
621             ts << " scrollWidth " << l.scrollWidth();
622         if (l.renderBox() && l.renderBox()->pixelSnappedClientHeight() != l.scrollHeight())
623             ts << " scrollHeight " << l.scrollHeight();
624     }
625
626     if (paintPhase == LayerPaintPhaseBackground)
627         ts << " layerType: background only";
628     else if (paintPhase == LayerPaintPhaseForeground)
629         ts << " layerType: foreground only";
630     
631 #if USE(ACCELERATED_COMPOSITING)
632     if (behavior & RenderAsTextShowCompositedLayers) {
633         if (l.isComposited())
634             ts << " (composited, bounds " << l.backing()->compositedBounds() << ")";
635     }
636 #else
637     UNUSED_PARAM(behavior);
638 #endif
639     
640     ts << "\n";
641
642     if (paintPhase != LayerPaintPhaseBackground)
643         write(ts, *l.renderer(), indent + 1, behavior);
644 }
645
646 static void writeRenderNamedFlowThreads(TextStream& ts, RenderView* renderView, const RenderLayer* rootLayer,
647                         const IntRect& paintRect, int indent, RenderAsTextBehavior behavior)
648 {
649     if (!renderView->hasRenderNamedFlowThreads())
650         return;
651
652     const RenderNamedFlowThreadList* list = renderView->flowThreadController()->renderNamedFlowThreadList();
653
654     writeIndent(ts, indent);
655     ts << "Flow Threads\n";
656
657     for (RenderNamedFlowThreadList::const_iterator iter = list->begin(); iter != list->end(); ++iter) {
658         const RenderNamedFlowThread* renderFlowThread = *iter;
659
660         writeIndent(ts, indent + 1);
661         ts << "Thread with flow-name '" << renderFlowThread->flowThreadName() << "'\n";
662
663         RenderLayer* layer = renderFlowThread->layer();
664         writeLayers(ts, rootLayer, layer, paintRect, indent + 2, behavior);
665
666         // Display the render regions attached to this flow thread
667         const RenderRegionList& flowThreadRegionList = renderFlowThread->renderRegionList();
668         if (!flowThreadRegionList.isEmpty()) {
669             writeIndent(ts, indent + 1);
670             ts << "Regions for flow '"<< renderFlowThread->flowThreadName() << "'\n";
671             for (RenderRegionList::const_iterator itRR = flowThreadRegionList.begin(); itRR != flowThreadRegionList.end(); ++itRR) {
672                 RenderRegion* renderRegion = *itRR;
673                 writeIndent(ts, indent + 2);
674                 ts << "RenderRegion";
675                 if (renderRegion->node()) {
676                     String tagName = getTagName(renderRegion->node());
677                     if (!tagName.isEmpty())
678                         ts << " {" << tagName << "}";
679                     if (renderRegion->node()->isElementNode() && renderRegion->node()->hasID()) {
680                         Element* element = static_cast<Element*>(renderRegion->node());
681                         ts << " #" << element->idForStyleResolution();
682                     }
683                     if (renderRegion->hasCustomRegionStyle())
684                         ts << " region style: 1";
685                 }
686                 if (!renderRegion->isValid())
687                     ts << " invalid";
688                 ts << "\n";
689             }
690         }
691     }
692 }
693
694 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
695                         const IntRect& paintRect, int indent, RenderAsTextBehavior behavior)
696 {
697     // FIXME: Apply overflow to the root layer to not break every test.  Complete hack.  Sigh.
698     IntRect paintDirtyRect(paintRect);
699     if (rootLayer == l) {
700         paintDirtyRect.setWidth(max(paintDirtyRect.width(), rootLayer->renderBox()->maxXLayoutOverflow()));
701         paintDirtyRect.setHeight(max(paintDirtyRect.height(), rootLayer->renderBox()->maxYLayoutOverflow()));
702         l->setSize(l->size().expandedTo(l->renderBox()->maxLayoutOverflow()));
703     }
704     
705     // Calculate the clip rects we should use.
706     IntRect layerBounds;
707     ClipRect damageRect, clipRectToApply, outlineRect;
708     l->calculateRects(rootLayer, 0, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, true);
709
710     // Ensure our lists are up-to-date.
711     l->updateZOrderLists();
712     l->updateNormalFlowList();
713
714     bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer);
715     Vector<RenderLayer*>* negList = l->negZOrderList();
716     bool paintsBackgroundSeparately = negList && negList->size() > 0;
717     if (shouldPaint && paintsBackgroundSeparately)
718         write(ts, *l, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), LayerPaintPhaseBackground, indent, behavior);
719
720     if (negList) {
721         int currIndent = indent;
722         if (behavior & RenderAsTextShowLayerNesting) {
723             writeIndent(ts, indent);
724             ts << " negative z-order list(" << negList->size() << ")\n";
725             ++currIndent;
726         }
727         for (unsigned i = 0; i != negList->size(); ++i)
728             writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior);
729     }
730
731     if (shouldPaint)
732         write(ts, *l, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
733
734     if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) {
735         int currIndent = indent;
736         if (behavior & RenderAsTextShowLayerNesting) {
737             writeIndent(ts, indent);
738             ts << " normal flow list(" << normalFlowList->size() << ")\n";
739             ++currIndent;
740         }
741         for (unsigned i = 0; i != normalFlowList->size(); ++i)
742             writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior);
743     }
744
745     if (Vector<RenderLayer*>* posList = l->posZOrderList()) {
746         int currIndent = indent;
747         if (behavior & RenderAsTextShowLayerNesting) {
748             writeIndent(ts, indent);
749             ts << " positive z-order list(" << posList->size() << ")\n";
750             ++currIndent;
751         }
752         for (unsigned i = 0; i != posList->size(); ++i)
753             writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior);
754     }
755     
756     // Altough the RenderFlowThread requires a layer, it is not collected by its parent,
757     // so we have to treat it as a special case.
758     if (l->renderer()->isRenderView()) {
759         RenderView* renderView = toRenderView(l->renderer());
760         writeRenderNamedFlowThreads(ts, renderView, rootLayer, paintDirtyRect, indent, behavior);
761     }
762 }
763
764 static String nodePosition(Node* node)
765 {
766     String result;
767
768     Element* body = node->document()->body();
769     Node* parent;
770     for (Node* n = node; n; n = parent) {
771         parent = n->parentOrHostNode();
772         if (n != node)
773             result += " of ";
774         if (parent) {
775             if (body && n == body) {
776                 // We don't care what offset body may be in the document.
777                 result += "body";
778                 break;
779             }
780             if (n->isShadowRoot())
781                 result += "{" + getTagName(n) + "}";
782             else
783                 result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
784         } else
785             result += "document";
786     }
787
788     return result;
789 }
790
791 static void writeSelection(TextStream& ts, const RenderObject* o)
792 {
793     Node* n = o->node();
794     if (!n || !n->isDocumentNode())
795         return;
796
797     Document* doc = static_cast<Document*>(n);
798     Frame* frame = doc->frame();
799     if (!frame)
800         return;
801
802     VisibleSelection selection = frame->selection()->selection();
803     if (selection.isCaret()) {
804         ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode());
805         if (selection.affinity() == UPSTREAM)
806             ts << " (upstream affinity)";
807         ts << "\n";
808     } else if (selection.isRange())
809         ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n"
810            << "selection end:   position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n";
811 }
812
813 static String externalRepresentation(RenderBox* renderer, RenderAsTextBehavior behavior)
814 {
815     TextStream ts;
816     if (!renderer->hasLayer())
817         return ts.release();
818         
819     RenderLayer* layer = renderer->layer();
820     writeLayers(ts, layer, layer, layer->rect(), 0, behavior);
821     writeSelection(ts, renderer);
822     return ts.release();
823 }
824
825 String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
826 {
827     RenderObject* renderer = frame->contentRenderer();
828     if (!renderer || !renderer->isBox())
829         return String();
830
831     PrintContext printContext(frame);
832     if (behavior & RenderAsTextPrintingMode)
833         printContext.begin(toRenderBox(renderer)->width());
834     if (!(behavior & RenderAsTextDontUpdateLayout))
835         frame->document()->updateLayout();
836
837     return externalRepresentation(toRenderBox(renderer), behavior);
838 }
839
840 String externalRepresentation(Element* element, RenderAsTextBehavior behavior)
841 {
842     RenderObject* renderer = element->renderer();
843     if (!renderer || !renderer->isBox())
844         return String();
845     // Doesn't support printing mode.
846     ASSERT(!(behavior & RenderAsTextPrintingMode));
847     if (!(behavior & RenderAsTextDontUpdateLayout) && element->document())
848         element->document()->updateLayout();
849     
850     return externalRepresentation(toRenderBox(renderer), behavior | RenderAsTextShowAllLayers);
851 }
852
853 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
854 {
855     for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) {
856         if (child->isCounter()) {
857             if (!isFirstCounter)
858                 stream << " ";
859             isFirstCounter = false;
860             String str(toRenderText(child)->text());
861             stream << str;
862         }
863     }
864 }
865
866 String counterValueForElement(Element* element)
867 {
868     // Make sure the element is not freed during the layout.
869     RefPtr<Element> elementRef(element);
870     element->document()->updateLayout();
871     TextStream stream;
872     bool isFirstCounter = true;
873     // The counter renderers should be children of :before or :after pseudo-elements.
874     if (RenderObject* renderer = element->renderer()) {
875         if (RenderObject* pseudoElement = renderer->beforePseudoElementRenderer())
876             writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
877         if (RenderObject* pseudoElement = renderer->afterPseudoElementRenderer())
878             writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
879     }
880     return stream.release();
881 }
882
883 String markerTextForListItem(Element* element)
884 {
885     // Make sure the element is not freed during the layout.
886     RefPtr<Element> elementRef(element);
887     element->document()->updateLayout();
888
889     RenderObject* renderer = element->renderer();
890     if (!renderer || !renderer->isListItem())
891         return String();
892
893     return toRenderListItem(renderer)->markerText();
894 }
895
896 } // namespace WebCore