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