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