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