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