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"
29 #include "CSSMutableStyleDeclaration.h"
32 #include "FrameView.h"
33 #include "HTMLElement.h"
34 #include "HTMLNames.h"
35 #include "InlineTextBox.h"
36 #include "PrintContext.h"
38 #include "RenderFileUploadControl.h"
39 #include "RenderInline.h"
40 #include "RenderLayer.h"
41 #include "RenderListItem.h"
42 #include "RenderListMarker.h"
43 #include "RenderPart.h"
44 #include "RenderTableCell.h"
45 #include "RenderView.h"
46 #include "RenderWidget.h"
47 #include "SelectionController.h"
48 #include <wtf/UnusedParam.h>
49 #include <wtf/Vector.h>
50 #include <wtf/unicode/CharacterNames.h>
53 #include "RenderSVGContainer.h"
54 #include "RenderSVGGradientStop.h"
55 #include "RenderSVGImage.h"
56 #include "RenderSVGInlineText.h"
57 #include "RenderSVGPath.h"
58 #include "RenderSVGRoot.h"
59 #include "RenderSVGText.h"
60 #include "SVGRenderTreeAsText.h"
63 #if USE(ACCELERATED_COMPOSITING)
64 #include "RenderLayerBacking.h"
73 using namespace HTMLNames;
75 static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal);
77 bool hasFractions(double val)
79 static const double s_epsilon = 0.0001;
80 int ival = static_cast<int>(val);
81 double dval = static_cast<double>(ival);
82 return fabs(val - dval) > s_epsilon;
85 TextStream& operator<<(TextStream& ts, const IntRect& r)
87 return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
90 TextStream& operator<<(TextStream& ts, const IntPoint& p)
92 return ts << "(" << p.x() << "," << p.y() << ")";
95 TextStream& operator<<(TextStream& ts, const FloatPoint& p)
98 if (hasFractions(p.x()))
103 if (hasFractions(p.y()))
110 TextStream& operator<<(TextStream& ts, const FloatSize& s)
113 if (hasFractions(s.width()))
116 ts << int(s.width());
118 if (hasFractions(s.height()))
121 ts << int(s.height());
125 void writeIndent(TextStream& ts, int indent)
127 for (int i = 0; i != indent; ++i)
131 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
133 switch (borderStyle) {
169 static String getTagName(Node* n)
171 if (n->isDocumentNode())
173 if (n->isCommentNode())
175 return n->nodeName();
178 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
180 if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
183 const HTMLElement* elem = static_cast<const HTMLElement*>(node);
184 if (elem->getAttribute(classAttr) != "Apple-style-span")
187 if (!node->hasChildNodes())
190 CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
191 return (!inlineStyleDecl || inlineStyleDecl->length() == 0);
194 String quoteAndEscapeNonPrintables(const String& s)
196 Vector<UChar> result;
198 for (unsigned i = 0; i != s.length(); ++i) {
203 } else if (c == '"') {
206 } else if (c == '\n' || c == noBreakSpace)
209 if (c >= 0x20 && c < 0x7F)
213 String hex = String::format("\\x{%X}", u);
214 unsigned len = hex.length();
215 for (unsigned i = 0; i < len; ++i)
216 result.append(hex[i]);
221 return String::adopt(result);
224 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
226 ts << o.renderName();
228 if (behavior & RenderAsTextShowAddresses)
229 ts << " " << static_cast<const void*>(&o);
231 if (o.style() && o.style()->zIndex())
232 ts << " zI: " << o.style()->zIndex();
235 String tagName = getTagName(o.node());
236 if (!tagName.isEmpty()) {
237 ts << " {" << tagName << "}";
238 // flag empty or unstyled AppleStyleSpan because we never
239 // want to leave them in the DOM
240 if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
241 ts << " *empty or unstyled AppleStyleSpan*";
245 bool adjustForTableCells = o.containingBlock()->isTableCell();
249 // 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
250 // many test results.
251 const RenderText& text = *toRenderText(&o);
252 IntRect linesBox = text.linesBoundingBox();
253 r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
254 if (adjustForTableCells && !text.firstTextBox())
255 adjustForTableCells = false;
256 } else if (o.isRenderInline()) {
257 // FIXME: Would be better not to just dump 0, 0 as the x and y here.
258 const RenderInline& inlineFlow = *toRenderInline(&o);
259 r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
260 adjustForTableCells = false;
261 } else if (o.isTableCell()) {
262 // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like
263 // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
264 // captured by the results.
265 const RenderTableCell& cell = *toRenderTableCell(&o);
266 r = IntRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
267 } else if (o.isBox())
268 r = toRenderBox(&o)->frameRect();
270 // FIXME: Temporary in order to ensure compatibility with existing layout test results.
271 if (adjustForTableCells)
272 r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
276 if (!(o.isText() && !o.isBR())) {
277 if (o.isFileUploadControl())
278 ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
280 if (o.parent() && (o.parent()->style()->color() != o.style()->color()))
281 ts << " [color=" << o.style()->color().name() << "]";
283 if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) &&
284 o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb())
285 // Do not dump invalid or transparent backgrounds, since that is the default.
286 ts << " [bgcolor=" << o.style()->backgroundColor().name() << "]";
288 if (o.parent() && (o.parent()->style()->textFillColor() != o.style()->textFillColor()) &&
289 o.style()->textFillColor().isValid() && o.style()->textFillColor() != o.style()->color() &&
290 o.style()->textFillColor().rgb())
291 ts << " [textFillColor=" << o.style()->textFillColor().name() << "]";
293 if (o.parent() && (o.parent()->style()->textStrokeColor() != o.style()->textStrokeColor()) &&
294 o.style()->textStrokeColor().isValid() && o.style()->textStrokeColor() != o.style()->color() &&
295 o.style()->textStrokeColor().rgb())
296 ts << " [textStrokeColor=" << o.style()->textStrokeColor().name() << "]";
298 if (o.parent() && (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth()) &&
299 o.style()->textStrokeWidth() > 0)
300 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
302 if (!o.isBoxModelObject())
305 const RenderBoxModelObject& box = *toRenderBoxModelObject(&o);
306 if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
309 BorderValue prevBorder;
310 if (o.style()->borderTop() != prevBorder) {
311 prevBorder = o.style()->borderTop();
312 if (!box.borderTop())
315 ts << " (" << box.borderTop() << "px ";
316 printBorderStyle(ts, o.style()->borderTopStyle());
317 Color col = o.style()->borderTopColor();
319 col = o.style()->color();
320 ts << col.name() << ")";
324 if (o.style()->borderRight() != prevBorder) {
325 prevBorder = o.style()->borderRight();
326 if (!box.borderRight())
329 ts << " (" << box.borderRight() << "px ";
330 printBorderStyle(ts, o.style()->borderRightStyle());
331 Color col = o.style()->borderRightColor();
333 col = o.style()->color();
334 ts << col.name() << ")";
338 if (o.style()->borderBottom() != prevBorder) {
339 prevBorder = box.style()->borderBottom();
340 if (!box.borderBottom())
343 ts << " (" << box.borderBottom() << "px ";
344 printBorderStyle(ts, o.style()->borderBottomStyle());
345 Color col = o.style()->borderBottomColor();
347 col = o.style()->color();
348 ts << col.name() << ")";
352 if (o.style()->borderLeft() != prevBorder) {
353 prevBorder = o.style()->borderLeft();
354 if (!box.borderLeft())
357 ts << " (" << box.borderLeft() << "px ";
358 printBorderStyle(ts, o.style()->borderLeftStyle());
359 Color col = o.style()->borderLeftColor();
361 col = o.style()->color();
362 ts << col.name() << ")";
370 if (o.isTableCell()) {
371 const RenderTableCell& c = *toRenderTableCell(&o);
372 ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
375 if (o.isListMarker()) {
376 String text = toRenderListMarker(&o)->text();
377 if (!text.isEmpty()) {
378 if (text.length() != 1)
379 text = quoteAndEscapeNonPrintables(text);
386 text = "black square";
389 text = "white bullet";
392 text = quoteAndEscapeNonPrintables(text);
399 if (behavior & RenderAsTextShowIDAndClass) {
400 if (Node* node = o.node()) {
402 ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\"";
404 if (node->hasClass()) {
405 StyledElement* styledElement = static_cast<StyledElement*>(node);
407 for (size_t i = 0; i < styledElement->classNames().size(); ++i) {
410 classes += styledElement->classNames()[i];
412 ts << " class=\"" + classes + "\"";
417 if (behavior & RenderAsTextShowLayoutState) {
418 bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
420 ts << " (needs layout:";
422 bool havePrevious = false;
423 if (o.selfNeedsLayout()) {
428 if (o.needsPositionedMovementLayout()) {
432 ts << " positioned movement";
435 if (o.normalChildNeedsLayout()) {
442 if (o.posChildNeedsLayout()) {
445 ts << " positioned child";
453 // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget
454 // is invisible the QWidget should be invisible too.
455 if (o.isRenderPart()) {
456 const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o));
457 if (part->widget() && part->widget()->platformWidget()) {
458 QWidget* wid = part->widget()->platformWidget();
461 ts << "geometry: {" << wid->geometry() << "} ";
462 ts << "isHidden: " << wid->isHidden() << " ";
463 ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " ";
464 ts << "isParentVisible: " << part->widget()->isParentVisible() << " ";
465 ts << "mask: {" << wid->mask().boundingRect() << "} ] ";
471 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
473 // FIXME: Table cell adjustment is temporary until results can be updated.
475 if (o.containingBlock()->isTableCell())
476 y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
477 ts << "text run at (" << run.m_x << "," << y << ") width " << run.m_logicalWidth;
478 if (!run.isLeftToRightDirection() || run.m_dirOverride) {
479 ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
480 if (run.m_dirOverride)
484 << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()))
488 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
492 write(ts, *toRenderSVGPath(&o), indent);
495 if (o.isSVGGradientStop()) {
496 writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent);
499 if (o.isSVGResourceContainer()) {
500 writeSVGResourceContainer(ts, o, indent);
503 if (o.isSVGContainer()) {
504 writeSVGContainer(ts, o, indent);
508 write(ts, *toRenderSVGRoot(&o), indent);
512 writeSVGText(ts, *toRenderBlock(&o), indent);
515 if (o.isSVGInlineText()) {
516 writeSVGInlineText(ts, *toRenderText(&o), indent);
519 if (o.isSVGImage()) {
520 writeSVGImage(ts, *toRenderSVGImage(&o), indent);
525 writeIndent(ts, indent);
527 RenderTreeAsText::writeRenderObject(ts, o, behavior);
530 if (o.isText() && !o.isBR()) {
531 const RenderText& text = *toRenderText(&o);
532 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
533 writeIndent(ts, indent + 1);
534 writeTextRun(ts, text, *box);
538 for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
539 if (child->hasLayer())
541 write(ts, *child, indent + 1, behavior);
545 Widget* widget = toRenderWidget(&o)->widget();
546 if (widget && widget->isFrameView()) {
547 FrameView* view = static_cast<FrameView*>(widget);
548 RenderView* root = view->frame()->contentRenderer();
551 RenderLayer* l = root->layer();
553 writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), indent + 1, behavior);
559 enum LayerPaintPhase {
560 LayerPaintPhaseAll = 0,
561 LayerPaintPhaseBackground = -1,
562 LayerPaintPhaseForeground = 1
565 static void write(TextStream& ts, RenderLayer& l,
566 const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect,
567 LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
569 writeIndent(ts, indent);
573 if (behavior & RenderAsTextShowAddresses)
574 ts << static_cast<const void*>(&l) << " ";
578 if (!layerBounds.isEmpty()) {
579 if (!backgroundClipRect.contains(layerBounds))
580 ts << " backgroundClip " << backgroundClipRect;
581 if (!clipRect.contains(layerBounds))
582 ts << " clip " << clipRect;
583 if (!outlineClipRect.contains(layerBounds))
584 ts << " outlineClip " << outlineClipRect;
587 if (l.renderer()->hasOverflowClip()) {
588 if (l.scrollXOffset())
589 ts << " scrollX " << l.scrollXOffset();
590 if (l.scrollYOffset())
591 ts << " scrollY " << l.scrollYOffset();
592 if (l.renderBox() && l.renderBox()->clientWidth() != l.scrollWidth())
593 ts << " scrollWidth " << l.scrollWidth();
594 if (l.renderBox() && l.renderBox()->clientHeight() != l.scrollHeight())
595 ts << " scrollHeight " << l.scrollHeight();
598 if (paintPhase == LayerPaintPhaseBackground)
599 ts << " layerType: background only";
600 else if (paintPhase == LayerPaintPhaseForeground)
601 ts << " layerType: foreground only";
603 #if USE(ACCELERATED_COMPOSITING)
604 if (behavior & RenderAsTextShowCompositedLayers) {
605 if (l.isComposited())
606 ts << " (composited, bounds " << l.backing()->compositedBounds() << ")";
609 UNUSED_PARAM(behavior);
614 if (paintPhase != LayerPaintPhaseBackground)
615 write(ts, *l.renderer(), indent + 1, behavior);
618 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
619 const IntRect& paintRect, int indent, RenderAsTextBehavior behavior)
621 // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
622 IntRect paintDirtyRect(paintRect);
623 if (rootLayer == l) {
624 paintDirtyRect.setWidth(max(paintDirtyRect.width(), rootLayer->renderBox()->maxXLayoutOverflow()));
625 paintDirtyRect.setHeight(max(paintDirtyRect.height(), rootLayer->renderBox()->maxYLayoutOverflow()));
626 l->setWidth(max(l->width(), l->renderBox()->maxXLayoutOverflow()));
627 l->setHeight(max(l->height(), l->renderBox()->maxYLayoutOverflow()));
630 // Calculate the clip rects we should use.
631 IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
632 l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, true);
634 // Ensure our lists are up-to-date.
635 l->updateZOrderLists();
636 l->updateNormalFlowList();
638 bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect, rootLayer);
639 Vector<RenderLayer*>* negList = l->negZOrderList();
640 bool paintsBackgroundSeparately = negList && negList->size() > 0;
641 if (shouldPaint && paintsBackgroundSeparately)
642 write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, LayerPaintPhaseBackground, indent, behavior);
645 int currIndent = indent;
646 if (behavior & RenderAsTextShowLayerNesting) {
647 writeIndent(ts, indent);
648 ts << " negative z-order list(" << negList->size() << ")\n";
651 for (unsigned i = 0; i != negList->size(); ++i)
652 writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior);
656 write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
658 if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) {
659 int currIndent = indent;
660 if (behavior & RenderAsTextShowLayerNesting) {
661 writeIndent(ts, indent);
662 ts << " normal flow list(" << normalFlowList->size() << ")\n";
665 for (unsigned i = 0; i != normalFlowList->size(); ++i)
666 writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior);
669 if (Vector<RenderLayer*>* posList = l->posZOrderList()) {
670 int currIndent = indent;
671 if (behavior & RenderAsTextShowLayerNesting) {
672 writeIndent(ts, indent);
673 ts << " positive z-order list(" << posList->size() << ")\n";
676 for (unsigned i = 0; i != posList->size(); ++i)
677 writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior);
681 static String nodePosition(Node* node)
685 Element* body = node->document()->body();
687 for (Node* n = node; n; n = parent) {
688 parent = n->parentOrHostNode();
692 if (body && n == body) {
693 // We don't care what offset body may be in the document.
697 result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
699 result += "document";
705 static void writeSelection(TextStream& ts, const RenderObject* o)
708 if (!n || !n->isDocumentNode())
711 Document* doc = static_cast<Document*>(n);
712 Frame* frame = doc->frame();
716 VisibleSelection selection = frame->selection()->selection();
717 if (selection.isCaret()) {
718 ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().node());
719 if (selection.affinity() == UPSTREAM)
720 ts << " (upstream affinity)";
722 } else if (selection.isRange())
723 ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().node()) << "\n"
724 << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().node()) << "\n";
727 String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior)
729 PrintContext printContext(frame);
730 if (behavior & RenderAsTextPrintingMode) {
731 if (!frame->contentRenderer())
733 printContext.begin(frame->contentRenderer()->width());
736 if (!(behavior & RenderAsTextDontUpdateLayout))
737 frame->document()->updateLayout();
739 RenderObject* o = frame->contentRenderer();
745 RenderLayer* l = toRenderBox(o)->layer();
746 writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), 0, behavior);
747 writeSelection(ts, o);
752 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
754 for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) {
755 if (child->isCounter()) {
758 isFirstCounter = false;
759 String str(toRenderText(child)->text());
765 String counterValueForElement(Element* element)
767 // Make sure the element is not freed during the layout.
768 RefPtr<Element> elementRef(element);
769 element->document()->updateLayout();
771 bool isFirstCounter = true;
772 // The counter renderers should be children of :before or :after pseudo-elements.
773 if (RenderObject* renderer = element->renderer()) {
774 if (RenderObject* pseudoElement = renderer->beforePseudoElementRenderer())
775 writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
776 if (RenderObject* pseudoElement = renderer->afterPseudoElementRenderer())
777 writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter);
779 return stream.release();
782 String markerTextForListItem(Element* element)
784 // Make sure the element is not freed during the layout.
785 RefPtr<Element> elementRef(element);
786 element->document()->updateLayout();
788 RenderObject* renderer = element->renderer();
789 if (!renderer || !renderer->isListItem())
792 return toRenderListItem(renderer)->markerText();
795 } // namespace WebCore