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