OSDN Git Service

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