• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 (o.isDetailsMarker()) {
378         ts << ": ";
379         switch (toRenderDetailsMarker(&o)->orientation()) {
380         case RenderDetailsMarker::Left:
381             ts << "left";
382             break;
383         case RenderDetailsMarker::Right:
384             ts << "right";
385             break;
386         case RenderDetailsMarker::Up:
387             ts << "up";
388             break;
389         case RenderDetailsMarker::Down:
390             ts << "down";
391             break;
392         }
393     }
394 
395     if (o.isListMarker()) {
396         String text = toRenderListMarker(&o)->text();
397         if (!text.isEmpty()) {
398             if (text.length() != 1)
399                 text = quoteAndEscapeNonPrintables(text);
400             else {
401                 switch (text[0]) {
402                     case bullet:
403                         text = "bullet";
404                         break;
405                     case blackSquare:
406                         text = "black square";
407                         break;
408                     case whiteBullet:
409                         text = "white bullet";
410                         break;
411                     default:
412                         text = quoteAndEscapeNonPrintables(text);
413                 }
414             }
415             ts << ": " << text;
416         }
417     }
418 
419     if (behavior & RenderAsTextShowIDAndClass) {
420         if (Node* node = o.node()) {
421             if (node->hasID())
422                 ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\"";
423 
424             if (node->hasClass()) {
425                 StyledElement* styledElement = static_cast<StyledElement*>(node);
426                 String classes;
427                 for (size_t i = 0; i < styledElement->classNames().size(); ++i) {
428                     if (i > 0)
429                         classes += " ";
430                     classes += styledElement->classNames()[i];
431                 }
432                 ts << " class=\"" + classes + "\"";
433             }
434         }
435     }
436 
437     if (behavior & RenderAsTextShowLayoutState) {
438         bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
439         if (needsLayout)
440             ts << " (needs layout:";
441 
442         bool havePrevious = false;
443         if (o.selfNeedsLayout()) {
444             ts << " self";
445             havePrevious = true;
446         }
447 
448         if (o.needsPositionedMovementLayout()) {
449             if (havePrevious)
450                 ts << ",";
451             havePrevious = true;
452             ts << " positioned movement";
453         }
454 
455         if (o.normalChildNeedsLayout()) {
456             if (havePrevious)
457                 ts << ",";
458             havePrevious = true;
459             ts << " child";
460         }
461 
462         if (o.posChildNeedsLayout()) {
463             if (havePrevious)
464                 ts << ",";
465             ts << " positioned child";
466         }
467 
468         if (needsLayout)
469             ts << ")";
470     }
471 
472 #if PLATFORM(QT)
473     // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget
474     // is invisible the QWidget should be invisible too.
475     if (o.isRenderPart()) {
476         const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o));
477         if (part->widget() && part->widget()->platformWidget()) {
478             QWidget* wid = part->widget()->platformWidget();
479 
480             ts << " [QT: ";
481             ts << "geometry: {" << wid->geometry() << "} ";
482             ts << "isHidden: " << wid->isHidden() << " ";
483             ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " ";
484             ts << "isParentVisible: " << part->widget()->isParentVisible() << " ";
485             ts << "mask: {" << wid->mask().boundingRect() << "} ] ";
486         }
487     }
488 #endif
489 }
490 
writeTextRun(TextStream & ts,const RenderText & o,const InlineTextBox & run)491 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
492 {
493     // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
494     // to detect any changes caused by the conversion to floating point. :(
495     int x = run.m_x;
496     int y = run.m_y;
497     int logicalWidth = ceilf(run.m_x + run.m_logicalWidth) - x;
498 
499     // FIXME: Table cell adjustment is temporary until results can be updated.
500     if (o.containingBlock()->isTableCell())
501         y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
502 
503     ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
504     if (!run.isLeftToRightDirection() || run.m_dirOverride) {
505         ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
506         if (run.m_dirOverride)
507             ts << " override";
508     }
509     ts << ": "
510         << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
511     if (run.hasHyphen())
512         ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
513     ts << "\n";
514 }
515 
write(TextStream & ts,const RenderObject & o,int indent,RenderAsTextBehavior behavior)516 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
517 {
518 #if ENABLE(SVG)
519     if (o.isSVGPath()) {
520         write(ts, *toRenderSVGPath(&o), indent);
521         return;
522     }
523     if (o.isSVGGradientStop()) {
524         writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent);
525         return;
526     }
527     if (o.isSVGResourceContainer()) {
528         writeSVGResourceContainer(ts, o, indent);
529         return;
530     }
531     if (o.isSVGContainer()) {
532         writeSVGContainer(ts, o, indent);
533         return;
534     }
535     if (o.isSVGRoot()) {
536         write(ts, *toRenderSVGRoot(&o), indent);
537         return;
538     }
539     if (o.isSVGText()) {
540         writeSVGText(ts, *toRenderBlock(&o), indent);
541         return;
542     }
543     if (o.isSVGInlineText()) {
544         writeSVGInlineText(ts, *toRenderText(&o), indent);
545         return;
546     }
547     if (o.isSVGImage()) {
548         writeSVGImage(ts, *toRenderSVGImage(&o), indent);
549         return;
550     }
551 #endif
552 
553     writeIndent(ts, indent);
554 
555     RenderTreeAsText::writeRenderObject(ts, o, behavior);
556     ts << "\n";
557 
558     if (o.isText() && !o.isBR()) {
559         const RenderText& text = *toRenderText(&o);
560         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
561             writeIndent(ts, indent + 1);
562             writeTextRun(ts, text, *box);
563         }
564     }
565 
566     for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
567         if (child->hasLayer())
568             continue;
569         write(ts, *child, indent + 1, behavior);
570     }
571 
572     if (o.isWidget()) {
573         Widget* widget = toRenderWidget(&o)->widget();
574         if (widget && widget->isFrameView()) {
575             FrameView* view = static_cast<FrameView*>(widget);
576             RenderView* root = view->frame()->contentRenderer();
577             if (root) {
578                 view->layout();
579                 RenderLayer* l = root->layer();
580                 if (l)
581                     writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), indent + 1, behavior);
582             }
583         }
584     }
585 }
586 
587 enum LayerPaintPhase {
588     LayerPaintPhaseAll = 0,
589     LayerPaintPhaseBackground = -1,
590     LayerPaintPhaseForeground = 1
591 };
592 
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)593 static void write(TextStream& ts, RenderLayer& l,
594                   const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect,
595                   LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
596 {
597     writeIndent(ts, indent);
598 
599     ts << "layer ";
600 
601     if (behavior & RenderAsTextShowAddresses)
602         ts << static_cast<const void*>(&l) << " ";
603 
604     ts << layerBounds;
605 
606     if (!layerBounds.isEmpty()) {
607         if (!backgroundClipRect.contains(layerBounds))
608             ts << " backgroundClip " << backgroundClipRect;
609         if (!clipRect.contains(layerBounds))
610             ts << " clip " << clipRect;
611         if (!outlineClipRect.contains(layerBounds))
612             ts << " outlineClip " << outlineClipRect;
613     }
614 
615     if (l.renderer()->hasOverflowClip()) {
616         if (l.scrollXOffset())
617             ts << " scrollX " << l.scrollXOffset();
618         if (l.scrollYOffset())
619             ts << " scrollY " << l.scrollYOffset();
620         if (l.renderBox() && l.renderBox()->clientWidth() != l.scrollWidth())
621             ts << " scrollWidth " << l.scrollWidth();
622         if (l.renderBox() && l.renderBox()->clientHeight() != l.scrollHeight())
623             ts << " scrollHeight " << l.scrollHeight();
624     }
625 
626     if (paintPhase == LayerPaintPhaseBackground)
627         ts << " layerType: background only";
628     else if (paintPhase == LayerPaintPhaseForeground)
629         ts << " layerType: foreground only";
630 
631 #if USE(ACCELERATED_COMPOSITING)
632     if (behavior & RenderAsTextShowCompositedLayers) {
633         if (l.isComposited())
634             ts << " (composited, bounds " << l.backing()->compositedBounds() << ")";
635     }
636 #else
637     UNUSED_PARAM(behavior);
638 #endif
639 
640     ts << "\n";
641 
642     if (paintPhase != LayerPaintPhaseBackground)
643         write(ts, *l.renderer(), indent + 1, behavior);
644 }
645 
writeLayers(TextStream & ts,const RenderLayer * rootLayer,RenderLayer * l,const IntRect & paintRect,int indent,RenderAsTextBehavior behavior)646 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
647                         const IntRect& paintRect, int indent, RenderAsTextBehavior behavior)
648 {
649     // FIXME: Apply overflow to the root layer to not break every test.  Complete hack.  Sigh.
650     IntRect paintDirtyRect(paintRect);
651     if (rootLayer == l) {
652         paintDirtyRect.setWidth(max(paintDirtyRect.width(), rootLayer->renderBox()->maxXLayoutOverflow()));
653         paintDirtyRect.setHeight(max(paintDirtyRect.height(), rootLayer->renderBox()->maxYLayoutOverflow()));
654         l->setWidth(max(l->width(), l->renderBox()->maxXLayoutOverflow()));
655         l->setHeight(max(l->height(), l->renderBox()->maxYLayoutOverflow()));
656     }
657 
658     // Calculate the clip rects we should use.
659     IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
660     l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, true);
661 
662     // Ensure our lists are up-to-date.
663     l->updateZOrderLists();
664     l->updateNormalFlowList();
665 
666     bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect, rootLayer);
667     Vector<RenderLayer*>* negList = l->negZOrderList();
668     bool paintsBackgroundSeparately = negList && negList->size() > 0;
669     if (shouldPaint && paintsBackgroundSeparately)
670         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, LayerPaintPhaseBackground, indent, behavior);
671 
672     if (negList) {
673         int currIndent = indent;
674         if (behavior & RenderAsTextShowLayerNesting) {
675             writeIndent(ts, indent);
676             ts << " negative z-order list(" << negList->size() << ")\n";
677             ++currIndent;
678         }
679         for (unsigned i = 0; i != negList->size(); ++i)
680             writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior);
681     }
682 
683     if (shouldPaint)
684         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
685 
686     if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) {
687         int currIndent = indent;
688         if (behavior & RenderAsTextShowLayerNesting) {
689             writeIndent(ts, indent);
690             ts << " normal flow list(" << normalFlowList->size() << ")\n";
691             ++currIndent;
692         }
693         for (unsigned i = 0; i != normalFlowList->size(); ++i)
694             writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior);
695     }
696 
697     if (Vector<RenderLayer*>* posList = l->posZOrderList()) {
698         int currIndent = indent;
699         if (behavior & RenderAsTextShowLayerNesting) {
700             writeIndent(ts, indent);
701             ts << " positive z-order list(" << posList->size() << ")\n";
702             ++currIndent;
703         }
704         for (unsigned i = 0; i != posList->size(); ++i)
705             writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior);
706     }
707 }
708 
nodePosition(Node * node)709 static String nodePosition(Node* node)
710 {
711     String result;
712 
713     Element* body = node->document()->body();
714     Node* parent;
715     for (Node* n = node; n; n = parent) {
716         parent = n->parentOrHostNode();
717         if (n != node)
718             result += " of ";
719         if (parent) {
720             if (body && n == body) {
721                 // We don't care what offset body may be in the document.
722                 result += "body";
723                 break;
724             }
725             if (n->isShadowBoundary())
726                 result += "{" + getTagName(n) + "}";
727             else
728                 result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
729         } else
730             result += "document";
731     }
732 
733     return result;
734 }
735 
writeSelection(TextStream & ts,const RenderObject * o)736 static void writeSelection(TextStream& ts, const RenderObject* o)
737 {
738     Node* n = o->node();
739     if (!n || !n->isDocumentNode())
740         return;
741 
742     Document* doc = static_cast<Document*>(n);
743     Frame* frame = doc->frame();
744     if (!frame)
745         return;
746 
747     VisibleSelection selection = frame->selection()->selection();
748     if (selection.isCaret()) {
749         ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode());
750         if (selection.affinity() == UPSTREAM)
751             ts << " (upstream affinity)";
752         ts << "\n";
753     } else if (selection.isRange())
754         ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n"
755            << "selection end:   position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n";
756 }
757 
externalRepresentation(Frame * frame,RenderAsTextBehavior behavior)758 String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
759 {
760     PrintContext printContext(frame);
761     if (behavior & RenderAsTextPrintingMode) {
762         if (!frame->contentRenderer())
763             return String();
764         printContext.begin(frame->contentRenderer()->width());
765     }
766 
767     if (!(behavior & RenderAsTextDontUpdateLayout))
768         frame->document()->updateLayout();
769 
770     RenderObject* o = frame->contentRenderer();
771     if (!o)
772         return String();
773 
774     TextStream ts;
775     if (o->hasLayer()) {
776         RenderLayer* l = toRenderBox(o)->layer();
777         writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), 0, behavior);
778         writeSelection(ts, o);
779     }
780     return ts.release();
781 }
782 
writeCounterValuesFromChildren(TextStream & stream,RenderObject * parent,bool & isFirstCounter)783 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
784 {
785     for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) {
786         if (child->isCounter()) {
787             if (!isFirstCounter)
788                 stream << " ";
789             isFirstCounter = false;
790             String str(toRenderText(child)->text());
791             stream << str;
792         }
793     }
794 }
795 
counterValueForElement(Element * element)796 String counterValueForElement(Element* element)
797 {
798     // Make sure the element is not freed during the layout.
799     RefPtr<Element> elementRef(element);
800     element->document()->updateLayout();
801     TextStream stream;
802     bool isFirstCounter = true;
803     // The counter renderers should be children of :before or :after pseudo-elements.
804     if (RenderObject* renderer = element->renderer()) {
805         if (RenderObject* pseudoElement = renderer->beforePseudoElementRenderer())
806             writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
807         if (RenderObject* pseudoElement = renderer->afterPseudoElementRenderer())
808             writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
809     }
810     return stream.release();
811 }
812 
markerTextForListItem(Element * element)813 String markerTextForListItem(Element* element)
814 {
815     // Make sure the element is not freed during the layout.
816     RefPtr<Element> elementRef(element);
817     element->document()->updateLayout();
818 
819     RenderObject* renderer = element->renderer();
820     if (!renderer || !renderer->isListItem())
821         return String();
822 
823     return toRenderListItem(renderer)->markerText();
824 }
825 
826 } // namespace WebCore
827