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