1 /*
2 * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved.
3 *
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.
12 *
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.
24 */
25
26 #include "config.h"
27 #include "core/rendering/RenderTreeAsText.h"
28
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"
63
64 namespace blink {
65
66 using namespace HTMLNames;
67
printBorderStyle(TextStream & ts,const EBorderStyle borderStyle)68 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle)
69 {
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;
101 }
102
103 ts << " ";
104 }
105
getTagName(Node * n)106 static String getTagName(Node* n)
107 {
108 if (n->isDocumentNode())
109 return "";
110 if (n->nodeType() == Node::COMMENT_NODE)
111 return "COMMENT";
112 return n->nodeName();
113 }
114
isEmptyOrUnstyledAppleStyleSpan(const Node * node)115 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
116 {
117 if (!isHTMLSpanElement(node))
118 return false;
119
120 const HTMLElement& elem = toHTMLElement(*node);
121 if (elem.getAttribute(classAttr) != "Apple-style-span")
122 return false;
123
124 if (!elem.hasChildren())
125 return true;
126
127 const StylePropertySet* inlineStyleDecl = elem.inlineStyle();
128 return (!inlineStyleDecl || inlineStyleDecl->isEmpty());
129 }
130
quoteAndEscapeNonPrintables(const String & s)131 String quoteAndEscapeNonPrintables(const String& s)
132 {
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 == noBreakSpace)
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('}');
154 }
155 }
156 }
157 result.append('"');
158 return result.toString();
159 }
160
operator <<(TextStream & ts,const Color & c)161 TextStream& operator<<(TextStream& ts, const Color& c)
162 {
163 return ts << c.nameForRenderTreeAsText();
164 }
165
writeRenderObject(TextStream & ts,const RenderObject & o,RenderAsTextBehavior behavior)166 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior)
167 {
168 ts << o.renderName();
169
170 if (behavior & RenderAsTextShowAddresses)
171 ts << " " << static_cast<const void*>(&o);
172
173 if (o.style() && o.style()->zIndex())
174 ts << " zI: " << o.style()->zIndex();
175
176 if (o.node()) {
177 String tagName = getTagName(o.node());
178 // FIXME: Temporary hack to make tests pass by simulating the old generated content output.
179 if (o.isPseudoElement() || (o.parent() && o.parent()->isPseudoElement()))
180 tagName = emptyAtom;
181 if (!tagName.isEmpty()) {
182 ts << " {" << tagName << "}";
183 // flag empty or unstyled AppleStyleSpan because we never
184 // want to leave them in the DOM
185 if (isEmptyOrUnstyledAppleStyleSpan(o.node()))
186 ts << " *empty or unstyled AppleStyleSpan*";
187 }
188 }
189
190 RenderBlock* cb = o.containingBlock();
191 bool adjustForTableCells = cb ? cb->isTableCell() : false;
192
193 LayoutRect r;
194 if (o.isText()) {
195 // 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
196 // many test results.
197 const RenderText& text = toRenderText(o);
198 IntRect linesBox = text.linesBoundingBox();
199 r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height());
200 if (adjustForTableCells && !text.firstTextBox())
201 adjustForTableCells = false;
202 } else if (o.isRenderInline()) {
203 // FIXME: Would be better not to just dump 0, 0 as the x and y here.
204 const RenderInline& inlineFlow = toRenderInline(o);
205 r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height());
206 adjustForTableCells = false;
207 } else if (o.isTableCell()) {
208 // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like
209 // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are
210 // captured by the results.
211 const RenderTableCell& cell = toRenderTableCell(o);
212 r = LayoutRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter());
213 } else if (o.isBox())
214 r = toRenderBox(&o)->frameRect();
215
216 // FIXME: Temporary in order to ensure compatibility with existing layout test results.
217 if (adjustForTableCells)
218 r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore());
219
220 ts << " " << r;
221
222 if (!(o.isText() && !o.isBR())) {
223 if (o.isFileUploadControl())
224 ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue());
225
226 if (o.parent()) {
227 Color color = o.resolveColor(CSSPropertyColor);
228 if (o.parent()->resolveColor(CSSPropertyColor) != color)
229 ts << " [color=" << color << "]";
230
231 // Do not dump invalid or transparent backgrounds, since that is the default.
232 Color backgroundColor = o.resolveColor(CSSPropertyBackgroundColor);
233 if (o.parent()->resolveColor(CSSPropertyBackgroundColor) != backgroundColor
234 && backgroundColor.rgb())
235 ts << " [bgcolor=" << backgroundColor << "]";
236
237 Color textFillColor = o.resolveColor(CSSPropertyWebkitTextFillColor);
238 if (o.parent()->resolveColor(CSSPropertyWebkitTextFillColor) != textFillColor
239 && textFillColor != color && textFillColor.rgb())
240 ts << " [textFillColor=" << textFillColor << "]";
241
242 Color textStrokeColor = o.resolveColor(CSSPropertyWebkitTextStrokeColor);
243 if (o.parent()->resolveColor(CSSPropertyWebkitTextStrokeColor) != textStrokeColor
244 && textStrokeColor != color && textStrokeColor.rgb())
245 ts << " [textStrokeColor=" << textStrokeColor << "]";
246
247 if (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth() && o.style()->textStrokeWidth() > 0)
248 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
249 }
250
251 if (!o.isBoxModelObject())
252 return;
253
254 const RenderBoxModelObject& box = toRenderBoxModelObject(o);
255 if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) {
256 ts << " [border:";
257
258 BorderValue prevBorder = o.style()->borderTop();
259 if (!box.borderTop())
260 ts << " none";
261 else {
262 ts << " (" << box.borderTop() << "px ";
263 printBorderStyle(ts, o.style()->borderTopStyle());
264 ts << o.resolveColor(CSSPropertyBorderTopColor) << ")";
265 }
266
267 if (o.style()->borderRight() != prevBorder) {
268 prevBorder = o.style()->borderRight();
269 if (!box.borderRight())
270 ts << " none";
271 else {
272 ts << " (" << box.borderRight() << "px ";
273 printBorderStyle(ts, o.style()->borderRightStyle());
274 ts << o.resolveColor(CSSPropertyBorderRightColor) << ")";
275 }
276 }
277
278 if (o.style()->borderBottom() != prevBorder) {
279 prevBorder = box.style()->borderBottom();
280 if (!box.borderBottom())
281 ts << " none";
282 else {
283 ts << " (" << box.borderBottom() << "px ";
284 printBorderStyle(ts, o.style()->borderBottomStyle());
285 ts << o.resolveColor(CSSPropertyBorderBottomColor) << ")";
286 }
287 }
288
289 if (o.style()->borderLeft() != prevBorder) {
290 prevBorder = o.style()->borderLeft();
291 if (!box.borderLeft())
292 ts << " none";
293 else {
294 ts << " (" << box.borderLeft() << "px ";
295 printBorderStyle(ts, o.style()->borderLeftStyle());
296 ts << o.resolveColor(CSSPropertyBorderLeftColor) << ")";
297 }
298 }
299
300 ts << "]";
301 }
302 }
303
304 if (o.isTableCell()) {
305 const RenderTableCell& c = toRenderTableCell(o);
306 ts << " [r=" << c.rowIndex() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
307 }
308
309 if (o.isDetailsMarker()) {
310 ts << ": ";
311 switch (toRenderDetailsMarker(&o)->orientation()) {
312 case RenderDetailsMarker::Left:
313 ts << "left";
314 break;
315 case RenderDetailsMarker::Right:
316 ts << "right";
317 break;
318 case RenderDetailsMarker::Up:
319 ts << "up";
320 break;
321 case RenderDetailsMarker::Down:
322 ts << "down";
323 break;
324 }
325 }
326
327 if (o.isListMarker()) {
328 String text = toRenderListMarker(o).text();
329 if (!text.isEmpty()) {
330 if (text.length() != 1)
331 text = quoteAndEscapeNonPrintables(text);
332 else {
333 switch (text[0]) {
334 case bullet:
335 text = "bullet";
336 break;
337 case blackSquare:
338 text = "black square";
339 break;
340 case whiteBullet:
341 text = "white bullet";
342 break;
343 default:
344 text = quoteAndEscapeNonPrintables(text);
345 }
346 }
347 ts << ": " << text;
348 }
349 }
350
351 if (behavior & RenderAsTextShowIDAndClass) {
352 Node* node = o.node();
353 if (node && node->isElementNode()) {
354 Element& element = toElement(*node);
355 if (element.hasID())
356 ts << " id=\"" + element.getIdAttribute() + "\"";
357
358 if (element.hasClass()) {
359 ts << " class=\"";
360 for (size_t i = 0; i < element.classNames().size(); ++i) {
361 if (i > 0)
362 ts << " ";
363 ts << element.classNames()[i];
364 }
365 ts << "\"";
366 }
367 }
368 }
369
370 if (behavior & RenderAsTextShowLayoutState) {
371 bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout();
372 if (needsLayout)
373 ts << " (needs layout:";
374
375 bool havePrevious = false;
376 if (o.selfNeedsLayout()) {
377 ts << " self";
378 havePrevious = true;
379 }
380
381 if (o.needsPositionedMovementLayout()) {
382 if (havePrevious)
383 ts << ",";
384 havePrevious = true;
385 ts << " positioned movement";
386 }
387
388 if (o.normalChildNeedsLayout()) {
389 if (havePrevious)
390 ts << ",";
391 havePrevious = true;
392 ts << " child";
393 }
394
395 if (o.posChildNeedsLayout()) {
396 if (havePrevious)
397 ts << ",";
398 ts << " positioned child";
399 }
400
401 if (needsLayout)
402 ts << ")";
403 }
404 }
405
writeTextRun(TextStream & ts,const RenderText & o,const InlineTextBox & run)406 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
407 {
408 // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder
409 // to detect any changes caused by the conversion to floating point. :(
410 int x = run.x();
411 int y = run.y();
412 int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x;
413
414 // FIXME: Table cell adjustment is temporary until results can be updated.
415 if (o.containingBlock()->isTableCell())
416 y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore();
417
418 ts << "text run at (" << x << "," << y << ") width " << logicalWidth;
419 if (!run.isLeftToRightDirection() || run.dirOverride()) {
420 ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR");
421 if (run.dirOverride())
422 ts << " override";
423 }
424 ts << ": "
425 << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len()));
426 if (run.hasHyphen())
427 ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString());
428 ts << "\n";
429 }
430
write(TextStream & ts,const RenderObject & o,int indent,RenderAsTextBehavior behavior)431 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior)
432 {
433 if (o.isSVGShape()) {
434 write(ts, toRenderSVGShape(o), indent);
435 return;
436 }
437 if (o.isSVGGradientStop()) {
438 writeSVGGradientStop(ts, toRenderSVGGradientStop(o), indent);
439 return;
440 }
441 if (o.isSVGResourceContainer()) {
442 writeSVGResourceContainer(ts, o, indent);
443 return;
444 }
445 if (o.isSVGContainer()) {
446 writeSVGContainer(ts, o, indent);
447 return;
448 }
449 if (o.isSVGRoot()) {
450 write(ts, toRenderSVGRoot(o), indent);
451 return;
452 }
453 if (o.isSVGText()) {
454 writeSVGText(ts, toRenderSVGText(o), indent);
455 return;
456 }
457 if (o.isSVGInlineText()) {
458 writeSVGInlineText(ts, toRenderSVGInlineText(o), indent);
459 return;
460 }
461 if (o.isSVGImage()) {
462 writeSVGImage(ts, toRenderSVGImage(o), indent);
463 return;
464 }
465
466 writeIndent(ts, indent);
467
468 RenderTreeAsText::writeRenderObject(ts, o, behavior);
469 ts << "\n";
470
471 if (o.isText() && !o.isBR()) {
472 const RenderText& text = toRenderText(o);
473 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
474 writeIndent(ts, indent + 1);
475 writeTextRun(ts, text, *box);
476 }
477 }
478
479 for (RenderObject* child = o.slowFirstChild(); child; child = child->nextSibling()) {
480 if (child->hasLayer())
481 continue;
482 write(ts, *child, indent + 1, behavior);
483 }
484
485 if (o.isWidget()) {
486 Widget* widget = toRenderWidget(o).widget();
487 if (widget && widget->isFrameView()) {
488 FrameView* view = toFrameView(widget);
489 RenderView* root = view->renderView();
490 if (root) {
491 view->layout();
492 RenderLayer* l = root->layer();
493 if (l)
494 RenderTreeAsText::writeLayers(ts, l, l, l->rect(), indent + 1, behavior);
495 }
496 }
497 }
498 }
499
500 enum LayerPaintPhase {
501 LayerPaintPhaseAll = 0,
502 LayerPaintPhaseBackground = -1,
503 LayerPaintPhaseForeground = 1
504 };
505
write(TextStream & ts,RenderLayer & l,const LayoutRect & layerBounds,const LayoutRect & backgroundClipRect,const LayoutRect & clipRect,const LayoutRect & outlineClipRect,LayerPaintPhase paintPhase=LayerPaintPhaseAll,int indent=0,RenderAsTextBehavior behavior=RenderAsTextBehaviorNormal)506 static void write(TextStream& ts, RenderLayer& l,
507 const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect, const LayoutRect& clipRect, const LayoutRect& outlineClipRect,
508 LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal)
509 {
510 IntRect adjustedLayoutBounds = pixelSnappedIntRect(layerBounds);
511 IntRect adjustedBackgroundClipRect = pixelSnappedIntRect(backgroundClipRect);
512 IntRect adjustedClipRect = pixelSnappedIntRect(clipRect);
513 IntRect adjustedOutlineClipRect = pixelSnappedIntRect(outlineClipRect);
514
515 writeIndent(ts, indent);
516
517 if (l.renderer()->style()->visibility() == HIDDEN)
518 ts << "hidden ";
519
520 ts << "layer ";
521
522 if (behavior & RenderAsTextShowAddresses)
523 ts << static_cast<const void*>(&l) << " ";
524
525 ts << adjustedLayoutBounds;
526
527 if (!adjustedLayoutBounds.isEmpty()) {
528 if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds))
529 ts << " backgroundClip " << adjustedBackgroundClipRect;
530 if (!adjustedClipRect.contains(adjustedLayoutBounds))
531 ts << " clip " << adjustedClipRect;
532 if (!adjustedOutlineClipRect.contains(adjustedLayoutBounds))
533 ts << " outlineClip " << adjustedOutlineClipRect;
534 }
535 if (l.isTransparent())
536 ts << " transparent";
537
538 if (l.renderer()->hasOverflowClip()) {
539 if (l.scrollableArea()->scrollXOffset())
540 ts << " scrollX " << l.scrollableArea()->scrollXOffset();
541 if (l.scrollableArea()->scrollYOffset())
542 ts << " scrollY " << l.scrollableArea()->scrollYOffset();
543 if (l.renderBox() && l.renderBox()->pixelSnappedClientWidth() != l.renderBox()->pixelSnappedScrollWidth())
544 ts << " scrollWidth " << l.renderBox()->pixelSnappedScrollWidth();
545 if (l.renderBox() && l.renderBox()->pixelSnappedClientHeight() != l.renderBox()->pixelSnappedScrollHeight())
546 ts << " scrollHeight " << l.renderBox()->pixelSnappedScrollHeight();
547 }
548
549 if (paintPhase == LayerPaintPhaseBackground)
550 ts << " layerType: background only";
551 else if (paintPhase == LayerPaintPhaseForeground)
552 ts << " layerType: foreground only";
553
554 if (l.renderer()->hasBlendMode())
555 ts << " blendMode: " << compositeOperatorName(CompositeSourceOver, l.renderer()->style()->blendMode());
556
557 if (behavior & RenderAsTextShowCompositedLayers) {
558 if (l.hasCompositedLayerMapping()) {
559 ts << " (composited, bounds="
560 << l.compositedLayerMapping()->compositedBounds()
561 << ", drawsContent="
562 << l.compositedLayerMapping()->mainGraphicsLayer()->drawsContent()
563 << ", paints into ancestor="
564 << l.compositedLayerMapping()->paintsIntoCompositedAncestor()
565 << (l.shouldIsolateCompositedDescendants() ? ", isolatesCompositedBlending" : "")
566 << ")";
567 }
568 }
569
570 ts << "\n";
571
572 if (paintPhase != LayerPaintPhaseBackground)
573 write(ts, *l.renderer(), indent + 1, behavior);
574 }
575
writeLayers(TextStream & ts,const RenderLayer * rootLayer,RenderLayer * layer,const LayoutRect & paintRect,int indent,RenderAsTextBehavior behavior)576 void RenderTreeAsText::writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* layer,
577 const LayoutRect& paintRect, int indent, RenderAsTextBehavior behavior)
578 {
579 // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
580 LayoutRect paintDirtyRect(paintRect);
581 if (rootLayer == layer) {
582 paintDirtyRect.setWidth(max<LayoutUnit>(paintDirtyRect.width(), rootLayer->renderBox()->layoutOverflowRect().maxX()));
583 paintDirtyRect.setHeight(max<LayoutUnit>(paintDirtyRect.height(), rootLayer->renderBox()->layoutOverflowRect().maxY()));
584 }
585
586 // Calculate the clip rects we should use.
587 LayoutRect layerBounds;
588 ClipRect damageRect, clipRectToApply, outlineRect;
589 layer->clipper().calculateRects(ClipRectsContext(rootLayer, UncachedClipRects), paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
590
591 // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh.
592 if (rootLayer == layer)
593 layerBounds.setSize(layer->size().expandedTo(pixelSnappedIntSize(layer->renderBox()->maxLayoutOverflow(), LayoutPoint(0, 0))));
594
595 // Ensure our lists are up-to-date.
596 layer->stackingNode()->updateLayerListsIfNeeded();
597
598 bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : layer->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer);
599
600 Vector<RenderLayerStackingNode*>* negList = layer->stackingNode()->negZOrderList();
601 bool paintsBackgroundSeparately = negList && negList->size() > 0;
602 if (shouldPaint && paintsBackgroundSeparately)
603 write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), LayerPaintPhaseBackground, indent, behavior);
604
605 if (negList) {
606 int currIndent = indent;
607 if (behavior & RenderAsTextShowLayerNesting) {
608 writeIndent(ts, indent);
609 ts << " negative z-order list(" << negList->size() << ")\n";
610 ++currIndent;
611 }
612 for (unsigned i = 0; i != negList->size(); ++i)
613 writeLayers(ts, rootLayer, negList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
614 }
615
616 if (shouldPaint)
617 write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior);
618
619 if (Vector<RenderLayerStackingNode*>* normalFlowList = layer->stackingNode()->normalFlowList()) {
620 int currIndent = indent;
621 if (behavior & RenderAsTextShowLayerNesting) {
622 writeIndent(ts, indent);
623 ts << " normal flow list(" << normalFlowList->size() << ")\n";
624 ++currIndent;
625 }
626 for (unsigned i = 0; i != normalFlowList->size(); ++i)
627 writeLayers(ts, rootLayer, normalFlowList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
628 }
629
630 if (Vector<RenderLayerStackingNode*>* posList = layer->stackingNode()->posZOrderList()) {
631 int currIndent = indent;
632 if (behavior & RenderAsTextShowLayerNesting) {
633 writeIndent(ts, indent);
634 ts << " positive z-order list(" << posList->size() << ")\n";
635 ++currIndent;
636 }
637 for (unsigned i = 0; i != posList->size(); ++i)
638 writeLayers(ts, rootLayer, posList->at(i)->layer(), paintDirtyRect, currIndent, behavior);
639 }
640 }
641
nodePositionAsStringForTesting(Node * node)642 String nodePositionAsStringForTesting(Node* node)
643 {
644 StringBuilder result;
645
646 Element* body = node->document().body();
647 Node* parent;
648 for (Node* n = node; n; n = parent) {
649 parent = n->parentOrShadowHostNode();
650 if (n != node)
651 result.appendLiteral(" of ");
652 if (parent) {
653 if (body && n == body) {
654 // We don't care what offset body may be in the document.
655 result.appendLiteral("body");
656 break;
657 }
658 if (n->isShadowRoot()) {
659 result.append('{');
660 result.append(getTagName(n));
661 result.append('}');
662 } else {
663 result.appendLiteral("child ");
664 result.appendNumber(n->nodeIndex());
665 result.appendLiteral(" {");
666 result.append(getTagName(n));
667 result.append('}');
668 }
669 } else
670 result.appendLiteral("document");
671 }
672
673 return result.toString();
674 }
675
writeSelection(TextStream & ts,const RenderObject * o)676 static void writeSelection(TextStream& ts, const RenderObject* o)
677 {
678 Node* n = o->node();
679 if (!n || !n->isDocumentNode())
680 return;
681
682 Document* doc = toDocument(n);
683 LocalFrame* frame = doc->frame();
684 if (!frame)
685 return;
686
687 VisibleSelection selection = frame->selection().selection();
688 if (selection.isCaret()) {
689 ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(selection.start().deprecatedNode());
690 if (selection.affinity() == UPSTREAM)
691 ts << " (upstream affinity)";
692 ts << "\n";
693 } else if (selection.isRange()) {
694 ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(selection.start().deprecatedNode()) << "\n"
695 << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePositionAsStringForTesting(selection.end().deprecatedNode()) << "\n";
696 }
697 }
698
externalRepresentation(RenderBox * renderer,RenderAsTextBehavior behavior)699 static String externalRepresentation(RenderBox* renderer, RenderAsTextBehavior behavior)
700 {
701 TextStream ts;
702 if (!renderer->hasLayer())
703 return ts.release();
704
705 RenderLayer* layer = renderer->layer();
706 RenderTreeAsText::writeLayers(ts, layer, layer, layer->rect(), 0, behavior);
707 writeSelection(ts, renderer);
708 return ts.release();
709 }
710
externalRepresentation(LocalFrame * frame,RenderAsTextBehavior behavior)711 String externalRepresentation(LocalFrame* frame, RenderAsTextBehavior behavior)
712 {
713 if (!(behavior & RenderAsTextDontUpdateLayout))
714 frame->document()->updateLayout();
715
716 RenderObject* renderer = frame->contentRenderer();
717 if (!renderer || !renderer->isBox())
718 return String();
719
720 PrintContext printContext(frame);
721 if (behavior & RenderAsTextPrintingMode)
722 printContext.begin(toRenderBox(renderer)->width().toFloat());
723
724 return externalRepresentation(toRenderBox(renderer), behavior);
725 }
726
externalRepresentation(Element * element,RenderAsTextBehavior behavior)727 String externalRepresentation(Element* element, RenderAsTextBehavior behavior)
728 {
729 // Doesn't support printing mode.
730 ASSERT(!(behavior & RenderAsTextPrintingMode));
731 if (!(behavior & RenderAsTextDontUpdateLayout))
732 element->document().updateLayout();
733
734 RenderObject* renderer = element->renderer();
735 if (!renderer || !renderer->isBox())
736 return String();
737
738 return externalRepresentation(toRenderBox(renderer), behavior | RenderAsTextShowAllLayers);
739 }
740
writeCounterValuesFromChildren(TextStream & stream,RenderObject * parent,bool & isFirstCounter)741 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter)
742 {
743 for (RenderObject* child = parent->slowFirstChild(); child; child = child->nextSibling()) {
744 if (child->isCounter()) {
745 if (!isFirstCounter)
746 stream << " ";
747 isFirstCounter = false;
748 String str(toRenderText(child)->text());
749 stream << str;
750 }
751 }
752 }
753
counterValueForElement(Element * element)754 String counterValueForElement(Element* element)
755 {
756 // Make sure the element is not freed during the layout.
757 RefPtrWillBeRawPtr<Element> protector(element);
758 element->document().updateLayout();
759 TextStream stream;
760 bool isFirstCounter = true;
761 // The counter renderers should be children of :before or :after pseudo-elements.
762 if (RenderObject* before = element->pseudoElementRenderer(BEFORE))
763 writeCounterValuesFromChildren(stream, before, isFirstCounter);
764 if (RenderObject* after = element->pseudoElementRenderer(AFTER))
765 writeCounterValuesFromChildren(stream, after, isFirstCounter);
766 return stream.release();
767 }
768
markerTextForListItem(Element * element)769 String markerTextForListItem(Element* element)
770 {
771 // Make sure the element is not freed during the layout.
772 RefPtrWillBeRawPtr<Element> protector(element);
773 element->document().updateLayout();
774
775 RenderObject* renderer = element->renderer();
776 if (!renderer || !renderer->isListItem())
777 return String();
778
779 return toRenderListItem(renderer)->markerText();
780 }
781
782 } // namespace blink
783