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 "RenderTreeAsText.h" 28 29 #include "CSSMutableStyleDeclaration.h" 30 #include "Document.h" 31 #include "Frame.h" 32 #include "FrameView.h" 33 #include "HTMLElement.h" 34 #include "HTMLNames.h" 35 #include "InlineTextBox.h" 36 #include "PrintContext.h" 37 #include "RenderBR.h" 38 #include "RenderDetailsMarker.h" 39 #include "RenderFileUploadControl.h" 40 #include "RenderInline.h" 41 #include "RenderLayer.h" 42 #include "RenderListItem.h" 43 #include "RenderListMarker.h" 44 #include "RenderPart.h" 45 #include "RenderTableCell.h" 46 #include "RenderView.h" 47 #include "RenderWidget.h" 48 #include "SelectionController.h" 49 #include <wtf/HexNumber.h> 50 #include <wtf/UnusedParam.h> 51 #include <wtf/Vector.h> 52 #include <wtf/unicode/CharacterNames.h> 53 54 #if ENABLE(SVG) 55 #include "RenderSVGContainer.h" 56 #include "RenderSVGGradientStop.h" 57 #include "RenderSVGImage.h" 58 #include "RenderSVGInlineText.h" 59 #include "RenderSVGPath.h" 60 #include "RenderSVGRoot.h" 61 #include "RenderSVGText.h" 62 #include "SVGRenderTreeAsText.h" 63 #endif 64 65 #if USE(ACCELERATED_COMPOSITING) 66 #include "RenderLayerBacking.h" 67 #endif 68 69 #if PLATFORM(QT) 70 #include <QWidget> 71 #endif 72 73 namespace WebCore { 74 75 using namespace HTMLNames; 76 77 static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal); 78 79 bool hasFractions(double val) 80 { 81 static const double s_epsilon = 0.0001; 82 int ival = static_cast<int>(val); 83 double dval = static_cast<double>(ival); 84 return fabs(val - dval) > s_epsilon; 85 } 86 87 TextStream& operator<<(TextStream& ts, const IntRect& r) 88 { 89 return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height(); 90 } 91 92 TextStream& operator<<(TextStream& ts, const IntPoint& p) 93 { 94 return ts << "(" << p.x() << "," << p.y() << ")"; 95 } 96 97 TextStream& operator<<(TextStream& ts, const FloatPoint& p) 98 { 99 ts << "("; 100 if (hasFractions(p.x())) 101 ts << p.x(); 102 else 103 ts << int(p.x()); 104 ts << ","; 105 if (hasFractions(p.y())) 106 ts << p.y(); 107 else 108 ts << int(p.y()); 109 return ts << ")"; 110 } 111 112 TextStream& operator<<(TextStream& ts, const FloatSize& s) 113 { 114 ts << "width="; 115 if (hasFractions(s.width())) 116 ts << s.width(); 117 else 118 ts << int(s.width()); 119 ts << " height="; 120 if (hasFractions(s.height())) 121 ts << s.height(); 122 else 123 ts << int(s.height()); 124 return ts; 125 } 126 127 void writeIndent(TextStream& ts, int indent) 128 { 129 for (int i = 0; i != indent; ++i) 130 ts << " "; 131 } 132 133 static void printBorderStyle(TextStream& ts, const EBorderStyle borderStyle) 134 { 135 switch (borderStyle) { 136 case BNONE: 137 ts << "none"; 138 break; 139 case BHIDDEN: 140 ts << "hidden"; 141 break; 142 case INSET: 143 ts << "inset"; 144 break; 145 case GROOVE: 146 ts << "groove"; 147 break; 148 case RIDGE: 149 ts << "ridge"; 150 break; 151 case OUTSET: 152 ts << "outset"; 153 break; 154 case DOTTED: 155 ts << "dotted"; 156 break; 157 case DASHED: 158 ts << "dashed"; 159 break; 160 case SOLID: 161 ts << "solid"; 162 break; 163 case DOUBLE: 164 ts << "double"; 165 break; 166 } 167 168 ts << " "; 169 } 170 171 static String getTagName(Node* n) 172 { 173 if (n->isDocumentNode()) 174 return ""; 175 if (n->isCommentNode()) 176 return "COMMENT"; 177 return n->nodeName(); 178 } 179 180 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node) 181 { 182 if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag)) 183 return false; 184 185 const HTMLElement* elem = static_cast<const HTMLElement*>(node); 186 if (elem->getAttribute(classAttr) != "Apple-style-span") 187 return false; 188 189 if (!node->hasChildNodes()) 190 return true; 191 192 CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl(); 193 return (!inlineStyleDecl || inlineStyleDecl->length() == 0); 194 } 195 196 String quoteAndEscapeNonPrintables(const String& s) 197 { 198 Vector<UChar> result; 199 result.append('"'); 200 for (unsigned i = 0; i != s.length(); ++i) { 201 UChar c = s[i]; 202 if (c == '\\') { 203 result.append('\\'); 204 result.append('\\'); 205 } else if (c == '"') { 206 result.append('\\'); 207 result.append('"'); 208 } else if (c == '\n' || c == noBreakSpace) 209 result.append(' '); 210 else { 211 if (c >= 0x20 && c < 0x7F) 212 result.append(c); 213 else { 214 result.append('\\'); 215 result.append('x'); 216 result.append('{'); 217 appendUnsignedAsHex(c, result); 218 result.append('}'); 219 } 220 } 221 } 222 result.append('"'); 223 return String::adopt(result); 224 } 225 226 void RenderTreeAsText::writeRenderObject(TextStream& ts, const RenderObject& o, RenderAsTextBehavior behavior) 227 { 228 ts << o.renderName(); 229 230 if (behavior & RenderAsTextShowAddresses) 231 ts << " " << static_cast<const void*>(&o); 232 233 if (o.style() && o.style()->zIndex()) 234 ts << " zI: " << o.style()->zIndex(); 235 236 if (o.node()) { 237 String tagName = getTagName(o.node()); 238 if (!tagName.isEmpty()) { 239 ts << " {" << tagName << "}"; 240 // flag empty or unstyled AppleStyleSpan because we never 241 // want to leave them in the DOM 242 if (isEmptyOrUnstyledAppleStyleSpan(o.node())) 243 ts << " *empty or unstyled AppleStyleSpan*"; 244 } 245 } 246 247 bool adjustForTableCells = o.containingBlock()->isTableCell(); 248 249 IntRect r; 250 if (o.isText()) { 251 // 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 252 // many test results. 253 const RenderText& text = *toRenderText(&o); 254 IntRect linesBox = text.linesBoundingBox(); 255 r = IntRect(text.firstRunX(), text.firstRunY(), linesBox.width(), linesBox.height()); 256 if (adjustForTableCells && !text.firstTextBox()) 257 adjustForTableCells = false; 258 } else if (o.isRenderInline()) { 259 // FIXME: Would be better not to just dump 0, 0 as the x and y here. 260 const RenderInline& inlineFlow = *toRenderInline(&o); 261 r = IntRect(0, 0, inlineFlow.linesBoundingBox().width(), inlineFlow.linesBoundingBox().height()); 262 adjustForTableCells = false; 263 } else if (o.isTableCell()) { 264 // FIXME: Deliberately dump the "inner" box of table cells, since that is what current results reflect. We'd like 265 // to clean up the results to dump both the outer box and the intrinsic padding so that both bits of information are 266 // captured by the results. 267 const RenderTableCell& cell = *toRenderTableCell(&o); 268 r = IntRect(cell.x(), cell.y() + cell.intrinsicPaddingBefore(), cell.width(), cell.height() - cell.intrinsicPaddingBefore() - cell.intrinsicPaddingAfter()); 269 } else if (o.isBox()) 270 r = toRenderBox(&o)->frameRect(); 271 272 // FIXME: Temporary in order to ensure compatibility with existing layout test results. 273 if (adjustForTableCells) 274 r.move(0, -toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore()); 275 276 ts << " " << r; 277 278 if (!(o.isText() && !o.isBR())) { 279 if (o.isFileUploadControl()) 280 ts << " " << quoteAndEscapeNonPrintables(toRenderFileUploadControl(&o)->fileTextValue()); 281 282 if (o.parent() && (o.parent()->style()->color() != o.style()->color())) 283 ts << " [color=" << o.style()->color().nameForRenderTreeAsText() << "]"; 284 285 if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) && 286 o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb()) 287 // Do not dump invalid or transparent backgrounds, since that is the default. 288 ts << " [bgcolor=" << o.style()->backgroundColor().nameForRenderTreeAsText() << "]"; 289 290 if (o.parent() && (o.parent()->style()->textFillColor() != o.style()->textFillColor()) && 291 o.style()->textFillColor().isValid() && o.style()->textFillColor() != o.style()->color() && 292 o.style()->textFillColor().rgb()) 293 ts << " [textFillColor=" << o.style()->textFillColor().nameForRenderTreeAsText() << "]"; 294 295 if (o.parent() && (o.parent()->style()->textStrokeColor() != o.style()->textStrokeColor()) && 296 o.style()->textStrokeColor().isValid() && o.style()->textStrokeColor() != o.style()->color() && 297 o.style()->textStrokeColor().rgb()) 298 ts << " [textStrokeColor=" << o.style()->textStrokeColor().nameForRenderTreeAsText() << "]"; 299 300 if (o.parent() && (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth()) && 301 o.style()->textStrokeWidth() > 0) 302 ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]"; 303 304 if (!o.isBoxModelObject()) 305 return; 306 307 const RenderBoxModelObject& box = *toRenderBoxModelObject(&o); 308 if (box.borderTop() || box.borderRight() || box.borderBottom() || box.borderLeft()) { 309 ts << " [border:"; 310 311 BorderValue prevBorder; 312 if (o.style()->borderTop() != prevBorder) { 313 prevBorder = o.style()->borderTop(); 314 if (!box.borderTop()) 315 ts << " none"; 316 else { 317 ts << " (" << box.borderTop() << "px "; 318 printBorderStyle(ts, o.style()->borderTopStyle()); 319 Color col = o.style()->borderTopColor(); 320 if (!col.isValid()) 321 col = o.style()->color(); 322 ts << col.nameForRenderTreeAsText() << ")"; 323 } 324 } 325 326 if (o.style()->borderRight() != prevBorder) { 327 prevBorder = o.style()->borderRight(); 328 if (!box.borderRight()) 329 ts << " none"; 330 else { 331 ts << " (" << box.borderRight() << "px "; 332 printBorderStyle(ts, o.style()->borderRightStyle()); 333 Color col = o.style()->borderRightColor(); 334 if (!col.isValid()) 335 col = o.style()->color(); 336 ts << col.nameForRenderTreeAsText() << ")"; 337 } 338 } 339 340 if (o.style()->borderBottom() != prevBorder) { 341 prevBorder = box.style()->borderBottom(); 342 if (!box.borderBottom()) 343 ts << " none"; 344 else { 345 ts << " (" << box.borderBottom() << "px "; 346 printBorderStyle(ts, o.style()->borderBottomStyle()); 347 Color col = o.style()->borderBottomColor(); 348 if (!col.isValid()) 349 col = o.style()->color(); 350 ts << col.nameForRenderTreeAsText() << ")"; 351 } 352 } 353 354 if (o.style()->borderLeft() != prevBorder) { 355 prevBorder = o.style()->borderLeft(); 356 if (!box.borderLeft()) 357 ts << " none"; 358 else { 359 ts << " (" << box.borderLeft() << "px "; 360 printBorderStyle(ts, o.style()->borderLeftStyle()); 361 Color col = o.style()->borderLeftColor(); 362 if (!col.isValid()) 363 col = o.style()->color(); 364 ts << col.nameForRenderTreeAsText() << ")"; 365 } 366 } 367 368 ts << "]"; 369 } 370 } 371 372 if (o.isTableCell()) { 373 const RenderTableCell& c = *toRenderTableCell(&o); 374 ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]"; 375 } 376 377 if (o.isDetailsMarker()) { 378 ts << ": "; 379 switch (toRenderDetailsMarker(&o)->orientation()) { 380 case RenderDetailsMarker::Left: 381 ts << "left"; 382 break; 383 case RenderDetailsMarker::Right: 384 ts << "right"; 385 break; 386 case RenderDetailsMarker::Up: 387 ts << "up"; 388 break; 389 case RenderDetailsMarker::Down: 390 ts << "down"; 391 break; 392 } 393 } 394 395 if (o.isListMarker()) { 396 String text = toRenderListMarker(&o)->text(); 397 if (!text.isEmpty()) { 398 if (text.length() != 1) 399 text = quoteAndEscapeNonPrintables(text); 400 else { 401 switch (text[0]) { 402 case bullet: 403 text = "bullet"; 404 break; 405 case blackSquare: 406 text = "black square"; 407 break; 408 case whiteBullet: 409 text = "white bullet"; 410 break; 411 default: 412 text = quoteAndEscapeNonPrintables(text); 413 } 414 } 415 ts << ": " << text; 416 } 417 } 418 419 if (behavior & RenderAsTextShowIDAndClass) { 420 if (Node* node = o.node()) { 421 if (node->hasID()) 422 ts << " id=\"" + static_cast<Element*>(node)->getIdAttribute() + "\""; 423 424 if (node->hasClass()) { 425 StyledElement* styledElement = static_cast<StyledElement*>(node); 426 String classes; 427 for (size_t i = 0; i < styledElement->classNames().size(); ++i) { 428 if (i > 0) 429 classes += " "; 430 classes += styledElement->classNames()[i]; 431 } 432 ts << " class=\"" + classes + "\""; 433 } 434 } 435 } 436 437 if (behavior & RenderAsTextShowLayoutState) { 438 bool needsLayout = o.selfNeedsLayout() || o.needsPositionedMovementLayout() || o.posChildNeedsLayout() || o.normalChildNeedsLayout(); 439 if (needsLayout) 440 ts << " (needs layout:"; 441 442 bool havePrevious = false; 443 if (o.selfNeedsLayout()) { 444 ts << " self"; 445 havePrevious = true; 446 } 447 448 if (o.needsPositionedMovementLayout()) { 449 if (havePrevious) 450 ts << ","; 451 havePrevious = true; 452 ts << " positioned movement"; 453 } 454 455 if (o.normalChildNeedsLayout()) { 456 if (havePrevious) 457 ts << ","; 458 havePrevious = true; 459 ts << " child"; 460 } 461 462 if (o.posChildNeedsLayout()) { 463 if (havePrevious) 464 ts << ","; 465 ts << " positioned child"; 466 } 467 468 if (needsLayout) 469 ts << ")"; 470 } 471 472 #if PLATFORM(QT) 473 // Print attributes of embedded QWidgets. E.g. when the WebCore::Widget 474 // is invisible the QWidget should be invisible too. 475 if (o.isRenderPart()) { 476 const RenderPart* part = toRenderPart(const_cast<RenderObject*>(&o)); 477 if (part->widget() && part->widget()->platformWidget()) { 478 QWidget* wid = part->widget()->platformWidget(); 479 480 ts << " [QT: "; 481 ts << "geometry: {" << wid->geometry() << "} "; 482 ts << "isHidden: " << wid->isHidden() << " "; 483 ts << "isSelfVisible: " << part->widget()->isSelfVisible() << " "; 484 ts << "isParentVisible: " << part->widget()->isParentVisible() << " "; 485 ts << "mask: {" << wid->mask().boundingRect() << "} ] "; 486 } 487 } 488 #endif 489 } 490 491 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run) 492 { 493 // FIXME: For now use an "enclosingIntRect" model for x, y and logicalWidth, although this makes it harder 494 // to detect any changes caused by the conversion to floating point. :( 495 int x = run.m_x; 496 int y = run.m_y; 497 int logicalWidth = ceilf(run.m_x + run.m_logicalWidth) - x; 498 499 // FIXME: Table cell adjustment is temporary until results can be updated. 500 if (o.containingBlock()->isTableCell()) 501 y -= toRenderTableCell(o.containingBlock())->intrinsicPaddingBefore(); 502 503 ts << "text run at (" << x << "," << y << ") width " << logicalWidth; 504 if (!run.isLeftToRightDirection() || run.m_dirOverride) { 505 ts << (!run.isLeftToRightDirection() ? " RTL" : " LTR"); 506 if (run.m_dirOverride) 507 ts << " override"; 508 } 509 ts << ": " 510 << quoteAndEscapeNonPrintables(String(o.text()).substring(run.start(), run.len())); 511 if (run.hasHyphen()) 512 ts << " + hyphen string " << quoteAndEscapeNonPrintables(o.style()->hyphenString()); 513 ts << "\n"; 514 } 515 516 void write(TextStream& ts, const RenderObject& o, int indent, RenderAsTextBehavior behavior) 517 { 518 #if ENABLE(SVG) 519 if (o.isSVGPath()) { 520 write(ts, *toRenderSVGPath(&o), indent); 521 return; 522 } 523 if (o.isSVGGradientStop()) { 524 writeSVGGradientStop(ts, *toRenderSVGGradientStop(&o), indent); 525 return; 526 } 527 if (o.isSVGResourceContainer()) { 528 writeSVGResourceContainer(ts, o, indent); 529 return; 530 } 531 if (o.isSVGContainer()) { 532 writeSVGContainer(ts, o, indent); 533 return; 534 } 535 if (o.isSVGRoot()) { 536 write(ts, *toRenderSVGRoot(&o), indent); 537 return; 538 } 539 if (o.isSVGText()) { 540 writeSVGText(ts, *toRenderBlock(&o), indent); 541 return; 542 } 543 if (o.isSVGInlineText()) { 544 writeSVGInlineText(ts, *toRenderText(&o), indent); 545 return; 546 } 547 if (o.isSVGImage()) { 548 writeSVGImage(ts, *toRenderSVGImage(&o), indent); 549 return; 550 } 551 #endif 552 553 writeIndent(ts, indent); 554 555 RenderTreeAsText::writeRenderObject(ts, o, behavior); 556 ts << "\n"; 557 558 if (o.isText() && !o.isBR()) { 559 const RenderText& text = *toRenderText(&o); 560 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) { 561 writeIndent(ts, indent + 1); 562 writeTextRun(ts, text, *box); 563 } 564 } 565 566 for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) { 567 if (child->hasLayer()) 568 continue; 569 write(ts, *child, indent + 1, behavior); 570 } 571 572 if (o.isWidget()) { 573 Widget* widget = toRenderWidget(&o)->widget(); 574 if (widget && widget->isFrameView()) { 575 FrameView* view = static_cast<FrameView*>(widget); 576 RenderView* root = view->frame()->contentRenderer(); 577 if (root) { 578 view->layout(); 579 RenderLayer* l = root->layer(); 580 if (l) 581 writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), indent + 1, behavior); 582 } 583 } 584 } 585 } 586 587 enum LayerPaintPhase { 588 LayerPaintPhaseAll = 0, 589 LayerPaintPhaseBackground = -1, 590 LayerPaintPhaseForeground = 1 591 }; 592 593 static void write(TextStream& ts, RenderLayer& l, 594 const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect, 595 LayerPaintPhase paintPhase = LayerPaintPhaseAll, int indent = 0, RenderAsTextBehavior behavior = RenderAsTextBehaviorNormal) 596 { 597 writeIndent(ts, indent); 598 599 ts << "layer "; 600 601 if (behavior & RenderAsTextShowAddresses) 602 ts << static_cast<const void*>(&l) << " "; 603 604 ts << layerBounds; 605 606 if (!layerBounds.isEmpty()) { 607 if (!backgroundClipRect.contains(layerBounds)) 608 ts << " backgroundClip " << backgroundClipRect; 609 if (!clipRect.contains(layerBounds)) 610 ts << " clip " << clipRect; 611 if (!outlineClipRect.contains(layerBounds)) 612 ts << " outlineClip " << outlineClipRect; 613 } 614 615 if (l.renderer()->hasOverflowClip()) { 616 if (l.scrollXOffset()) 617 ts << " scrollX " << l.scrollXOffset(); 618 if (l.scrollYOffset()) 619 ts << " scrollY " << l.scrollYOffset(); 620 if (l.renderBox() && l.renderBox()->clientWidth() != l.scrollWidth()) 621 ts << " scrollWidth " << l.scrollWidth(); 622 if (l.renderBox() && l.renderBox()->clientHeight() != l.scrollHeight()) 623 ts << " scrollHeight " << l.scrollHeight(); 624 } 625 626 if (paintPhase == LayerPaintPhaseBackground) 627 ts << " layerType: background only"; 628 else if (paintPhase == LayerPaintPhaseForeground) 629 ts << " layerType: foreground only"; 630 631 #if USE(ACCELERATED_COMPOSITING) 632 if (behavior & RenderAsTextShowCompositedLayers) { 633 if (l.isComposited()) 634 ts << " (composited, bounds " << l.backing()->compositedBounds() << ")"; 635 } 636 #else 637 UNUSED_PARAM(behavior); 638 #endif 639 640 ts << "\n"; 641 642 if (paintPhase != LayerPaintPhaseBackground) 643 write(ts, *l.renderer(), indent + 1, behavior); 644 } 645 646 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l, 647 const IntRect& paintRect, int indent, RenderAsTextBehavior behavior) 648 { 649 // FIXME: Apply overflow to the root layer to not break every test. Complete hack. Sigh. 650 IntRect paintDirtyRect(paintRect); 651 if (rootLayer == l) { 652 paintDirtyRect.setWidth(max(paintDirtyRect.width(), rootLayer->renderBox()->maxXLayoutOverflow())); 653 paintDirtyRect.setHeight(max(paintDirtyRect.height(), rootLayer->renderBox()->maxYLayoutOverflow())); 654 l->setWidth(max(l->width(), l->renderBox()->maxXLayoutOverflow())); 655 l->setHeight(max(l->height(), l->renderBox()->maxYLayoutOverflow())); 656 } 657 658 // Calculate the clip rects we should use. 659 IntRect layerBounds, damageRect, clipRectToApply, outlineRect; 660 l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, true); 661 662 // Ensure our lists are up-to-date. 663 l->updateZOrderLists(); 664 l->updateNormalFlowList(); 665 666 bool shouldPaint = (behavior & RenderAsTextShowAllLayers) ? true : l->intersectsDamageRect(layerBounds, damageRect, rootLayer); 667 Vector<RenderLayer*>* negList = l->negZOrderList(); 668 bool paintsBackgroundSeparately = negList && negList->size() > 0; 669 if (shouldPaint && paintsBackgroundSeparately) 670 write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, LayerPaintPhaseBackground, indent, behavior); 671 672 if (negList) { 673 int currIndent = indent; 674 if (behavior & RenderAsTextShowLayerNesting) { 675 writeIndent(ts, indent); 676 ts << " negative z-order list(" << negList->size() << ")\n"; 677 ++currIndent; 678 } 679 for (unsigned i = 0; i != negList->size(); ++i) 680 writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, currIndent, behavior); 681 } 682 683 if (shouldPaint) 684 write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, paintsBackgroundSeparately ? LayerPaintPhaseForeground : LayerPaintPhaseAll, indent, behavior); 685 686 if (Vector<RenderLayer*>* normalFlowList = l->normalFlowList()) { 687 int currIndent = indent; 688 if (behavior & RenderAsTextShowLayerNesting) { 689 writeIndent(ts, indent); 690 ts << " normal flow list(" << normalFlowList->size() << ")\n"; 691 ++currIndent; 692 } 693 for (unsigned i = 0; i != normalFlowList->size(); ++i) 694 writeLayers(ts, rootLayer, normalFlowList->at(i), paintDirtyRect, currIndent, behavior); 695 } 696 697 if (Vector<RenderLayer*>* posList = l->posZOrderList()) { 698 int currIndent = indent; 699 if (behavior & RenderAsTextShowLayerNesting) { 700 writeIndent(ts, indent); 701 ts << " positive z-order list(" << posList->size() << ")\n"; 702 ++currIndent; 703 } 704 for (unsigned i = 0; i != posList->size(); ++i) 705 writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, currIndent, behavior); 706 } 707 } 708 709 static String nodePosition(Node* node) 710 { 711 String result; 712 713 Element* body = node->document()->body(); 714 Node* parent; 715 for (Node* n = node; n; n = parent) { 716 parent = n->parentOrHostNode(); 717 if (n != node) 718 result += " of "; 719 if (parent) { 720 if (body && n == body) { 721 // We don't care what offset body may be in the document. 722 result += "body"; 723 break; 724 } 725 if (n->isShadowBoundary()) 726 result += "{" + getTagName(n) + "}"; 727 else 728 result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}"; 729 } else 730 result += "document"; 731 } 732 733 return result; 734 } 735 736 static void writeSelection(TextStream& ts, const RenderObject* o) 737 { 738 Node* n = o->node(); 739 if (!n || !n->isDocumentNode()) 740 return; 741 742 Document* doc = static_cast<Document*>(n); 743 Frame* frame = doc->frame(); 744 if (!frame) 745 return; 746 747 VisibleSelection selection = frame->selection()->selection(); 748 if (selection.isCaret()) { 749 ts << "caret: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()); 750 if (selection.affinity() == UPSTREAM) 751 ts << " (upstream affinity)"; 752 ts << "\n"; 753 } else if (selection.isRange()) 754 ts << "selection start: position " << selection.start().deprecatedEditingOffset() << " of " << nodePosition(selection.start().deprecatedNode()) << "\n" 755 << "selection end: position " << selection.end().deprecatedEditingOffset() << " of " << nodePosition(selection.end().deprecatedNode()) << "\n"; 756 } 757 758 String externalRepresentation(Frame* frame, RenderAsTextBehavior behavior) 759 { 760 PrintContext printContext(frame); 761 if (behavior & RenderAsTextPrintingMode) { 762 if (!frame->contentRenderer()) 763 return String(); 764 printContext.begin(frame->contentRenderer()->width()); 765 } 766 767 if (!(behavior & RenderAsTextDontUpdateLayout)) 768 frame->document()->updateLayout(); 769 770 RenderObject* o = frame->contentRenderer(); 771 if (!o) 772 return String(); 773 774 TextStream ts; 775 if (o->hasLayer()) { 776 RenderLayer* l = toRenderBox(o)->layer(); 777 writeLayers(ts, l, l, IntRect(l->x(), l->y(), l->width(), l->height()), 0, behavior); 778 writeSelection(ts, o); 779 } 780 return ts.release(); 781 } 782 783 static void writeCounterValuesFromChildren(TextStream& stream, RenderObject* parent, bool& isFirstCounter) 784 { 785 for (RenderObject* child = parent->firstChild(); child; child = child->nextSibling()) { 786 if (child->isCounter()) { 787 if (!isFirstCounter) 788 stream << " "; 789 isFirstCounter = false; 790 String str(toRenderText(child)->text()); 791 stream << str; 792 } 793 } 794 } 795 796 String counterValueForElement(Element* element) 797 { 798 // Make sure the element is not freed during the layout. 799 RefPtr<Element> elementRef(element); 800 element->document()->updateLayout(); 801 TextStream stream; 802 bool isFirstCounter = true; 803 // The counter renderers should be children of :before or :after pseudo-elements. 804 if (RenderObject* renderer = element->renderer()) { 805 if (RenderObject* pseudoElement = renderer->beforePseudoElementRenderer()) 806 writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter); 807 if (RenderObject* pseudoElement = renderer->afterPseudoElementRenderer()) 808 writeCounterValuesFromChildren(stream, pseudoElement, isFirstCounter); 809 } 810 return stream.release(); 811 } 812 813 String markerTextForListItem(Element* element) 814 { 815 // Make sure the element is not freed during the layout. 816 RefPtr<Element> elementRef(element); 817 element->document()->updateLayout(); 818 819 RenderObject* renderer = element->renderer(); 820 if (!renderer || !renderer->isListItem()) 821 return String(); 822 823 return toRenderListItem(renderer)->markerText(); 824 } 825 826 } // namespace WebCore 827