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