6a639edc17c4048d2ca22dc6d39da9b2af224bb8
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 "core/rendering/RenderTreeAsText.h"
28
29 #include "HTMLNames.h"
30 #include "core/css/StylePropertySet.h"
31 #include "core/dom/Document.h"
32 #include "core/editing/FrameSelection.h"
33 #include "core/html/HTMLElement.h"
34 #include "core/frame/Frame.h"
35 #include "core/frame/FrameView.h"
36 #include "core/page/PrintContext.h"
37 #include "core/rendering/CompositedLayerMapping.h"
38 #include "core/rendering/FlowThreadController.h"
39 #include "core/rendering/InlineTextBox.h"
40 #include "core/rendering/RenderBR.h"
41 #include "core/rendering/RenderDetailsMarker.h"
42 #include "core/rendering/RenderFileUploadControl.h"
43 #include "core/rendering/RenderFlowThread.h"
44 #include "core/rendering/RenderInline.h"
45 #include "core/rendering/RenderLayer.h"
46 #include "core/rendering/RenderListItem.h"
47 #include "core/rendering/RenderListMarker.h"
48 #include "core/rendering/RenderPart.h"
49 #include "core/rendering/RenderTableCell.h"
50 #include "core/rendering/RenderView.h"
51 #include "core/rendering/RenderWidget.h"
52 #include "core/rendering/svg/RenderSVGContainer.h"
53 #include "core/rendering/svg/RenderSVGGradientStop.h"
54 #include "core/rendering/svg/RenderSVGImage.h"
55 #include "core/rendering/svg/RenderSVGInlineText.h"
56 #include "core/rendering/svg/RenderSVGPath.h"
57 #include "core/rendering/svg/RenderSVGRoot.h"
58 #include "core/rendering/svg/RenderSVGText.h"
59 #include "core/rendering/svg/SVGRenderTreeAsText.h"
60 #include "wtf/HexNumber.h"
61 #include "wtf/Vector.h"
62 #include "wtf/unicode/CharacterNames.h"
63
64 namespace WebCore {
65
66 using namespace HTMLNames;
67
68 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
69 {
70     switch (borderStyle) {
71         case BNONE:
72             ts << "none";
73             break;
74         case BHIDDEN:
75             ts << "hidden";
76             break;
77         case INSET:
78             ts << "inset";
79             break;
80         case GROOVE:
81             ts << "groove";
82             break;
83         case RIDGE:
84             ts << "ridge";
85             break;
86         case OUTSET:
87             ts << "outset";
88             break;
89         case DOTTED:
90             ts << "dotted";
91             break;
92         case DASHED:
93             ts << "dashed";
94             break;
95         case SOLID:
96             ts << "solid";
97             break;
98         case DOUBLE:
99             ts << "double";
100             break;
101     }
102
103     ts << " ";
104 }
105
106 static String getTagName(Node* n)
107 {
108     if (n->isDocumentNode())
109         return "";
110     if (n->nodeType() == Node::COMMENT_NODE)
111         return "COMMENT";
112     return n->nodeName();
113 }
114
115 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
116 {
117     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
118         return false;
119
120     const HTMLElement* elem = toHTMLElement(node);
121     if (elem->getAttribute(classAttr) != "Apple-style-span")
122         return false;
123
124     if (!node->hasChildNodes())
125         return true;
126
127     const StylePropertySet* inlineStyleDecl = elem->inlineStyle();
128     return (!inlineStyleDecl || inlineStyleDecl->isEmpty());
129 }
130
131 String quoteAndEscapeNonPrintables(const String& s)
132 {
133     StringBuilder result;
134     result.append('"');
135     for (unsigned i = 0; i != s.length(); ++i) {
136         UChar c = s[i];
137         if (c == '\\') {
138             result.append('\\');
139             result.append('\\');
140         } else if (c == '"') {
141             result.append('\\');
142             result.append('"');
143         } else if (c == '\n' || c == noBreakSpace)
144             result.append(' ');
145         else {
146             if (c >= 0x20 && c < 0x7F)
147                 result.append(c);
148             else {
149                 result.append('\\');
150                 result.append('x');
151                 result.append('{');
152                 appendUnsignedAsHex(c, result);
153                 result.append('}');
154             }
155         }
156     }
157     result.append('"');
158     return result.toString();
159 }
160
161 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
162 {
163     ts << o.renderName();
164
165     if (behavior & RenderAsTextShowAddresses)
166         ts << " " << static_cast<const void*>(&o);
167
168     if (o.style() && o.style()->zIndex())
169         ts << " zI: " << o.style()->zIndex();
170
171     if (o.node()) {
172         String tagName = getTagName(o.node());
173         // FIXME: Temporary hack to make tests pass by simulating the old generated content output.
174         if (o.isPseudoElement() || (o.parent() && o.parent()->isPseudoElement()))
175             tagName = emptyAtom;
176         if (!tagName.isEmpty()) {
177             ts << " {" << tagName << "}";
178             // flag empty or unstyled AppleStyleSpan because we never
179             // want to leave them in the DOM
180             if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
181                 ts << " *empty or unstyled AppleStyleSpan*";
182         }
183     }
184
185     RenderBlock* cb = o.containingBlock();
186     bool adjustForTableCells = cb ? cb->isTableCell() : false;
187
188     LayoutRect r;
189     if (o.isText()) {
190         // 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
191         // many test results.
192         const RenderText& text = *toRenderText(&o);
193         IntRect linesBox = text.linesBoundingBox();
194         r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
195         if (adjustForTableCells && !text.firstTextBox())
196             adjustForTableCells = false;
197     } else if (o.isRenderInline()) {
198         // FIXME: Would be better not to just dump 0, 0 as the x and y here.
199         const RenderInline& inlineFlow = *toRenderInline(&o);
200         r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
201         adjustForTableCells = false;
202     } else if (o.isTableCell()) {
203         // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect.  We'd like
204         // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
205         // captured by the results.
206         const RenderTableCell& cell = *toRenderTableCell(&o);
207         r = LayoutRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
208     } else if (o.isBox())
209         r = toRenderBox(&o)->frameRect();
210
211     // FIXME: Temporary in order to ensure compatibility with existing layout test results.
212     if (adjustForTableCells)
213         r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
214
215     ts << " " << r;
216
217     if (!(o.isText() && !o.isBR())) {
218         if (o.isFileUploadControl())
219             ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
220
221         if (o.parent()) {
222             Color color = o.resolveColor(CSSPropertyColor);
223             if (o.parent()->resolveColor(CSSPropertyColor) != color)
224                 ts << " [color=" << color.nameForRenderTreeAsText() << "]";
225
226             // Do not dump invalid or transparent backgrounds, since that is the default.
227             Color backgroundColor = o.resolveColor(CSSPropertyBackgroundColor);
228             if (o.parent()->resolveColor(CSSPropertyBackgroundColor) != backgroundColor
229                 && backgroundColor.rgb())
230                 ts << " [bgcolor=" << backgroundColor.nameForRenderTreeAsText() << "]";
231
232             Color textFillColor = o.resolveColor(CSSPropertyWebkitTextFillColor);
233             if (o.parent()->resolveColor(CSSPropertyWebkitTextFillColor) != textFillColor
234                 && textFillColor != color && textFillColor.rgb())
235                 ts << " [textFillColor=" << textFillColor.nameForRenderTreeAsText() << "]";
236
237             Color textStrokeColor = o.resolveColor(CSSPropertyWebkitTextStrokeColor);
238             if (o.parent()->resolveColor(CSSPropertyWebkitTextStrokeColor) != textStrokeColor
239                 && textStrokeColor != color && textStrokeColor.rgb())
240                 ts << " [textStrokeColor=" << textStrokeColor.nameForRenderTreeAsText() << "]";
241
242             if (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth() && o.style()->textStrokeWidth() > 0)
243                 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
244         }
245
246         if (!o.isBoxModelObject())
247             return;
248
249         const RenderBoxModelObject& box = *toRenderBoxModelObject(&o);
250         if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
251             ts << " [border:";
252
253             BorderValue prevBorder = o.style()->borderTop();
254             if (!box.borderTop())
255                 ts << " none";
256             else {
257                 ts << " (" << box.borderTop() << "px ";
258                 printBorderStyle(ts, o.style()->borderTopStyle());
259                 Color col = o.resolveColor(CSSPropertyBorderTopColor);
260                 ts << col.nameForRenderTreeAsText() << ")";
261             }
262
263             if (o.style()->borderRight() != prevBorder) {
264                 prevBorder = o.style()->borderRight();
265                 if (!box.borderRight())
266                     ts << " none";
267                 else {
268                     ts << " (" << box.borderRight() << "px ";
269                     printBorderStyle(ts, o.style()->borderRightStyle());
270                     Color col = o.resolveColor(CSSPropertyBorderRightColor);
271                     ts << col.nameForRenderTreeAsText() << ")";
272                 }
273             }
274
275             if (o.style()->borderBottom() != prevBorder) {
276                 prevBorder = box.style()->borderBottom();
277                 if (!box.borderBottom())
278                     ts << " none";
279                 else {
280                     ts << " (" << box.borderBottom() << "px ";
281                     printBorderStyle(ts, o.style()->borderBottomStyle());
282                     Color col = o.resolveColor(CSSPropertyBorderBottomColor);
283                     ts << col.nameForRenderTreeAsText() << ")";
284                 }
285             }
286
287             if (o.style()->borderLeft() != prevBorder) {
288                 prevBorder = o.style()->borderLeft();
289                 if (!box.borderLeft())
290                     ts << " none";
291                 else {
292                     ts << " (" << box.borderLeft() << "px ";
293                     printBorderStyle(ts, o.style()->borderLeftStyle());
294                     Color col = o.resolveColor(CSSPropertyBorderLeftColor);
295                     ts << col.nameForRenderTreeAsText() << ")";
296                 }
297             }
298
299             ts << "]";
300         }
301     }
302
303     if (o.isTableCell()) {
304         const RenderTableCell& c = *toRenderTableCell(&o);
305         ts << " [r=" << c.rowIndex() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
306     }
307
308     if (o.isDetailsMarker()) {
309         ts << ": ";
310         switch (toRenderDetailsMarker(&o)->orientation()) {
311         case RenderDetailsMarker::Left:
312             ts << "left";
313             break;
314         case RenderDetailsMarker::Right:
315             ts << "right";
316             break;
317         case RenderDetailsMarker::Up:
318             ts << "up";
319             break;
320         case RenderDetailsMarker::Down:
321             ts << "down";
322             break;
323         }
324     }
325
326     if (o.isListMarker()) {
327         String text = toRenderListMarker(&o)->text();
328         if (!text.isEmpty()) {
329             if (text.length() != 1)
330                 text = quoteAndEscapeNonPrintables(text);
331             else {
332                 switch (text[0]) {
333                     case bullet:
334                         text = "bullet";
335                         break;
336                     case blackSquare:
337                         text = "black square";
338                         break;
339                     case whiteBullet:
340                         text = "white bullet";
341                         break;
342                     default:
343                         text = quoteAndEscapeNonPrintables(text);
344                 }
345             }
346             ts << ": " << text;
347         }
348     }
349
350     if (behavior & RenderAsTextShowIDAndClass) {
351         if (Node* node = o.node()) {
352             if (node->hasID())
353                 ts << " id=\"" + toElement(node)->getIdAttribute() + "\"";
354
355             if (node->hasClass()) {
356                 ts << " class=\"";
357                 Element* element = toElement(node);
358                 for (size_t i = 0; i < element->classNames().size(); ++i) {
359                     if (i > 0)
360                         ts << " ";
361                     ts << element->classNames()[i];
362                 }
363                 ts << "\"";
364             }
365         }
366     }
367
368     if (behavior & RenderAsTextShowLayoutState) {
369         bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
370         if (needsLayout)
371             ts << " (needs layout:";
372
373         bool havePrevious = false;
374         if (o.selfNeedsLayout()) {
375             ts << " self";
376             havePrevious = true;
377         }
378
379         if (o.needsPositionedMovementLayout()) {
380             if (havePrevious)
381                 ts << ",";
382             havePrevious = true;
383             ts << " positioned movement";
384         }
385
386         if (o.normalChildNeedsLayout()) {
387             if (havePrevious)
388                 ts << ",";
389             havePrevious = true;
390             ts << " child";
391         }
392
393         if (o.posChildNeedsLayout()) {
394             if (havePrevious)
395                 ts << ",";
396             ts << " positioned child";
397         }
398
399         if (needsLayout)
400             ts << ")";
401     }
402 }
403
404 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
405 {
406     // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
407     // to detect any changes caused by the conversion to floating point. :(
408     int x = run.x();
409     int y = run.y();
410     int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
411
412     // FIXME: Table cell adjustment is temporary until results can be updated.
413     if (o.containingBlock()->isTableCell())
414         y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
415
416     ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
417     if (!run.isLeftToRightDirection() || run.dirOverride()) {
418         ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
419         if (run.dirOverride())
420             ts << " override";
421     }
422     ts << ": "
423         << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
424     if (run.hasHyphen())
425         ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
426     ts << "\n";
427 }
428
429 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
430 {
431     if (o.isSVGShape()) {
432         write(ts, *toRenderSVGShape(&o), indent);
433         return;
434     }
435     if (o.isSVGGradientStop()) {
436         writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent);
437         return;
438     }
439     if (o.isSVGResourceContainer()) {
440         writeSVGResourceContainer(ts, o, indent);
441         return;
442     }
443     if (o.isSVGContainer()) {
444         writeSVGContainer(ts, o, indent);
445         return;
446     }
447     if (o.isSVGRoot()) {
448         write(ts, *toRenderSVGRoot(&o), indent);
449         return;
450     }
451     if (o.isSVGText()) {
452         writeSVGText(ts, *toRenderSVGText(&o), indent);
453         return;
454     }
455     if (o.isSVGInlineText()) {
456         writeSVGInlineText(ts, *toRenderSVGInlineText(&o), indent);
457         return;
458     }
459     if (o.isSVGImage()) {
460         writeSVGImage(ts, *toRenderSVGImage(&o), indent);
461         return;
462     }
463
464     writeIndent(ts, indent);
465
466     RenderTreeAsText::writeRenderObject(ts, o, behavior);
467     ts << "\n";
468
469     if (o.isText() && !o.isBR()) {
470         const RenderText& text = *toRenderText(&o);
471         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
472             writeIndent(ts, indent + 1);
473             writeTextRun(ts, text, *box);
474         }
475     }
476
477     for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
478         if (child->hasLayer())
479             continue;
480         write(ts, *child, indent + 1, behavior);
481     }
482
483     if (o.isWidget()) {
484         Widget* widget = toRenderWidget(&o)->widget();
485         if (widget && widget->isFrameView()) {
486             FrameView* view = toFrameView(widget);
487             RenderView* root = view->frame().contentRenderer();
488             if (root) {
489                 view->layout();
490                 RenderLayer* l = root->layer();
491                 if (l)
492                     RenderTreeAsText::writeLayers(ts, l, l, l->rect(), indent + 1, behavior);
493             }
494         }
495     }
496 }
497
498 enum LayerPaintPhase {
499     LayerPaintPhaseAll = 0,
500     LayerPaintPhaseBackground = -1,
501     LayerPaintPhaseForeground = 1
502 };
503
504 static void write(TextStream& ts, RenderLayer& l,
505                   const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect, const LayoutRect& clipRect, const LayoutRect& outlineClipRect,
506                   LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
507 {
508     IntRect adjustedLayoutBounds = pixelSnappedIntRect(layerBounds);
509     IntRect adjustedBackgroundClipRect = pixelSnappedIntRect(backgroundClipRect);
510     IntRect adjustedClipRect = pixelSnappedIntRect(clipRect);
511     IntRect adjustedOutlineClipRect = pixelSnappedIntRect(outlineClipRect);
512
513     writeIndent(ts, indent);
514
515     if (l.renderer()->style()->visibility() == HIDDEN)
516         ts << "hidden ";
517
518     ts << "layer ";
519
520     if (behavior & RenderAsTextShowAddresses)
521         ts << static_cast<const void*>(&l) << " ";
522
523     ts << adjustedLayoutBounds;
524
525     if (!adjustedLayoutBounds.isEmpty()) {
526         if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
527             ts << " backgroundClip " << adjustedBackgroundClipRect;
528         if (!adjustedClipRect.contains(adjustedLayoutBounds))
529             ts << " clip " << adjustedClipRect;
530         if (!adjustedOutlineClipRect.contains(adjustedLayoutBounds))
531             ts << " outlineClip " << adjustedOutlineClipRect;
532     }
533     if (l.isTransparent())
534         ts << " transparent";
535
536     if (l.renderer()->hasOverflowClip()) {
537         if (l.scrollableArea()->scrollXOffset())
538             ts << " scrollX " << l.scrollableArea()->scrollXOffset();
539         if (l.scrollableArea()->scrollYOffset())
540             ts << " scrollY " << l.scrollableArea()->scrollYOffset();
541         if (l.renderBox() && l.renderBox()->pixelSnappedClientWidth() != l.renderBox()->scrollWidth())
542             ts << " scrollWidth " << l.renderBox()->scrollWidth();
543         if (l.renderBox() && l.renderBox()->pixelSnappedClientHeight() != l.renderBox()->scrollHeight())
544             ts << " scrollHeight " << l.renderBox()->scrollHeight();
545     }
546
547     if (paintPhase == LayerPaintPhaseBackground)
548         ts << " layerType: background only";
549     else if (paintPhase == LayerPaintPhaseForeground)
550         ts << " layerType: foreground only";
551
552     if (behavior & RenderAsTextShowCompositedLayers) {
553         if (l.hasCompositedLayerMapping()) {
554             ts << " (composited, bounds="
555                 << l.compositedLayerMapping()->compositedBounds()
556                 << ", drawsContent="
557                 << l.compositedLayerMapping()->mainGraphicsLayer()->drawsContent()
558                 << ", paints into ancestor="
559                 << l.compositedLayerMapping()->paintsIntoCompositedAncestor()
560                 << ")";
561         }
562     }
563
564     ts << "\n";
565
566     if (paintPhase != LayerPaintPhaseBackground)
567         write(ts, *l.renderer(), indent + 1, behavior);
568 }
569
570 void RenderTreeAsText::writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* layer,
571                         const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
572 {
573     // FIXME: Apply overflow to the root layer to not break every test.  Complete hack.  Sigh.
574     LayoutRect paintDirtyRect(paintRect);
575     if (rootLayer == layer) {
576         paintDirtyRect.setWidth(max<LayoutUnit>(paintDirtyRect.width(), rootLayer->renderBox()->layoutOverflowRect().maxX()));
577         paintDirtyRect.setHeight(max<LayoutUnit>(paintDirtyRect.height(), rootLayer->renderBox()->layoutOverflowRect().maxY()));
578         layer->setSize(layer->size().expandedTo(pixelSnappedIntSize(layer->renderBox()->maxLayoutOverflow(), LayoutPoint(0, 0))));
579     }
580
581     // Calculate the clip rects we should use.
582     LayoutRect layerBounds;
583     ClipRect damageRect, clipRectToApply, outlineRect;
584     layer->clipper().calculateRects(ClipRectsContext(rootLayer, TemporaryClipRects), paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
585
586     // Ensure our lists are up-to-date.
587     layer->stackingNode()->updateLayerListsIfNeeded();
588
589     bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : layer->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer);
590
591     Vector<RenderLayerStackingNode*>* negList = layer->stackingNode()->negZOrderList();
592     bool paintsBackgroundSeparately = negList && negList->size() > 0;
593     if (shouldPaint && paintsBackgroundSeparately)
594         write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), LayerPaintPhaseBackground, indent, behavior);
595
596     if (negList) {
597         int currIndent = indent;
598         if (behavior & RenderAsTextShowLayerNesting) {
599             writeIndent(ts, indent);
600             ts << " negative z-order list(" << negList->size() << ")\n";
601             ++currIndent;
602         }
603         for (unsigned i = 0; i != negList->size(); ++i)
604             writeLayers(ts, rootLayer, negList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
605     }
606
607     if (shouldPaint)
608         write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
609
610     if (Vector<RenderLayerStackingNode*>* normalFlowList = layer->stackingNode()->normalFlowList()) {
611         int currIndent = indent;
612         if (behavior & RenderAsTextShowLayerNesting) {
613             writeIndent(ts, indent);
614             ts << " normal flow list(" << normalFlowList->size() << ")\n";
615             ++currIndent;
616         }
617         for (unsigned i = 0; i != normalFlowList->size(); ++i)
618             writeLayers(ts, rootLayer, normalFlowList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
619     }
620
621     if (Vector<RenderLayerStackingNode*>* posList = layer->stackingNode()->posZOrderList()) {
622         int currIndent = indent;
623         if (behavior & RenderAsTextShowLayerNesting) {
624             writeIndent(ts, indent);
625             ts << " positive z-order list(" << posList->size() << ")\n";
626             ++currIndent;
627         }
628         for (unsigned i = 0; i != posList->size(); ++i)
629             writeLayers(ts, rootLayer, posList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
630     }
631 }
632
633 static String nodePosition(Node* node)
634 {
635     StringBuilder result;
636
637     Element* body = node->document().body();
638     Node* parent;
639     for (Node* n = node; n; n = parent) {
640         parent = n->parentOrShadowHostNode();
641         if (n != node)
642             result.appendLiteral(" of ");
643         if (parent) {
644             if (body && n == body) {
645                 // We don't care what offset body may be in the document.
646                 result.appendLiteral("body");
647                 break;
648             }
649             if (n->isShadowRoot()) {
650                 result.append('{');
651                 result.append(getTagName(n));
652                 result.append('}');
653             } else {
654                 result.appendLiteral("child ");
655                 result.appendNumber(n->nodeIndex());
656                 result.appendLiteral(" {");
657                 result.append(getTagName(n));
658                 result.append('}');
659             }
660         } else
661             result.appendLiteral("document");
662     }
663
664     return result.toString();
665 }
666
667 static void writeSelection(TextStream& ts, const RenderObject* o)
668 {
669     Node* n = o->node();
670     if (!n || !n->isDocumentNode())
671         return;
672
673     Document* doc = toDocument(n);
674     Frame* frame = doc->frame();
675     if (!frame)
676         return;
677
678     VisibleSelection selection = frame->selection().selection();
679     if (selection.isCaret()) {
680         ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode());
681         if (selection.affinity() == UPSTREAM)
682             ts << " (upstream affinity)";
683         ts << "\n";
684     } else if (selection.isRange())
685         ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n"
686            << "selection end:   position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n";
687 }
688
689 static String externalRepresentation(RenderBox* renderer, RenderAsTextBehavior behavior)
690 {
691     TextStream ts;
692     if (!renderer->hasLayer())
693         return ts.release();
694
695     RenderLayer* layer = renderer->layer();
696     RenderTreeAsText::writeLayers(ts, layer, layer, layer->rect(), 0, behavior);
697     writeSelection(ts, renderer);
698     return ts.release();
699 }
700
701 String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
702 {
703     if (!(behavior & RenderAsTextDontUpdateLayout))
704         frame->document()->updateLayout();
705
706     RenderObject* renderer = frame->contentRenderer();
707     if (!renderer || !renderer->isBox())
708         return String();
709
710     PrintContext printContext(frame);
711     if (behavior & RenderAsTextPrintingMode)
712         printContext.begin(toRenderBox(renderer)->width());
713
714     return externalRepresentation(toRenderBox(renderer), behavior);
715 }
716
717 String externalRepresentation(Element* element, RenderAsTextBehavior behavior)
718 {
719     // Doesn't support printing mode.
720     ASSERT(!(behavior & RenderAsTextPrintingMode));
721     if (!(behavior & RenderAsTextDontUpdateLayout))
722         element->document().updateLayout();
723
724     RenderObject* renderer = element->renderer();
725     if (!renderer || !renderer->isBox())
726         return String();
727
728     return externalRepresentation(toRenderBox(renderer), behavior | RenderAsTextShowAllLayers);
729 }
730
731 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
732 {
733     for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) {
734         if (child->isCounter()) {
735             if (!isFirstCounter)
736                 stream << " ";
737             isFirstCounter = false;
738             String str(toRenderText(child)->text());
739             stream << str;
740         }
741     }
742 }
743
744 String counterValueForElement(Element* element)
745 {
746     // Make sure the element is not freed during the layout.
747     RefPtr<Element> elementRef(element);
748     element->document().updateLayout();
749     TextStream stream;
750     bool isFirstCounter = true;
751     // The counter renderers should be children of :before or :after pseudo-elements.
752     if (RenderObject* before = element->pseudoElementRenderer(BEFORE))
753         writeCounterValuesFromChildren(stream, before, isFirstCounter);
754     if (RenderObject* after = element->pseudoElementRenderer(AFTER))
755         writeCounterValuesFromChildren(stream, after, isFirstCounter);
756     return stream.release();
757 }
758
759 String markerTextForListItem(Element* element)
760 {
761     // Make sure the element is not freed during the layout.
762     RefPtr<Element> elementRef(element);
763     element->document().updateLayout();
764
765     RenderObject* renderer = element->renderer();
766     if (!renderer || !renderer->isListItem())
767         return String();
768
769     return toRenderListItem(renderer)->markerText();
770 }
771
772 } // namespace WebCore