dd90c5be99e8dcde4f7c2ecd5e5a6ca0b948f115
[WebKit-https.git] / Source / WebCore / 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 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 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 "Document.h"
30 #include "FlowThreadController.h"
31 #include "Frame.h"
32 #include "FrameSelection.h"
33 #include "FrameView.h"
34 #include "HTMLElement.h"
35 #include "HTMLNames.h"
36 #include "HTMLSpanElement.h"
37 #include "InlineTextBox.h"
38 #include "PrintContext.h"
39 #include "PseudoElement.h"
40 #include "RenderBlockFlow.h"
41 #include "RenderCounter.h"
42 #include "RenderDetailsMarker.h"
43 #include "RenderFileUploadControl.h"
44 #include "RenderInline.h"
45 #include "RenderIterator.h"
46 #include "RenderLayer.h"
47 #include "RenderLayerBacking.h"
48 #include "RenderLineBreak.h"
49 #include "RenderListItem.h"
50 #include "RenderListMarker.h"
51 #include "RenderNamedFlowFragment.h"
52 #include "RenderNamedFlowThread.h"
53 #include "RenderRegion.h"
54 #include "RenderSVGContainer.h"
55 #include "RenderSVGGradientStop.h"
56 #include "RenderSVGImage.h"
57 #include "RenderSVGInlineText.h"
58 #include "RenderSVGPath.h"
59 #include "RenderSVGResourceContainer.h"
60 #include "RenderSVGRoot.h"
61 #include "RenderSVGText.h"
62 #include "RenderTableCell.h"
63 #include "RenderView.h"
64 #include "RenderWidget.h"
65 #include "SVGRenderTreeAsText.h"
66 #include "ShadowRoot.h"
67 #include "SimpleLineLayoutResolver.h"
68 #include "StyleProperties.h"
69 #include <wtf/HexNumber.h>
70 #include <wtf/Vector.h>
71 #include <wtf/unicode/CharacterNames.h>
72
73 namespace WebCore {
74
75 using namespace HTMLNames;
76
77 static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const LayoutRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior = RenderAsTextBehaviorNormal);
78
79 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
80 {
81     switch (borderStyle) {
82         case BNONE:
83             ts << "none";
84             break;
85         case BHIDDEN:
86             ts << "hidden";
87             break;
88         case INSET:
89             ts << "inset";
90             break;
91         case GROOVE:
92             ts << "groove";
93             break;
94         case RIDGE:
95             ts << "ridge";
96             break;
97         case OUTSET:
98             ts << "outset";
99             break;
100         case DOTTED:
101             ts << "dotted";
102             break;
103         case DASHED:
104             ts << "dashed";
105             break;
106         case SOLID:
107             ts << "solid";
108             break;
109         case DOUBLE:
110             ts << "double";
111             break;
112     }
113
114     ts << " ";
115 }
116
117 static String getTagName(Node* n)
118 {
119     if (n->isDocumentNode())
120         return "";
121     if (n->nodeType() == Node::COMMENT_NODE)
122         return "COMMENT";
123     return n->nodeName();
124 }
125
126 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
127 {
128     if (!is<HTMLSpanElement>(node))
129         return false;
130
131     const HTMLElement& element = downcast<HTMLSpanElement>(*node);
132     if (element.getAttribute(classAttr) != "Apple-style-span")
133         return false;
134
135     if (!node->hasChildNodes())
136         return true;
137
138     const StyleProperties* inlineStyleDecl = element.inlineStyle();
139     return (!inlineStyleDecl || inlineStyleDecl->isEmpty());
140 }
141
142 String quoteAndEscapeNonPrintables(StringView s)
143 {
144     StringBuilder result;
145     result.append('"');
146     for (unsigned i = 0; i != s.length(); ++i) {
147         UChar c = s[i];
148         if (c == '\\') {
149             result.append('\\');
150             result.append('\\');
151         } else if (c == '"') {
152             result.append('\\');
153             result.append('"');
154         } else if (c == '\n' || c == noBreakSpace)
155             result.append(' ');
156         else {
157             if (c >= 0x20 && c < 0x7F)
158                 result.append(c);
159             else {
160                 result.append('\\');
161                 result.append('x');
162                 result.append('{');
163                 appendUnsignedAsHex(c, result); 
164                 result.append('}');
165             }
166         }
167     }
168     result.append('"');
169     return result.toString();
170 }
171
172 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
173 {
174     ts << o.renderName();
175
176     if (behavior & RenderAsTextShowAddresses)
177         ts << " " << static_cast<const void*>(&o);
178
179     if (o.style().zIndex())
180         ts << " zI: " << o.style().zIndex();
181
182     if (o.node()) {
183         String tagName = getTagName(o.node());
184         // FIXME: Temporary hack to make tests pass by simulating the old generated content output.
185         if (o.isPseudoElement() || (o.parent() && o.parent()->isPseudoElement()))
186             tagName = emptyAtom;
187         if (!tagName.isEmpty()) {
188             ts << " {" << tagName << "}";
189             // flag empty or unstyled AppleStyleSpan because we never
190             // want to leave them in the DOM
191             if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
192                 ts << " *empty or unstyled AppleStyleSpan*";
193         }
194     }
195     
196     RenderBlock* cb = o.containingBlock();
197     bool adjustForTableCells = cb ? cb->isTableCell() : false;
198
199     LayoutRect r;
200     if (is<RenderText>(o)) {
201         // 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
202         // many test results.
203         const RenderText& text = downcast<RenderText>(o);
204         r = IntRect(text.firstRunLocation(), text.linesBoundingBox().size());
205         if (!text.firstTextBox() && !text.simpleLineLayout())
206             adjustForTableCells = false;
207     } else if (o.isBR()) {
208         const RenderLineBreak& br = downcast<RenderLineBreak>(o);
209         IntRect linesBox = br.linesBoundingBox();
210         r = IntRect(linesBox.x(), linesBox.y(), linesBox.width(), linesBox.height());
211         if (!br.inlineBoxWrapper())
212             adjustForTableCells = false;
213     } else if (is<RenderInline>(o)) {
214         const RenderInline& inlineFlow = downcast<RenderInline>(o);
215         // FIXME: Would be better not to just dump 0, 0 as the x and y here.
216         r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
217         adjustForTableCells = false;
218     } else if (is<RenderTableCell>(o)) {
219         // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect.  We'd like
220         // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
221         // captured by the results.
222         const RenderTableCell& cell = downcast<RenderTableCell>(o);
223         r = LayoutRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
224     } else if (is<RenderBox>(o))
225         r = downcast<RenderBox>(o).frameRect();
226
227     // FIXME: Temporary in order to ensure compatibility with existing layout test results.
228     if (adjustForTableCells)
229         r.move(0, -downcast<RenderTableCell>(*o.containingBlock()).intrinsicPaddingBefore());
230
231     // FIXME: Convert layout test results to report sub-pixel values, in the meantime using enclosingIntRect
232     // for consistency with old results.
233     ts << " " << enclosingIntRect(r);
234
235     if (!is<RenderText>(o)) {
236         if (is<RenderFileUploadControl>(o))
237             ts << " " << quoteAndEscapeNonPrintables(downcast<RenderFileUploadControl>(o).fileTextValue());
238
239         if (o.parent()) {
240             Color color = o.style().visitedDependentColor(CSSPropertyColor);
241             if (o.parent()->style().visitedDependentColor(CSSPropertyColor) != color)
242                 ts << " [color=" << color.nameForRenderTreeAsText() << "]";
243
244             // Do not dump invalid or transparent backgrounds, since that is the default.
245             Color backgroundColor = o.style().visitedDependentColor(CSSPropertyBackgroundColor);
246             if (o.parent()->style().visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor
247                 && backgroundColor.isValid() && backgroundColor.rgb())
248                 ts << " [bgcolor=" << backgroundColor.nameForRenderTreeAsText() << "]";
249             
250             Color textFillColor = o.style().visitedDependentColor(CSSPropertyWebkitTextFillColor);
251             if (o.parent()->style().visitedDependentColor(CSSPropertyWebkitTextFillColor) != textFillColor
252                 && textFillColor.isValid() && textFillColor != color && textFillColor.rgb())
253                 ts << " [textFillColor=" << textFillColor.nameForRenderTreeAsText() << "]";
254
255             Color textStrokeColor = o.style().visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
256             if (o.parent()->style().visitedDependentColor(CSSPropertyWebkitTextStrokeColor) != textStrokeColor
257                 && textStrokeColor.isValid() && textStrokeColor != color && textStrokeColor.rgb())
258                 ts << " [textStrokeColor=" << textStrokeColor.nameForRenderTreeAsText() << "]";
259
260             if (o.parent()->style().textStrokeWidth() != o.style().textStrokeWidth() && o.style().textStrokeWidth() > 0)
261                 ts << " [textStrokeWidth=" << o.style().textStrokeWidth() << "]";
262         }
263
264         if (!is<RenderBoxModelObject>(o) || is<RenderLineBreak>(o))
265             return;
266
267         const RenderBoxModelObject& box = downcast<RenderBoxModelObject>(o);
268         if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
269             ts << " [border:";
270
271             BorderValue 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.nameForRenderTreeAsText() << ")";
281             }
282
283             if (o.style().borderRight() != prevBorder) {
284                 prevBorder = o.style().borderRight();
285                 if (!box.borderRight())
286                     ts << " none";
287                 else {
288                     ts << " (" << box.borderRight() << "px ";
289                     printBorderStyle(ts, o.style().borderRightStyle());
290                     Color col = o.style().borderRightColor();
291                     if (!col.isValid())
292                         col = o.style().color();
293                     ts << col.nameForRenderTreeAsText() << ")";
294                 }
295             }
296
297             if (o.style().borderBottom() != prevBorder) {
298                 prevBorder = box.style().borderBottom();
299                 if (!box.borderBottom())
300                     ts << " none";
301                 else {
302                     ts << " (" << box.borderBottom() << "px ";
303                     printBorderStyle(ts, o.style().borderBottomStyle());
304                     Color col = o.style().borderBottomColor();
305                     if (!col.isValid())
306                         col = o.style().color();
307                     ts << col.nameForRenderTreeAsText() << ")";
308                 }
309             }
310
311             if (o.style().borderLeft() != prevBorder) {
312                 prevBorder = o.style().borderLeft();
313                 if (!box.borderLeft())
314                     ts << " none";
315                 else {
316                     ts << " (" << box.borderLeft() << "px ";
317                     printBorderStyle(ts, o.style().borderLeftStyle());
318                     Color col = o.style().borderLeftColor();
319                     if (!col.isValid())
320                         col = o.style().color();
321                     ts << col.nameForRenderTreeAsText() << ")";
322                 }
323             }
324
325             ts << "]";
326         }
327
328 #if ENABLE(MATHML)
329         // We want to show any layout padding, both CSS padding and intrinsic padding, so we can't just check o.style().hasPadding().
330         if (o.isRenderMathMLBlock() && (box.paddingTop() || box.paddingRight() || box.paddingBottom() || box.paddingLeft())) {
331             ts << " [";
332             LayoutUnit cssTop = box.computedCSSPaddingTop();
333             LayoutUnit cssRight = box.computedCSSPaddingRight();
334             LayoutUnit cssBottom = box.computedCSSPaddingBottom();
335             LayoutUnit cssLeft = box.computedCSSPaddingLeft();
336             if (box.paddingTop() != cssTop || box.paddingRight() != cssRight || box.paddingBottom() != cssBottom || box.paddingLeft() != cssLeft) {
337                 ts << "intrinsic ";
338                 if (cssTop || cssRight || cssBottom || cssLeft)
339                     ts << "+ CSS ";
340             }
341             ts << "padding: " << roundToInt(box.paddingTop()) << " " << roundToInt(box.paddingRight()) << " " << roundToInt(box.paddingBottom()) << " " << roundToInt(box.paddingLeft()) << "]";
342         }
343 #endif
344     }
345
346     if (is<RenderTableCell>(o)) {
347         const RenderTableCell& c = downcast<RenderTableCell>(o);
348         ts << " [r=" << c.rowIndex() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
349     }
350
351 #if ENABLE(DETAILS_ELEMENT)
352     if (is<RenderDetailsMarker>(o)) {
353         ts << ": ";
354         switch (downcast<RenderDetailsMarker>(o).orientation()) {
355         case RenderDetailsMarker::Left:
356             ts << "left";
357             break;
358         case RenderDetailsMarker::Right:
359             ts << "right";
360             break;
361         case RenderDetailsMarker::Up:
362             ts << "up";
363             break;
364         case RenderDetailsMarker::Down:
365             ts << "down";
366             break;
367         }
368     }
369 #endif
370
371     if (is<RenderListMarker>(o)) {
372         String text = downcast<RenderListMarker>(o).text();
373         if (!text.isEmpty()) {
374             if (text.length() != 1)
375                 text = quoteAndEscapeNonPrintables(text);
376             else {
377                 switch (text[0]) {
378                     case bullet:
379                         text = "bullet";
380                         break;
381                     case blackSquare:
382                         text = "black square";
383                         break;
384                     case whiteBullet:
385                         text = "white bullet";
386                         break;
387                     default:
388                         text = quoteAndEscapeNonPrintables(text);
389                 }
390             }
391             ts << ": " << text;
392         }
393     }
394     
395     if (behavior & RenderAsTextShowIDAndClass) {
396         if (Element* element = is<Element>(o.node()) ? downcast<Element>(o.node()) : nullptr) {
397             if (element->hasID())
398                 ts << " id=\"" + element->getIdAttribute() + "\"";
399
400             if (element->hasClass()) {
401                 ts << " class=\"";
402                 for (size_t i = 0; i < element->classNames().size(); ++i) {
403                     if (i > 0)
404                         ts << " ";
405                     ts << element->classNames()[i];
406                 }
407                 ts << "\"";
408             }
409         }
410     }
411     
412     if (behavior & RenderAsTextShowLayoutState) {
413         bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
414         if (needsLayout)
415             ts << " (needs layout:";
416         
417         bool havePrevious = false;
418         if (o.selfNeedsLayout()) {
419             ts << " self";
420             havePrevious = true;
421         }
422
423         if (o.needsPositionedMovementLayout()) {
424             if (havePrevious)
425                 ts << ",";
426             havePrevious = true;
427             ts << " positioned movement";
428         }
429
430         if (o.normalChildNeedsLayout()) {
431             if (havePrevious)
432                 ts << ",";
433             havePrevious = true;
434             ts << " child";
435         }
436
437         if (o.posChildNeedsLayout()) {
438             if (havePrevious)
439                 ts << ",";
440             ts << " positioned child";
441         }
442
443         if (needsLayout)
444             ts << ")";
445     }
446     
447     if (behavior & RenderAsTextShowOverflow && is<RenderBox>(o)) {
448         const RenderBox& box = downcast<RenderBox>(o);
449         if (box.hasRenderOverflow()) {
450             LayoutRect layoutOverflow = box.layoutOverflowRect();
451             ts << " (layout overflow " << layoutOverflow.x().toInt() << "," << layoutOverflow.y().toInt() << " " << layoutOverflow.width().toInt() << "x" << layoutOverflow.height().toInt() << ")";
452             
453             if (box.hasVisualOverflow()) {
454                 LayoutRect visualOverflow = box.visualOverflowRect();
455                 ts << " (visual overflow " << visualOverflow.x().toInt() << "," << visualOverflow.y().toInt() << " " << visualOverflow.width().toInt() << "x" << visualOverflow.height().toInt() << ")";
456             }
457         }
458     }
459 }
460
461 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
462 {
463     // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
464     // to detect any changes caused by the conversion to floating point. :(
465     int x = run.x();
466     int y = run.y();
467     int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
468
469     // FIXME: Table cell adjustment is temporary until results can be updated.
470     if (o.containingBlock()->isTableCell())
471         y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
472         
473     ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
474     if (!run.isLeftToRightDirection() || run.dirOverride()) {
475         ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
476         if (run.dirOverride())
477             ts << " override";
478     }
479     ts << ": "
480         << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
481     if (run.hasHyphen())
482         ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style().hyphenString().string());
483     ts << "\n";
484 }
485
486 static void writeSimpleLine(TextStream& ts, const RenderText& o, const LayoutRect& rect, StringView text)
487 {
488     int x = rect.x();
489     int y = rect.y();
490     int logicalWidth = ceilf(rect.x() + rect.width()) - x;
491
492     if (o.containingBlock()->isTableCell())
493         y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
494         
495     ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
496     ts << ": "
497         << quoteAndEscapeNonPrintables(text);
498     ts << "\n";
499 }
500
501 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
502 {
503     if (is<RenderSVGShape>(o)) {
504         write(ts, downcast<RenderSVGShape>(o), indent);
505         return;
506     }
507     if (is<RenderSVGGradientStop>(o)) {
508         writeSVGGradientStop(ts, downcast<RenderSVGGradientStop>(o), indent);
509         return;
510     }
511     if (is<RenderSVGResourceContainer>(o)) {
512         writeSVGResourceContainer(ts, downcast<RenderSVGResourceContainer>(o), indent);
513         return;
514     }
515     if (is<RenderSVGContainer>(o)) {
516         writeSVGContainer(ts, downcast<RenderSVGContainer>(o), indent);
517         return;
518     }
519     if (is<RenderSVGRoot>(o)) {
520         write(ts, downcast<RenderSVGRoot>(o), indent);
521         return;
522     }
523     if (is<RenderSVGText>(o)) {
524         writeSVGText(ts, downcast<RenderSVGText>(o), indent);
525         return;
526     }
527     if (is<RenderSVGInlineText>(o)) {
528         writeSVGInlineText(ts, downcast<RenderSVGInlineText>(o), indent);
529         return;
530     }
531     if (is<RenderSVGImage>(o)) {
532         writeSVGImage(ts, downcast<RenderSVGImage>(o), indent);
533         return;
534     }
535
536     writeIndent(ts, indent);
537
538     RenderTreeAsText::writeRenderObject(ts, o, behavior);
539     ts << "\n";
540
541     if (is<RenderText>(o)) {
542         auto& text = downcast<RenderText>(o);
543         if (auto layout = text.simpleLineLayout()) {
544             ASSERT(!text.firstTextBox());
545             auto resolver = runResolver(downcast<RenderBlockFlow>(*text.parent()), *layout);
546             for (auto it = resolver.begin(), end = resolver.end(); it != end; ++it) {
547                 auto run = *it;
548                 writeIndent(ts, indent + 1);
549                 writeSimpleLine(ts, text, run.rect(), run.text());
550             }
551         } else {
552             for (auto box = text.firstTextBox(); box; box = box->nextTextBox()) {
553                 writeIndent(ts, indent + 1);
554                 writeTextRun(ts, text, *box);
555             }
556         }
557
558     } else {
559         for (RenderObject* child = downcast<RenderElement>(o).firstChild(); child; child = child->nextSibling()) {
560             if (child->hasLayer())
561                 continue;
562             write(ts, *child, indent + 1, behavior);
563         }
564     }
565
566     if (is<RenderWidget>(o)) {
567         Widget* widget = downcast<RenderWidget>(o).widget();
568         if (is<FrameView>(widget)) {
569             FrameView& view = downcast<FrameView>(*widget);
570             if (RenderView* root = view.frame().contentRenderer()) {
571                 if (!(behavior & RenderAsTextDontUpdateLayout))
572                     view.layout();
573                 if (RenderLayer* layer = root->layer())
574                     writeLayers(ts, layer, layer, layer->rect(), indent + 1, behavior);
575             }
576         }
577     }
578 }
579
580 enum LayerPaintPhase {
581     LayerPaintPhaseAll = 0,
582     LayerPaintPhaseBackground = -1,
583     LayerPaintPhaseForeground = 1
584 };
585
586 static void write(TextStream& ts, RenderLayer& l,
587                   const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect, const LayoutRect& clipRect, const LayoutRect& outlineClipRect,
588                   LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
589 {
590     IntRect adjustedLayoutBounds = snappedIntRect(layerBounds);
591     IntRect adjustedBackgroundClipRect = snappedIntRect(backgroundClipRect);
592     IntRect adjustedClipRect = snappedIntRect(clipRect);
593     IntRect adjustedOutlineClipRect = snappedIntRect(outlineClipRect);
594
595     writeIndent(ts, indent);
596
597     ts << "layer ";
598     
599     if (behavior & RenderAsTextShowAddresses)
600         ts << static_cast<const void*>(&l) << " ";
601       
602     ts << adjustedLayoutBounds;
603
604     if (!adjustedLayoutBounds.isEmpty()) {
605         if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
606             ts << " backgroundClip " << adjustedBackgroundClipRect;
607         if (!adjustedClipRect.contains(adjustedLayoutBounds))
608             ts << " clip " << adjustedClipRect;
609         if (!adjustedOutlineClipRect.contains(adjustedLayoutBounds))
610             ts << " outlineClip " << adjustedOutlineClipRect;
611     }
612
613     if (l.renderer().hasOverflowClip()) {
614         if (l.scrollXOffset())
615             ts << " scrollX " << l.scrollXOffset();
616         if (l.scrollYOffset())
617             ts << " scrollY " << l.scrollYOffset();
618         if (l.renderBox() && l.renderBox()->pixelSnappedClientWidth() != l.scrollWidth())
619             ts << " scrollWidth " << l.scrollWidth();
620         if (l.renderBox() && l.renderBox()->pixelSnappedClientHeight() != l.scrollHeight())
621             ts << " scrollHeight " << l.scrollHeight();
622     }
623
624     if (paintPhase == LayerPaintPhaseBackground)
625         ts << " layerType: background only";
626     else if (paintPhase == LayerPaintPhaseForeground)
627         ts << " layerType: foreground only";
628
629     if (behavior & RenderAsTextShowCompositedLayers) {
630         if (l.isComposited())
631             ts << " (composited, bounds=" << l.backing()->compositedBounds() << ", drawsContent=" << l.backing()->graphicsLayer()->drawsContent() << ", paints into ancestor=" << l.backing()->paintsIntoCompositedAncestor() << ")";
632     }
633
634 #if ENABLE(CSS_COMPOSITING)
635     if (l.isolatesBlending())
636         ts << " isolatesBlending";
637     if (l.hasBlendMode())
638         ts << " blendMode: " << compositeOperatorName(CompositeSourceOver, l.blendMode());
639 #endif
640     
641     ts << "\n";
642
643     if (paintPhase != LayerPaintPhaseBackground)
644         write(ts, l.renderer(), indent + 1, behavior);
645 }
646
647 static void writeRenderRegionList(const RenderRegionList& flowThreadRegionList, TextStream& ts, int indent)
648 {
649     for (const auto& renderRegion : flowThreadRegionList) {
650         writeIndent(ts, indent);
651         ts << static_cast<const RenderObject*>(renderRegion)->renderName();
652
653         Element* generatingElement = renderRegion->generatingElement();
654         if (generatingElement) {
655             bool isRenderNamedFlowFragment = renderRegion->isRenderNamedFlowFragment();
656             if (isRenderNamedFlowFragment && toRenderNamedFlowFragment(renderRegion)->hasCustomRegionStyle())
657                 ts << " region style: 1";
658             if (renderRegion->hasAutoLogicalHeight())
659                 ts << " hasAutoLogicalHeight";
660
661             if (isRenderNamedFlowFragment)
662                 ts << " (anonymous child of";
663
664             StringBuilder tagName;
665             tagName.append(generatingElement->nodeName());
666
667             RenderElement* renderElementForRegion = isRenderNamedFlowFragment ? renderRegion->parent() : renderRegion;
668             if (renderElementForRegion->isPseudoElement()) {
669                 if (renderElementForRegion->element()->isBeforePseudoElement())
670                     tagName.append("::before");
671                 else if (renderElementForRegion->element()->isAfterPseudoElement())
672                     tagName.append("::after");
673             }
674
675             ts << " {" << tagName.toString() << "}";
676
677             if (generatingElement->hasID())
678                 ts << " #" << generatingElement->idForStyleResolution();
679
680             if (isRenderNamedFlowFragment)
681                 ts << ")";
682         }
683
684         ts << "\n";
685     }
686 }
687
688 static void writeRenderNamedFlowThreads(TextStream& ts, RenderView& renderView, const RenderLayer* rootLayer,
689     const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
690 {
691     if (!renderView.hasRenderNamedFlowThreads())
692         return;
693
694     const RenderNamedFlowThreadList* list = renderView.flowThreadController().renderNamedFlowThreadList();
695
696     writeIndent(ts, indent);
697     ts << "Named flows\n";
698
699     for (RenderNamedFlowThreadList::const_iterator iter = list->begin(); iter != list->end(); ++iter) {
700         const RenderNamedFlowThread* renderFlowThread = *iter;
701
702         writeIndent(ts, indent + 1);
703         ts << "Named flow '" << renderFlowThread->flowThreadName() << "'\n";
704
705         RenderLayer* layer = renderFlowThread->layer();
706         writeLayers(ts, rootLayer, layer, paintRect, indent + 2, behavior);
707
708         // Display the valid and invalid render regions attached to this flow thread.
709         const RenderRegionList& validRegionsList = renderFlowThread->renderRegionList();
710         const RenderRegionList& invalidRegionsList = renderFlowThread->invalidRenderRegionList();
711         if (!validRegionsList.isEmpty()) {
712             writeIndent(ts, indent + 2);
713             ts << "Regions for named flow '" << renderFlowThread->flowThreadName() << "'\n";
714             writeRenderRegionList(validRegionsList, ts, indent + 3);
715         }
716         if (!invalidRegionsList.isEmpty()) {
717             writeIndent(ts, indent + 2);
718             ts << "Invalid regions for named flow '" << renderFlowThread->flowThreadName() << "'\n";
719             writeRenderRegionList(invalidRegionsList, ts, indent + 3);
720         }
721     }
722 }
723
724 static LayoutSize maxLayoutOverflow(const RenderBox* box)
725 {
726     LayoutRect overflowRect = box->layoutOverflowRect();
727     return LayoutSize(overflowRect.maxX(), overflowRect.maxY());
728 }
729
730 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
731                         const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
732 {
733     // FIXME: Apply overflow to the root layer to not break every test.  Complete hack.  Sigh.
734     LayoutRect paintDirtyRect(paintRect);
735     if (rootLayer == l) {
736         paintDirtyRect.setWidth(std::max<LayoutUnit>(paintDirtyRect.width(), rootLayer->renderBox()->layoutOverflowRect().maxX()));
737         paintDirtyRect.setHeight(std::max<LayoutUnit>(paintDirtyRect.height(), rootLayer->renderBox()->layoutOverflowRect().maxY()));
738         l->setSize(l->size().expandedTo(snappedIntSize(maxLayoutOverflow(l->renderBox()), LayoutPoint(0, 0))));
739     }
740     
741     // Calculate the clip rects we should use.
742     LayoutRect layerBounds;
743     ClipRect damageRect, clipRectToApply, outlineRect;
744     l->calculateRects(RenderLayer::ClipRectsContext(rootLayer, TemporaryClipRects), paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, l->offsetFromAncestor(rootLayer));
745
746     // Ensure our lists are up-to-date.
747     l->updateLayerListsIfNeeded();
748
749     bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer, l->offsetFromAncestor(rootLayer));
750     Vector<RenderLayer*>* negList = l->negZOrderList();
751     bool paintsBackgroundSeparately = negList && negList->size() > 0;
752     if (shouldPaint && paintsBackgroundSeparately)
753         write(ts, *l, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), LayerPaintPhaseBackground, indent, behavior);
754
755     if (negList) {
756         int currIndent = indent;
757         if (behavior & RenderAsTextShowLayerNesting) {
758             writeIndent(ts, indent);
759             ts << " negative z-order list(" << negList->size() << ")\n";
760             ++currIndent;
761         }
762         for (unsigned i = 0; i != negList->size(); ++i)
763             writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior);
764     }
765
766     if (shouldPaint)
767         write(ts, *l, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
768
769     if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) {
770         int currIndent = indent;
771         if (behavior & RenderAsTextShowLayerNesting) {
772             writeIndent(ts, indent);
773             ts << " normal flow list(" << normalFlowList->size() << ")\n";
774             ++currIndent;
775         }
776         for (unsigned i = 0; i != normalFlowList->size(); ++i)
777             writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior);
778     }
779
780     if (Vector<RenderLayer*>* posList = l->posZOrderList()) {
781         size_t layerCount = 0;
782         for (unsigned i = 0; i != posList->size(); ++i)
783             if (!posList->at(i)->isFlowThreadCollectingGraphicsLayersUnderRegions())
784                 ++layerCount;
785         if (layerCount) {
786             int currIndent = indent;
787             // We only print the header if there's at list a non-RenderNamedFlowThread part of the list.
788             if (!posList->size() || !posList->at(0)->isFlowThreadCollectingGraphicsLayersUnderRegions()) {
789                 if (behavior & RenderAsTextShowLayerNesting) {
790                     writeIndent(ts, indent);
791                     ts << " positive z-order list(" << posList->size() << ")\n";
792                     ++currIndent;
793                 }
794                 for (unsigned i = 0; i != posList->size(); ++i) {
795                     // Do not print named flows twice.
796                     if (!posList->at(i)->isFlowThreadCollectingGraphicsLayersUnderRegions())
797                         writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior);
798                 }
799             }
800         }
801     }
802     
803     // Altough the RenderFlowThread requires a layer, it is not collected by its parent,
804     // so we have to treat it as a special case.
805     if (l->renderer().isRenderView())
806         writeRenderNamedFlowThreads(ts, toRenderView(l->renderer()), rootLayer, paintDirtyRect, indent, behavior);
807 }
808
809 static String nodePosition(Node* node)
810 {
811     StringBuilder result;
812
813     Element* body = node->document().body();
814     Node* parent;
815     for (Node* n = node; n; n = parent) {
816         parent = n->parentOrShadowHostNode();
817         if (n != node)
818             result.appendLiteral(" of ");
819         if (parent) {
820             if (body && n == body) {
821                 // We don't care what offset body may be in the document.
822                 result.appendLiteral("body");
823                 break;
824             }
825             if (n->isShadowRoot()) {
826                 result.append('{');
827                 result.append(getTagName(n));
828                 result.append('}');
829             } else {
830                 result.appendLiteral("child ");
831                 result.appendNumber(n->computeNodeIndex());
832                 result.appendLiteral(" {");
833                 result.append(getTagName(n));
834                 result.append('}');
835             }
836         } else
837             result.appendLiteral("document");
838     }
839
840     return result.toString();
841 }
842
843 static void writeSelection(TextStream& ts, const RenderObject* renderer)
844 {
845     if (!renderer->isRenderView())
846         return;
847
848     Frame* frame = renderer->document().frame();
849     if (!frame)
850         return;
851
852     VisibleSelection selection = frame->selection().selection();
853     if (selection.isCaret()) {
854         ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode());
855         if (selection.affinity() == UPSTREAM)
856             ts << " (upstream affinity)";
857         ts << "\n";
858     } else if (selection.isRange())
859         ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n"
860            << "selection end:   position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n";
861 }
862
863 static String externalRepresentation(RenderBox* renderer, RenderAsTextBehavior behavior)
864 {
865     TextStream ts;
866     if (!renderer->hasLayer())
867         return ts.release();
868         
869     RenderLayer* layer = renderer->layer();
870     writeLayers(ts, layer, layer, layer->rect(), 0, behavior);
871     writeSelection(ts, renderer);
872     return ts.release();
873 }
874
875 String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
876 {
877     RenderObject* renderer = frame->contentRenderer();
878     if (!renderer || !renderer->isBox())
879         return String();
880
881     PrintContext printContext(frame);
882     if (behavior & RenderAsTextPrintingMode)
883         printContext.begin(toRenderBox(renderer)->width());
884     if (!(behavior & RenderAsTextDontUpdateLayout))
885         frame->document()->updateLayout();
886
887     return externalRepresentation(toRenderBox(renderer), behavior);
888 }
889
890 String externalRepresentation(Element* element, RenderAsTextBehavior behavior)
891 {
892     RenderObject* renderer = element->renderer();
893     if (!renderer || !renderer->isBox())
894         return String();
895     // Doesn't support printing mode.
896     ASSERT(!(behavior & RenderAsTextPrintingMode));
897     if (!(behavior & RenderAsTextDontUpdateLayout))
898         element->document().updateLayout();
899     
900     return externalRepresentation(toRenderBox(renderer), behavior | RenderAsTextShowAllLayers);
901 }
902
903 static void writeCounterValuesFromChildren(TextStream& stream, const RenderElement* parent, bool& isFirstCounter)
904 {
905     if (!parent)
906         return;
907     for (auto& counter : childrenOfType<RenderCounter>(*parent)) {
908         if (!isFirstCounter)
909             stream << " ";
910         isFirstCounter = false;
911         String str(counter.text());
912         stream << str;
913     }
914 }
915
916 String counterValueForElement(Element* element)
917 {
918     // Make sure the element is not freed during the layout.
919     RefPtr<Element> elementRef(element);
920     element->document().updateLayout();
921     TextStream stream;
922     bool isFirstCounter = true;
923     // The counter renderers should be children of :before or :after pseudo-elements.
924     if (PseudoElement* before = element->beforePseudoElement())
925         writeCounterValuesFromChildren(stream, before->renderer(), isFirstCounter);
926     if (PseudoElement* after = element->afterPseudoElement())
927         writeCounterValuesFromChildren(stream, after->renderer(), isFirstCounter);
928     return stream.release();
929 }
930
931 String markerTextForListItem(Element* element)
932 {
933     // Make sure the element is not freed during the layout.
934     RefPtr<Element> elementRef(element);
935     element->document().updateLayout();
936
937     RenderObject* renderer = element->renderer();
938     if (!renderer || !renderer->isListItem())
939         return String();
940
941     return toRenderListItem(renderer)->markerText();
942 }
943
944 } // namespace WebCore