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