1 /*
2  * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, 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 COMPUTER, 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 "CSSMutableStyleDeclaration.h"
30 #include "Document.h"
31 #include "Frame.h"
32 #include "FrameView.h"
33 #include "HTMLElement.h"
34 #include "HTMLNames.h"
35 #include "InlineTextBox.h"
36 #include "PrintContext.h"
37 #include "RenderBR.h"
38 #include "RenderDetailsMarker.h"
39 #include "RenderFileUploadControl.h"
40 #include "RenderInline.h"
41 #include "RenderLayer.h"
42 #include "RenderListItem.h"
43 #include "RenderListMarker.h"
44 #include "RenderPart.h"
45 #include "RenderTableCell.h"
46 #include "RenderView.h"
47 #include "RenderWidget.h"
48 #include "SelectionController.h"
49 #include <wtf/HexNumber.h>
50 #include <wtf/UnusedParam.h>
51 #include <wtf/Vector.h>
52 #include <wtf/unicode/CharacterNames.h>
53 
54 #if ENABLE(SVG)
55 #include "RenderSVGContainer.h"
56 #include "RenderSVGGradientStop.h"
57 #include "RenderSVGImage.h"
58 #include "RenderSVGInlineText.h"
59 #include "RenderSVGPath.h"
60 #include "RenderSVGRoot.h"
61 #include "RenderSVGText.h"
62 #include "SVGRenderTreeAsText.h"
63 #endif
64 
65 #if USE(ACCELERATED_COMPOSITING)
66 #include "RenderLayerBacking.h"
67 #endif
68 
69 #if PLATFORM(QT)
70 #include <QWidget>
71 #endif
72 
73 namespace WebCore {
74 
75 using namespace HTMLNames;
76 
77 static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal);
78 
hasFractions(double val)79 bool hasFractions(double val)
80 {
81     static const double s_epsilon = 0.0001;
82     int ival = static_cast<int>(val);
83     double dval = static_cast<double>(ival);
84     return fabs(val - dval) > s_epsilon;
85 }
86 
operator <<(TextStream & ts,const IntRect & r)87 TextStream& operator<<(TextStream& ts, const IntRect& r)
88 {
89     return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
90 }
91 
operator <<(TextStream & ts,const IntPoint & p)92 TextStream& operator<<(TextStream& ts, const IntPoint& p)
93 {
94     return ts << "(" << p.x() << "," << p.y() << ")";
95 }
96 
operator <<(TextStream & ts,const FloatPoint & p)97 TextStream& operator<<(TextStream& ts, const FloatPoint& p)
98 {
99     ts << "(";
100     if (hasFractions(p.x()))
101         ts << p.x();
102     else
103         ts << int(p.x());
104     ts << ",";
105     if (hasFractions(p.y()))
106         ts << p.y();
107     else
108         ts << int(p.y());
109     return ts << ")";
110 }
111 
operator <<(TextStream & ts,const FloatSize & s)112 TextStream& operator<<(TextStream& ts, const FloatSize& s)
113 {
114     ts << "width=";
115     if (hasFractions(s.width()))
116         ts << s.width();
117     else
118         ts << int(s.width());
119     ts << " height=";
120     if (hasFractions(s.height()))
121         ts << s.height();
122     else
123         ts << int(s.height());
124     return ts;
125 }
126 
writeIndent(TextStream & ts,int indent)127 void writeIndent(TextStream& ts, int indent)
128 {
129     for (int i = 0; i != indent; ++i)
130         ts << "  ";
131 }
132 
printBorderStyle(TextStream & ts,const EBorderStyle borderStyle)133 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
134 {
135     switch (borderStyle) {
136         case BNONE:
137             ts << "none";
138             break;
139         case BHIDDEN:
140             ts << "hidden";
141             break;
142         case INSET:
143             ts << "inset";
144             break;
145         case GROOVE:
146             ts << "groove";
147             break;
148         case RIDGE:
149             ts << "ridge";
150             break;
151         case OUTSET:
152             ts << "outset";
153             break;
154         case DOTTED:
155             ts << "dotted";
156             break;
157         case DASHED:
158             ts << "dashed";
159             break;
160         case SOLID:
161             ts << "solid";
162             break;
163         case DOUBLE:
164             ts << "double";
165             break;
166     }
167 
168     ts << " ";
169 }
170 
getTagName(Node * n)171 static String getTagName(Node* n)
172 {
173     if (n->isDocumentNode())
174         return "";
175     if (n->isCommentNode())
176         return "COMMENT";
177     return n->nodeName();
178 }
179 
isEmptyOrUnstyledAppleStyleSpan(const Node * node)180 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
181 {
182     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
183         return false;
184 
185     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
186     if (elem->getAttribute(classAttr) != "Apple-style-span")
187         return false;
188 
189     if (!node->hasChildNodes())
190         return true;
191 
192     CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
193     return (!inlineStyleDecl || inlineStyleDecl->length() == 0);
194 }
195 
quoteAndEscapeNonPrintables(const String & s)196 String quoteAndEscapeNonPrintables(const String& s)
197 {
198     Vector<UChar> result;
199     result.append('"');
200     for (unsigned i = 0; i != s.length(); ++i) {
201         UChar c = s[i];
202         if (c == '\\') {
203             result.append('\\');
204             result.append('\\');
205         } else if (c == '"') {
206             result.append('\\');
207             result.append('"');
208         } else if (c == '\n' || c == noBreakSpace)
209             result.append(' ');
210         else {
211             if (c >= 0x20 && c < 0x7F)
212                 result.append(c);
213             else {
214                 result.append('\\');
215                 result.append('x');
216                 result.append('{');
217                 appendUnsignedAsHex(c, result);
218                 result.append('}');
219             }
220         }
221     }
222     result.append('"');
223     return String::adopt(result);
224 }
225 
writeRenderObject(TextStream & ts,const RenderObject & o,RenderAsTextBehavior behavior)226 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
227 {
228     ts << o.renderName();
229 
230     if (behavior & RenderAsTextShowAddresses)
231         ts << " " << static_cast<const void*>(&o);
232 
233     if (o.style() && o.style()->zIndex())
234         ts << " zI: " << o.style()->zIndex();
235 
236     if (o.node()) {
237         String tagName = getTagName(o.node());
238         if (!tagName.isEmpty()) {
239             ts << " {" << tagName << "}";
240             // flag empty or unstyled AppleStyleSpan because we never
241             // want to leave them in the DOM
242             if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
243                 ts << " *empty or unstyled AppleStyleSpan*";
244         }
245     }
246 
247     bool adjustForTableCells = o.containingBlock()->isTableCell();
248 
249     IntRect r;
250     if (o.isText()) {
251         // 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
252         // many test results.
253         const RenderText& text = *toRenderText(&o);
254         IntRect linesBox = text.linesBoundingBox();
255         r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
256         if (adjustForTableCells && !text.firstTextBox())
257             adjustForTableCells = false;
258     } else if (o.isRenderInline()) {
259         // FIXME: Would be better not to just dump 0, 0 as the x and y here.
260         const RenderInline& inlineFlow = *toRenderInline(&o);
261         r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
262         adjustForTableCells = false;
263     } else if (o.isTableCell()) {
264         // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect.  We'd like
265         // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
266         // captured by the results.
267         const RenderTableCell& cell = *toRenderTableCell(&o);
268         r = IntRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
269     } else if (o.isBox())
270         r = toRenderBox(&o)->frameRect();
271 
272     // FIXME: Temporary in order to ensure compatibility with existing layout test results.
273     if (adjustForTableCells)
274         r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
275 
276     ts << " " << r;
277 
278     if (!(o.isText() && !o.isBR())) {
279         if (o.isFileUploadControl())
280             ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
281 
282         if (o.parent() && (o.parent()->style()->color() != o.style()->color()))
283             ts << " [color=" << o.style()->color().nameForRenderTreeAsText() << "]";
284 
285         if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) &&
286             o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb())
287             // Do not dump invalid or transparent backgrounds, since that is the default.
288             ts << " [bgcolor=" << o.style()->backgroundColor().nameForRenderTreeAsText() << "]";
289 
290         if (o.parent() && (o.parent()->style()->textFillColor() != o.style()->textFillColor()) &&
291             o.style()->textFillColor().isValid() && o.style()->textFillColor() != o.style()->color() &&
292             o.style()->textFillColor().rgb())
293             ts << " [textFillColor=" << o.style()->textFillColor().nameForRenderTreeAsText() << "]";
294 
295         if (o.parent() && (o.parent()->style()->textStrokeColor() != o.style()->textStrokeColor()) &&
296             o.style()->textStrokeColor().isValid() && o.style()->textStrokeColor() != o.style()->color() &&
297             o.style()->textStrokeColor().rgb())
298             ts << " [textStrokeColor=" << o.style()->textStrokeColor().nameForRenderTreeAsText() << "]";
299 
300         if (o.parent() && (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth()) &&
301             o.style()->textStrokeWidth() > 0)
302             ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
303 
304         if (!o.isBoxModelObject())
305             return;
306 
307         const RenderBoxModelObject& box = *toRenderBoxModelObject(&o);
308         if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
309             ts << " [border:";
310 
311             BorderValue prevBorder;
312             if (o.style()->borderTop() != prevBorder) {
313                 prevBorder = o.style()->borderTop();
314                 if (!box.borderTop())
315                     ts << " none";
316                 else {
317                     ts << " (" << box.borderTop() << "px ";
318                     printBorderStyle(ts, o.style()->borderTopStyle());
319                     Color col = o.style()->borderTopColor();
320                     if (!col.isValid())
321                         col = o.style()->color();
322                     ts << col.nameForRenderTreeAsText() << ")";
323                 }
324             }
325 
326             if (o.style()->borderRight() != prevBorder) {
327                 prevBorder = o.style()->borderRight();
328                 if (!box.borderRight())
329                     ts << " none";
330                 else {
331                     ts << " (" << box.borderRight() << "px ";
332                     printBorderStyle(ts, o.style()->borderRightStyle());
333                     Color col = o.style()->borderRightColor();
334                     if (!col.isValid())
335                         col = o.style()->color();
336                     ts << col.nameForRenderTreeAsText() << ")";
337                 }
338             }
339 
340             if (o.style()->borderBottom() != prevBorder) {
341                 prevBorder = box.style()->borderBottom();
342                 if (!box.borderBottom())
343                     ts << " none";
344                 else {
345                     ts << " (" << box.borderBottom() << "px ";
346                     printBorderStyle(ts, o.style()->borderBottomStyle());
347                     Color col = o.style()->borderBottomColor();
348                     if (!col.isValid())
349                         col = o.style()->color();
350                     ts << col.nameForRenderTreeAsText() << ")";
351                 }
352             }
353 
354             if (o.style()->borderLeft() != prevBorder) {
355                 prevBorder = o.style()->borderLeft();
356                 if (!box.borderLeft())
357                     ts << " none";
358                 else {
359                     ts << " (" << box.borderLeft() << "px ";
360                     printBorderStyle(ts, o.style()->borderLeftStyle());
361                     Color col = o.style()->borderLeftColor();
362                     if (!col.isValid())
363                         col = o.style()->color();
364                     ts << col.nameForRenderTreeAsText() << ")";
365                 }
366             }
367 
368             ts << "]";
369         }
370     }
371 
372     if (o.isTableCell()) {
373         const RenderTableCell& c = *toRenderTableCell(&o);
374         ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
375     }
376 
377 #if ENABLE(DETAILS)
378     if (o.isDetailsMarker()) {
379         ts << ": ";
380         switch (toRenderDetailsMarker(&o)->orientation()) {
381         case RenderDetailsMarker::Left:
382             ts << "left";
383             break;
384         case RenderDetailsMarker::Right:
385             ts << "right";
386             break;
387         case RenderDetailsMarker::Up:
388             ts << "up";
389             break;
390         case RenderDetailsMarker::Down:
391             ts << "down";
392             break;
393         }
394     }
395 #endif
396 
397     if (o.isListMarker()) {
398         String text = toRenderListMarker(&o)->text();
399         if (!text.isEmpty()) {
400             if (text.length() != 1)
401                 text = quoteAndEscapeNonPrintables(text);
402             else {
403                 switch (text[0]) {
404                     case bullet:
405                         text = "bullet";
406                         break;
407                     case blackSquare:
408                         text = "black square";
409                         break;
410                     case whiteBullet:
411                         text = "white bullet";
412                         break;
413                     default:
414                         text = quoteAndEscapeNonPrintables(text);
415                 }
416             }
417             ts << ": " << text;
418         }
419     }
420 
421     if (behavior & RenderAsTextShowIDAndClass) {
422         if (Node* node = o.node()) {
423             if (node->hasID())
424                 ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\"";
425 
426             if (node->hasClass()) {
427                 StyledElement* styledElement = static_cast<StyledElement*>(node);
428                 String classes;
429                 for (size_t i = 0; i < styledElement->classNames().size(); ++i) {
430                     if (i > 0)
431                         classes += " ";
432                     classes += styledElement->classNames()[i];
433                 }
434                 ts << " class=\"" + classes + "\"";
435             }
436         }
437     }
438 
439     if (behavior & RenderAsTextShowLayoutState) {
440         bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
441         if (needsLayout)
442             ts << " (needs layout:";
443 
444         bool havePrevious = false;
445         if (o.selfNeedsLayout()) {
446             ts << " self";
447             havePrevious = true;
448         }
449 
450         if (o.needsPositionedMovementLayout()) {
451             if (havePrevious)
452                 ts << ",";
453             havePrevious = true;
454             ts << " positioned movement";
455         }
456 
457         if (o.normalChildNeedsLayout()) {
458             if (havePrevious)
459                 ts << ",";
460             havePrevious = true;
461             ts << " child";
462         }
463 
464         if (o.posChildNeedsLayout()) {
465             if (havePrevious)
466                 ts << ",";
467             ts << " positioned child";
468         }
469 
470         if (needsLayout)
471             ts << ")";
472     }
473 
474 #if PLATFORM(QT)
475     // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget
476     // is invisible the QWidget should be invisible too.
477     if (o.isRenderPart()) {
478         const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o));
479         if (part->widget() && part->widget()->platformWidget()) {
480             QWidget* wid = part->widget()->platformWidget();
481 
482             ts << " [QT: ";
483             ts << "geometry: {" << wid->geometry() << "} ";
484             ts << "isHidden: " << wid->isHidden() << " ";
485             ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " ";
486             ts << "isParentVisible: " << part->widget()->isParentVisible() << " ";
487             ts << "mask: {" << wid->mask().boundingRect() << "} ] ";
488         }
489     }
490 #endif
491 }
492 
writeTextRun(TextStream & ts,const RenderText & o,const InlineTextBox & run)493 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
494 {
495     // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
496     // to detect any changes caused by the conversion to floating point. :(
497     int x = run.m_x;
498     int y = run.m_y;
499     int logicalWidth = ceilf(run.m_x + run.m_logicalWidth) - x;
500 
501     // FIXME: Table cell adjustment is temporary until results can be updated.
502     if (o.containingBlock()->isTableCell())
503         y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
504 
505     ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
506     if (!run.isLeftToRightDirection() || run.m_dirOverride) {
507         ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
508         if (run.m_dirOverride)
509             ts << " override";
510     }
511     ts << ": "
512         << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
513     if (run.hasHyphen())
514         ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
515     ts << "\n";
516 }
517 
write(TextStream & ts,const RenderObject & o,int indent,RenderAsTextBehavior behavior)518 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
519 {
520 #if ENABLE(SVG)
521     if (o.isSVGPath()) {
522         write(ts, *toRenderSVGPath(&o), indent);
523         return;
524     }
525     if (o.isSVGGradientStop()) {
526         writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent);
527         return;
528     }
529     if (o.isSVGResourceContainer()) {
530         writeSVGResourceContainer(ts, o, indent);
531         return;
532     }
533     if (o.isSVGContainer()) {
534         writeSVGContainer(ts, o, indent);
535         return;
536     }
537     if (o.isSVGRoot()) {
538         write(ts, *toRenderSVGRoot(&o), indent);
539         return;
540     }
541     if (o.isSVGText()) {
542         writeSVGText(ts, *toRenderBlock(&o), indent);
543         return;
544     }
545     if (o.isSVGInlineText()) {
546         writeSVGInlineText(ts, *toRenderText(&o), indent);
547         return;
548     }
549     if (o.isSVGImage()) {
550         writeSVGImage(ts, *toRenderSVGImage(&o), indent);
551         return;
552     }
553 #endif
554 
555     writeIndent(ts, indent);
556 
557     RenderTreeAsText::writeRenderObject(ts, o, behavior);
558     ts << "\n";
559 
560     if (o.isText() && !o.isBR()) {
561         const RenderText& text = *toRenderText(&o);
562         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
563             writeIndent(ts, indent + 1);
564             writeTextRun(ts, text, *box);
565         }
566     }
567 
568     for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
569         if (child->hasLayer())
570             continue;
571         write(ts, *child, indent + 1, behavior);
572     }
573 
574     if (o.isWidget()) {
575         Widget* widget = toRenderWidget(&o)->widget();
576         if (widget && widget->isFrameView()) {
577             FrameView* view = static_cast<FrameView*>(widget);
578             RenderView* root = view->frame()->contentRenderer();
579             if (root) {
580                 view->layout();
581                 RenderLayer* l = root->layer();
582                 if (l)
583                     writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), indent + 1, behavior);
584             }
585         }
586     }
587 }
588 
589 enum LayerPaintPhase {
590     LayerPaintPhaseAll = 0,
591     LayerPaintPhaseBackground = -1,
592     LayerPaintPhaseForeground = 1
593 };
594 
write(TextStream & ts,RenderLayer & l,const IntRect & layerBounds,const IntRect & backgroundClipRect,const IntRect & clipRect,const IntRect & outlineClipRect,LayerPaintPhase paintPhase=LayerPaintPhaseAll,int indent=0,RenderAsTextBehavior behavior=RenderAsTextBehaviorNormal)595 static void write(TextStream& ts, RenderLayer& l,
596                   const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect,
597                   LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
598 {
599     writeIndent(ts, indent);
600 
601     ts << "layer ";
602 
603     if (behavior & RenderAsTextShowAddresses)
604         ts << static_cast<const void*>(&l) << " ";
605 
606     ts << layerBounds;
607 
608     if (!layerBounds.isEmpty()) {
609         if (!backgroundClipRect.contains(layerBounds))
610             ts << " backgroundClip " << backgroundClipRect;
611         if (!clipRect.contains(layerBounds))
612             ts << " clip " << clipRect;
613         if (!outlineClipRect.contains(layerBounds))
614             ts << " outlineClip " << outlineClipRect;
615     }
616 
617     if (l.renderer()->hasOverflowClip()) {
618         if (l.scrollXOffset())
619             ts << " scrollX " << l.scrollXOffset();
620         if (l.scrollYOffset())
621             ts << " scrollY " << l.scrollYOffset();
622         if (l.renderBox() && l.renderBox()->clientWidth() != l.scrollWidth())
623             ts << " scrollWidth " << l.scrollWidth();
624         if (l.renderBox() && l.renderBox()->clientHeight() != l.scrollHeight())
625             ts << " scrollHeight " << l.scrollHeight();
626     }
627 
628     if (paintPhase == LayerPaintPhaseBackground)
629         ts << " layerType: background only";
630     else if (paintPhase == LayerPaintPhaseForeground)
631         ts << " layerType: foreground only";
632 
633 #if USE(ACCELERATED_COMPOSITING)
634     if (behavior & RenderAsTextShowCompositedLayers) {
635         if (l.isComposited())
636             ts << " (composited, bounds " << l.backing()->compositedBounds() << ")";
637     }
638 #else
639     UNUSED_PARAM(behavior);
640 #endif
641 
642     ts << "\n";
643 
644     if (paintPhase != LayerPaintPhaseBackground)
645         write(ts, *l.renderer(), indent + 1, behavior);
646 }
647 
writeLayers(TextStream & ts,const RenderLayer * rootLayer,RenderLayer * l,const IntRect & paintRect,int indent,RenderAsTextBehavior behavior)648 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
649                         const IntRect& paintRect, int indent, RenderAsTextBehavior behavior)
650 {
651     // FIXME: Apply overflow to the root layer to not break every test.  Complete hack.  Sigh.
652     IntRect paintDirtyRect(paintRect);
653     if (rootLayer == l) {
654         paintDirtyRect.setWidth(max(paintDirtyRect.width(), rootLayer->renderBox()->maxXLayoutOverflow()));
655         paintDirtyRect.setHeight(max(paintDirtyRect.height(), rootLayer->renderBox()->maxYLayoutOverflow()));
656         l->setWidth(max(l->width(), l->renderBox()->maxXLayoutOverflow()));
657         l->setHeight(max(l->height(), l->renderBox()->maxYLayoutOverflow()));
658     }
659 
660     // Calculate the clip rects we should use.
661     IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
662     l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, true);
663 
664     // Ensure our lists are up-to-date.
665     l->updateZOrderLists();
666     l->updateNormalFlowList();
667 
668     bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect, rootLayer);
669     Vector<RenderLayer*>* negList = l->negZOrderList();
670     bool paintsBackgroundSeparately = negList && negList->size() > 0;
671     if (shouldPaint && paintsBackgroundSeparately)
672         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, LayerPaintPhaseBackground, indent, behavior);
673 
674     if (negList) {
675         int currIndent = indent;
676         if (behavior & RenderAsTextShowLayerNesting) {
677             writeIndent(ts, indent);
678             ts << " negative z-order list(" << negList->size() << ")\n";
679             ++currIndent;
680         }
681         for (unsigned i = 0; i != negList->size(); ++i)
682             writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior);
683     }
684 
685     if (shouldPaint)
686         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
687 
688     if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) {
689         int currIndent = indent;
690         if (behavior & RenderAsTextShowLayerNesting) {
691             writeIndent(ts, indent);
692             ts << " normal flow list(" << normalFlowList->size() << ")\n";
693             ++currIndent;
694         }
695         for (unsigned i = 0; i != normalFlowList->size(); ++i)
696             writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior);
697     }
698 
699     if (Vector<RenderLayer*>* posList = l->posZOrderList()) {
700         int currIndent = indent;
701         if (behavior & RenderAsTextShowLayerNesting) {
702             writeIndent(ts, indent);
703             ts << " positive z-order list(" << posList->size() << ")\n";
704             ++currIndent;
705         }
706         for (unsigned i = 0; i != posList->size(); ++i)
707             writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior);
708     }
709 }
710 
nodePosition(Node * node)711 static String nodePosition(Node* node)
712 {
713     String result;
714 
715     Element* body = node->document()->body();
716     Node* parent;
717     for (Node* n = node; n; n = parent) {
718         parent = n->parentOrHostNode();
719         if (n != node)
720             result += " of ";
721         if (parent) {
722             if (body && n == body) {
723                 // We don't care what offset body may be in the document.
724                 result += "body";
725                 break;
726             }
727             if (n->isShadowBoundary())
728                 result += "{" + getTagName(n) + "}";
729             else
730                 result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
731         } else
732             result += "document";
733     }
734 
735     return result;
736 }
737 
writeSelection(TextStream & ts,const RenderObject * o)738 static void writeSelection(TextStream& ts, const RenderObject* o)
739 {
740     Node* n = o->node();
741     if (!n || !n->isDocumentNode())
742         return;
743 
744     Document* doc = static_cast<Document*>(n);
745     Frame* frame = doc->frame();
746     if (!frame)
747         return;
748 
749     VisibleSelection selection = frame->selection()->selection();
750     if (selection.isCaret()) {
751         ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode());
752         if (selection.affinity() == UPSTREAM)
753             ts << " (upstream affinity)";
754         ts << "\n";
755     } else if (selection.isRange())
756         ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n"
757            << "selection end:   position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n";
758 }
759 
externalRepresentation(Frame * frame,RenderAsTextBehavior behavior)760 String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
761 {
762     PrintContext printContext(frame);
763     if (behavior & RenderAsTextPrintingMode) {
764         if (!frame->contentRenderer())
765             return String();
766         printContext.begin(frame->contentRenderer()->width());
767     }
768 
769     if (!(behavior & RenderAsTextDontUpdateLayout))
770         frame->document()->updateLayout();
771 
772     RenderObject* o = frame->contentRenderer();
773     if (!o)
774         return String();
775 
776     TextStream ts;
777     if (o->hasLayer()) {
778         RenderLayer* l = toRenderBox(o)->layer();
779         writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), 0, behavior);
780         writeSelection(ts, o);
781     }
782     return ts.release();
783 }
784 
writeCounterValuesFromChildren(TextStream & stream,RenderObject * parent,bool & isFirstCounter)785 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
786 {
787     for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) {
788         if (child->isCounter()) {
789             if (!isFirstCounter)
790                 stream << " ";
791             isFirstCounter = false;
792             String str(toRenderText(child)->text());
793             stream << str;
794         }
795     }
796 }
797 
counterValueForElement(Element * element)798 String counterValueForElement(Element* element)
799 {
800     // Make sure the element is not freed during the layout.
801     RefPtr<Element> elementRef(element);
802     element->document()->updateLayout();
803     TextStream stream;
804     bool isFirstCounter = true;
805     // The counter renderers should be children of :before or :after pseudo-elements.
806     if (RenderObject* renderer = element->renderer()) {
807         if (RenderObject* pseudoElement = renderer->beforePseudoElementRenderer())
808             writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
809         if (RenderObject* pseudoElement = renderer->afterPseudoElementRenderer())
810             writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
811     }
812     return stream.release();
813 }
814 
markerTextForListItem(Element * element)815 String markerTextForListItem(Element* element)
816 {
817     // Make sure the element is not freed during the layout.
818     RefPtr<Element> elementRef(element);
819     element->document()->updateLayout();
820 
821     RenderObject* renderer = element->renderer();
822     if (!renderer || !renderer->isListItem())
823         return String();
824 
825     return toRenderListItem(renderer)->markerText();
826 }
827 
828 } // namespace WebCore
829