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