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