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 WebCore { 65 66 using namespace HTMLNames; 67 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 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 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 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 161 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior) 162 { 163 ts << o.renderName(); 164 165 if (behavior & RenderAsTextShowAddresses) 166 ts << " " << static_cast<const void*>(&o); 167 168 if (o.style() && o.style()->zIndex()) 169 ts << " zI: " << o.style()->zIndex(); 170 171 if (o.node()) { 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())) 175 tagName = emptyAtom; 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*"; 182 } 183 } 184 185 RenderBlock* cb = o.containingBlock(); 186 bool adjustForTableCells = cb ? cb->isTableCell() : false; 187 188 LayoutRect r; 189 if (o.isText()) { 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(); 210 211 // FIXME: Temporary in order to ensure compatibility with existing layout test results. 212 if (adjustForTableCells) 213 r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore()); 214 215 ts << " " << r; 216 217 if (!(o.isText() && !o.isBR())) { 218 if (o.isFileUploadControl()) 219 ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue()); 220 221 if (o.parent()) { 222 Color color = o.resolveColor(CSSPropertyColor); 223 if (o.parent()->resolveColor(CSSPropertyColor) != color) 224 ts << " [color=" << color.nameForRenderTreeAsText() << "]"; 225 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() << "]"; 231 232 Color textFillColor = o.resolveColor(CSSPropertyWebkitTextFillColor); 233 if (o.parent()->resolveColor(CSSPropertyWebkitTextFillColor) != textFillColor 234 && textFillColor != color && textFillColor.rgb()) 235 ts << " [textFillColor=" << textFillColor.nameForRenderTreeAsText() << "]"; 236 237 Color textStrokeColor = o.resolveColor(CSSPropertyWebkitTextStrokeColor); 238 if (o.parent()->resolveColor(CSSPropertyWebkitTextStrokeColor) != textStrokeColor 239 && textStrokeColor != color && textStrokeColor.rgb()) 240 ts << " [textStrokeColor=" << textStrokeColor.nameForRenderTreeAsText() << "]"; 241 242 if (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth() && o.style()->textStrokeWidth() > 0) 243 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]"; 244 } 245 246 if (!o.isBoxModelObject()) 247 return; 248 249 const RenderBoxModelObject& box = *toRenderBoxModelObject(&o); 250 if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) { 251 ts << " [border:"; 252 253 BorderValue prevBorder = o.style()->borderTop(); 254 if (!box.borderTop()) 255 ts << " none"; 256 else { 257 ts << " (" << box.borderTop() << "px "; 258 printBorderStyle(ts, o.style()->borderTopStyle()); 259 Color col = o.resolveColor(CSSPropertyBorderTopColor); 260 ts << col.nameForRenderTreeAsText() << ")"; 261 } 262 263 if (o.style()->borderRight() != prevBorder) { 264 prevBorder = o.style()->borderRight(); 265 if (!box.borderRight()) 266 ts << " none"; 267 else { 268 ts << " (" << box.borderRight() << "px "; 269 printBorderStyle(ts, o.style()->borderRightStyle()); 270 Color col = o.resolveColor(CSSPropertyBorderRightColor); 271 ts << col.nameForRenderTreeAsText() << ")"; 272 } 273 } 274 275 if (o.style()->borderBottom() != prevBorder) { 276 prevBorder = box.style()->borderBottom(); 277 if (!box.borderBottom()) 278 ts << " none"; 279 else { 280 ts << " (" << box.borderBottom() << "px "; 281 printBorderStyle(ts, o.style()->borderBottomStyle()); 282 Color col = o.resolveColor(CSSPropertyBorderBottomColor); 283 ts << col.nameForRenderTreeAsText() << ")"; 284 } 285 } 286 287 if (o.style()->borderLeft() != prevBorder) { 288 prevBorder = o.style()->borderLeft(); 289 if (!box.borderLeft()) 290 ts << " none"; 291 else { 292 ts << " (" << box.borderLeft() << "px "; 293 printBorderStyle(ts, o.style()->borderLeftStyle()); 294 Color col = o.resolveColor(CSSPropertyBorderLeftColor); 295 ts << col.nameForRenderTreeAsText() << ")"; 296 } 297 } 298 299 ts << "]"; 300 } 301 } 302 303 if (o.isTableCell()) { 304 const RenderTableCell& c = *toRenderTableCell(&o); 305 ts << " [r=" << c.rowIndex() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]"; 306 } 307 308 if (o.isDetailsMarker()) { 309 ts << ": "; 310 switch (toRenderDetailsMarker(&o)->orientation()) { 311 case RenderDetailsMarker::Left: 312 ts << "left"; 313 break; 314 case RenderDetailsMarker::Right: 315 ts << "right"; 316 break; 317 case RenderDetailsMarker::Up: 318 ts << "up"; 319 break; 320 case RenderDetailsMarker::Down: 321 ts << "down"; 322 break; 323 } 324 } 325 326 if (o.isListMarker()) { 327 String text = toRenderListMarker(&o)->text(); 328 if (!text.isEmpty()) { 329 if (text.length() != 1) 330 text = quoteAndEscapeNonPrintables(text); 331 else { 332 switch (text[0]) { 333 case bullet: 334 text = "bullet"; 335 break; 336 case blackSquare: 337 text = "black square"; 338 break; 339 case whiteBullet: 340 text = "white bullet"; 341 break; 342 default: 343 text = quoteAndEscapeNonPrintables(text); 344 } 345 } 346 ts << ": " << text; 347 } 348 } 349 350 if (behavior & RenderAsTextShowIDAndClass) { 351 if (Node* node = o.node()) { 352 if (node->hasID()) 353 ts << " id=\"" + toElement(node)->getIdAttribute() + "\""; 354 355 if (node->hasClass()) { 356 ts << " class=\""; 357 Element* element = toElement(node); 358 for (size_t i = 0; i < element->classNames().size(); ++i) { 359 if (i > 0) 360 ts << " "; 361 ts << element->classNames()[i]; 362 } 363 ts << "\""; 364 } 365 } 366 } 367 368 if (behavior & RenderAsTextShowLayoutState) { 369 bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout(); 370 if (needsLayout) 371 ts << " (needs layout:"; 372 373 bool havePrevious = false; 374 if (o.selfNeedsLayout()) { 375 ts << " self"; 376 havePrevious = true; 377 } 378 379 if (o.needsPositionedMovementLayout()) { 380 if (havePrevious) 381 ts << ","; 382 havePrevious = true; 383 ts << " positioned movement"; 384 } 385 386 if (o.normalChildNeedsLayout()) { 387 if (havePrevious) 388 ts << ","; 389 havePrevious = true; 390 ts << " child"; 391 } 392 393 if (o.posChildNeedsLayout()) { 394 if (havePrevious) 395 ts << ","; 396 ts << " positioned child"; 397 } 398 399 if (needsLayout) 400 ts << ")"; 401 } 402 } 403 404 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run) 405 { 406 // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder 407 // to detect any changes caused by the conversion to floating point. :( 408 int x = run.x(); 409 int y = run.y(); 410 int logicalWidth = ceilf(run.left() + run.logicalWidth()) - x; 411 412 // FIXME: Table cell adjustment is temporary until results can be updated. 413 if (o.containingBlock()->isTableCell()) 414 y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore(); 415 416 ts << "text run at (" << x << "," << y << ") width " << logicalWidth; 417 if (!run.isLeftToRightDirection() || run.dirOverride()) { 418 ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR"); 419 if (run.dirOverride()) 420 ts << " override"; 421 } 422 ts << ": " 423 << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len())); 424 if (run.hasHyphen()) 425 ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString()); 426 ts << "\n"; 427 } 428 429 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior) 430 { 431 if (o.isSVGShape()) { 432 write(ts, *toRenderSVGShape(&o), indent); 433 return; 434 } 435 if (o.isSVGGradientStop()) { 436 writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent); 437 return; 438 } 439 if (o.isSVGResourceContainer()) { 440 writeSVGResourceContainer(ts, o, indent); 441 return; 442 } 443 if (o.isSVGContainer()) { 444 writeSVGContainer(ts, o, indent); 445 return; 446 } 447 if (o.isSVGRoot()) { 448 write(ts, *toRenderSVGRoot(&o), indent); 449 return; 450 } 451 if (o.isSVGText()) { 452 writeSVGText(ts, *toRenderSVGText(&o), indent); 453 return; 454 } 455 if (o.isSVGInlineText()) { 456 writeSVGInlineText(ts, *toRenderSVGInlineText(&o), indent); 457 return; 458 } 459 if (o.isSVGImage()) { 460 writeSVGImage(ts, *toRenderSVGImage(&o), indent); 461 return; 462 } 463 464 writeIndent(ts, indent); 465 466 RenderTreeAsText::writeRenderObject(ts, o, behavior); 467 ts << "\n"; 468 469 if (o.isText() && !o.isBR()) { 470 const RenderText& text = *toRenderText(&o); 471 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) { 472 writeIndent(ts, indent + 1); 473 writeTextRun(ts, text, *box); 474 } 475 } 476 477 for (RenderObject* child = o.slowFirstChild(); child; child = child->nextSibling()) { 478 if (child->hasLayer()) 479 continue; 480 write(ts, *child, indent + 1, behavior); 481 } 482 483 if (o.isWidget()) { 484 Widget* widget = toRenderWidget(&o)->widget(); 485 if (widget && widget->isFrameView()) { 486 FrameView* view = toFrameView(widget); 487 RenderView* root = view->frame().contentRenderer(); 488 if (root) { 489 view->layout(); 490 RenderLayer* l = root->layer(); 491 if (l) 492 RenderTreeAsText::writeLayers(ts, l, l, l->rect(), indent + 1, behavior); 493 } 494 } 495 } 496 } 497 498 enum LayerPaintPhase { 499 LayerPaintPhaseAll = 0, 500 LayerPaintPhaseBackground = -1, 501 LayerPaintPhaseForeground = 1 502 }; 503 504 static void write(TextStream& ts, RenderLayer& l, 505 const LayoutRect& layerBounds, const LayoutRect& backgroundClipRect, const LayoutRect& clipRect, const LayoutRect& outlineClipRect, 506 LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal) 507 { 508 IntRect adjustedLayoutBounds = pixelSnappedIntRect(layerBounds); 509 IntRect adjustedBackgroundClipRect = pixelSnappedIntRect(backgroundClipRect); 510 IntRect adjustedClipRect = pixelSnappedIntRect(clipRect); 511 IntRect adjustedOutlineClipRect = pixelSnappedIntRect(outlineClipRect); 512 513 writeIndent(ts, indent); 514 515 if (l.renderer()->style()->visibility() == HIDDEN) 516 ts << "hidden "; 517 518 ts << "layer "; 519 520 if (behavior & RenderAsTextShowAddresses) 521 ts << static_cast<const void*>(&l) << " "; 522 523 ts << adjustedLayoutBounds; 524 525 if (!adjustedLayoutBounds.isEmpty()) { 526 if (!adjustedBackgroundClipRect.contains(adjustedLayoutBounds)) 527 ts << " backgroundClip " << adjustedBackgroundClipRect; 528 if (!adjustedClipRect.contains(adjustedLayoutBounds)) 529 ts << " clip " << adjustedClipRect; 530 if (!adjustedOutlineClipRect.contains(adjustedLayoutBounds)) 531 ts << " outlineClip " << adjustedOutlineClipRect; 532 } 533 if (l.isTransparent()) 534 ts << " transparent"; 535 536 if (l.renderer()->hasOverflowClip()) { 537 if (l.scrollableArea()->scrollXOffset()) 538 ts << " scrollX " << l.scrollableArea()->scrollXOffset(); 539 if (l.scrollableArea()->scrollYOffset()) 540 ts << " scrollY " << l.scrollableArea()->scrollYOffset(); 541 if (l.renderBox() && l.renderBox()->pixelSnappedClientWidth() != l.renderBox()->pixelSnappedScrollWidth()) 542 ts << " scrollWidth " << l.renderBox()->pixelSnappedScrollWidth(); 543 if (l.renderBox() && l.renderBox()->pixelSnappedClientHeight() != l.renderBox()->pixelSnappedScrollHeight()) 544 ts << " scrollHeight " << l.renderBox()->pixelSnappedScrollHeight(); 545 } 546 547 if (paintPhase == LayerPaintPhaseBackground) 548 ts << " layerType: background only"; 549 else if (paintPhase == LayerPaintPhaseForeground) 550 ts << " layerType: foreground only"; 551 552 if (l.blendInfo().childLayerHasBlendMode()) 553 ts << " isolatesBlending"; 554 if (l.blendInfo().hasBlendMode()) 555 ts << " blendMode: " << compositeOperatorName(CompositeSourceOver, l.blendInfo().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 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 layer->setSize(layer->size().expandedTo(pixelSnappedIntSize(layer->renderBox()->maxLayoutOverflow(), LayoutPoint(0, 0)))); 585 } 586 587 // Calculate the clip rects we should use. 588 LayoutRect layerBounds; 589 ClipRect damageRect, clipRectToApply, outlineRect; 590 layer->clipper().calculateRects(ClipRectsContext(rootLayer, TemporaryClipRects), paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect); 591 592 // Ensure our lists are up-to-date. 593 layer->stackingNode()->updateLayerListsIfNeeded(); 594 595 bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : layer->intersectsDamageRect(layerBounds, damageRect.rect(), rootLayer); 596 597 Vector<RenderLayerStackingNode*>* negList = layer->stackingNode()->negZOrderList(); 598 bool paintsBackgroundSeparately = negList && negList->size() > 0; 599 if (shouldPaint && paintsBackgroundSeparately) 600 write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), LayerPaintPhaseBackground, indent, behavior); 601 602 if (negList) { 603 int currIndent = indent; 604 if (behavior & RenderAsTextShowLayerNesting) { 605 writeIndent(ts, indent); 606 ts << " negative z-order list(" << negList->size() << ")\n"; 607 ++currIndent; 608 } 609 for (unsigned i = 0; i != negList->size(); ++i) 610 writeLayers(ts, rootLayer, negList->at(i)->layer(), paintDirtyRect, currIndent, behavior); 611 } 612 613 if (shouldPaint) 614 write(ts, *layer, layerBounds, damageRect.rect(), clipRectToApply.rect(), outlineRect.rect(), paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior); 615 616 if (Vector<RenderLayerStackingNode*>* normalFlowList = layer->stackingNode()->normalFlowList()) { 617 int currIndent = indent; 618 if (behavior & RenderAsTextShowLayerNesting) { 619 writeIndent(ts, indent); 620 ts << " normal flow list(" << normalFlowList->size() << ")\n"; 621 ++currIndent; 622 } 623 for (unsigned i = 0; i != normalFlowList->size(); ++i) 624 writeLayers(ts, rootLayer, normalFlowList->at(i)->layer(), paintDirtyRect, currIndent, behavior); 625 } 626 627 if (Vector<RenderLayerStackingNode*>* posList = layer->stackingNode()->posZOrderList()) { 628 int currIndent = indent; 629 if (behavior & RenderAsTextShowLayerNesting) { 630 writeIndent(ts, indent); 631 ts << " positive z-order list(" << posList->size() << ")\n"; 632 ++currIndent; 633 } 634 for (unsigned i = 0; i != posList->size(); ++i) 635 writeLayers(ts, rootLayer, posList->at(i)->layer(), paintDirtyRect, currIndent, behavior); 636 } 637 } 638 639 static String nodePosition(Node* node) 640 { 641 StringBuilder result; 642 643 Element* body = node->document().body(); 644 Node* parent; 645 for (Node* n = node; n; n = parent) { 646 parent = n->parentOrShadowHostNode(); 647 if (n != node) 648 result.appendLiteral(" of "); 649 if (parent) { 650 if (body && n == body) { 651 // We don't care what offset body may be in the document. 652 result.appendLiteral("body"); 653 break; 654 } 655 if (n->isShadowRoot()) { 656 result.append('{'); 657 result.append(getTagName(n)); 658 result.append('}'); 659 } else { 660 result.appendLiteral("child "); 661 result.appendNumber(n->nodeIndex()); 662 result.appendLiteral(" {"); 663 result.append(getTagName(n)); 664 result.append('}'); 665 } 666 } else 667 result.appendLiteral("document"); 668 } 669 670 return result.toString(); 671 } 672 673 static void writeSelection(TextStream& ts, const RenderObject* o) 674 { 675 Node* n = o->node(); 676 if (!n || !n->isDocumentNode()) 677 return; 678 679 Document* doc = toDocument(n); 680 LocalFrame* frame = doc->frame(); 681 if (!frame) 682 return; 683 684 VisibleSelection selection = frame->selection().selection(); 685 if (selection.isCaret()) { 686 ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()); 687 if (selection.affinity() == UPSTREAM) 688 ts << " (upstream affinity)"; 689 ts << "\n"; 690 } else if (selection.isRange()) 691 ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n" 692 << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n"; 693 } 694 695 static String externalRepresentation(RenderBox* renderer, RenderAsTextBehavior behavior) 696 { 697 TextStream ts; 698 if (!renderer->hasLayer()) 699 return ts.release(); 700 701 RenderLayer* layer = renderer->layer(); 702 RenderTreeAsText::writeLayers(ts, layer, layer, layer->rect(), 0, behavior); 703 writeSelection(ts, renderer); 704 return ts.release(); 705 } 706 707 String externalRepresentation(LocalFrame* frame, RenderAsTextBehavior behavior) 708 { 709 if (!(behavior & RenderAsTextDontUpdateLayout)) 710 frame->document()->updateLayout(); 711 712 RenderObject* renderer = frame->contentRenderer(); 713 if (!renderer || !renderer->isBox()) 714 return String(); 715 716 PrintContext printContext(frame); 717 if (behavior & RenderAsTextPrintingMode) 718 printContext.begin(toRenderBox(renderer)->width().toFloat()); 719 720 return externalRepresentation(toRenderBox(renderer), behavior); 721 } 722 723 String externalRepresentation(Element* element, RenderAsTextBehavior behavior) 724 { 725 // Doesn't support printing mode. 726 ASSERT(!(behavior & RenderAsTextPrintingMode)); 727 if (!(behavior & RenderAsTextDontUpdateLayout)) 728 element->document().updateLayout(); 729 730 RenderObject* renderer = element->renderer(); 731 if (!renderer || !renderer->isBox()) 732 return String(); 733 734 return externalRepresentation(toRenderBox(renderer), behavior | RenderAsTextShowAllLayers); 735 } 736 737 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter) 738 { 739 for (RenderObject* child = parent->slowFirstChild(); child; child = child->nextSibling()) { 740 if (child->isCounter()) { 741 if (!isFirstCounter) 742 stream << " "; 743 isFirstCounter = false; 744 String str(toRenderText(child)->text()); 745 stream << str; 746 } 747 } 748 } 749 750 String counterValueForElement(Element* element) 751 { 752 // Make sure the element is not freed during the layout. 753 RefPtrWillBeRawPtr<Element> protector(element); 754 element->document().updateLayout(); 755 TextStream stream; 756 bool isFirstCounter = true; 757 // The counter renderers should be children of :before or :after pseudo-elements. 758 if (RenderObject* before = element->pseudoElementRenderer(BEFORE)) 759 writeCounterValuesFromChildren(stream, before, isFirstCounter); 760 if (RenderObject* after = element->pseudoElementRenderer(AFTER)) 761 writeCounterValuesFromChildren(stream, after, isFirstCounter); 762 return stream.release(); 763 } 764 765 String markerTextForListItem(Element* element) 766 { 767 // Make sure the element is not freed during the layout. 768 RefPtrWillBeRawPtr<Element> protector(element); 769 element->document().updateLayout(); 770 771 RenderObject* renderer = element->renderer(); 772 if (!renderer || !renderer->isListItem()) 773 return String(); 774 775 return toRenderListItem(renderer)->markerText(); 776 } 777 778 } // namespace WebCore 779