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 "core/rendering/RenderTreeAsText.h"
29 #include "core/HTMLNames.h"
30 #include "core/css/StylePropertySet.h"
31 #include "core/dom/Document.h"
32 #include "core/editing/FrameSelection.h"
33 #include "core/frame/FrameView.h"
34 #include "core/frame/LocalFrame.h"
35 #include "core/html/HTMLElement.h"
36 #include "core/page/PrintContext.h"
37 #include "core/rendering/FlowThreadController.h"
38 #include "core/rendering/InlineTextBox.h"
39 #include "core/rendering/RenderBR.h"
40 #include "core/rendering/RenderDetailsMarker.h"
41 #include "core/rendering/RenderFileUploadControl.h"
42 #include "core/rendering/RenderFlowThread.h"
43 #include "core/rendering/RenderInline.h"
44 #include "core/rendering/RenderLayer.h"
45 #include "core/rendering/RenderListItem.h"
46 #include "core/rendering/RenderListMarker.h"
47 #include "core/rendering/RenderPart.h"
48 #include "core/rendering/RenderTableCell.h"
49 #include "core/rendering/RenderView.h"
50 #include "core/rendering/RenderWidget.h"
51 #include "core/rendering/compositing/CompositedLayerMapping.h"
52 #include "core/rendering/svg/RenderSVGContainer.h"
53 #include "core/rendering/svg/RenderSVGGradientStop.h"
54 #include "core/rendering/svg/RenderSVGImage.h"
55 #include "core/rendering/svg/RenderSVGInlineText.h"
56 #include "core/rendering/svg/RenderSVGPath.h"
57 #include "core/rendering/svg/RenderSVGRoot.h"
58 #include "core/rendering/svg/RenderSVGText.h"
59 #include "core/rendering/svg/SVGRenderTreeAsText.h"
60 #include "wtf/HexNumber.h"
61 #include "wtf/Vector.h"
62 #include "wtf/unicode/CharacterNames.h"
66 using namespace HTMLNames;
68 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
70 switch (borderStyle) {
106 static String getTagName(Node* n)
108 if (n->isDocumentNode())
110 if (n->nodeType() == Node::COMMENT_NODE)
112 return n->nodeName();
115 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
117 if (!isHTMLSpanElement(node))
120 const HTMLElement& elem = toHTMLElement(*node);
121 if (elem.getAttribute(classAttr) != "Apple-style-span")
124 if (!elem.hasChildren())
127 const StylePropertySet* inlineStyleDecl = elem.inlineStyle();
128 return (!inlineStyleDecl || inlineStyleDecl->isEmpty());
131 String quoteAndEscapeNonPrintables(const String& s)
133 StringBuilder result;
135 for (unsigned i = 0; i != s.length(); ++i) {
140 } else if (c == '"') {
143 } else if (c == '\n' || c == noBreakSpace)
146 if (c >= 0x20 && c < 0x7F)
152 appendUnsignedAsHex(c, result);
158 return result.toString();
161 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
163 ts << o.renderName();
165 if (behavior & RenderAsTextShowAddresses)
166 ts << " " << static_cast<const void*>(&o);
168 if (o.style() && o.style()->zIndex())
169 ts << " zI: " << o.style()->zIndex();
172 String tagName = getTagName(o.node());
173 // FIXME: Temporary hack to make tests pass by simulating the old generated content output.
174 if (o.isPseudoElement() || (o.parent() && o.parent()->isPseudoElement()))
176 if (!tagName.isEmpty()) {
177 ts << " {" << tagName << "}";
178 // flag empty or unstyled AppleStyleSpan because we never
179 // want to leave them in the DOM
180 if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
181 ts << " *empty or unstyled AppleStyleSpan*";
185 RenderBlock* cb = o.containingBlock();
186 bool adjustForTableCells = cb ? cb->isTableCell() : false;
190 // 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
191 // many test results.
192 const RenderText& text = toRenderText(o);
193 IntRect linesBox = text.linesBoundingBox();
194 r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
195 if (adjustForTableCells && !text.firstTextBox())
196 adjustForTableCells = false;
197 } else if (o.isRenderInline()) {
198 // FIXME: Would be better not to just dump 0, 0 as the x and y here.
199 const RenderInline& inlineFlow = toRenderInline(o);
200 r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
201 adjustForTableCells = false;
202 } else if (o.isTableCell()) {
203 // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like
204 // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
205 // captured by the results.
206 const RenderTableCell& cell = toRenderTableCell(o);
207 r = LayoutRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
208 } else if (o.isBox())
209 r = toRenderBox(&o)->frameRect();
211 // FIXME: Temporary in order to ensure compatibility with existing layout test results.
212 if (adjustForTableCells)
213 r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
217 if (!(o.isText() && !o.isBR())) {
218 if (o.isFileUploadControl())
219 ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
222 Color color = o.resolveColor(CSSPropertyColor);
223 if (o.parent()->resolveColor(CSSPropertyColor) != color)
224 ts << " [color=" << color.nameForRenderTreeAsText() << "]";
226 // Do not dump invalid or transparent backgrounds, since that is the default.
227 Color backgroundColor = o.resolveColor(CSSPropertyBackgroundColor);
228 if (o.parent()->resolveColor(CSSPropertyBackgroundColor) != backgroundColor
229 && backgroundColor.rgb())
230 ts << " [bgcolor=" << backgroundColor.nameForRenderTreeAsText() << "]";
232 Color textFillColor = o.resolveColor(CSSPropertyWebkitTextFillColor);
233 if (o.parent()->resolveColor(CSSPropertyWebkitTextFillColor) != textFillColor
234 && textFillColor != color && textFillColor.rgb())
235 ts << " [textFillColor=" << textFillColor.nameForRenderTreeAsText() << "]";
237 Color textStrokeColor = o.resolveColor(CSSPropertyWebkitTextStrokeColor);
238 if (o.parent()->resolveColor(CSSPropertyWebkitTextStrokeColor) != textStrokeColor
239 && textStrokeColor != color && textStrokeColor.rgb())
240 ts << " [textStrokeColor=" << textStrokeColor.nameForRenderTreeAsText() << "]";
242 if (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth() && o.style()->textStrokeWidth() > 0)
243 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
246 if (!o.isBoxModelObject())
249 const RenderBoxModelObject& box = toRenderBoxModelObject(o);
250 if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
253 BorderValue prevBorder = o.style()->borderTop();
254 if (!box.borderTop())
257 ts << " (" << box.borderTop() << "px ";
258 printBorderStyle(ts, o.style()->borderTopStyle());
259 Color col = o.resolveColor(CSSPropertyBorderTopColor);
260 ts << col.nameForRenderTreeAsText() << ")";
263 if (o.style()->borderRight() != prevBorder) {
264 prevBorder = o.style()->borderRight();
265 if (!box.borderRight())
268 ts << " (" << box.borderRight() << "px ";
269 printBorderStyle(ts, o.style()->borderRightStyle());
270 Color col = o.resolveColor(CSSPropertyBorderRightColor);
271 ts << col.nameForRenderTreeAsText() << ")";
275 if (o.style()->borderBottom() != prevBorder) {
276 prevBorder = box.style()->borderBottom();
277 if (!box.borderBottom())
280 ts << " (" << box.borderBottom() << "px ";
281 printBorderStyle(ts, o.style()->borderBottomStyle());
282 Color col = o.resolveColor(CSSPropertyBorderBottomColor);
283 ts << col.nameForRenderTreeAsText() << ")";
287 if (o.style()->borderLeft() != prevBorder) {
288 prevBorder = o.style()->borderLeft();
289 if (!box.borderLeft())
292 ts << " (" << box.borderLeft() << "px ";
293 printBorderStyle(ts, o.style()->borderLeftStyle());
294 Color col = o.resolveColor(CSSPropertyBorderLeftColor);
295 ts << col.nameForRenderTreeAsText() << ")";
303 if (o.isTableCell()) {
304 const RenderTableCell& c = toRenderTableCell(o);
305 ts << " [r=" << c.rowIndex() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
308 if (o.isDetailsMarker()) {
310 switch (toRenderDetailsMarker(&o)->orientation()) {
311 case RenderDetailsMarker::Left:
314 case RenderDetailsMarker::Right:
317 case RenderDetailsMarker::Up:
320 case RenderDetailsMarker::Down:
326 if (o.isListMarker()) {
327 String text = toRenderListMarker(o).text();
328 if (!text.isEmpty()) {
329 if (text.length() != 1)
330 text = quoteAndEscapeNonPrintables(text);
337 text = "black square";
340 text = "white bullet";
343 text = quoteAndEscapeNonPrintables(text);
350 if (behavior & RenderAsTextShowIDAndClass) {
351 Node* node = o.node();
352 if (node && node->isElementNode()) {
353 Element& element = toElement(*node);
355 ts << " id=\"" + element.getIdAttribute() + "\"";
357 if (element.hasClass()) {
359 for (size_t i = 0; i < element.classNames().size(); ++i) {
362 ts << element.classNames()[i];
369 if (behavior & RenderAsTextShowLayoutState) {
370 bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
372 ts << " (needs layout:";
374 bool havePrevious = false;
375 if (o.selfNeedsLayout()) {
380 if (o.needsPositionedMovementLayout()) {
384 ts << " positioned movement";
387 if (o.normalChildNeedsLayout()) {
394 if (o.posChildNeedsLayout()) {
397 ts << " positioned child";
405 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
407 // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
408 // to detect any changes caused by the conversion to floating point. :(
411 int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
413 // FIXME: Table cell adjustment is temporary until results can be updated.
414 if (o.containingBlock()->isTableCell())
415 y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
417 ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
418 if (!run.isLeftToRightDirection() || run.dirOverride()) {
419 ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
420 if (run.dirOverride())
424 << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
426 ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
430 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
432 if (o.isSVGShape()) {
433 write(ts, toRenderSVGShape(o), indent);
436 if (o.isSVGGradientStop()) {
437 writeSVGGradientStop(ts, toRenderSVGGradientStop(o), indent);
440 if (o.isSVGResourceContainer()) {
441 writeSVGResourceContainer(ts, o, indent);
444 if (o.isSVGContainer()) {
445 writeSVGContainer(ts, o, indent);
449 write(ts, toRenderSVGRoot(o), indent);
453 writeSVGText(ts, toRenderSVGText(o), indent);
456 if (o.isSVGInlineText()) {
457 writeSVGInlineText(ts, toRenderSVGInlineText(o), indent);
460 if (o.isSVGImage()) {
461 writeSVGImage(ts, toRenderSVGImage(o), indent);
465 writeIndent(ts, indent);
467 RenderTreeAsText::writeRenderObject(ts, o, behavior);
470 if (o.isText() && !o.isBR()) {
471 const RenderText& text = toRenderText(o);
472 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
473 writeIndent(ts, indent + 1);
474 writeTextRun(ts, text, *box);
478 for (RenderObject* child = o.slowFirstChild(); child; child = child->nextSibling()) {
479 if (child->hasLayer())
481 write(ts, *child, indent + 1, behavior);
485 Widget* widget = toRenderWidget(o).widget();
486 if (widget && widget->isFrameView()) {
487 FrameView* view = toFrameView(widget);
488 RenderView* root = view->renderView();
491 RenderLayer* l = root->layer();
493 RenderTreeAsText::writeLayers(ts, l, l, l->rect(), indent + 1, behavior);
499 enum LayerPaintPhase {
500 LayerPaintPhaseAll = 0,
501 LayerPaintPhaseBackground = -1,
502 LayerPaintPhaseForeground = 1
505 static void write(TextStream& ts, RenderLayer& l,
506 const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect, const LayoutRect& clipRect, const LayoutRect& outlineClipRect,
507 LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
509 IntRect adjustedLayoutBounds = pixelSnappedIntRect(layerBounds);
510 IntRect adjustedBackgroundClipRect = pixelSnappedIntRect(backgroundClipRect);
511 IntRect adjustedClipRect = pixelSnappedIntRect(clipRect);
512 IntRect adjustedOutlineClipRect = pixelSnappedIntRect(outlineClipRect);
514 writeIndent(ts, indent);
516 if (l.renderer()->style()->visibility() == HIDDEN)
521 if (behavior & RenderAsTextShowAddresses)
522 ts << static_cast<const void*>(&l) << " ";
524 ts << adjustedLayoutBounds;
526 if (!adjustedLayoutBounds.isEmpty()) {
527 if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
528 ts << " backgroundClip " << adjustedBackgroundClipRect;
529 if (!adjustedClipRect.contains(adjustedLayoutBounds))
530 ts << " clip " << adjustedClipRect;
531 if (!adjustedOutlineClipRect.contains(adjustedLayoutBounds))
532 ts << " outlineClip " << adjustedOutlineClipRect;
534 if (l.isTransparent())
535 ts << " transparent";
537 if (l.renderer()->hasOverflowClip()) {
538 if (l.scrollableArea()->scrollXOffset())
539 ts << " scrollX " << l.scrollableArea()->scrollXOffset();
540 if (l.scrollableArea()->scrollYOffset())
541 ts << " scrollY " << l.scrollableArea()->scrollYOffset();
542 if (l.renderBox() && l.renderBox()->pixelSnappedClientWidth() != l.renderBox()->pixelSnappedScrollWidth())
543 ts << " scrollWidth " << l.renderBox()->pixelSnappedScrollWidth();
544 if (l.renderBox() && l.renderBox()->pixelSnappedClientHeight() != l.renderBox()->pixelSnappedScrollHeight())
545 ts << " scrollHeight " << l.renderBox()->pixelSnappedScrollHeight();
548 if (paintPhase == LayerPaintPhaseBackground)
549 ts << " layerType: background only";
550 else if (paintPhase == LayerPaintPhaseForeground)
551 ts << " layerType: foreground only";
553 if (l.renderer()->hasBlendMode())
554 ts << " blendMode: " << compositeOperatorName(CompositeSourceOver, l.renderer()->style()->blendMode());
556 if (behavior & RenderAsTextShowCompositedLayers) {
557 if (l.hasCompositedLayerMapping()) {
558 ts << " (composited, bounds="
559 << l.compositedLayerMapping()->compositedBounds()
561 << l.compositedLayerMapping()->mainGraphicsLayer()->drawsContent()
562 << ", paints into ancestor="
563 << l.compositedLayerMapping()->paintsIntoCompositedAncestor()
564 << (l.shouldIsolateCompositedDescendants() ? ", isolatesCompositedBlending" : "")
571 if (paintPhase != LayerPaintPhaseBackground)
572 write(ts, *l.renderer(), indent + 1, behavior);
575 void RenderTreeAsText::writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* layer,
576 const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
578 // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
579 LayoutRect paintDirtyRect(paintRect);
580 if (rootLayer == layer) {
581 paintDirtyRect.setWidth(max<LayoutUnit>(paintDirtyRect.width(), rootLayer->renderBox()->layoutOverflowRect().maxX()));
582 paintDirtyRect.setHeight(max<LayoutUnit>(paintDirtyRect.height(), rootLayer->renderBox()->layoutOverflowRect().maxY()));
585 // Calculate the clip rects we should use.
586 LayoutRect layerBounds;
587 ClipRect damageRect, clipRectToApply, outlineRect;
588 layer->clipper().calculateRects(ClipRectsContext(rootLayer, UncachedClipRects), paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
590 // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
591 if (rootLayer == layer)
592 layerBounds.setSize(layer->size().expandedTo(pixelSnappedIntSize(layer->renderBox()->maxLayoutOverflow(), LayoutPoint(0, 0))));
594 // Ensure our lists are up-to-date.
595 layer->stackingNode()->updateLayerListsIfNeeded();
597 bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : layer->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer);
599 Vector<RenderLayerStackingNode*>* negList = layer->stackingNode()->negZOrderList();
600 bool paintsBackgroundSeparately = negList && negList->size() > 0;
601 if (shouldPaint && paintsBackgroundSeparately)
602 write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), LayerPaintPhaseBackground, indent, behavior);
605 int currIndent = indent;
606 if (behavior & RenderAsTextShowLayerNesting) {
607 writeIndent(ts, indent);
608 ts << " negative z-order list(" << negList->size() << ")\n";
611 for (unsigned i = 0; i != negList->size(); ++i)
612 writeLayers(ts, rootLayer, negList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
616 write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
618 if (Vector<RenderLayerStackingNode*>* normalFlowList = layer->stackingNode()->normalFlowList()) {
619 int currIndent = indent;
620 if (behavior & RenderAsTextShowLayerNesting) {
621 writeIndent(ts, indent);
622 ts << " normal flow list(" << normalFlowList->size() << ")\n";
625 for (unsigned i = 0; i != normalFlowList->size(); ++i)
626 writeLayers(ts, rootLayer, normalFlowList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
629 if (Vector<RenderLayerStackingNode*>* posList = layer->stackingNode()->posZOrderList()) {
630 int currIndent = indent;
631 if (behavior & RenderAsTextShowLayerNesting) {
632 writeIndent(ts, indent);
633 ts << " positive z-order list(" << posList->size() << ")\n";
636 for (unsigned i = 0; i != posList->size(); ++i)
637 writeLayers(ts, rootLayer, posList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
641 String nodePositionAsStringForTesting(Node* node)
643 StringBuilder result;
645 Element* body = node->document().body();
647 for (Node* n = node; n; n = parent) {
648 parent = n->parentOrShadowHostNode();
650 result.appendLiteral(" of ");
652 if (body && n == body) {
653 // We don't care what offset body may be in the document.
654 result.appendLiteral("body");
657 if (n->isShadowRoot()) {
659 result.append(getTagName(n));
662 result.appendLiteral("child ");
663 result.appendNumber(n->nodeIndex());
664 result.appendLiteral(" {");
665 result.append(getTagName(n));
669 result.appendLiteral("document");
672 return result.toString();
675 static void writeSelection(TextStream& ts, const RenderObject* o)
678 if (!n || !n->isDocumentNode())
681 Document* doc = toDocument(n);
682 LocalFrame* frame = doc->frame();
686 VisibleSelection selection = frame->selection().selection();
687 if (selection.isCaret()) {
688 ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(selection.start().deprecatedNode());
689 if (selection.affinity() == UPSTREAM)
690 ts << " (upstream affinity)";
692 } else if (selection.isRange()) {
693 ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(selection.start().deprecatedNode()) << "\n"
694 << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(selection.end().deprecatedNode()) << "\n";
698 static String externalRepresentation(RenderBox* renderer, RenderAsTextBehavior behavior)
701 if (!renderer->hasLayer())
704 RenderLayer* layer = renderer->layer();
705 RenderTreeAsText::writeLayers(ts, layer, layer, layer->rect(), 0, behavior);
706 writeSelection(ts, renderer);
710 String externalRepresentation(LocalFrame* frame, RenderAsTextBehavior behavior)
712 if (!(behavior & RenderAsTextDontUpdateLayout))
713 frame->document()->updateLayout();
715 RenderObject* renderer = frame->contentRenderer();
716 if (!renderer || !renderer->isBox())
719 PrintContext printContext(frame);
720 if (behavior & RenderAsTextPrintingMode)
721 printContext.begin(toRenderBox(renderer)->width().toFloat());
723 return externalRepresentation(toRenderBox(renderer), behavior);
726 String externalRepresentation(Element* element, RenderAsTextBehavior behavior)
728 // Doesn't support printing mode.
729 ASSERT(!(behavior & RenderAsTextPrintingMode));
730 if (!(behavior & RenderAsTextDontUpdateLayout))
731 element->document().updateLayout();
733 RenderObject* renderer = element->renderer();
734 if (!renderer || !renderer->isBox())
737 return externalRepresentation(toRenderBox(renderer), behavior | RenderAsTextShowAllLayers);
740 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
742 for (RenderObject* child = parent->slowFirstChild(); child; child = child->nextSibling()) {
743 if (child->isCounter()) {
746 isFirstCounter = false;
747 String str(toRenderText(child)->text());
753 String counterValueForElement(Element* element)
755 // Make sure the element is not freed during the layout.
756 RefPtrWillBeRawPtr<Element> protector(element);
757 element->document().updateLayout();
759 bool isFirstCounter = true;
760 // The counter renderers should be children of :before or :after pseudo-elements.
761 if (RenderObject* before = element->pseudoElementRenderer(BEFORE))
762 writeCounterValuesFromChildren(stream, before, isFirstCounter);
763 if (RenderObject* after = element->pseudoElementRenderer(AFTER))
764 writeCounterValuesFromChildren(stream, after, isFirstCounter);
765 return stream.release();
768 String markerTextForListItem(Element* element)
770 // Make sure the element is not freed during the layout.
771 RefPtrWillBeRawPtr<Element> protector(element);
772 element->document().updateLayout();
774 RenderObject* renderer = element->renderer();
775 if (!renderer || !renderer->isListItem())
778 return toRenderListItem(renderer)->markerText();