Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / layout / LayoutTreeAsText.cpp
blob8b8a7695c9f8d7b8659df18e1cc7c311543c66e1
1 /*
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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
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.
26 #include "config.h"
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"
64 namespace blink {
66 using namespace HTMLNames;
68 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
70 switch (borderStyle) {
71 case BNONE:
72 ts << "none";
73 break;
74 case BHIDDEN:
75 ts << "hidden";
76 break;
77 case INSET:
78 ts << "inset";
79 break;
80 case GROOVE:
81 ts << "groove";
82 break;
83 case RIDGE:
84 ts << "ridge";
85 break;
86 case OUTSET:
87 ts << "outset";
88 break;
89 case DOTTED:
90 ts << "dotted";
91 break;
92 case DASHED:
93 ts << "dashed";
94 break;
95 case SOLID:
96 ts << "solid";
97 break;
98 case DOUBLE:
99 ts << "double";
100 break;
103 ts << " ";
106 static String getTagName(Node* n)
108 if (n->isDocumentNode())
109 return "";
110 if (n->nodeType() == Node::COMMENT_NODE)
111 return "COMMENT";
112 return n->nodeName();
115 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
117 if (!isHTMLSpanElement(node))
118 return false;
120 const HTMLElement& elem = toHTMLElement(*node);
121 if (elem.getAttribute(classAttr) != "Apple-style-span")
122 return false;
124 if (!elem.hasChildren())
125 return true;
127 const StylePropertySet* inlineStyleDecl = elem.inlineStyle();
128 return (!inlineStyleDecl || inlineStyleDecl->isEmpty());
131 String quoteAndEscapeNonPrintables(const String& s)
133 StringBuilder result;
134 result.append('"');
135 for (unsigned i = 0; i != s.length(); ++i) {
136 UChar c = s[i];
137 if (c == '\\') {
138 result.append('\\');
139 result.append('\\');
140 } else if (c == '"') {
141 result.append('\\');
142 result.append('"');
143 } else if (c == '\n' || c == noBreakSpaceCharacter) {
144 result.append(' ');
145 } else {
146 if (c >= 0x20 && c < 0x7F) {
147 result.append(c);
148 } else {
149 result.append('\\');
150 result.append('x');
151 result.append('{');
152 appendUnsignedAsHex(c, result);
153 result.append('}');
157 result.append('"');
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();
176 if (o.node()) {
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;
190 LayoutRect r;
191 if (o.isText()) {
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));
223 ts << " " << r;
225 if (!(o.isText() && !o.isBR())) {
226 if (o.isFileUploadControl())
227 ts << " " << quoteAndEscapeNonPrintables(toLayoutFileUploadControl(&o)->fileTextValue());
229 if (o.parent()) {
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())
255 return;
257 const LayoutBoxModelObject& box = toLayoutBoxModelObject(o);
258 if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
259 ts << " [border:";
261 BorderValue prevBorder = o.style()->borderTop();
262 if (!box.borderTop()) {
263 ts << " none";
264 } else {
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()) {
273 ts << " none";
274 } else {
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()) {
284 ts << " none";
285 } else {
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()) {
295 ts << " none";
296 } else {
297 ts << " (" << box.borderLeft() << "px ";
298 printBorderStyle(ts, o.style()->borderLeftStyle());
299 ts << o.resolveColor(CSSPropertyBorderLeftColor) << ")";
303 ts << "]";
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()) {
313 ts << ": ";
314 switch (toLayoutDetailsMarker(&o)->orientation()) {
315 case LayoutDetailsMarker::Left:
316 ts << "left";
317 break;
318 case LayoutDetailsMarker::Right:
319 ts << "right";
320 break;
321 case LayoutDetailsMarker::Up:
322 ts << "up";
323 break;
324 case LayoutDetailsMarker::Down:
325 ts << "down";
326 break;
330 if (o.isListMarker()) {
331 String text = toLayoutListMarker(o).text();
332 if (!text.isEmpty()) {
333 if (text.length() != 1) {
334 text = quoteAndEscapeNonPrintables(text);
335 } else {
336 switch (text[0]) {
337 case bulletCharacter:
338 text = "bullet";
339 break;
340 case blackSquareCharacter:
341 text = "black square";
342 break;
343 case whiteBulletCharacter:
344 text = "white bullet";
345 break;
346 default:
347 text = quoteAndEscapeNonPrintables(text);
350 ts << ": " << text;
354 if (behavior & LayoutAsTextShowIDAndClass) {
355 Node* node = o.node();
356 if (node && node->isElementNode()) {
357 Element& element = toElement(*node);
358 if (element.hasID())
359 ts << " id=\"" + element.getIdAttribute() + "\"";
361 if (element.hasClass()) {
362 ts << " class=\"";
363 for (size_t i = 0; i < element.classNames().size(); ++i) {
364 if (i > 0)
365 ts << " ";
366 ts << element.classNames()[i];
368 ts << "\"";
373 if (behavior & LayoutAsTextShowLayoutState) {
374 bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
375 if (needsLayout)
376 ts << " (needs layout:";
378 bool havePrevious = false;
379 if (o.selfNeedsLayout()) {
380 ts << " self";
381 havePrevious = true;
384 if (o.needsPositionedMovementLayout()) {
385 if (havePrevious)
386 ts << ",";
387 havePrevious = true;
388 ts << " positioned movement";
391 if (o.normalChildNeedsLayout()) {
392 if (havePrevious)
393 ts << ",";
394 havePrevious = true;
395 ts << " child";
398 if (o.posChildNeedsLayout()) {
399 if (havePrevious)
400 ts << ",";
401 ts << " positioned child";
404 if (needsLayout)
405 ts << ")";
409 static void writeInlineBox(TextStream& ts, const InlineBox& box, int indent)
411 writeIndent(ts, indent);
412 ts << "+ ";
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);
434 ts << "\n";
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);
438 continue;
440 if (box->isInlineTextBox())
441 writeInlineTextBox(ts, static_cast<const InlineTextBox&>(*box), indent + 1);
442 else
443 writeInlineBox(ts, *box, indent + 1);
444 ts << "\n";
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. :(
459 int x = run.x();
460 int y = run.y();
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())
471 ts << " override";
473 ts << ": "
474 << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
475 if (run.hasHyphen())
476 ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
477 ts << "\n";
480 void write(TextStream& ts, const LayoutObject& o, int indent, LayoutAsTextBehavior behavior)
482 if (o.isSVGShape()) {
483 write(ts, toLayoutSVGShape(o), indent);
484 return;
486 if (o.isSVGGradientStop()) {
487 writeSVGGradientStop(ts, toLayoutSVGGradientStop(o), indent);
488 return;
490 if (o.isSVGResourceContainer()) {
491 writeSVGResourceContainer(ts, o, indent);
492 return;
494 if (o.isSVGContainer()) {
495 writeSVGContainer(ts, o, indent);
496 return;
498 if (o.isSVGRoot()) {
499 write(ts, toLayoutSVGRoot(o), indent);
500 return;
502 if (o.isSVGText()) {
503 writeSVGText(ts, toLayoutSVGText(o), indent);
504 return;
506 if (o.isSVGInlineText()) {
507 writeSVGInlineText(ts, toLayoutSVGInlineText(o), indent);
508 return;
510 if (o.isSVGImage()) {
511 writeSVGImage(ts, toLayoutSVGImage(o), indent);
512 return;
515 writeIndent(ts, indent);
517 LayoutTreeAsText::writeLayoutObject(ts, o, behavior);
518 ts << "\n";
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())
534 continue;
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();
543 if (root) {
544 root->document().updateLayout();
545 DeprecatedPaintLayer* layer = root->layer();
546 if (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)
581 ts << "hidden ";
583 ts << "layer ";
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();
603 else
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()
629 << ", drawsContent="
630 << layer.compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->drawsContent()
631 << (layer.shouldIsolateCompositedDescendants() ? ", isolatesCompositedBlending" : "")
632 << ")";
636 ts << "\n";
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);
648 return vector;
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);
671 if (negList) {
672 int currIndent = indent;
673 if (behavior & LayoutAsTextShowLayerNesting) {
674 writeIndent(ts, indent);
675 ts << " negative z-order list(" << negList->size() << ")\n";
676 ++currIndent;
678 for (unsigned i = 0; i != negList->size(); ++i)
679 writeLayers(ts, rootLayer, negList->at(i)->layer(), paintRect, currIndent, behavior);
682 if (shouldPaint)
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";
691 ++currIndent;
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";
702 ++currIndent;
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();
714 Node* parent;
715 for (Node* n = node; n; n = parent) {
716 parent = n->parentOrShadowHostNode();
717 if (n != node)
718 result.appendLiteral(" of ");
719 if (parent) {
720 if (body && n == body) {
721 // We don't care what offset body may be in the document.
722 result.appendLiteral("body");
723 break;
725 if (n->isShadowRoot()) {
726 result.append('{');
727 result.append(getTagName(n));
728 result.append('}');
729 } else {
730 result.appendLiteral("child ");
731 result.appendNumber(n->nodeIndex());
732 result.appendLiteral(" {");
733 result.append(getTagName(n));
734 result.append('}');
736 } else {
737 result.appendLiteral("document");
741 return result.toString();
744 static void writeSelection(TextStream& ts, const LayoutObject* o)
746 Node* n = o->node();
747 if (!n || !n->isDocumentNode())
748 return;
750 Document* doc = toDocument(n);
751 LocalFrame* frame = doc->frame();
752 if (!frame)
753 return;
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)";
760 ts << "\n";
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)
769 TextStream ts;
770 if (!layoutObject->hasLayer())
771 return ts.release();
773 DeprecatedPaintLayer* layer = layoutObject->layer();
774 LayoutTreeAsText::writeLayers(ts, layer, layer, layer->rect(), 0, behavior);
775 writeSelection(ts, layoutObject);
776 return ts.release();
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())
786 return String();
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())
804 return String();
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()) {
813 if (!isFirstCounter)
814 stream << " ";
815 isFirstCounter = false;
816 String str(toLayoutText(child)->text());
817 stream << str;
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();
827 TextStream stream;
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())
845 return String();
847 return toLayoutListItem(layoutObject)->markerText();
850 } // namespace blink