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 blink {
     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 TextStream& operator<<(TextStream& ts, const Color& c)
    162 {
    163     return ts << c.nameForRenderTreeAsText();
    164 }
    165 
    166 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
    167 {
    168     ts << o.renderName();
    169 
    170     if (behavior & RenderAsTextShowAddresses)
    171         ts << " " << static_cast<const void*>(&o);
    172 
    173     if (o.style() && o.style()->zIndex())
    174         ts << " zI: " << o.style()->zIndex();
    175 
    176     if (o.node()) {
    177         String tagName = getTagName(o.node());
    178         // FIXME: Temporary hack to make tests pass by simulating the old generated content output.
    179         if (o.isPseudoElement() || (o.parent() && o.parent()->isPseudoElement()))
    180             tagName = emptyAtom;
    181         if (!tagName.isEmpty()) {
    182             ts << " {" << tagName << "}";
    183             // flag empty or unstyled AppleStyleSpan because we never
    184             // want to leave them in the DOM
    185             if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
    186                 ts << " *empty or unstyled AppleStyleSpan*";
    187         }
    188     }
    189 
    190     RenderBlock* cb = o.containingBlock();
    191     bool adjustForTableCells = cb ? cb->isTableCell() : false;
    192 
    193     LayoutRect r;
    194     if (o.isText()) {
    195         // 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
    196         // many test results.
    197         const RenderText& text = toRenderText(o);
    198         IntRect linesBox = text.linesBoundingBox();
    199         r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
    200         if (adjustForTableCells && !text.firstTextBox())
    201             adjustForTableCells = false;
    202     } else if (o.isRenderInline()) {
    203         // FIXME: Would be better not to just dump 0, 0 as the x and y here.
    204         const RenderInline& inlineFlow = toRenderInline(o);
    205         r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
    206         adjustForTableCells = false;
    207     } else if (o.isTableCell()) {
    208         // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect.  We'd like
    209         // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
    210         // captured by the results.
    211         const RenderTableCell& cell = toRenderTableCell(o);
    212         r = LayoutRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
    213     } else if (o.isBox())
    214         r = toRenderBox(&o)->frameRect();
    215 
    216     // FIXME: Temporary in order to ensure compatibility with existing layout test results.
    217     if (adjustForTableCells)
    218         r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
    219 
    220     ts << " " << r;
    221 
    222     if (!(o.isText() && !o.isBR())) {
    223         if (o.isFileUploadControl())
    224             ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
    225 
    226         if (o.parent()) {
    227             Color color = o.resolveColor(CSSPropertyColor);
    228             if (o.parent()->resolveColor(CSSPropertyColor) != color)
    229                 ts << " [color=" << color << "]";
    230 
    231             // Do not dump invalid or transparent backgrounds, since that is the default.
    232             Color backgroundColor = o.resolveColor(CSSPropertyBackgroundColor);
    233             if (o.parent()->resolveColor(CSSPropertyBackgroundColor) != backgroundColor
    234                 && backgroundColor.rgb())
    235                 ts << " [bgcolor=" << backgroundColor << "]";
    236 
    237             Color textFillColor = o.resolveColor(CSSPropertyWebkitTextFillColor);
    238             if (o.parent()->resolveColor(CSSPropertyWebkitTextFillColor) != textFillColor
    239                 && textFillColor != color && textFillColor.rgb())
    240                 ts << " [textFillColor=" << textFillColor << "]";
    241 
    242             Color textStrokeColor = o.resolveColor(CSSPropertyWebkitTextStrokeColor);
    243             if (o.parent()->resolveColor(CSSPropertyWebkitTextStrokeColor) != textStrokeColor
    244                 && textStrokeColor != color && textStrokeColor.rgb())
    245                 ts << " [textStrokeColor=" << textStrokeColor << "]";
    246 
    247             if (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth() && o.style()->textStrokeWidth() > 0)
    248                 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
    249         }
    250 
    251         if (!o.isBoxModelObject())
    252             return;
    253 
    254         const RenderBoxModelObject& box = toRenderBoxModelObject(o);
    255         if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
    256             ts << " [border:";
    257 
    258             BorderValue prevBorder = o.style()->borderTop();
    259             if (!box.borderTop())
    260                 ts << " none";
    261             else {
    262                 ts << " (" << box.borderTop() << "px ";
    263                 printBorderStyle(ts, o.style()->borderTopStyle());
    264                 ts << o.resolveColor(CSSPropertyBorderTopColor) << ")";
    265             }
    266 
    267             if (o.style()->borderRight() != prevBorder) {
    268                 prevBorder = o.style()->borderRight();
    269                 if (!box.borderRight())
    270                     ts << " none";
    271                 else {
    272                     ts << " (" << box.borderRight() << "px ";
    273                     printBorderStyle(ts, o.style()->borderRightStyle());
    274                     ts << o.resolveColor(CSSPropertyBorderRightColor) << ")";
    275                 }
    276             }
    277 
    278             if (o.style()->borderBottom() != prevBorder) {
    279                 prevBorder = box.style()->borderBottom();
    280                 if (!box.borderBottom())
    281                     ts << " none";
    282                 else {
    283                     ts << " (" << box.borderBottom() << "px ";
    284                     printBorderStyle(ts, o.style()->borderBottomStyle());
    285                     ts << o.resolveColor(CSSPropertyBorderBottomColor) << ")";
    286                 }
    287             }
    288 
    289             if (o.style()->borderLeft() != prevBorder) {
    290                 prevBorder = o.style()->borderLeft();
    291                 if (!box.borderLeft())
    292                     ts << " none";
    293                 else {
    294                     ts << " (" << box.borderLeft() << "px ";
    295                     printBorderStyle(ts, o.style()->borderLeftStyle());
    296                     ts << o.resolveColor(CSSPropertyBorderLeftColor) << ")";
    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         Node* node = o.node();
    353         if (node && node->isElementNode()) {
    354             Element& element = toElement(*node);
    355             if (element.hasID())
    356                 ts << " id=\"" + element.getIdAttribute() + "\"";
    357 
    358             if (element.hasClass()) {
    359                 ts << " class=\"";
    360                 for (size_t i = 0; i < element.classNames().size(); ++i) {
    361                     if (i > 0)
    362                         ts << " ";
    363                     ts << element.classNames()[i];
    364                 }
    365                 ts << "\"";
    366             }
    367         }
    368     }
    369 
    370     if (behavior & RenderAsTextShowLayoutState) {
    371         bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
    372         if (needsLayout)
    373             ts << " (needs layout:";
    374 
    375         bool havePrevious = false;
    376         if (o.selfNeedsLayout()) {
    377             ts << " self";
    378             havePrevious = true;
    379         }
    380 
    381         if (o.needsPositionedMovementLayout()) {
    382             if (havePrevious)
    383                 ts << ",";
    384             havePrevious = true;
    385             ts << " positioned movement";
    386         }
    387 
    388         if (o.normalChildNeedsLayout()) {
    389             if (havePrevious)
    390                 ts << ",";
    391             havePrevious = true;
    392             ts << " child";
    393         }
    394 
    395         if (o.posChildNeedsLayout()) {
    396             if (havePrevious)
    397                 ts << ",";
    398             ts << " positioned child";
    399         }
    400 
    401         if (needsLayout)
    402             ts << ")";
    403     }
    404 }
    405 
    406 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
    407 {
    408     // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
    409     // to detect any changes caused by the conversion to floating point. :(
    410     int x = run.x();
    411     int y = run.y();
    412     int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
    413 
    414     // FIXME: Table cell adjustment is temporary until results can be updated.
    415     if (o.containingBlock()->isTableCell())
    416         y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
    417 
    418     ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
    419     if (!run.isLeftToRightDirection() || run.dirOverride()) {
    420         ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
    421         if (run.dirOverride())
    422             ts << " override";
    423     }
    424     ts << ": "
    425         << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
    426     if (run.hasHyphen())
    427         ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
    428     ts << "\n";
    429 }
    430 
    431 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
    432 {
    433     if (o.isSVGShape()) {
    434         write(ts, toRenderSVGShape(o), indent);
    435         return;
    436     }
    437     if (o.isSVGGradientStop()) {
    438         writeSVGGradientStop(ts, toRenderSVGGradientStop(o), indent);
    439         return;
    440     }
    441     if (o.isSVGResourceContainer()) {
    442         writeSVGResourceContainer(ts, o, indent);
    443         return;
    444     }
    445     if (o.isSVGContainer()) {
    446         writeSVGContainer(ts, o, indent);
    447         return;
    448     }
    449     if (o.isSVGRoot()) {
    450         write(ts, toRenderSVGRoot(o), indent);
    451         return;
    452     }
    453     if (o.isSVGText()) {
    454         writeSVGText(ts, toRenderSVGText(o), indent);
    455         return;
    456     }
    457     if (o.isSVGInlineText()) {
    458         writeSVGInlineText(ts, toRenderSVGInlineText(o), indent);
    459         return;
    460     }
    461     if (o.isSVGImage()) {
    462         writeSVGImage(ts, toRenderSVGImage(o), indent);
    463         return;
    464     }
    465 
    466     writeIndent(ts, indent);
    467 
    468     RenderTreeAsText::writeRenderObject(ts, o, behavior);
    469     ts << "\n";
    470 
    471     if (o.isText() && !o.isBR()) {
    472         const RenderText& text = toRenderText(o);
    473         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
    474             writeIndent(ts, indent + 1);
    475             writeTextRun(ts, text, *box);
    476         }
    477     }
    478 
    479     for (RenderObject* child = o.slowFirstChild(); child; child = child->nextSibling()) {
    480         if (child->hasLayer())
    481             continue;
    482         write(ts, *child, indent + 1, behavior);
    483     }
    484 
    485     if (o.isWidget()) {
    486         Widget* widget = toRenderWidget(o).widget();
    487         if (widget && widget->isFrameView()) {
    488             FrameView* view = toFrameView(widget);
    489             RenderView* root = view->renderView();
    490             if (root) {
    491                 view->layout();
    492                 RenderLayer* l = root->layer();
    493                 if (l)
    494                     RenderTreeAsText::writeLayers(ts, l, l, l->rect(), indent + 1, behavior);
    495             }
    496         }
    497     }
    498 }
    499 
    500 enum LayerPaintPhase {
    501     LayerPaintPhaseAll = 0,
    502     LayerPaintPhaseBackground = -1,
    503     LayerPaintPhaseForeground = 1
    504 };
    505 
    506 static void write(TextStream& ts, RenderLayer& l,
    507                   const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect, const LayoutRect& clipRect, const LayoutRect& outlineClipRect,
    508                   LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
    509 {
    510     IntRect adjustedLayoutBounds = pixelSnappedIntRect(layerBounds);
    511     IntRect adjustedBackgroundClipRect = pixelSnappedIntRect(backgroundClipRect);
    512     IntRect adjustedClipRect = pixelSnappedIntRect(clipRect);
    513     IntRect adjustedOutlineClipRect = pixelSnappedIntRect(outlineClipRect);
    514 
    515     writeIndent(ts, indent);
    516 
    517     if (l.renderer()->style()->visibility() == HIDDEN)
    518         ts << "hidden ";
    519 
    520     ts << "layer ";
    521 
    522     if (behavior & RenderAsTextShowAddresses)
    523         ts << static_cast<const void*>(&l) << " ";
    524 
    525     ts << adjustedLayoutBounds;
    526 
    527     if (!adjustedLayoutBounds.isEmpty()) {
    528         if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
    529             ts << " backgroundClip " << adjustedBackgroundClipRect;
    530         if (!adjustedClipRect.contains(adjustedLayoutBounds))
    531             ts << " clip " << adjustedClipRect;
    532         if (!adjustedOutlineClipRect.contains(adjustedLayoutBounds))
    533             ts << " outlineClip " << adjustedOutlineClipRect;
    534     }
    535     if (l.isTransparent())
    536         ts << " transparent";
    537 
    538     if (l.renderer()->hasOverflowClip()) {
    539         if (l.scrollableArea()->scrollXOffset())
    540             ts << " scrollX " << l.scrollableArea()->scrollXOffset();
    541         if (l.scrollableArea()->scrollYOffset())
    542             ts << " scrollY " << l.scrollableArea()->scrollYOffset();
    543         if (l.renderBox() && l.renderBox()->pixelSnappedClientWidth() != l.renderBox()->pixelSnappedScrollWidth())
    544             ts << " scrollWidth " << l.renderBox()->pixelSnappedScrollWidth();
    545         if (l.renderBox() && l.renderBox()->pixelSnappedClientHeight() != l.renderBox()->pixelSnappedScrollHeight())
    546             ts << " scrollHeight " << l.renderBox()->pixelSnappedScrollHeight();
    547     }
    548 
    549     if (paintPhase == LayerPaintPhaseBackground)
    550         ts << " layerType: background only";
    551     else if (paintPhase == LayerPaintPhaseForeground)
    552         ts << " layerType: foreground only";
    553 
    554     if (l.renderer()->hasBlendMode())
    555         ts << " blendMode: " << compositeOperatorName(CompositeSourceOver, l.renderer()->style()->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     }
    585 
    586     // Calculate the clip rects we should use.
    587     LayoutRect layerBounds;
    588     ClipRect damageRect, clipRectToApply, outlineRect;
    589     layer->clipper().calculateRects(ClipRectsContext(rootLayer, UncachedClipRects), paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
    590 
    591     // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
    592     if (rootLayer == layer)
    593         layerBounds.setSize(layer->size().expandedTo(pixelSnappedIntSize(layer->renderBox()->maxLayoutOverflow(), LayoutPoint(0, 0))));
    594 
    595     // Ensure our lists are up-to-date.
    596     layer->stackingNode()->updateLayerListsIfNeeded();
    597 
    598     bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : layer->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer);
    599 
    600     Vector<RenderLayerStackingNode*>* negList = layer->stackingNode()->negZOrderList();
    601     bool paintsBackgroundSeparately = negList && negList->size() > 0;
    602     if (shouldPaint && paintsBackgroundSeparately)
    603         write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), LayerPaintPhaseBackground, indent, behavior);
    604 
    605     if (negList) {
    606         int currIndent = indent;
    607         if (behavior & RenderAsTextShowLayerNesting) {
    608             writeIndent(ts, indent);
    609             ts << " negative z-order list(" << negList->size() << ")\n";
    610             ++currIndent;
    611         }
    612         for (unsigned i = 0; i != negList->size(); ++i)
    613             writeLayers(ts, rootLayer, negList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
    614     }
    615 
    616     if (shouldPaint)
    617         write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
    618 
    619     if (Vector<RenderLayerStackingNode*>* normalFlowList = layer->stackingNode()->normalFlowList()) {
    620         int currIndent = indent;
    621         if (behavior & RenderAsTextShowLayerNesting) {
    622             writeIndent(ts, indent);
    623             ts << " normal flow list(" << normalFlowList->size() << ")\n";
    624             ++currIndent;
    625         }
    626         for (unsigned i = 0; i != normalFlowList->size(); ++i)
    627             writeLayers(ts, rootLayer, normalFlowList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
    628     }
    629 
    630     if (Vector<RenderLayerStackingNode*>* posList = layer->stackingNode()->posZOrderList()) {
    631         int currIndent = indent;
    632         if (behavior & RenderAsTextShowLayerNesting) {
    633             writeIndent(ts, indent);
    634             ts << " positive z-order list(" << posList->size() << ")\n";
    635             ++currIndent;
    636         }
    637         for (unsigned i = 0; i != posList->size(); ++i)
    638             writeLayers(ts, rootLayer, posList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
    639     }
    640 }
    641 
    642 String nodePositionAsStringForTesting(Node* node)
    643 {
    644     StringBuilder result;
    645 
    646     Element* body = node->document().body();
    647     Node* parent;
    648     for (Node* n = node; n; n = parent) {
    649         parent = n->parentOrShadowHostNode();
    650         if (n != node)
    651             result.appendLiteral(" of ");
    652         if (parent) {
    653             if (body && n == body) {
    654                 // We don't care what offset body may be in the document.
    655                 result.appendLiteral("body");
    656                 break;
    657             }
    658             if (n->isShadowRoot()) {
    659                 result.append('{');
    660                 result.append(getTagName(n));
    661                 result.append('}');
    662             } else {
    663                 result.appendLiteral("child ");
    664                 result.appendNumber(n->nodeIndex());
    665                 result.appendLiteral(" {");
    666                 result.append(getTagName(n));
    667                 result.append('}');
    668             }
    669         } else
    670             result.appendLiteral("document");
    671     }
    672 
    673     return result.toString();
    674 }
    675 
    676 static void writeSelection(TextStream& ts, const RenderObject* o)
    677 {
    678     Node* n = o->node();
    679     if (!n || !n->isDocumentNode())
    680         return;
    681 
    682     Document* doc = toDocument(n);
    683     LocalFrame* frame = doc->frame();
    684     if (!frame)
    685         return;
    686 
    687     VisibleSelection selection = frame->selection().selection();
    688     if (selection.isCaret()) {
    689         ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(selection.start().deprecatedNode());
    690         if (selection.affinity() == UPSTREAM)
    691             ts << " (upstream affinity)";
    692         ts << "\n";
    693     } else if (selection.isRange()) {
    694         ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(selection.start().deprecatedNode()) << "\n"
    695             << "selection end:   position " << selection.end().deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(selection.end().deprecatedNode()) << "\n";
    696     }
    697 }
    698 
    699 static String externalRepresentation(RenderBox* renderer, RenderAsTextBehavior behavior)
    700 {
    701     TextStream ts;
    702     if (!renderer->hasLayer())
    703         return ts.release();
    704 
    705     RenderLayer* layer = renderer->layer();
    706     RenderTreeAsText::writeLayers(ts, layer, layer, layer->rect(), 0, behavior);
    707     writeSelection(ts, renderer);
    708     return ts.release();
    709 }
    710 
    711 String externalRepresentation(LocalFrame* frame, RenderAsTextBehavior behavior)
    712 {
    713     if (!(behavior & RenderAsTextDontUpdateLayout))
    714         frame->document()->updateLayout();
    715 
    716     RenderObject* renderer = frame->contentRenderer();
    717     if (!renderer || !renderer->isBox())
    718         return String();
    719 
    720     PrintContext printContext(frame);
    721     if (behavior & RenderAsTextPrintingMode)
    722         printContext.begin(toRenderBox(renderer)->width().toFloat());
    723 
    724     return externalRepresentation(toRenderBox(renderer), behavior);
    725 }
    726 
    727 String externalRepresentation(Element* element, RenderAsTextBehavior behavior)
    728 {
    729     // Doesn't support printing mode.
    730     ASSERT(!(behavior & RenderAsTextPrintingMode));
    731     if (!(behavior & RenderAsTextDontUpdateLayout))
    732         element->document().updateLayout();
    733 
    734     RenderObject* renderer = element->renderer();
    735     if (!renderer || !renderer->isBox())
    736         return String();
    737 
    738     return externalRepresentation(toRenderBox(renderer), behavior | RenderAsTextShowAllLayers);
    739 }
    740 
    741 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
    742 {
    743     for (RenderObject* child = parent->slowFirstChild(); child; child = child->nextSibling()) {
    744         if (child->isCounter()) {
    745             if (!isFirstCounter)
    746                 stream << " ";
    747             isFirstCounter = false;
    748             String str(toRenderText(child)->text());
    749             stream << str;
    750         }
    751     }
    752 }
    753 
    754 String counterValueForElement(Element* element)
    755 {
    756     // Make sure the element is not freed during the layout.
    757     RefPtrWillBeRawPtr<Element> protector(element);
    758     element->document().updateLayout();
    759     TextStream stream;
    760     bool isFirstCounter = true;
    761     // The counter renderers should be children of :before or :after pseudo-elements.
    762     if (RenderObject* before = element->pseudoElementRenderer(BEFORE))
    763         writeCounterValuesFromChildren(stream, before, isFirstCounter);
    764     if (RenderObject* after = element->pseudoElementRenderer(AFTER))
    765         writeCounterValuesFromChildren(stream, after, isFirstCounter);
    766     return stream.release();
    767 }
    768 
    769 String markerTextForListItem(Element* element)
    770 {
    771     // Make sure the element is not freed during the layout.
    772     RefPtrWillBeRawPtr<Element> protector(element);
    773     element->document().updateLayout();
    774 
    775     RenderObject* renderer = element->renderer();
    776     if (!renderer || !renderer->isListItem())
    777         return String();
    778 
    779     return toRenderListItem(renderer)->markerText();
    780 }
    781 
    782 } // namespace blink
    783