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