2 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "RenderTreeAsText.h"
30 #include "FlowThreadController.h"
32 #include "FrameSelection.h"
33 #include "FrameView.h"
34 #include "HTMLElement.h"
35 #include "HTMLNames.h"
36 #include "InlineTextBox.h"
37 #include "PrintContext.h"
39 #include "RenderDetailsMarker.h"
40 #include "RenderFileUploadControl.h"
41 #include "RenderInline.h"
42 #include "RenderLayer.h"
43 #include "RenderListItem.h"
44 #include "RenderListMarker.h"
45 #include "RenderNamedFlowThread.h"
46 #include "RenderPart.h"
47 #include "RenderRegion.h"
48 #include "RenderTableCell.h"
49 #include "RenderView.h"
50 #include "RenderWidget.h"
51 #include "StylePropertySet.h"
52 #include <wtf/HexNumber.h>
53 #include <wtf/UnusedParam.h>
54 #include <wtf/Vector.h>
55 #include <wtf/unicode/CharacterNames.h>
58 #include "RenderSVGContainer.h"
59 #include "RenderSVGGradientStop.h"
60 #include "RenderSVGImage.h"
61 #include "RenderSVGInlineText.h"
62 #include "RenderSVGPath.h"
63 #include "RenderSVGRoot.h"
64 #include "RenderSVGText.h"
65 #include "SVGRenderTreeAsText.h"
68 #if USE(ACCELERATED_COMPOSITING)
69 #include "RenderLayerBacking.h"
78 using namespace HTMLNames;
80 static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const LayoutRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior = RenderAsTextBehaviorNormal);
82 static inline bool hasFractions(double val)
84 static const double s_epsilon = 0.0001;
85 int ival = static_cast<int>(val);
86 double dval = static_cast<double>(ival);
87 return fabs(val - dval) > s_epsilon;
90 String formatNumberRespectingIntegers(double value)
92 if (!hasFractions(value))
93 return String::number(static_cast<int>(value));
94 return String::number(value, ShouldRoundDecimalPlaces, 2);
97 TextStream& operator<<(TextStream& ts, const IntRect& r)
99 return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
102 TextStream& operator<<(TextStream& ts, const IntPoint& p)
104 return ts << "(" << p.x() << "," << p.y() << ")";
107 TextStream& operator<<(TextStream& ts, const FractionalLayoutPoint& p)
109 // FIXME: These should be printed as floats. Keeping them ints for consistency with pervious test expectations.
110 return ts << "(" << p.x().toInt() << "," << p.y().toInt() << ")";
113 TextStream& operator<<(TextStream& ts, const FloatPoint& p)
115 ts << "(" << formatNumberRespectingIntegers(p.x());
116 ts << "," << formatNumberRespectingIntegers(p.y());
121 TextStream& operator<<(TextStream& ts, const FloatSize& s)
123 ts << "width=" << formatNumberRespectingIntegers(s.width());
124 ts << " height=" << formatNumberRespectingIntegers(s.height());
128 void writeIndent(TextStream& ts, int indent)
130 for (int i = 0; i != indent; ++i)
134 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
136 switch (borderStyle) {
172 static String getTagName(Node* n)
174 if (n->isDocumentNode())
176 if (n->nodeType() == Node::COMMENT_NODE)
178 return n->nodeName();
181 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
183 if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
186 const HTMLElement* elem = static_cast<const HTMLElement*>(node);
187 if (elem->getAttribute(classAttr) != "Apple-style-span")
190 if (!node->hasChildNodes())
193 const StylePropertySet* inlineStyleDecl = elem->inlineStyle();
194 return (!inlineStyleDecl || inlineStyleDecl->isEmpty());
197 String quoteAndEscapeNonPrintables(const String& s)
199 StringBuilder result;
201 for (unsigned i = 0; i != s.length(); ++i) {
206 } else if (c == '"') {
209 } else if (c == '\n' || c == noBreakSpace)
212 if (c >= 0x20 && c < 0x7F)
218 appendUnsignedAsHex(c, result);
224 return result.toString();
227 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
229 ts << o.renderName();
231 if (behavior & RenderAsTextShowAddresses)
232 ts << " " << static_cast<const void*>(&o);
234 if (o.style() && o.style()->zIndex())
235 ts << " zI: " << o.style()->zIndex();
238 String tagName = getTagName(o.node());
239 if (!tagName.isEmpty()) {
240 ts << " {" << tagName << "}";
241 // flag empty or unstyled AppleStyleSpan because we never
242 // want to leave them in the DOM
243 if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
244 ts << " *empty or unstyled AppleStyleSpan*";
248 RenderBlock* cb = o.containingBlock();
249 bool adjustForTableCells = cb ? cb->isTableCell() : false;
253 // 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
254 // many test results.
255 const RenderText& text = *toRenderText(&o);
256 IntRect linesBox = text.linesBoundingBox();
257 r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
258 if (adjustForTableCells && !text.firstTextBox())
259 adjustForTableCells = false;
260 } else if (o.isRenderInline()) {
261 // FIXME: Would be better not to just dump 0, 0 as the x and y here.
262 const RenderInline& inlineFlow = *toRenderInline(&o);
263 r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
264 adjustForTableCells = false;
265 } else if (o.isTableCell()) {
266 // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like
267 // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
268 // captured by the results.
269 const RenderTableCell& cell = *toRenderTableCell(&o);
270 r = LayoutRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
271 } else if (o.isBox())
272 r = toRenderBox(&o)->frameRect();
274 // FIXME: Temporary in order to ensure compatibility with existing layout test results.
275 if (adjustForTableCells)
276 r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
278 // FIXME: Convert layout test results to report sub-pixel values, in the meantime using enclosingIntRect
279 // for consistency with old results.
280 ts << " " << enclosingIntRect(r);
282 if (!(o.isText() && !o.isBR())) {
283 if (o.isFileUploadControl())
284 ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
287 Color color = o.style()->visitedDependentColor(CSSPropertyColor);
288 if (o.parent()->style()->visitedDependentColor(CSSPropertyColor) != color)
289 ts << " [color=" << color.nameForRenderTreeAsText() << "]";
291 // Do not dump invalid or transparent backgrounds, since that is the default.
292 Color backgroundColor = o.style()->visitedDependentColor(CSSPropertyBackgroundColor);
293 if (o.parent()->style()->visitedDependentColor(CSSPropertyBackgroundColor) != backgroundColor
294 && backgroundColor.isValid() && backgroundColor.rgb())
295 ts << " [bgcolor=" << backgroundColor.nameForRenderTreeAsText() << "]";
297 Color textFillColor = o.style()->visitedDependentColor(CSSPropertyWebkitTextFillColor);
298 if (o.parent()->style()->visitedDependentColor(CSSPropertyWebkitTextFillColor) != textFillColor
299 && textFillColor.isValid() && textFillColor != color && textFillColor.rgb())
300 ts << " [textFillColor=" << textFillColor.nameForRenderTreeAsText() << "]";
302 Color textStrokeColor = o.style()->visitedDependentColor(CSSPropertyWebkitTextStrokeColor);
303 if (o.parent()->style()->visitedDependentColor(CSSPropertyWebkitTextStrokeColor) != textStrokeColor
304 && textStrokeColor.isValid() && textStrokeColor != color && textStrokeColor.rgb())
305 ts << " [textStrokeColor=" << textStrokeColor.nameForRenderTreeAsText() << "]";
307 if (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth() && o.style()->textStrokeWidth() > 0)
308 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
311 if (!o.isBoxModelObject())
314 const RenderBoxModelObject& box = *toRenderBoxModelObject(&o);
315 if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
318 BorderValue prevBorder = o.style()->borderTop();
319 if (!box.borderTop())
322 ts << " (" << box.borderTop() << "px ";
323 printBorderStyle(ts, o.style()->borderTopStyle());
324 Color col = o.style()->borderTopColor();
326 col = o.style()->color();
327 ts << col.nameForRenderTreeAsText() << ")";
330 if (o.style()->borderRight() != prevBorder) {
331 prevBorder = o.style()->borderRight();
332 if (!box.borderRight())
335 ts << " (" << box.borderRight() << "px ";
336 printBorderStyle(ts, o.style()->borderRightStyle());
337 Color col = o.style()->borderRightColor();
339 col = o.style()->color();
340 ts << col.nameForRenderTreeAsText() << ")";
344 if (o.style()->borderBottom() != prevBorder) {
345 prevBorder = box.style()->borderBottom();
346 if (!box.borderBottom())
349 ts << " (" << box.borderBottom() << "px ";
350 printBorderStyle(ts, o.style()->borderBottomStyle());
351 Color col = o.style()->borderBottomColor();
353 col = o.style()->color();
354 ts << col.nameForRenderTreeAsText() << ")";
358 if (o.style()->borderLeft() != prevBorder) {
359 prevBorder = o.style()->borderLeft();
360 if (!box.borderLeft())
363 ts << " (" << box.borderLeft() << "px ";
364 printBorderStyle(ts, o.style()->borderLeftStyle());
365 Color col = o.style()->borderLeftColor();
367 col = o.style()->color();
368 ts << col.nameForRenderTreeAsText() << ")";
376 // We want to show any layout padding, both CSS padding and intrinsic padding, so we can't just check o.style()->hasPadding().
377 if (o.isRenderMathMLBlock() && (box.paddingTop() || box.paddingRight() || box.paddingBottom() || box.paddingLeft())) {
379 LayoutUnit cssTop = box.computedCSSPaddingTop();
380 LayoutUnit cssRight = box.computedCSSPaddingRight();
381 LayoutUnit cssBottom = box.computedCSSPaddingBottom();
382 LayoutUnit cssLeft = box.computedCSSPaddingLeft();
383 if (box.paddingTop() != cssTop || box.paddingRight() != cssRight || box.paddingBottom() != cssBottom || box.paddingLeft() != cssLeft) {
385 if (cssTop || cssRight || cssBottom || cssLeft)
388 ts << "padding: " << roundToInt(box.paddingTop()) << " " << roundToInt(box.paddingRight()) << " " << roundToInt(box.paddingBottom()) << " " << roundToInt(box.paddingLeft()) << "]";
393 if (o.isTableCell()) {
394 const RenderTableCell& c = *toRenderTableCell(&o);
395 ts << " [r=" << c.rowIndex() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
398 #if ENABLE(DETAILS_ELEMENT)
399 if (o.isDetailsMarker()) {
401 switch (toRenderDetailsMarker(&o)->orientation()) {
402 case RenderDetailsMarker::Left:
405 case RenderDetailsMarker::Right:
408 case RenderDetailsMarker::Up:
411 case RenderDetailsMarker::Down:
418 if (o.isListMarker()) {
419 String text = toRenderListMarker(&o)->text();
420 if (!text.isEmpty()) {
421 if (text.length() != 1)
422 text = quoteAndEscapeNonPrintables(text);
429 text = "black square";
432 text = "white bullet";
435 text = quoteAndEscapeNonPrintables(text);
442 if (behavior & RenderAsTextShowIDAndClass) {
443 if (Node* node = o.node()) {
445 ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\"";
447 if (node->hasClass()) {
448 StyledElement* styledElement = static_cast<StyledElement*>(node);
450 for (size_t i = 0; i < styledElement->classNames().size(); ++i) {
453 classes += styledElement->classNames()[i];
455 ts << " class=\"" + classes + "\"";
460 if (behavior & RenderAsTextShowLayoutState) {
461 bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
463 ts << " (needs layout:";
465 bool havePrevious = false;
466 if (o.selfNeedsLayout()) {
471 if (o.needsPositionedMovementLayout()) {
475 ts << " positioned movement";
478 if (o.normalChildNeedsLayout()) {
485 if (o.posChildNeedsLayout()) {
488 ts << " positioned child";
496 // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget
497 // is invisible the QWidget should be invisible too.
498 if (o.isRenderPart()) {
499 const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o));
500 if (part->widget() && part->widget()->platformWidget()) {
501 QObject* wid = part->widget()->platformWidget();
504 ts << "geometry: {" << wid->property("geometry").toRect() << "} ";
505 ts << "isHidden: " << !wid->property("isVisible").toBool() << " ";
506 ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " ";
507 ts << "isParentVisible: " << part->widget()->isParentVisible() << " ] ";
513 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
515 // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
516 // to detect any changes caused by the conversion to floating point. :(
519 int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
521 // FIXME: Table cell adjustment is temporary until results can be updated.
522 if (o.containingBlock()->isTableCell())
523 y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
525 ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
526 if (!run.isLeftToRightDirection() || run.dirOverride()) {
527 ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
528 if (run.dirOverride())
532 << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
534 ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
538 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
541 if (o.isSVGShape()) {
542 write(ts, *toRenderSVGShape(&o), indent);
545 if (o.isSVGGradientStop()) {
546 writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent);
549 if (o.isSVGResourceContainer()) {
550 writeSVGResourceContainer(ts, o, indent);
553 if (o.isSVGContainer()) {
554 writeSVGContainer(ts, o, indent);
558 write(ts, *toRenderSVGRoot(&o), indent);
562 writeSVGText(ts, *toRenderSVGText(&o), indent);
565 if (o.isSVGInlineText()) {
566 writeSVGInlineText(ts, *toRenderSVGInlineText(&o), indent);
569 if (o.isSVGImage()) {
570 writeSVGImage(ts, *toRenderSVGImage(&o), indent);
575 writeIndent(ts, indent);
577 RenderTreeAsText::writeRenderObject(ts, o, behavior);
580 if (o.isText() && !o.isBR()) {
581 const RenderText& text = *toRenderText(&o);
582 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
583 writeIndent(ts, indent + 1);
584 writeTextRun(ts, text, *box);
588 for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
589 if (child->hasLayer())
591 write(ts, *child, indent + 1, behavior);
595 Widget* widget = toRenderWidget(&o)->widget();
596 if (widget && widget->isFrameView()) {
597 FrameView* view = static_cast<FrameView*>(widget);
598 RenderView* root = view->frame()->contentRenderer();
601 RenderLayer* l = root->layer();
603 writeLayers(ts, l, l, l->rect(), indent + 1, behavior);
609 enum LayerPaintPhase {
610 LayerPaintPhaseAll = 0,
611 LayerPaintPhaseBackground = -1,
612 LayerPaintPhaseForeground = 1
615 static void write(TextStream& ts, RenderLayer& l,
616 const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect, const LayoutRect& clipRect, const LayoutRect& outlineClipRect,
617 LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
619 IntRect adjustedLayoutBounds = pixelSnappedIntRect(layerBounds);
620 IntRect adjustedBackgroundClipRect = pixelSnappedIntRect(backgroundClipRect);
621 IntRect adjustedClipRect = pixelSnappedIntRect(clipRect);
622 IntRect adjustedOutlineClipRect = pixelSnappedIntRect(outlineClipRect);
624 writeIndent(ts, indent);
628 if (behavior & RenderAsTextShowAddresses)
629 ts << static_cast<const void*>(&l) << " ";
631 ts << adjustedLayoutBounds;
633 if (!adjustedLayoutBounds.isEmpty()) {
634 if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
635 ts << " backgroundClip " << adjustedBackgroundClipRect;
636 if (!adjustedClipRect.contains(adjustedLayoutBounds))
637 ts << " clip " << adjustedClipRect;
638 if (!adjustedOutlineClipRect.contains(adjustedLayoutBounds))
639 ts << " outlineClip " << adjustedOutlineClipRect;
642 if (l.renderer()->hasOverflowClip()) {
643 if (l.scrollXOffset())
644 ts << " scrollX " << l.scrollXOffset();
645 if (l.scrollYOffset())
646 ts << " scrollY " << l.scrollYOffset();
647 if (l.renderBox() && l.renderBox()->pixelSnappedClientWidth() != l.scrollWidth())
648 ts << " scrollWidth " << l.scrollWidth();
649 if (l.renderBox() && l.renderBox()->pixelSnappedClientHeight() != l.scrollHeight())
650 ts << " scrollHeight " << l.scrollHeight();
653 if (paintPhase == LayerPaintPhaseBackground)
654 ts << " layerType: background only";
655 else if (paintPhase == LayerPaintPhaseForeground)
656 ts << " layerType: foreground only";
658 #if USE(ACCELERATED_COMPOSITING)
659 if (behavior & RenderAsTextShowCompositedLayers) {
660 if (l.isComposited())
661 ts << " (composited, bounds=" << l.backing()->compositedBounds() << ", drawsContent=" << l.backing()->graphicsLayer()->drawsContent() << ", paints into ancestor=" << l.backing()->paintsIntoCompositedAncestor() << ")";
664 UNUSED_PARAM(behavior);
669 if (paintPhase != LayerPaintPhaseBackground)
670 write(ts, *l.renderer(), indent + 1, behavior);
673 static void writeRenderNamedFlowThreads(TextStream& ts, RenderView* renderView, const RenderLayer* rootLayer,
674 const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
676 if (!renderView->hasRenderNamedFlowThreads())
679 const RenderNamedFlowThreadList* list = renderView->flowThreadController()->renderNamedFlowThreadList();
681 writeIndent(ts, indent);
682 ts << "Flow Threads\n";
684 for (RenderNamedFlowThreadList::const_iterator iter = list->begin(); iter != list->end(); ++iter) {
685 const RenderNamedFlowThread* renderFlowThread = *iter;
687 writeIndent(ts, indent + 1);
688 ts << "Thread with flow-name '" << renderFlowThread->flowThreadName() << "'\n";
690 RenderLayer* layer = renderFlowThread->layer();
691 writeLayers(ts, rootLayer, layer, paintRect, indent + 2, behavior);
693 // Display the render regions attached to this flow thread
694 const RenderRegionList& flowThreadRegionList = renderFlowThread->renderRegionList();
695 if (!flowThreadRegionList.isEmpty()) {
696 writeIndent(ts, indent + 1);
697 ts << "Regions for flow '"<< renderFlowThread->flowThreadName() << "'\n";
698 for (RenderRegionList::const_iterator itRR = flowThreadRegionList.begin(); itRR != flowThreadRegionList.end(); ++itRR) {
699 RenderRegion* renderRegion = *itRR;
700 writeIndent(ts, indent + 2);
701 ts << "RenderRegion";
702 if (renderRegion->node()) {
703 String tagName = getTagName(renderRegion->node());
704 if (!tagName.isEmpty())
705 ts << " {" << tagName << "}";
706 if (renderRegion->node()->isElementNode() && renderRegion->node()->hasID()) {
707 Element* element = static_cast<Element*>(renderRegion->node());
708 ts << " #" << element->idForStyleResolution();
710 if (renderRegion->hasCustomRegionStyle())
711 ts << " region style: 1";
713 if (!renderRegion->isValid())
721 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
722 const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
724 // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
725 LayoutRect paintDirtyRect(paintRect);
726 if (rootLayer == l) {
727 paintDirtyRect.setWidth(max<LayoutUnit>(paintDirtyRect.width(), rootLayer->renderBox()->layoutOverflowRect().maxX()));
728 paintDirtyRect.setHeight(max<LayoutUnit>(paintDirtyRect.height(), rootLayer->renderBox()->layoutOverflowRect().maxY()));
729 l->setSize(l->size().expandedTo(pixelSnappedIntSize(l->renderBox()->maxLayoutOverflow(), LayoutPoint(0, 0))));
732 // Calculate the clip rects we should use.
733 LayoutRect layerBounds;
734 ClipRect damageRect, clipRectToApply, outlineRect;
735 l->calculateRects(rootLayer, 0, TemporaryClipRects, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
737 // Ensure our lists are up-to-date.
738 l->updateLayerListsIfNeeded();
740 bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer);
741 Vector<RenderLayer*>* negList = l->negZOrderList();
742 bool paintsBackgroundSeparately = negList && negList->size() > 0;
743 if (shouldPaint && paintsBackgroundSeparately)
744 write(ts, *l, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), LayerPaintPhaseBackground, indent, behavior);
747 int currIndent = indent;
748 if (behavior & RenderAsTextShowLayerNesting) {
749 writeIndent(ts, indent);
750 ts << " negative z-order list(" << negList->size() << ")\n";
753 for (unsigned i = 0; i != negList->size(); ++i)
754 writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior);
758 write(ts, *l, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
760 if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) {
761 int currIndent = indent;
762 if (behavior & RenderAsTextShowLayerNesting) {
763 writeIndent(ts, indent);
764 ts << " normal flow list(" << normalFlowList->size() << ")\n";
767 for (unsigned i = 0; i != normalFlowList->size(); ++i)
768 writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior);
771 if (Vector<RenderLayer*>* posList = l->posZOrderList()) {
772 int currIndent = indent;
773 if (behavior & RenderAsTextShowLayerNesting) {
774 writeIndent(ts, indent);
775 ts << " positive z-order list(" << posList->size() << ")\n";
778 for (unsigned i = 0; i != posList->size(); ++i)
779 writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior);
782 // Altough the RenderFlowThread requires a layer, it is not collected by its parent,
783 // so we have to treat it as a special case.
784 if (l->renderer()->isRenderView()) {
785 RenderView* renderView = toRenderView(l->renderer());
786 writeRenderNamedFlowThreads(ts, renderView, rootLayer, paintDirtyRect, indent, behavior);
790 static String nodePosition(Node* node)
794 Element* body = node->document()->body();
796 for (Node* n = node; n; n = parent) {
797 parent = n->parentOrHostNode();
801 if (body && n == body) {
802 // We don't care what offset body may be in the document.
806 if (n->isShadowRoot())
807 result += "{" + getTagName(n) + "}";
809 result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
811 result += "document";
817 static void writeSelection(TextStream& ts, const RenderObject* o)
820 if (!n || !n->isDocumentNode())
823 Document* doc = static_cast<Document*>(n);
824 Frame* frame = doc->frame();
828 VisibleSelection selection = frame->selection()->selection();
829 if (selection.isCaret()) {
830 ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode());
831 if (selection.affinity() == UPSTREAM)
832 ts << " (upstream affinity)";
834 } else if (selection.isRange())
835 ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n"
836 << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n";
839 static String externalRepresentation(RenderBox* renderer, RenderAsTextBehavior behavior)
842 if (!renderer->hasLayer())
845 RenderLayer* layer = renderer->layer();
846 writeLayers(ts, layer, layer, layer->rect(), 0, behavior);
847 writeSelection(ts, renderer);
851 String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
853 RenderObject* renderer = frame->contentRenderer();
854 if (!renderer || !renderer->isBox())
857 PrintContext printContext(frame);
858 if (behavior & RenderAsTextPrintingMode)
859 printContext.begin(toRenderBox(renderer)->width());
860 if (!(behavior & RenderAsTextDontUpdateLayout))
861 frame->document()->updateLayout();
863 return externalRepresentation(toRenderBox(renderer), behavior);
866 String externalRepresentation(Element* element, RenderAsTextBehavior behavior)
868 RenderObject* renderer = element->renderer();
869 if (!renderer || !renderer->isBox())
871 // Doesn't support printing mode.
872 ASSERT(!(behavior & RenderAsTextPrintingMode));
873 if (!(behavior & RenderAsTextDontUpdateLayout) && element->document())
874 element->document()->updateLayout();
876 return externalRepresentation(toRenderBox(renderer), behavior | RenderAsTextShowAllLayers);
879 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
881 for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) {
882 if (child->isCounter()) {
885 isFirstCounter = false;
886 String str(toRenderText(child)->text());
892 String counterValueForElement(Element* element)
894 // Make sure the element is not freed during the layout.
895 RefPtr<Element> elementRef(element);
896 element->document()->updateLayout();
898 bool isFirstCounter = true;
899 // The counter renderers should be children of :before or :after pseudo-elements.
900 if (RenderObject* renderer = element->renderer()) {
901 if (RenderObject* pseudoElement = renderer->beforePseudoElementRenderer())
902 writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
903 if (RenderObject* pseudoElement = renderer->afterPseudoElementRenderer())
904 writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
906 return stream.release();
909 String markerTextForListItem(Element* element)
911 // Make sure the element is not freed during the layout.
912 RefPtr<Element> elementRef(element);
913 element->document()->updateLayout();
915 RenderObject* renderer = element->renderer();
916 if (!renderer || !renderer->isListItem())
919 return toRenderListItem(renderer)->markerText();
922 } // namespace WebCore