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