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/layout/LayoutTreeAsText.h"
29 #include "core/HTMLNames.h"
30 #include "core/css/StylePropertySet.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/PseudoElement.h"
33 #include "core/editing/FrameSelection.h"
34 #include "core/frame/FrameView.h"
35 #include "core/frame/LocalFrame.h"
36 #include "core/frame/Settings.h"
37 #include "core/html/HTMLElement.h"
38 #include "core/layout/LayoutBlockFlow.h"
39 #include "core/layout/LayoutDetailsMarker.h"
40 #include "core/layout/LayoutFileUploadControl.h"
41 #include "core/layout/LayoutInline.h"
42 #include "core/layout/LayoutListItem.h"
43 #include "core/layout/LayoutListMarker.h"
44 #include "core/layout/LayoutPart.h"
45 #include "core/layout/LayoutTableCell.h"
46 #include "core/layout/LayoutView.h"
47 #include "core/layout/compositing/CompositedDeprecatedPaintLayerMapping.h"
48 #include "core/layout/line/InlineTextBox.h"
49 #include "core/layout/svg/LayoutSVGContainer.h"
50 #include "core/layout/svg/LayoutSVGGradientStop.h"
51 #include "core/layout/svg/LayoutSVGImage.h"
52 #include "core/layout/svg/LayoutSVGInlineText.h"
53 #include "core/layout/svg/LayoutSVGPath.h"
54 #include "core/layout/svg/LayoutSVGRoot.h"
55 #include "core/layout/svg/LayoutSVGText.h"
56 #include "core/layout/svg/SVGLayoutTreeAsText.h"
57 #include "core/page/PrintContext.h"
58 #include "core/paint/DeprecatedPaintLayer.h"
59 #include "platform/LayoutUnit.h"
60 #include "wtf/HexNumber.h"
61 #include "wtf/Vector.h"
62 #include "wtf/text/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
== noBreakSpaceCharacter
) {
146 if (c
>= 0x20 && c
< 0x7F) {
152 appendUnsignedAsHex(c
, result
);
158 return result
.toString();
161 TextStream
& operator<<(TextStream
& ts
, const Color
& c
)
163 return ts
<< c
.nameForLayoutTreeAsText();
166 void LayoutTreeAsText::writeLayoutObject(TextStream
& ts
, const LayoutObject
& o
, LayoutAsTextBehavior behavior
)
168 ts
<< o
.decoratedName();
170 if (behavior
& LayoutAsTextShowAddresses
)
171 ts
<< " " << static_cast<const void*>(&o
);
173 if (o
.style() && o
.style()->zIndex())
174 ts
<< " zI: " << o
.style()->zIndex();
177 String tagName
= getTagName(o
.node());
178 if (!tagName
.isEmpty()) {
179 ts
<< " {" << tagName
<< "}";
180 // flag empty or unstyled AppleStyleSpan because we never
181 // want to leave them in the DOM
182 if (isEmptyOrUnstyledAppleStyleSpan(o
.node()))
183 ts
<< " *empty or unstyled AppleStyleSpan*";
187 LayoutBlock
* cb
= o
.containingBlock();
188 bool adjustForTableCells
= cb
? cb
->isTableCell() : false;
192 // 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
193 // many test results.
194 const LayoutText
& text
= toLayoutText(o
);
195 IntRect linesBox
= text
.linesBoundingBox();
196 r
= LayoutRect(IntRect(text
.firstRunX(), text
.firstRunY(), linesBox
.width(), linesBox
.height()));
197 if (adjustForTableCells
&& !text
.firstTextBox())
198 adjustForTableCells
= false;
199 } else if (o
.isLayoutInline()) {
200 // FIXME: Would be better not to just dump 0, 0 as the x and y here.
201 const LayoutInline
& inlineFlow
= toLayoutInline(o
);
202 r
= LayoutRect(IntRect(0, 0, inlineFlow
.linesBoundingBox().width(), inlineFlow
.linesBoundingBox().height()));
203 adjustForTableCells
= false;
204 } else if (o
.isTableCell()) {
205 // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like
206 // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
207 // captured by the results.
208 const LayoutTableCell
& cell
= toLayoutTableCell(o
);
209 r
= LayoutRect(cell
.location().x(), cell
.location().y() + cell
.intrinsicPaddingBefore(), cell
.size().width(), cell
.size().height() - cell
.intrinsicPaddingBefore() - cell
.intrinsicPaddingAfter());
210 } else if (o
.isBox()) {
211 r
= toLayoutBox(&o
)->frameRect();
214 // FIXME: Temporary in order to ensure compatibility with existing layout test results.
215 if (adjustForTableCells
)
216 r
.move(0, -toLayoutTableCell(o
.containingBlock())->intrinsicPaddingBefore());
218 if (o
.isLayoutView()) {
219 r
.setWidth(toLayoutView(o
).viewWidth(IncludeScrollbars
));
220 r
.setHeight(toLayoutView(o
).viewHeight(IncludeScrollbars
));
225 if (!(o
.isText() && !o
.isBR())) {
226 if (o
.isFileUploadControl())
227 ts
<< " " << quoteAndEscapeNonPrintables(toLayoutFileUploadControl(&o
)->fileTextValue());
230 Color color
= o
.resolveColor(CSSPropertyColor
);
231 if (o
.parent()->resolveColor(CSSPropertyColor
) != color
)
232 ts
<< " [color=" << color
<< "]";
234 // Do not dump invalid or transparent backgrounds, since that is the default.
235 Color backgroundColor
= o
.resolveColor(CSSPropertyBackgroundColor
);
236 if (o
.parent()->resolveColor(CSSPropertyBackgroundColor
) != backgroundColor
237 && backgroundColor
.rgb())
238 ts
<< " [bgcolor=" << backgroundColor
<< "]";
240 Color textFillColor
= o
.resolveColor(CSSPropertyWebkitTextFillColor
);
241 if (o
.parent()->resolveColor(CSSPropertyWebkitTextFillColor
) != textFillColor
242 && textFillColor
!= color
&& textFillColor
.rgb())
243 ts
<< " [textFillColor=" << textFillColor
<< "]";
245 Color textStrokeColor
= o
.resolveColor(CSSPropertyWebkitTextStrokeColor
);
246 if (o
.parent()->resolveColor(CSSPropertyWebkitTextStrokeColor
) != textStrokeColor
247 && textStrokeColor
!= color
&& textStrokeColor
.rgb())
248 ts
<< " [textStrokeColor=" << textStrokeColor
<< "]";
250 if (o
.parent()->style()->textStrokeWidth() != o
.style()->textStrokeWidth() && o
.style()->textStrokeWidth() > 0)
251 ts
<< " [textStrokeWidth=" << o
.style()->textStrokeWidth() << "]";
254 if (!o
.isBoxModelObject())
257 const LayoutBoxModelObject
& box
= toLayoutBoxModelObject(o
);
258 if (box
.borderTop() || box
.borderRight() || box
.borderBottom() || box
.borderLeft()) {
261 BorderValue prevBorder
= o
.style()->borderTop();
262 if (!box
.borderTop()) {
265 ts
<< " (" << box
.borderTop() << "px ";
266 printBorderStyle(ts
, o
.style()->borderTopStyle());
267 ts
<< o
.resolveColor(CSSPropertyBorderTopColor
) << ")";
270 if (o
.style()->borderRight() != prevBorder
) {
271 prevBorder
= o
.style()->borderRight();
272 if (!box
.borderRight()) {
275 ts
<< " (" << box
.borderRight() << "px ";
276 printBorderStyle(ts
, o
.style()->borderRightStyle());
277 ts
<< o
.resolveColor(CSSPropertyBorderRightColor
) << ")";
281 if (o
.style()->borderBottom() != prevBorder
) {
282 prevBorder
= box
.style()->borderBottom();
283 if (!box
.borderBottom()) {
286 ts
<< " (" << box
.borderBottom() << "px ";
287 printBorderStyle(ts
, o
.style()->borderBottomStyle());
288 ts
<< o
.resolveColor(CSSPropertyBorderBottomColor
) << ")";
292 if (o
.style()->borderLeft() != prevBorder
) {
293 prevBorder
= o
.style()->borderLeft();
294 if (!box
.borderLeft()) {
297 ts
<< " (" << box
.borderLeft() << "px ";
298 printBorderStyle(ts
, o
.style()->borderLeftStyle());
299 ts
<< o
.resolveColor(CSSPropertyBorderLeftColor
) << ")";
307 if (o
.isTableCell()) {
308 const LayoutTableCell
& c
= toLayoutTableCell(o
);
309 ts
<< " [r=" << c
.rowIndex() << " c=" << c
.col() << " rs=" << c
.rowSpan() << " cs=" << c
.colSpan() << "]";
312 if (o
.isDetailsMarker()) {
314 switch (toLayoutDetailsMarker(&o
)->orientation()) {
315 case LayoutDetailsMarker::Left
:
318 case LayoutDetailsMarker::Right
:
321 case LayoutDetailsMarker::Up
:
324 case LayoutDetailsMarker::Down
:
330 if (o
.isListMarker()) {
331 String text
= toLayoutListMarker(o
).text();
332 if (!text
.isEmpty()) {
333 if (text
.length() != 1) {
334 text
= quoteAndEscapeNonPrintables(text
);
337 case bulletCharacter
:
340 case blackSquareCharacter
:
341 text
= "black square";
343 case whiteBulletCharacter
:
344 text
= "white bullet";
347 text
= quoteAndEscapeNonPrintables(text
);
354 if (behavior
& LayoutAsTextShowIDAndClass
) {
355 Node
* node
= o
.node();
356 if (node
&& node
->isElementNode()) {
357 Element
& element
= toElement(*node
);
359 ts
<< " id=\"" + element
.getIdAttribute() + "\"";
361 if (element
.hasClass()) {
363 for (size_t i
= 0; i
< element
.classNames().size(); ++i
) {
366 ts
<< element
.classNames()[i
];
373 if (behavior
& LayoutAsTextShowLayoutState
) {
374 bool needsLayout
= o
.selfNeedsLayout() || o
.needsPositionedMovementLayout() || o
.posChildNeedsLayout() || o
.normalChildNeedsLayout();
376 ts
<< " (needs layout:";
378 bool havePrevious
= false;
379 if (o
.selfNeedsLayout()) {
384 if (o
.needsPositionedMovementLayout()) {
388 ts
<< " positioned movement";
391 if (o
.normalChildNeedsLayout()) {
398 if (o
.posChildNeedsLayout()) {
401 ts
<< " positioned child";
409 static void writeInlineBox(TextStream
& ts
, const InlineBox
& box
, int indent
)
411 writeIndent(ts
, indent
);
413 ts
<< box
.boxName() << " {" << box
.layoutObject().debugName() << "}"
414 << " pos=(" << box
.x() << "," << box
.y() << ")"
415 << " size=(" << box
.width() << "," << box
.height() << ")"
416 << " baseline=" << box
.baselinePosition(AlphabeticBaseline
)
417 << "/" << box
.baselinePosition(IdeographicBaseline
);
420 static void writeInlineTextBox(TextStream
& ts
, const InlineTextBox
& textBox
, int indent
)
422 writeInlineBox(ts
, textBox
, indent
);
423 String value
= textBox
.text();
424 value
.replaceWithLiteral('\\', "\\\\");
425 value
.replaceWithLiteral('\n', "\\n");
426 value
.replaceWithLiteral('"', "\\\"");
427 ts
<< " range=(" << textBox
.start() << "," << (textBox
.start() + textBox
.len()) << ")"
428 << " \"" << value
<< "\"";
431 static void writeInlineFlowBox(TextStream
& ts
, const InlineFlowBox
& rootBox
, int indent
)
433 writeInlineBox(ts
, rootBox
, indent
);
435 for (const InlineBox
* box
= rootBox
.firstChild(); box
; box
= box
->nextOnLine()) {
436 if (box
->isInlineFlowBox()) {
437 writeInlineFlowBox(ts
, static_cast<const InlineFlowBox
&>(*box
), indent
+ 1);
440 if (box
->isInlineTextBox())
441 writeInlineTextBox(ts
, static_cast<const InlineTextBox
&>(*box
), indent
+ 1);
443 writeInlineBox(ts
, *box
, indent
+ 1);
448 void LayoutTreeAsText::writeLineBoxTree(TextStream
& ts
, const LayoutBlockFlow
& o
, int indent
)
450 for (const InlineFlowBox
* rootBox
= o
.firstLineBox(); rootBox
; rootBox
= rootBox
->nextLineBox()) {
451 writeInlineFlowBox(ts
, *rootBox
, indent
);
455 static void writeTextRun(TextStream
& ts
, const LayoutText
& o
, const InlineTextBox
& run
)
457 // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
458 // to detect any changes caused by the conversion to floating point. :(
461 int logicalWidth
= (run
.left() + run
.logicalWidth()).ceil() - x
;
463 // FIXME: Table cell adjustment is temporary until results can be updated.
464 if (o
.containingBlock()->isTableCell())
465 y
-= toLayoutTableCell(o
.containingBlock())->intrinsicPaddingBefore();
467 ts
<< "text run at (" << x
<< "," << y
<< ") width " << logicalWidth
;
468 if (!run
.isLeftToRightDirection() || run
.dirOverride()) {
469 ts
<< (!run
.isLeftToRightDirection() ? " RTL" : " LTR");
470 if (run
.dirOverride())
474 << quoteAndEscapeNonPrintables(String(o
.text()).substring(run
.start(), run
.len()));
476 ts
<< " + hyphen string " << quoteAndEscapeNonPrintables(o
.style()->hyphenString());
480 void write(TextStream
& ts
, const LayoutObject
& o
, int indent
, LayoutAsTextBehavior behavior
)
482 if (o
.isSVGShape()) {
483 write(ts
, toLayoutSVGShape(o
), indent
);
486 if (o
.isSVGGradientStop()) {
487 writeSVGGradientStop(ts
, toLayoutSVGGradientStop(o
), indent
);
490 if (o
.isSVGResourceContainer()) {
491 writeSVGResourceContainer(ts
, o
, indent
);
494 if (o
.isSVGContainer()) {
495 writeSVGContainer(ts
, o
, indent
);
499 write(ts
, toLayoutSVGRoot(o
), indent
);
503 writeSVGText(ts
, toLayoutSVGText(o
), indent
);
506 if (o
.isSVGInlineText()) {
507 writeSVGInlineText(ts
, toLayoutSVGInlineText(o
), indent
);
510 if (o
.isSVGImage()) {
511 writeSVGImage(ts
, toLayoutSVGImage(o
), indent
);
515 writeIndent(ts
, indent
);
517 LayoutTreeAsText::writeLayoutObject(ts
, o
, behavior
);
520 if ((behavior
& LayoutAsTextShowLineTrees
) && o
.isLayoutBlockFlow()) {
521 LayoutTreeAsText::writeLineBoxTree(ts
, toLayoutBlockFlow(o
), indent
+ 1);
524 if (o
.isText() && !o
.isBR()) {
525 const LayoutText
& text
= toLayoutText(o
);
526 for (InlineTextBox
* box
= text
.firstTextBox(); box
; box
= box
->nextTextBox()) {
527 writeIndent(ts
, indent
+ 1);
528 writeTextRun(ts
, text
, *box
);
532 for (LayoutObject
* child
= o
.slowFirstChild(); child
; child
= child
->nextSibling()) {
533 if (child
->hasLayer())
535 write(ts
, *child
, indent
+ 1, behavior
);
538 if (o
.isLayoutPart()) {
539 Widget
* widget
= toLayoutPart(o
).widget();
540 if (widget
&& widget
->isFrameView()) {
541 FrameView
* view
= toFrameView(widget
);
542 LayoutView
* root
= view
->layoutView();
544 root
->document().updateLayout();
545 DeprecatedPaintLayer
* layer
= root
->layer();
547 LayoutTreeAsText::writeLayers(ts
, layer
, layer
, layer
->rect(), indent
+ 1, behavior
);
553 enum LayerPaintPhase
{
554 LayerPaintPhaseAll
= 0,
555 LayerPaintPhaseBackground
= -1,
556 LayerPaintPhaseForeground
= 1
559 static void write(TextStream
& ts
, DeprecatedPaintLayer
& layer
,
560 const LayoutRect
& layerBounds
, const LayoutRect
& backgroundClipRect
, const LayoutRect
& clipRect
,
561 LayerPaintPhase paintPhase
= LayerPaintPhaseAll
, int indent
= 0, LayoutAsTextBehavior behavior
= LayoutAsTextBehaviorNormal
)
563 IntRect adjustedLayoutBounds
= pixelSnappedIntRect(layerBounds
);
564 IntRect adjustedLayoutBoundsWithScrollbars
= adjustedLayoutBounds
;
565 IntRect adjustedBackgroundClipRect
= pixelSnappedIntRect(backgroundClipRect
);
566 IntRect adjustedClipRect
= pixelSnappedIntRect(clipRect
);
568 Settings
* settings
= layer
.layoutObject()->document().settings();
569 bool reportFrameScrollInfo
= layer
.layoutObject()->isLayoutView() && settings
&& !settings
->rootLayerScrolls();
571 if (reportFrameScrollInfo
) {
572 LayoutView
* layoutView
= toLayoutView(layer
.layoutObject());
574 adjustedLayoutBoundsWithScrollbars
.setWidth(layoutView
->viewWidth(IncludeScrollbars
));
575 adjustedLayoutBoundsWithScrollbars
.setHeight(layoutView
->viewHeight(IncludeScrollbars
));
578 writeIndent(ts
, indent
);
580 if (layer
.layoutObject()->style()->visibility() == HIDDEN
)
585 if (behavior
& LayoutAsTextShowAddresses
)
586 ts
<< static_cast<const void*>(&layer
) << " ";
588 ts
<< adjustedLayoutBoundsWithScrollbars
;
590 if (!adjustedLayoutBounds
.isEmpty()) {
591 if (!adjustedBackgroundClipRect
.contains(adjustedLayoutBounds
))
592 ts
<< " backgroundClip " << adjustedBackgroundClipRect
;
593 if (!adjustedClipRect
.contains(adjustedLayoutBoundsWithScrollbars
))
594 ts
<< " clip " << adjustedClipRect
;
596 if (layer
.isTransparent())
597 ts
<< " transparent";
599 if (layer
.layoutObject()->hasOverflowClip() || reportFrameScrollInfo
) {
600 ScrollableArea
* scrollableArea
;
601 if (reportFrameScrollInfo
)
602 scrollableArea
= toLayoutView(layer
.layoutObject())->frameView();
604 scrollableArea
= layer
.scrollableArea();
606 DoublePoint adjustedScrollOffset
= scrollableArea
->scrollPositionDouble() + toDoubleSize(scrollableArea
->scrollOrigin());
607 if (adjustedScrollOffset
.x())
608 ts
<< " scrollX " << adjustedScrollOffset
.x();
609 if (adjustedScrollOffset
.y())
610 ts
<< " scrollY " << adjustedScrollOffset
.y();
611 if (layer
.layoutBox() && layer
.layoutBox()->pixelSnappedClientWidth() != layer
.layoutBox()->pixelSnappedScrollWidth())
612 ts
<< " scrollWidth " << layer
.layoutBox()->pixelSnappedScrollWidth();
613 if (layer
.layoutBox() && layer
.layoutBox()->pixelSnappedClientHeight() != layer
.layoutBox()->pixelSnappedScrollHeight())
614 ts
<< " scrollHeight " << layer
.layoutBox()->pixelSnappedScrollHeight();
617 if (paintPhase
== LayerPaintPhaseBackground
)
618 ts
<< " layerType: background only";
619 else if (paintPhase
== LayerPaintPhaseForeground
)
620 ts
<< " layerType: foreground only";
622 if (layer
.layoutObject()->style()->hasBlendMode())
623 ts
<< " blendMode: " << compositeOperatorName(CompositeSourceOver
, layer
.layoutObject()->style()->blendMode());
625 if (behavior
& LayoutAsTextShowCompositedLayers
) {
626 if (layer
.hasCompositedDeprecatedPaintLayerMapping()) {
627 ts
<< " (composited, bounds="
628 << layer
.compositedDeprecatedPaintLayerMapping()->compositedBounds()
630 << layer
.compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->drawsContent()
631 << (layer
.shouldIsolateCompositedDescendants() ? ", isolatesCompositedBlending" : "")
638 if (paintPhase
!= LayerPaintPhaseBackground
)
639 write(ts
, *layer
.layoutObject(), indent
+ 1, behavior
);
642 static Vector
<DeprecatedPaintLayerStackingNode
*> normalFlowListFor(DeprecatedPaintLayerStackingNode
* node
)
644 DeprecatedPaintLayerStackingNodeIterator
it(*node
, NormalFlowChildren
);
645 Vector
<DeprecatedPaintLayerStackingNode
*> vector
;
646 while (DeprecatedPaintLayerStackingNode
* normalFlowChild
= it
.next())
647 vector
.append(normalFlowChild
);
651 void LayoutTreeAsText::writeLayers(TextStream
& ts
, const DeprecatedPaintLayer
* rootLayer
, DeprecatedPaintLayer
* layer
,
652 const LayoutRect
& paintRect
, int indent
, LayoutAsTextBehavior behavior
)
654 // Calculate the clip rects we should use.
655 LayoutRect layerBounds
;
656 ClipRect damageRect
, clipRectToApply
;
657 layer
->clipper().calculateRects(ClipRectsContext(rootLayer
, UncachedClipRects
), paintRect
, layerBounds
, damageRect
, clipRectToApply
);
659 // Ensure our lists are up-to-date.
660 layer
->stackingNode()->updateLayerListsIfNeeded();
662 LayoutPoint offsetFromRoot
;
663 layer
->convertToLayerCoords(rootLayer
, offsetFromRoot
);
664 bool shouldPaint
= (behavior
& LayoutAsTextShowAllLayers
) ? true : layer
->intersectsDamageRect(layerBounds
, damageRect
.rect(), offsetFromRoot
);
666 Vector
<DeprecatedPaintLayerStackingNode
*>* negList
= layer
->stackingNode()->negZOrderList();
667 bool paintsBackgroundSeparately
= negList
&& negList
->size() > 0;
668 if (shouldPaint
&& paintsBackgroundSeparately
)
669 write(ts
, *layer
, layerBounds
, damageRect
.rect(), clipRectToApply
.rect(), LayerPaintPhaseBackground
, indent
, behavior
);
672 int currIndent
= indent
;
673 if (behavior
& LayoutAsTextShowLayerNesting
) {
674 writeIndent(ts
, indent
);
675 ts
<< " negative z-order list(" << negList
->size() << ")\n";
678 for (unsigned i
= 0; i
!= negList
->size(); ++i
)
679 writeLayers(ts
, rootLayer
, negList
->at(i
)->layer(), paintRect
, currIndent
, behavior
);
683 write(ts
, *layer
, layerBounds
, damageRect
.rect(), clipRectToApply
.rect(), paintsBackgroundSeparately
? LayerPaintPhaseForeground
: LayerPaintPhaseAll
, indent
, behavior
);
685 Vector
<DeprecatedPaintLayerStackingNode
*> normalFlowList
= normalFlowListFor(layer
->stackingNode());
686 if (!normalFlowList
.isEmpty()) {
687 int currIndent
= indent
;
688 if (behavior
& LayoutAsTextShowLayerNesting
) {
689 writeIndent(ts
, indent
);
690 ts
<< " normal flow list(" << normalFlowList
.size() << ")\n";
693 for (unsigned i
= 0; i
!= normalFlowList
.size(); ++i
)
694 writeLayers(ts
, rootLayer
, normalFlowList
.at(i
)->layer(), paintRect
, currIndent
, behavior
);
697 if (Vector
<DeprecatedPaintLayerStackingNode
*>* posList
= layer
->stackingNode()->posZOrderList()) {
698 int currIndent
= indent
;
699 if (behavior
& LayoutAsTextShowLayerNesting
) {
700 writeIndent(ts
, indent
);
701 ts
<< " positive z-order list(" << posList
->size() << ")\n";
704 for (unsigned i
= 0; i
!= posList
->size(); ++i
)
705 writeLayers(ts
, rootLayer
, posList
->at(i
)->layer(), paintRect
, currIndent
, behavior
);
709 String
nodePositionAsStringForTesting(Node
* node
)
711 StringBuilder result
;
713 Element
* body
= node
->document().body();
715 for (Node
* n
= node
; n
; n
= parent
) {
716 parent
= n
->parentOrShadowHostNode();
718 result
.appendLiteral(" of ");
720 if (body
&& n
== body
) {
721 // We don't care what offset body may be in the document.
722 result
.appendLiteral("body");
725 if (n
->isShadowRoot()) {
727 result
.append(getTagName(n
));
730 result
.appendLiteral("child ");
731 result
.appendNumber(n
->nodeIndex());
732 result
.appendLiteral(" {");
733 result
.append(getTagName(n
));
737 result
.appendLiteral("document");
741 return result
.toString();
744 static void writeSelection(TextStream
& ts
, const LayoutObject
* o
)
747 if (!n
|| !n
->isDocumentNode())
750 Document
* doc
= toDocument(n
);
751 LocalFrame
* frame
= doc
->frame();
755 VisibleSelection selection
= frame
->selection().selection();
756 if (selection
.isCaret()) {
757 ts
<< "caret: position " << selection
.start().computeEditingOffset() << " of " << nodePositionAsStringForTesting(selection
.start().anchorNode());
758 if (selection
.affinity() == TextAffinity::Upstream
)
759 ts
<< " (upstream affinity)";
761 } else if (selection
.isRange()) {
762 ts
<< "selection start: position " << selection
.start().computeEditingOffset() << " of " << nodePositionAsStringForTesting(selection
.start().anchorNode()) << "\n"
763 << "selection end: position " << selection
.end().computeEditingOffset() << " of " << nodePositionAsStringForTesting(selection
.end().anchorNode()) << "\n";
767 static String
externalRepresentation(LayoutBox
* layoutObject
, LayoutAsTextBehavior behavior
)
770 if (!layoutObject
->hasLayer())
773 DeprecatedPaintLayer
* layer
= layoutObject
->layer();
774 LayoutTreeAsText::writeLayers(ts
, layer
, layer
, layer
->rect(), 0, behavior
);
775 writeSelection(ts
, layoutObject
);
779 String
externalRepresentation(LocalFrame
* frame
, LayoutAsTextBehavior behavior
)
781 if (!(behavior
& LayoutAsTextDontUpdateLayout
))
782 frame
->document()->updateLayout();
784 LayoutObject
* layoutObject
= frame
->contentLayoutObject();
785 if (!layoutObject
|| !layoutObject
->isBox())
788 PrintContext
printContext(frame
);
789 if (behavior
& LayoutAsTextPrintingMode
)
790 printContext
.begin(toLayoutBox(layoutObject
)->size().width().toFloat());
792 return externalRepresentation(toLayoutBox(layoutObject
), behavior
);
795 String
externalRepresentation(Element
* element
, LayoutAsTextBehavior behavior
)
797 // Doesn't support printing mode.
798 ASSERT(!(behavior
& LayoutAsTextPrintingMode
));
799 if (!(behavior
& LayoutAsTextDontUpdateLayout
))
800 element
->document().updateLayout();
802 LayoutObject
* layoutObject
= element
->layoutObject();
803 if (!layoutObject
|| !layoutObject
->isBox())
806 return externalRepresentation(toLayoutBox(layoutObject
), behavior
| LayoutAsTextShowAllLayers
);
809 static void writeCounterValuesFromChildren(TextStream
& stream
, LayoutObject
* parent
, bool& isFirstCounter
)
811 for (LayoutObject
* child
= parent
->slowFirstChild(); child
; child
= child
->nextSibling()) {
812 if (child
->isCounter()) {
815 isFirstCounter
= false;
816 String
str(toLayoutText(child
)->text());
822 String
counterValueForElement(Element
* element
)
824 // Make sure the element is not freed during the layout.
825 RefPtrWillBeRawPtr
<Element
> protector(element
);
826 element
->document().updateLayout();
828 bool isFirstCounter
= true;
829 // The counter layoutObjects should be children of :before or :after pseudo-elements.
830 if (LayoutObject
* before
= element
->pseudoElementLayoutObject(BEFORE
))
831 writeCounterValuesFromChildren(stream
, before
, isFirstCounter
);
832 if (LayoutObject
* after
= element
->pseudoElementLayoutObject(AFTER
))
833 writeCounterValuesFromChildren(stream
, after
, isFirstCounter
);
834 return stream
.release();
837 String
markerTextForListItem(Element
* element
)
839 // Make sure the element is not freed during the layout.
840 RefPtrWillBeRawPtr
<Element
> protector(element
);
841 element
->document().updateLayout();
843 LayoutObject
* layoutObject
= element
->layoutObject();
844 if (!layoutObject
|| !layoutObject
->isListItem())
847 return toLayoutListItem(layoutObject
)->markerText();