1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2000 Dirk Mueller (mueller (at) kde.org) 5 * (C) 2004 Allan Sandfeld Jensen (kde (at) carewolf.com) 6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. 7 * Copyright (C) 2009 Google Inc. All rights reserved. 8 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 9 * 10 * This library is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Library General Public 12 * License as published by the Free Software Foundation; either 13 * version 2 of the License, or (at your option) any later version. 14 * 15 * This library is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Library General Public License for more details. 19 * 20 * You should have received a copy of the GNU Library General Public License 21 * along with this library; see the file COPYING.LIB. If not, write to 22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 23 * Boston, MA 02110-1301, USA. 24 * 25 */ 26 27 #include "config.h" 28 #include "core/rendering/RenderObject.h" 29 30 #include "HTMLNames.h" 31 #include "RuntimeEnabledFeatures.h" 32 #include "core/accessibility/AXObjectCache.h" 33 #include "core/animation/ActiveAnimations.h" 34 #include "core/css/resolver/StyleResolver.h" 35 #include "core/editing/EditingBoundary.h" 36 #include "core/editing/FrameSelection.h" 37 #include "core/editing/htmlediting.h" 38 #include "core/fetch/ResourceLoader.h" 39 #include "core/html/HTMLAnchorElement.h" 40 #include "core/html/HTMLElement.h" 41 #include "core/html/HTMLHtmlElement.h" 42 #include "core/html/HTMLTableElement.h" 43 #include "core/page/AutoscrollController.h" 44 #include "core/page/EventHandler.h" 45 #include "core/frame/Frame.h" 46 #include "core/frame/FrameView.h" 47 #include "core/page/Page.h" 48 #include "core/frame/Settings.h" 49 #include "core/frame/UseCounter.h" 50 #include "core/frame/animation/AnimationController.h" 51 #include "core/rendering/CompositedLayerMapping.h" 52 #include "core/rendering/FlowThreadController.h" 53 #include "core/rendering/HitTestResult.h" 54 #include "core/rendering/LayoutRectRecorder.h" 55 #include "core/rendering/RenderCounter.h" 56 #include "core/rendering/RenderDeprecatedFlexibleBox.h" 57 #include "core/rendering/RenderFlexibleBox.h" 58 #include "core/rendering/RenderGeometryMap.h" 59 #include "core/rendering/RenderGrid.h" 60 #include "core/rendering/RenderImage.h" 61 #include "core/rendering/RenderImageResourceStyleImage.h" 62 #include "core/rendering/RenderInline.h" 63 #include "core/rendering/RenderLayer.h" 64 #include "core/rendering/RenderLayerCompositor.h" 65 #include "core/rendering/RenderListItem.h" 66 #include "core/rendering/RenderMarquee.h" 67 #include "core/rendering/RenderMultiColumnBlock.h" 68 #include "core/rendering/RenderNamedFlowThread.h" 69 #include "core/rendering/RenderRegion.h" 70 #include "core/rendering/RenderRuby.h" 71 #include "core/rendering/RenderRubyText.h" 72 #include "core/rendering/RenderScrollbarPart.h" 73 #include "core/rendering/RenderTableCaption.h" 74 #include "core/rendering/RenderTableCell.h" 75 #include "core/rendering/RenderTableCol.h" 76 #include "core/rendering/RenderTableRow.h" 77 #include "core/rendering/RenderTheme.h" 78 #include "core/rendering/RenderView.h" 79 #include "core/rendering/style/ContentData.h" 80 #include "core/rendering/style/CursorList.h" 81 #include "core/rendering/style/ShadowList.h" 82 #include "core/rendering/svg/SVGRenderSupport.h" 83 #include "platform/Partitions.h" 84 #include "platform/geometry/TransformState.h" 85 #include "platform/graphics/GraphicsContext.h" 86 #include "wtf/RefCountedLeakCounter.h" 87 #include "wtf/text/StringBuilder.h" 88 #include <algorithm> 89 #ifndef NDEBUG 90 #include <stdio.h> 91 #endif 92 93 using namespace std; 94 95 namespace WebCore { 96 97 using namespace HTMLNames; 98 99 #ifndef NDEBUG 100 101 RenderObject::SetLayoutNeededForbiddenScope::SetLayoutNeededForbiddenScope(RenderObject* renderObject) 102 : m_renderObject(renderObject) 103 , m_preexistingForbidden(m_renderObject->isSetNeedsLayoutForbidden()) 104 { 105 m_renderObject->setNeedsLayoutIsForbidden(true); 106 } 107 108 RenderObject::SetLayoutNeededForbiddenScope::~SetLayoutNeededForbiddenScope() 109 { 110 m_renderObject->setNeedsLayoutIsForbidden(m_preexistingForbidden); 111 } 112 #endif 113 114 struct SameSizeAsRenderObject { 115 virtual ~SameSizeAsRenderObject() { } // Allocate vtable pointer. 116 void* pointers[5]; 117 #ifndef NDEBUG 118 unsigned m_debugBitfields : 2; 119 #endif 120 unsigned m_bitfields; 121 unsigned m_bitfields2; 122 LayoutRect rects[2]; // Stores the old/new repaint rects. 123 }; 124 125 COMPILE_ASSERT(sizeof(RenderObject) == sizeof(SameSizeAsRenderObject), RenderObject_should_stay_small); 126 127 bool RenderObject::s_affectsParentBlock = false; 128 129 void* RenderObject::operator new(size_t sz) 130 { 131 ASSERT(isMainThread()); 132 return partitionAlloc(Partitions::getRenderingPartition(), sz); 133 } 134 135 void RenderObject::operator delete(void* ptr) 136 { 137 ASSERT(isMainThread()); 138 partitionFree(ptr); 139 } 140 141 RenderObject* RenderObject::createObject(Element* element, RenderStyle* style) 142 { 143 Document& doc = element->document(); 144 145 // Minimal support for content properties replacing an entire element. 146 // Works only if we have exactly one piece of content and it's a URL. 147 // Otherwise acts as if we didn't support this feature. 148 const ContentData* contentData = style->contentData(); 149 if (contentData && !contentData->next() && contentData->isImage() && !element->isPseudoElement()) { 150 RenderImage* image = new RenderImage(element); 151 // RenderImageResourceStyleImage requires a style being present on the image but we don't want to 152 // trigger a style change now as the node is not fully attached. Moving this code to style change 153 // doesn't make sense as it should be run once at renderer creation. 154 image->setStyleInternal(style); 155 if (const StyleImage* styleImage = static_cast<const ImageContentData*>(contentData)->image()) { 156 image->setImageResource(RenderImageResourceStyleImage::create(const_cast<StyleImage*>(styleImage))); 157 image->setIsGeneratedContent(); 158 } else 159 image->setImageResource(RenderImageResource::create()); 160 image->setStyleInternal(0); 161 return image; 162 } 163 164 if (element->hasTagName(rubyTag)) { 165 if (style->display() == INLINE) 166 return new RenderRubyAsInline(element); 167 else if (style->display() == BLOCK) 168 return new RenderRubyAsBlock(element); 169 } 170 // treat <rt> as ruby text ONLY if it still has its default treatment of block 171 if (element->hasTagName(rtTag) && style->display() == BLOCK) 172 return new RenderRubyText(element); 173 174 switch (style->display()) { 175 case NONE: 176 return 0; 177 case INLINE: 178 return new RenderInline(element); 179 case BLOCK: 180 case INLINE_BLOCK: 181 if ((!style->hasAutoColumnCount() || !style->hasAutoColumnWidth()) && doc.regionBasedColumnsEnabled()) 182 return new RenderMultiColumnBlock(element); 183 return new RenderBlockFlow(element); 184 case LIST_ITEM: 185 return new RenderListItem(element); 186 case TABLE: 187 case INLINE_TABLE: 188 return new RenderTable(element); 189 case TABLE_ROW_GROUP: 190 case TABLE_HEADER_GROUP: 191 case TABLE_FOOTER_GROUP: 192 return new RenderTableSection(element); 193 case TABLE_ROW: 194 return new RenderTableRow(element); 195 case TABLE_COLUMN_GROUP: 196 case TABLE_COLUMN: 197 return new RenderTableCol(element); 198 case TABLE_CELL: 199 return new RenderTableCell(element); 200 case TABLE_CAPTION: 201 return new RenderTableCaption(element); 202 case BOX: 203 case INLINE_BOX: 204 return new RenderDeprecatedFlexibleBox(element); 205 case FLEX: 206 case INLINE_FLEX: 207 return new RenderFlexibleBox(element); 208 case GRID: 209 case INLINE_GRID: 210 return new RenderGrid(element); 211 } 212 213 return 0; 214 } 215 216 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, renderObjectCounter, ("RenderObject")); 217 218 RenderObject::RenderObject(Node* node) 219 : ImageResourceClient() 220 , m_style(0) 221 , m_node(node) 222 , m_parent(0) 223 , m_previous(0) 224 , m_next(0) 225 #ifndef NDEBUG 226 , m_hasAXObject(false) 227 , m_setNeedsLayoutForbidden(false) 228 #endif 229 , m_bitfields(node) 230 { 231 #ifndef NDEBUG 232 renderObjectCounter.increment(); 233 #endif 234 } 235 236 RenderObject::~RenderObject() 237 { 238 #ifndef NDEBUG 239 ASSERT(!m_hasAXObject); 240 renderObjectCounter.decrement(); 241 #endif 242 } 243 244 String RenderObject::debugName() const 245 { 246 StringBuilder name; 247 name.append(renderName()); 248 249 if (Node* node = this->node()) { 250 name.append(' '); 251 name.append(node->debugName()); 252 } 253 254 return name.toString(); 255 } 256 257 bool RenderObject::isDescendantOf(const RenderObject* obj) const 258 { 259 for (const RenderObject* r = this; r; r = r->m_parent) { 260 if (r == obj) 261 return true; 262 } 263 return false; 264 } 265 266 bool RenderObject::isBody() const 267 { 268 return node() && node()->hasTagName(bodyTag); 269 } 270 271 bool RenderObject::isHR() const 272 { 273 return node() && node()->hasTagName(hrTag); 274 } 275 276 bool RenderObject::isLegend() const 277 { 278 return node() && node()->hasTagName(legendTag); 279 } 280 281 void RenderObject::setFlowThreadStateIncludingDescendants(FlowThreadState state) 282 { 283 setFlowThreadState(state); 284 285 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 286 // If the child is a fragmentation context it already updated the descendants flag accordingly. 287 if (child->isRenderFlowThread()) 288 continue; 289 ASSERT(state != child->flowThreadState()); 290 child->setFlowThreadStateIncludingDescendants(state); 291 } 292 } 293 294 bool RenderObject::requiresAnonymousTableWrappers(const RenderObject* newChild) const 295 { 296 // Check should agree with: 297 // CSS 2.1 Tables: 17.2.1 Anonymous table objects 298 // http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes 299 if (newChild->isRenderTableCol()) { 300 const RenderTableCol* newTableColumn = toRenderTableCol(newChild); 301 bool isColumnInColumnGroup = newTableColumn->isTableColumn() && isRenderTableCol(); 302 return !isTable() && !isColumnInColumnGroup; 303 } else if (newChild->isTableCaption()) 304 return !isTable(); 305 else if (newChild->isTableSection()) 306 return !isTable(); 307 else if (newChild->isTableRow()) 308 return !isTableSection(); 309 else if (newChild->isTableCell()) 310 return !isTableRow(); 311 return false; 312 } 313 314 void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) 315 { 316 RenderObjectChildList* children = virtualChildren(); 317 ASSERT(children); 318 if (!children) 319 return; 320 321 if (requiresAnonymousTableWrappers(newChild)) { 322 // Generate an anonymous table or reuse existing one from previous child 323 // Per: 17.2.1 Anonymous table objects 3. Generate missing parents 324 // http://www.w3.org/TR/CSS21/tables.html#anonymous-boxes 325 RenderTable* table; 326 RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : children->lastChild(); 327 if (afterChild && afterChild->isAnonymous() && afterChild->isTable() && !afterChild->isBeforeContent()) 328 table = toRenderTable(afterChild); 329 else { 330 table = RenderTable::createAnonymousWithParentRenderer(this); 331 addChild(table, beforeChild); 332 } 333 table->addChild(newChild); 334 } else 335 children->insertChildNode(this, newChild, beforeChild); 336 337 if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) 338 toRenderText(newChild)->transformText(); 339 340 // SVG creates renderers for <g display="none">, as SVG requires children of hidden 341 // <g>s to have renderers - at least that's how our implementation works. Consider: 342 // <g display="none"><foreignObject><body style="position: relative">FOO... 343 // - requiresLayer() would return true for the <body>, creating a new RenderLayer 344 // - when the document is painted, both layers are painted. The <body> layer doesn't 345 // know that it's inside a "hidden SVG subtree", and thus paints, even if it shouldn't. 346 // To avoid the problem alltogether, detect early if we're inside a hidden SVG subtree 347 // and stop creating layers at all for these cases - they're not used anyways. 348 if (newChild->hasLayer() && !layerCreationAllowedForSubtree()) 349 toRenderLayerModelObject(newChild)->layer()->removeOnlyThisLayer(); 350 } 351 352 void RenderObject::removeChild(RenderObject* oldChild) 353 { 354 RenderObjectChildList* children = virtualChildren(); 355 ASSERT(children); 356 if (!children) 357 return; 358 359 children->removeChildNode(this, oldChild); 360 } 361 362 RenderObject* RenderObject::nextInPreOrder() const 363 { 364 if (RenderObject* o = firstChild()) 365 return o; 366 367 return nextInPreOrderAfterChildren(); 368 } 369 370 RenderObject* RenderObject::nextInPreOrderAfterChildren() const 371 { 372 RenderObject* o; 373 if (!(o = nextSibling())) { 374 o = parent(); 375 while (o && !o->nextSibling()) 376 o = o->parent(); 377 if (o) 378 o = o->nextSibling(); 379 } 380 381 return o; 382 } 383 384 RenderObject* RenderObject::nextInPreOrder(const RenderObject* stayWithin) const 385 { 386 if (RenderObject* o = firstChild()) 387 return o; 388 389 return nextInPreOrderAfterChildren(stayWithin); 390 } 391 392 RenderObject* RenderObject::nextInPreOrderAfterChildren(const RenderObject* stayWithin) const 393 { 394 if (this == stayWithin) 395 return 0; 396 397 const RenderObject* current = this; 398 RenderObject* next; 399 while (!(next = current->nextSibling())) { 400 current = current->parent(); 401 if (!current || current == stayWithin) 402 return 0; 403 } 404 return next; 405 } 406 407 RenderObject* RenderObject::previousInPreOrder() const 408 { 409 if (RenderObject* o = previousSibling()) { 410 while (o->lastChild()) 411 o = o->lastChild(); 412 return o; 413 } 414 415 return parent(); 416 } 417 418 RenderObject* RenderObject::previousInPreOrder(const RenderObject* stayWithin) const 419 { 420 if (this == stayWithin) 421 return 0; 422 423 return previousInPreOrder(); 424 } 425 426 RenderObject* RenderObject::childAt(unsigned index) const 427 { 428 RenderObject* child = firstChild(); 429 for (unsigned i = 0; child && i < index; i++) 430 child = child->nextSibling(); 431 return child; 432 } 433 434 RenderObject* RenderObject::lastLeafChild() const 435 { 436 RenderObject* r = lastChild(); 437 while (r) { 438 RenderObject* n = 0; 439 n = r->lastChild(); 440 if (!n) 441 break; 442 r = n; 443 } 444 return r; 445 } 446 447 static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& newObject, 448 RenderLayer*& beforeChild) 449 { 450 if (obj->hasLayer()) { 451 if (!beforeChild && newObject) { 452 // We need to figure out the layer that follows newObject. We only do 453 // this the first time we find a child layer, and then we update the 454 // pointer values for newObject and beforeChild used by everyone else. 455 beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject); 456 newObject = 0; 457 } 458 parentLayer->addChild(toRenderLayerModelObject(obj)->layer(), beforeChild); 459 return; 460 } 461 462 for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling()) 463 addLayers(curr, parentLayer, newObject, beforeChild); 464 } 465 466 void RenderObject::addLayers(RenderLayer* parentLayer) 467 { 468 if (!parentLayer) 469 return; 470 471 RenderObject* object = this; 472 RenderLayer* beforeChild = 0; 473 WebCore::addLayers(this, parentLayer, object, beforeChild); 474 } 475 476 void RenderObject::removeLayers(RenderLayer* parentLayer) 477 { 478 if (!parentLayer) 479 return; 480 481 if (hasLayer()) { 482 parentLayer->removeChild(toRenderLayerModelObject(this)->layer()); 483 return; 484 } 485 486 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) 487 curr->removeLayers(parentLayer); 488 } 489 490 void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) 491 { 492 if (!newParent) 493 return; 494 495 if (hasLayer()) { 496 RenderLayer* layer = toRenderLayerModelObject(this)->layer(); 497 ASSERT(oldParent == layer->parent()); 498 if (oldParent) 499 oldParent->removeChild(layer); 500 newParent->addChild(layer); 501 return; 502 } 503 504 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) 505 curr->moveLayers(oldParent, newParent); 506 } 507 508 RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint, 509 bool checkParent) 510 { 511 // Error check the parent layer passed in. If it's null, we can't find anything. 512 if (!parentLayer) 513 return 0; 514 515 // Step 1: If our layer is a child of the desired parent, then return our layer. 516 RenderLayer* ourLayer = hasLayer() ? toRenderLayerModelObject(this)->layer() : 0; 517 if (ourLayer && ourLayer->parent() == parentLayer) 518 return ourLayer; 519 520 // Step 2: If we don't have a layer, or our layer is the desired parent, then descend 521 // into our siblings trying to find the next layer whose parent is the desired parent. 522 if (!ourLayer || ourLayer == parentLayer) { 523 for (RenderObject* curr = startPoint ? startPoint->nextSibling() : firstChild(); 524 curr; curr = curr->nextSibling()) { 525 RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, false); 526 if (nextLayer) 527 return nextLayer; 528 } 529 } 530 531 // Step 3: If our layer is the desired parent layer, then we're finished. We didn't 532 // find anything. 533 if (parentLayer == ourLayer) 534 return 0; 535 536 // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that 537 // follow us to see if we can locate a layer. 538 if (checkParent && parent()) 539 return parent()->findNextLayer(parentLayer, this, true); 540 541 return 0; 542 } 543 544 RenderLayer* RenderObject::enclosingLayer() const 545 { 546 const RenderObject* curr = this; 547 while (curr) { 548 RenderLayer* layer = curr->hasLayer() ? toRenderLayerModelObject(curr)->layer() : 0; 549 if (layer) 550 return layer; 551 curr = curr->parent(); 552 } 553 return 0; 554 } 555 556 bool RenderObject::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) 557 { 558 RenderBox* enclosingBox = this->enclosingBox(); 559 if (!enclosingBox) 560 return false; 561 562 enclosingBox->scrollRectToVisible(rect, alignX, alignY); 563 return true; 564 } 565 566 RenderBox* RenderObject::enclosingBox() const 567 { 568 RenderObject* curr = const_cast<RenderObject*>(this); 569 while (curr) { 570 if (curr->isBox()) 571 return toRenderBox(curr); 572 curr = curr->parent(); 573 } 574 575 ASSERT_NOT_REACHED(); 576 return 0; 577 } 578 579 RenderBoxModelObject* RenderObject::enclosingBoxModelObject() const 580 { 581 RenderObject* curr = const_cast<RenderObject*>(this); 582 while (curr) { 583 if (curr->isBoxModelObject()) 584 return toRenderBoxModelObject(curr); 585 curr = curr->parent(); 586 } 587 588 ASSERT_NOT_REACHED(); 589 return 0; 590 } 591 592 RenderBox* RenderObject::enclosingScrollableBox() const 593 { 594 for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { 595 if (!ancestor->isBox()) 596 continue; 597 598 RenderBox* ancestorBox = toRenderBox(ancestor); 599 if (ancestorBox->canBeScrolledAndHasScrollableArea()) 600 return ancestorBox; 601 } 602 603 return 0; 604 } 605 606 RenderFlowThread* RenderObject::locateFlowThreadContainingBlock() const 607 { 608 ASSERT(flowThreadState() != NotInsideFlowThread); 609 610 // See if we have the thread cached because we're in the middle of layout. 611 RenderFlowThread* flowThread = view()->flowThreadController()->currentRenderFlowThread(); 612 if (flowThread) 613 return flowThread; 614 615 // Not in the middle of layout so have to find the thread the slow way. 616 RenderObject* curr = const_cast<RenderObject*>(this); 617 while (curr) { 618 if (curr->isRenderFlowThread()) 619 return toRenderFlowThread(curr); 620 curr = curr->containingBlock(); 621 } 622 return 0; 623 } 624 625 RenderNamedFlowThread* RenderObject::renderNamedFlowThreadWrapper() const 626 { 627 RenderObject* object = const_cast<RenderObject*>(this); 628 while (object && object->isAnonymousBlock() && !object->isRenderNamedFlowThread()) 629 object = object->parent(); 630 631 return object && object->isRenderNamedFlowThread() ? toRenderNamedFlowThread(object) : 0; 632 } 633 634 RenderBlock* RenderObject::firstLineBlock() const 635 { 636 return 0; 637 } 638 639 static inline bool objectIsRelayoutBoundary(const RenderObject* object) 640 { 641 // FIXME: In future it may be possible to broaden these conditions in order to improve performance. 642 if (object->isTextControl()) 643 return true; 644 645 if (object->isSVGRoot()) 646 return true; 647 648 if (!object->hasOverflowClip()) 649 return false; 650 651 if (object->style()->width().isIntrinsicOrAuto() || object->style()->height().isIntrinsicOrAuto() || object->style()->height().isPercent()) 652 return false; 653 654 // Table parts can't be relayout roots since the table is responsible for layouting all the parts. 655 if (object->isTablePart()) 656 return false; 657 658 return true; 659 } 660 661 void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderObject* newRoot, SubtreeLayoutScope* layouter) 662 { 663 ASSERT(!scheduleRelayout || !newRoot); 664 ASSERT(!isSetNeedsLayoutForbidden()); 665 ASSERT(!layouter || this != layouter->root()); 666 667 RenderObject* object = container(); 668 RenderObject* last = this; 669 670 bool simplifiedNormalFlowLayout = needsSimplifiedNormalFlowLayout() && !selfNeedsLayout() && !normalChildNeedsLayout(); 671 672 while (object) { 673 if (object->selfNeedsLayout()) 674 return; 675 676 // Don't mark the outermost object of an unrooted subtree. That object will be 677 // marked when the subtree is added to the document. 678 RenderObject* container = object->container(); 679 if (!container && !object->isRenderView()) 680 return; 681 if (!last->isText() && last->style()->hasOutOfFlowPosition()) { 682 bool willSkipRelativelyPositionedInlines = !object->isRenderBlock() || object->isAnonymousBlock(); 683 // Skip relatively positioned inlines and anonymous blocks to get to the enclosing RenderBlock. 684 while (object && (!object->isRenderBlock() || object->isAnonymousBlock())) 685 object = object->container(); 686 if (!object || object->posChildNeedsLayout()) 687 return; 688 if (willSkipRelativelyPositionedInlines) 689 container = object->container(); 690 object->setPosChildNeedsLayout(true); 691 simplifiedNormalFlowLayout = true; 692 ASSERT(!object->isSetNeedsLayoutForbidden()); 693 } else if (simplifiedNormalFlowLayout) { 694 if (object->needsSimplifiedNormalFlowLayout()) 695 return; 696 object->setNeedsSimplifiedNormalFlowLayout(true); 697 ASSERT(!object->isSetNeedsLayoutForbidden()); 698 } else { 699 if (object->normalChildNeedsLayout()) 700 return; 701 object->setNormalChildNeedsLayout(true); 702 ASSERT(!object->isSetNeedsLayoutForbidden()); 703 } 704 705 if (layouter) { 706 layouter->addRendererToLayout(object); 707 if (object == layouter->root()) 708 return; 709 } 710 711 if (object == newRoot) 712 return; 713 714 last = object; 715 if (scheduleRelayout && objectIsRelayoutBoundary(last)) 716 break; 717 object = container; 718 } 719 720 if (scheduleRelayout) 721 last->scheduleRelayout(); 722 } 723 724 #ifndef NDEBUG 725 void RenderObject::checkBlockPositionedObjectsNeedLayout() 726 { 727 ASSERT(!needsLayout()); 728 729 if (isRenderBlock()) 730 toRenderBlock(this)->checkPositionedObjectsNeedLayout(); 731 } 732 733 void RenderObject::checkNotInPartialLayout() 734 { 735 // During partial layout, setNeedsLayout(true or false) should not be called. 736 ASSERT(!frameView()->partialLayout().isStopping()); 737 } 738 #endif 739 740 void RenderObject::setPreferredLogicalWidthsDirty(MarkingBehavior markParents) 741 { 742 bool alreadyDirty = preferredLogicalWidthsDirty(); 743 m_bitfields.setPreferredLogicalWidthsDirty(true); 744 if (!alreadyDirty && markParents == MarkContainingBlockChain && (isText() || !style()->hasOutOfFlowPosition())) 745 invalidateContainerPreferredLogicalWidths(); 746 } 747 748 void RenderObject::clearPreferredLogicalWidthsDirty() 749 { 750 m_bitfields.setPreferredLogicalWidthsDirty(false); 751 } 752 753 void RenderObject::invalidateContainerPreferredLogicalWidths() 754 { 755 // In order to avoid pathological behavior when inlines are deeply nested, we do include them 756 // in the chain that we mark dirty (even though they're kind of irrelevant). 757 RenderObject* o = isTableCell() ? containingBlock() : container(); 758 while (o && !o->preferredLogicalWidthsDirty()) { 759 // Don't invalidate the outermost object of an unrooted subtree. That object will be 760 // invalidated when the subtree is added to the document. 761 RenderObject* container = o->isTableCell() ? o->containingBlock() : o->container(); 762 if (!container && !o->isRenderView()) 763 break; 764 765 o->m_bitfields.setPreferredLogicalWidthsDirty(true); 766 if (o->style()->hasOutOfFlowPosition()) 767 // A positioned object has no effect on the min/max width of its containing block ever. 768 // We can optimize this case and not go up any further. 769 break; 770 o = container; 771 } 772 } 773 774 void RenderObject::setLayerNeedsFullRepaint() 775 { 776 ASSERT(hasLayer()); 777 toRenderLayerModelObject(this)->layer()->repainter().setRepaintStatus(NeedsFullRepaint); 778 } 779 780 void RenderObject::setLayerNeedsFullRepaintForPositionedMovementLayout() 781 { 782 ASSERT(hasLayer()); 783 toRenderLayerModelObject(this)->layer()->repainter().setRepaintStatus(NeedsFullRepaintForPositionedMovementLayout); 784 } 785 786 RenderBlock* RenderObject::containerForFixedPosition(const RenderLayerModelObject* repaintContainer, bool* repaintContainerSkipped) const 787 { 788 ASSERT(!repaintContainerSkipped || !*repaintContainerSkipped); 789 ASSERT(!isText()); 790 ASSERT(style()->position() == FixedPosition); 791 792 RenderObject* ancestor = parent(); 793 for (; ancestor && !ancestor->canContainFixedPositionObjects(); ancestor = ancestor->parent()) { 794 if (repaintContainerSkipped && ancestor == repaintContainer) 795 *repaintContainerSkipped = true; 796 } 797 798 ASSERT(!ancestor || !ancestor->isAnonymousBlock()); 799 return toRenderBlock(ancestor); 800 } 801 802 RenderBlock* RenderObject::containingBlock() const 803 { 804 RenderObject* o = parent(); 805 if (!o && isRenderScrollbarPart()) 806 o = toRenderScrollbarPart(this)->rendererOwningScrollbar(); 807 if (!isText() && m_style->position() == FixedPosition) { 808 return containerForFixedPosition(); 809 } else if (!isText() && m_style->position() == AbsolutePosition) { 810 while (o) { 811 // For relpositioned inlines, we return the nearest non-anonymous enclosing block. We don't try 812 // to return the inline itself. This allows us to avoid having a positioned objects 813 // list in all RenderInlines and lets us return a strongly-typed RenderBlock* result 814 // from this method. The container() method can actually be used to obtain the 815 // inline directly. 816 if (o->style()->position() != StaticPosition && (!o->isInline() || o->isReplaced())) 817 break; 818 819 if (o->canContainAbsolutePositionObjects()) 820 break; 821 822 if (o->style()->hasInFlowPosition() && o->isInline() && !o->isReplaced()) { 823 o = o->containingBlock(); 824 break; 825 } 826 827 o = o->parent(); 828 } 829 830 while (o && o->isAnonymousBlock()) 831 o = o->containingBlock(); 832 } else { 833 while (o && ((o->isInline() && !o->isReplaced()) || !o->isRenderBlock())) 834 o = o->parent(); 835 } 836 837 if (!o || !o->isRenderBlock()) 838 return 0; // This can still happen in case of an orphaned tree 839 840 return toRenderBlock(o); 841 } 842 843 static bool mustRepaintFillLayers(const RenderObject* renderer, const FillLayer* layer) 844 { 845 // Nobody will use multiple layers without wanting fancy positioning. 846 if (layer->next()) 847 return true; 848 849 // Make sure we have a valid image. 850 StyleImage* img = layer->image(); 851 if (!img || !img->canRender(renderer, renderer->style()->effectiveZoom())) 852 return false; 853 854 if (!layer->xPosition().isZero() || !layer->yPosition().isZero()) 855 return true; 856 857 EFillSizeType sizeType = layer->sizeType(); 858 859 if (sizeType == Contain || sizeType == Cover) 860 return true; 861 862 if (sizeType == SizeLength) { 863 if (layer->sizeLength().width().isPercent() || layer->sizeLength().height().isPercent()) 864 return true; 865 if (img->isGeneratedImage() && (layer->sizeLength().width().isAuto() || layer->sizeLength().height().isAuto())) 866 return true; 867 } else if (img->usesImageContainerSize()) { 868 return true; 869 } 870 871 return false; 872 } 873 874 bool RenderObject::borderImageIsLoadedAndCanBeRendered() const 875 { 876 ASSERT(style()->hasBorder()); 877 878 StyleImage* borderImage = style()->borderImage().image(); 879 return borderImage && borderImage->canRender(this, style()->effectiveZoom()) && borderImage->isLoaded(); 880 } 881 882 bool RenderObject::mustRepaintBackgroundOrBorder() const 883 { 884 if (hasMask() && mustRepaintFillLayers(this, style()->maskLayers())) 885 return true; 886 887 // If we don't have a background/border/mask, then nothing to do. 888 if (!hasBoxDecorations()) 889 return false; 890 891 if (mustRepaintFillLayers(this, style()->backgroundLayers())) 892 return true; 893 894 // Our fill layers are ok. Let's check border. 895 if (style()->hasBorder() && borderImageIsLoadedAndCanBeRendered()) 896 return true; 897 898 return false; 899 } 900 901 void RenderObject::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, 902 BoxSide side, Color color, EBorderStyle style, 903 int adjacentWidth1, int adjacentWidth2, bool antialias) 904 { 905 int thickness; 906 int length; 907 if (side == BSTop || side == BSBottom) { 908 thickness = y2 - y1; 909 length = x2 - x1; 910 } else { 911 thickness = x2 - x1; 912 length = y2 - y1; 913 } 914 915 // FIXME: We really would like this check to be an ASSERT as we don't want to draw empty borders. However 916 // nothing guarantees that the following recursive calls to drawLineForBoxSide will have non-null dimensions. 917 if (!thickness || !length) 918 return; 919 920 if (style == DOUBLE && thickness < 3) 921 style = SOLID; 922 923 switch (style) { 924 case BNONE: 925 case BHIDDEN: 926 return; 927 case DOTTED: 928 case DASHED: 929 drawDashedOrDottedBoxSide(graphicsContext, x1, y1, x2, y2, side, 930 color, thickness, style, antialias); 931 break; 932 case DOUBLE: 933 drawDoubleBoxSide(graphicsContext, x1, y1, x2, y2, length, side, color, 934 thickness, adjacentWidth1, adjacentWidth2, antialias); 935 break; 936 case RIDGE: 937 case GROOVE: 938 drawRidgeOrGrooveBoxSide(graphicsContext, x1, y1, x2, y2, side, color, 939 style, adjacentWidth1, adjacentWidth2, antialias); 940 break; 941 case INSET: 942 // FIXME: Maybe we should lighten the colors on one side like Firefox. 943 // https://bugs.webkit.org/show_bug.cgi?id=58608 944 if (side == BSTop || side == BSLeft) 945 color = color.dark(); 946 // fall through 947 case OUTSET: 948 if (style == OUTSET && (side == BSBottom || side == BSRight)) 949 color = color.dark(); 950 // fall through 951 case SOLID: 952 drawSolidBoxSide(graphicsContext, x1, y1, x2, y2, side, color, adjacentWidth1, adjacentWidth2, antialias); 953 break; 954 } 955 } 956 957 void RenderObject::drawDashedOrDottedBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, 958 BoxSide side, Color color, int thickness, EBorderStyle style, bool antialias) 959 { 960 if (thickness <= 0) 961 return; 962 963 bool wasAntialiased = graphicsContext->shouldAntialias(); 964 StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); 965 graphicsContext->setShouldAntialias(antialias); 966 graphicsContext->setStrokeColor(color); 967 graphicsContext->setStrokeThickness(thickness); 968 graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke); 969 970 switch (side) { 971 case BSBottom: 972 case BSTop: 973 graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2)); 974 break; 975 case BSRight: 976 case BSLeft: 977 graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2)); 978 break; 979 } 980 graphicsContext->setShouldAntialias(wasAntialiased); 981 graphicsContext->setStrokeStyle(oldStrokeStyle); 982 } 983 984 void RenderObject::drawDoubleBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, 985 int length, BoxSide side, Color color, int thickness, int adjacentWidth1, int adjacentWidth2, bool antialias) 986 { 987 int thirdOfThickness = (thickness + 1) / 3; 988 ASSERT(thirdOfThickness); 989 990 if (!adjacentWidth1 && !adjacentWidth2) { 991 StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); 992 graphicsContext->setStrokeStyle(NoStroke); 993 graphicsContext->setFillColor(color); 994 995 bool wasAntialiased = graphicsContext->shouldAntialias(); 996 graphicsContext->setShouldAntialias(antialias); 997 998 switch (side) { 999 case BSTop: 1000 case BSBottom: 1001 graphicsContext->drawRect(IntRect(x1, y1, length, thirdOfThickness)); 1002 graphicsContext->drawRect(IntRect(x1, y2 - thirdOfThickness, length, thirdOfThickness)); 1003 break; 1004 case BSLeft: 1005 case BSRight: 1006 // FIXME: Why do we offset the border by 1 in this case but not the other one? 1007 if (length > 1) { 1008 graphicsContext->drawRect(IntRect(x1, y1 + 1, thirdOfThickness, length - 1)); 1009 graphicsContext->drawRect(IntRect(x2 - thirdOfThickness, y1 + 1, thirdOfThickness, length - 1)); 1010 } 1011 break; 1012 } 1013 1014 graphicsContext->setShouldAntialias(wasAntialiased); 1015 graphicsContext->setStrokeStyle(oldStrokeStyle); 1016 return; 1017 } 1018 1019 int adjacent1BigThird = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 3; 1020 int adjacent2BigThird = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 3; 1021 1022 switch (side) { 1023 case BSTop: 1024 drawLineForBoxSide(graphicsContext, x1 + max((-adjacentWidth1 * 2 + 1) / 3, 0), 1025 y1, x2 - max((-adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness, 1026 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); 1027 drawLineForBoxSide(graphicsContext, x1 + max((adjacentWidth1 * 2 + 1) / 3, 0), 1028 y2 - thirdOfThickness, x2 - max((adjacentWidth2 * 2 + 1) / 3, 0), y2, 1029 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); 1030 break; 1031 case BSLeft: 1032 drawLineForBoxSide(graphicsContext, x1, y1 + max((-adjacentWidth1 * 2 + 1) / 3, 0), 1033 x1 + thirdOfThickness, y2 - max((-adjacentWidth2 * 2 + 1) / 3, 0), 1034 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); 1035 drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + max((adjacentWidth1 * 2 + 1) / 3, 0), 1036 x2, y2 - max((adjacentWidth2 * 2 + 1) / 3, 0), 1037 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); 1038 break; 1039 case BSBottom: 1040 drawLineForBoxSide(graphicsContext, x1 + max((adjacentWidth1 * 2 + 1) / 3, 0), 1041 y1, x2 - max((adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness, 1042 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); 1043 drawLineForBoxSide(graphicsContext, x1 + max((-adjacentWidth1 * 2 + 1) / 3, 0), 1044 y2 - thirdOfThickness, x2 - max((-adjacentWidth2 * 2 + 1) / 3, 0), y2, 1045 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); 1046 break; 1047 case BSRight: 1048 drawLineForBoxSide(graphicsContext, x1, y1 + max((adjacentWidth1 * 2 + 1) / 3, 0), 1049 x1 + thirdOfThickness, y2 - max((adjacentWidth2 * 2 + 1) / 3, 0), 1050 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); 1051 drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + max((-adjacentWidth1 * 2 + 1) / 3, 0), 1052 x2, y2 - max((-adjacentWidth2 * 2 + 1) / 3, 0), 1053 side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias); 1054 break; 1055 default: 1056 break; 1057 } 1058 } 1059 1060 void RenderObject::drawRidgeOrGrooveBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, 1061 BoxSide side, Color color, EBorderStyle style, int adjacentWidth1, int adjacentWidth2, bool antialias) 1062 { 1063 EBorderStyle s1; 1064 EBorderStyle s2; 1065 if (style == GROOVE) { 1066 s1 = INSET; 1067 s2 = OUTSET; 1068 } else { 1069 s1 = OUTSET; 1070 s2 = INSET; 1071 } 1072 1073 int adjacent1BigHalf = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 2; 1074 int adjacent2BigHalf = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 2; 1075 1076 switch (side) { 1077 case BSTop: 1078 drawLineForBoxSide(graphicsContext, x1 + max(-adjacentWidth1, 0) / 2, y1, x2 - max(-adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2, 1079 side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias); 1080 drawLineForBoxSide(graphicsContext, x1 + max(adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(adjacentWidth2 + 1, 0) / 2, y2, 1081 side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); 1082 break; 1083 case BSLeft: 1084 drawLineForBoxSide(graphicsContext, x1, y1 + max(-adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(-adjacentWidth2, 0) / 2, 1085 side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias); 1086 drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(adjacentWidth1 + 1, 0) / 2, x2, y2 - max(adjacentWidth2 + 1, 0) / 2, 1087 side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); 1088 break; 1089 case BSBottom: 1090 drawLineForBoxSide(graphicsContext, x1 + max(adjacentWidth1, 0) / 2, y1, x2 - max(adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2, 1091 side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias); 1092 drawLineForBoxSide(graphicsContext, x1 + max(-adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(-adjacentWidth2 + 1, 0) / 2, y2, 1093 side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); 1094 break; 1095 case BSRight: 1096 drawLineForBoxSide(graphicsContext, x1, y1 + max(adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(adjacentWidth2, 0) / 2, 1097 side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias); 1098 drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(-adjacentWidth1 + 1, 0) / 2, x2, y2 - max(-adjacentWidth2 + 1, 0) / 2, 1099 side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias); 1100 break; 1101 } 1102 } 1103 1104 void RenderObject::drawSolidBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, 1105 BoxSide side, Color color, int adjacentWidth1, int adjacentWidth2, bool antialias) 1106 { 1107 StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle(); 1108 graphicsContext->setStrokeStyle(NoStroke); 1109 graphicsContext->setFillColor(color); 1110 ASSERT(x2 >= x1); 1111 ASSERT(y2 >= y1); 1112 if (!adjacentWidth1 && !adjacentWidth2) { 1113 // Turn off antialiasing to match the behavior of drawConvexPolygon(); 1114 // this matters for rects in transformed contexts. 1115 bool wasAntialiased = graphicsContext->shouldAntialias(); 1116 graphicsContext->setShouldAntialias(antialias); 1117 graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1)); 1118 graphicsContext->setShouldAntialias(wasAntialiased); 1119 graphicsContext->setStrokeStyle(oldStrokeStyle); 1120 return; 1121 } 1122 FloatPoint quad[4]; 1123 switch (side) { 1124 case BSTop: 1125 quad[0] = FloatPoint(x1 + max(-adjacentWidth1, 0), y1); 1126 quad[1] = FloatPoint(x1 + max(adjacentWidth1, 0), y2); 1127 quad[2] = FloatPoint(x2 - max(adjacentWidth2, 0), y2); 1128 quad[3] = FloatPoint(x2 - max(-adjacentWidth2, 0), y1); 1129 break; 1130 case BSBottom: 1131 quad[0] = FloatPoint(x1 + max(adjacentWidth1, 0), y1); 1132 quad[1] = FloatPoint(x1 + max(-adjacentWidth1, 0), y2); 1133 quad[2] = FloatPoint(x2 - max(-adjacentWidth2, 0), y2); 1134 quad[3] = FloatPoint(x2 - max(adjacentWidth2, 0), y1); 1135 break; 1136 case BSLeft: 1137 quad[0] = FloatPoint(x1, y1 + max(-adjacentWidth1, 0)); 1138 quad[1] = FloatPoint(x1, y2 - max(-adjacentWidth2, 0)); 1139 quad[2] = FloatPoint(x2, y2 - max(adjacentWidth2, 0)); 1140 quad[3] = FloatPoint(x2, y1 + max(adjacentWidth1, 0)); 1141 break; 1142 case BSRight: 1143 quad[0] = FloatPoint(x1, y1 + max(adjacentWidth1, 0)); 1144 quad[1] = FloatPoint(x1, y2 - max(adjacentWidth2, 0)); 1145 quad[2] = FloatPoint(x2, y2 - max(-adjacentWidth2, 0)); 1146 quad[3] = FloatPoint(x2, y1 + max(-adjacentWidth1, 0)); 1147 break; 1148 } 1149 1150 graphicsContext->drawConvexPolygon(4, quad, antialias); 1151 graphicsContext->setStrokeStyle(oldStrokeStyle); 1152 } 1153 1154 void RenderObject::paintFocusRing(PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderStyle* style) 1155 { 1156 Vector<IntRect> focusRingRects; 1157 addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer()); 1158 if (style->outlineStyleIsAuto()) 1159 paintInfo.context->drawFocusRing(focusRingRects, style->outlineWidth(), style->outlineOffset(), resolveColor(style, CSSPropertyOutlineColor)); 1160 else 1161 addPDFURLRect(paintInfo.context, unionRect(focusRingRects)); 1162 } 1163 1164 void RenderObject::addPDFURLRect(GraphicsContext* context, const LayoutRect& rect) 1165 { 1166 if (rect.isEmpty()) 1167 return; 1168 Node* n = node(); 1169 if (!n || !n->isLink() || !n->isElementNode()) 1170 return; 1171 const AtomicString& href = toElement(n)->getAttribute(hrefAttr); 1172 if (href.isNull()) 1173 return; 1174 KURL url = n->document().completeURL(href); 1175 if (!url.isValid()) 1176 return; 1177 if (context->supportsURLFragments() && url.hasFragmentIdentifier() && equalIgnoringFragmentIdentifier(url, n->document().baseURL())) { 1178 String name = url.fragmentIdentifier(); 1179 if (document().findAnchor(name)) 1180 context->setURLFragmentForRect(name, pixelSnappedIntRect(rect)); 1181 return; 1182 } 1183 context->setURLForRect(url, pixelSnappedIntRect(rect)); 1184 } 1185 1186 void RenderObject::paintOutline(PaintInfo& paintInfo, const LayoutRect& paintRect) 1187 { 1188 if (!hasOutline()) 1189 return; 1190 1191 RenderStyle* styleToUse = style(); 1192 LayoutUnit outlineWidth = styleToUse->outlineWidth(); 1193 1194 int outlineOffset = styleToUse->outlineOffset(); 1195 1196 if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) { 1197 if (RenderTheme::theme().shouldDrawDefaultFocusRing(this)) { 1198 // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. 1199 paintFocusRing(paintInfo, paintRect.location(), styleToUse); 1200 } 1201 } 1202 1203 if (styleToUse->outlineStyleIsAuto() || styleToUse->outlineStyle() == BNONE) 1204 return; 1205 1206 IntRect inner = pixelSnappedIntRect(paintRect); 1207 inner.inflate(outlineOffset); 1208 1209 IntRect outer = pixelSnappedIntRect(inner); 1210 outer.inflate(outlineWidth); 1211 1212 // FIXME: This prevents outlines from painting inside the object. See bug 12042 1213 if (outer.isEmpty()) 1214 return; 1215 1216 EBorderStyle outlineStyle = styleToUse->outlineStyle(); 1217 Color outlineColor = resolveColor(styleToUse, CSSPropertyOutlineColor); 1218 1219 GraphicsContext* graphicsContext = paintInfo.context; 1220 bool useTransparencyLayer = outlineColor.hasAlpha(); 1221 if (useTransparencyLayer) { 1222 if (outlineStyle == SOLID) { 1223 Path path; 1224 path.addRect(outer); 1225 path.addRect(inner); 1226 graphicsContext->setFillRule(RULE_EVENODD); 1227 graphicsContext->setFillColor(outlineColor); 1228 graphicsContext->fillPath(path); 1229 return; 1230 } 1231 graphicsContext->beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255); 1232 outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue()); 1233 } 1234 1235 int leftOuter = outer.x(); 1236 int leftInner = inner.x(); 1237 int rightOuter = outer.maxX(); 1238 int rightInner = inner.maxX(); 1239 int topOuter = outer.y(); 1240 int topInner = inner.y(); 1241 int bottomOuter = outer.maxY(); 1242 int bottomInner = inner.maxY(); 1243 1244 drawLineForBoxSide(graphicsContext, leftOuter, topOuter, leftInner, bottomOuter, BSLeft, outlineColor, outlineStyle, outlineWidth, outlineWidth); 1245 drawLineForBoxSide(graphicsContext, leftOuter, topOuter, rightOuter, topInner, BSTop, outlineColor, outlineStyle, outlineWidth, outlineWidth); 1246 drawLineForBoxSide(graphicsContext, rightInner, topOuter, rightOuter, bottomOuter, BSRight, outlineColor, outlineStyle, outlineWidth, outlineWidth); 1247 drawLineForBoxSide(graphicsContext, leftOuter, bottomInner, rightOuter, bottomOuter, BSBottom, outlineColor, outlineStyle, outlineWidth, outlineWidth); 1248 1249 if (useTransparencyLayer) 1250 graphicsContext->endLayer(); 1251 } 1252 1253 IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) const 1254 { 1255 if (useTransforms) { 1256 Vector<FloatQuad> quads; 1257 absoluteQuads(quads); 1258 1259 size_t n = quads.size(); 1260 if (!n) 1261 return IntRect(); 1262 1263 IntRect result = quads[0].enclosingBoundingBox(); 1264 for (size_t i = 1; i < n; ++i) 1265 result.unite(quads[i].enclosingBoundingBox()); 1266 return result; 1267 } 1268 1269 FloatPoint absPos = localToAbsolute(); 1270 Vector<IntRect> rects; 1271 absoluteRects(rects, flooredLayoutPoint(absPos)); 1272 1273 size_t n = rects.size(); 1274 if (!n) 1275 return IntRect(); 1276 1277 LayoutRect result = rects[0]; 1278 for (size_t i = 1; i < n; ++i) 1279 result.unite(rects[i]); 1280 return pixelSnappedIntRect(result); 1281 } 1282 1283 void RenderObject::absoluteFocusRingQuads(Vector<FloatQuad>& quads) 1284 { 1285 Vector<IntRect> rects; 1286 // FIXME: addFocusRingRects() needs to be passed this transform-unaware 1287 // localToAbsolute() offset here because RenderInline::addFocusRingRects() 1288 // implicitly assumes that. This doesn't work correctly with transformed 1289 // descendants. 1290 FloatPoint absolutePoint = localToAbsolute(); 1291 addFocusRingRects(rects, flooredLayoutPoint(absolutePoint)); 1292 size_t count = rects.size(); 1293 for (size_t i = 0; i < count; ++i) { 1294 IntRect rect = rects[i]; 1295 rect.move(-absolutePoint.x(), -absolutePoint.y()); 1296 quads.append(localToAbsoluteQuad(FloatQuad(rect))); 1297 } 1298 } 1299 1300 FloatRect RenderObject::absoluteBoundingBoxRectForRange(const Range* range) 1301 { 1302 if (!range || !range->startContainer()) 1303 return FloatRect(); 1304 1305 range->ownerDocument().updateLayout(); 1306 1307 Vector<FloatQuad> quads; 1308 range->textQuads(quads); 1309 1310 FloatRect result; 1311 for (size_t i = 0; i < quads.size(); ++i) 1312 result.unite(quads[i].boundingBox()); 1313 1314 return result; 1315 } 1316 1317 void RenderObject::addAbsoluteRectForLayer(LayoutRect& result) 1318 { 1319 if (hasLayer()) 1320 result.unite(absoluteBoundingBoxRectIgnoringTransforms()); 1321 for (RenderObject* current = firstChild(); current; current = current->nextSibling()) 1322 current->addAbsoluteRectForLayer(result); 1323 } 1324 1325 LayoutRect RenderObject::paintingRootRect(LayoutRect& topLevelRect) 1326 { 1327 LayoutRect result = absoluteBoundingBoxRectIgnoringTransforms(); 1328 topLevelRect = result; 1329 for (RenderObject* current = firstChild(); current; current = current->nextSibling()) 1330 current->addAbsoluteRectForLayer(result); 1331 return result; 1332 } 1333 1334 void RenderObject::paint(PaintInfo&, const LayoutPoint&) 1335 { 1336 } 1337 1338 RenderLayerModelObject* RenderObject::containerForRepaint() const 1339 { 1340 RenderView* v = view(); 1341 if (!v) 1342 return 0; 1343 1344 RenderLayerModelObject* repaintContainer = 0; 1345 1346 if (v->usesCompositing()) { 1347 if (RenderLayer* parentLayer = enclosingLayer()) { 1348 RenderLayer* compLayer = parentLayer->enclosingCompositingLayerForRepaint(); 1349 if (compLayer) 1350 repaintContainer = compLayer->renderer(); 1351 } 1352 } 1353 1354 if (document().view()->hasSoftwareFilters()) { 1355 if (RenderLayer* parentLayer = enclosingLayer()) { 1356 RenderLayer* enclosingFilterLayer = parentLayer->enclosingFilterLayer(); 1357 if (enclosingFilterLayer) 1358 return enclosingFilterLayer->renderer(); 1359 } 1360 } 1361 1362 // If we have a flow thread, then we need to do individual repaints within the RenderRegions instead. 1363 // Return the flow thread as a repaint container in order to create a chokepoint that allows us to change 1364 // repainting to do individual region repaints. 1365 RenderFlowThread* parentRenderFlowThread = flowThreadContainingBlock(); 1366 if (parentRenderFlowThread) { 1367 // The ancestor document will do the reparenting when the repaint propagates further up. 1368 // We're just a seamless child document, and we don't need to do the hacking. 1369 if (parentRenderFlowThread->document() != document()) 1370 return repaintContainer; 1371 // If we have already found a repaint container then we will repaint into that container only if it is part of the same 1372 // flow thread. Otherwise we will need to catch the repaint call and send it to the flow thread. 1373 RenderFlowThread* repaintContainerFlowThread = repaintContainer ? repaintContainer->flowThreadContainingBlock() : 0; 1374 if (!repaintContainerFlowThread || repaintContainerFlowThread != parentRenderFlowThread) 1375 repaintContainer = parentRenderFlowThread; 1376 } 1377 return repaintContainer; 1378 } 1379 1380 void RenderObject::repaintUsingContainer(const RenderLayerModelObject* repaintContainer, const IntRect& r) const 1381 { 1382 if (!repaintContainer) { 1383 view()->repaintViewRectangle(r); 1384 return; 1385 } 1386 1387 if (repaintContainer->compositingState() == PaintsIntoGroupedBacking) { 1388 ASSERT(repaintContainer->groupedMapping()); 1389 1390 // Not clean, but if squashing layer does not yet exist here (e.g. repaint invalidation coming from within recomputing compositing requirements) 1391 // then it's ok to just exit here, since the squashing layer will get repainted when it is newly created. 1392 if (!repaintContainer->groupedMapping()->squashingLayer()) 1393 return; 1394 1395 1396 IntRect offsetRect(r); 1397 1398 // First, convert the repaint rect into the space of the repaintContainer 1399 TransformState transformState(TransformState::ApplyTransformDirection, FloatQuad(FloatRect(r))); 1400 mapLocalToContainer(repaintContainer, transformState, ApplyContainerFlip); 1401 transformState.flatten(); 1402 offsetRect = transformState.lastPlanarQuad().enclosingBoundingBox(); 1403 1404 // FIXME: the repaint rect computed below could be tighter in uncommon nested transform cases, if we passed the quad 1405 // directly to the next chunk of code. 1406 1407 // Then, convert the repaint rect from repaintConainer space into the squashing GraphicsLayer's coordinates. 1408 if (repaintContainer->hasTransform()) 1409 offsetRect = repaintContainer->layer()->transform()->mapRect(r); 1410 offsetRect.move(-repaintContainer->layer()->offsetFromSquashingLayerOrigin()); 1411 repaintContainer->groupedMapping()->squashingLayer()->setNeedsDisplayInRect(offsetRect); 1412 return; 1413 } 1414 1415 if (repaintContainer->isRenderFlowThread()) { 1416 toRenderFlowThread(repaintContainer)->repaintRectangleInRegions(r); 1417 return; 1418 } 1419 1420 if (repaintContainer->hasFilter() && repaintContainer->layer()->requiresFullLayerImageForFilters()) { 1421 repaintContainer->layer()->repainter().setFilterBackendNeedsRepaintingInRect(r); 1422 return; 1423 } 1424 1425 RenderView* v = view(); 1426 if (repaintContainer->isRenderView()) { 1427 ASSERT(repaintContainer == v); 1428 bool viewHasCompositedLayer = v->hasLayer() && v->layer()->compositingState() == PaintsIntoOwnBacking; 1429 if (!viewHasCompositedLayer) { 1430 IntRect repaintRectangle = r; 1431 if (viewHasCompositedLayer && v->layer()->transform()) 1432 repaintRectangle = v->layer()->transform()->mapRect(r); 1433 v->repaintViewRectangle(repaintRectangle); 1434 return; 1435 } 1436 } 1437 1438 if (v->usesCompositing()) { 1439 ASSERT(repaintContainer->hasLayer() && repaintContainer->layer()->compositingState() == PaintsIntoOwnBacking); 1440 repaintContainer->layer()->repainter().setBackingNeedsRepaintInRect(r); 1441 } 1442 } 1443 1444 void RenderObject::repaint() const 1445 { 1446 // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) 1447 RenderView* view; 1448 if (!isRooted(&view)) 1449 return; 1450 1451 if (view->document().printing()) 1452 return; // Don't repaint if we're printing. 1453 1454 RenderLayerModelObject* repaintContainer = containerForRepaint(); 1455 repaintUsingContainer(repaintContainer ? repaintContainer : view, pixelSnappedIntRect(clippedOverflowRectForRepaint(repaintContainer))); 1456 } 1457 1458 void RenderObject::repaintRectangle(const LayoutRect& r) const 1459 { 1460 // Don't repaint if we're unrooted (note that view() still returns the view when unrooted) 1461 RenderView* view; 1462 if (!isRooted(&view)) 1463 return; 1464 1465 if (view->document().printing()) 1466 return; // Don't repaint if we're printing. 1467 1468 LayoutRect dirtyRect(r); 1469 1470 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 1471 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 1472 dirtyRect.move(view->layoutDelta()); 1473 1474 RenderLayerModelObject* repaintContainer = containerForRepaint(); 1475 computeRectForRepaint(repaintContainer, dirtyRect); 1476 repaintUsingContainer(repaintContainer ? repaintContainer : view, pixelSnappedIntRect(dirtyRect)); 1477 } 1478 1479 IntRect RenderObject::pixelSnappedAbsoluteClippedOverflowRect() const 1480 { 1481 return pixelSnappedIntRect(absoluteClippedOverflowRect()); 1482 } 1483 1484 bool RenderObject::repaintAfterLayoutIfNeeded(const RenderLayerModelObject* repaintContainer, bool wasSelfLayout, 1485 const LayoutRect& oldBounds, const LayoutRect& oldOutlineBox, 1486 const LayoutRect* newBoundsPtr, const LayoutRect* newOutlineBoxRectPtr) 1487 { 1488 RenderView* v = view(); 1489 if (v->document().printing()) 1490 return false; // Don't repaint if we're printing. 1491 1492 // This ASSERT fails due to animations. See https://bugs.webkit.org/show_bug.cgi?id=37048 1493 // ASSERT(!newBoundsPtr || *newBoundsPtr == clippedOverflowRectForRepaint(repaintContainer)); 1494 LayoutRect newBounds = newBoundsPtr ? *newBoundsPtr : clippedOverflowRectForRepaint(repaintContainer); 1495 LayoutRect newOutlineBox; 1496 1497 bool fullRepaint = wasSelfLayout; 1498 // Presumably a background or a border exists if border-fit:lines was specified. 1499 if (!fullRepaint && style()->borderFit() == BorderFitLines) 1500 fullRepaint = true; 1501 if (!fullRepaint && style()->hasBorderRadius()) { 1502 // If a border-radius exists and width/height is smaller than 1503 // radius width/height, we cannot use delta-repaint. 1504 RoundedRect oldRoundedRect = style()->getRoundedBorderFor(oldBounds, v); 1505 RoundedRect newRoundedRect = style()->getRoundedBorderFor(newBounds, v); 1506 fullRepaint = oldRoundedRect.radii() != newRoundedRect.radii(); 1507 } 1508 if (!fullRepaint) { 1509 // This ASSERT fails due to animations. See https://bugs.webkit.org/show_bug.cgi?id=37048 1510 // ASSERT(!newOutlineBoxRectPtr || *newOutlineBoxRectPtr == outlineBoundsForRepaint(repaintContainer)); 1511 newOutlineBox = newOutlineBoxRectPtr ? *newOutlineBoxRectPtr : outlineBoundsForRepaint(repaintContainer); 1512 if (newOutlineBox.location() != oldOutlineBox.location() || (mustRepaintBackgroundOrBorder() && (newBounds != oldBounds || newOutlineBox != oldOutlineBox))) 1513 fullRepaint = true; 1514 } 1515 1516 if (!repaintContainer) 1517 repaintContainer = v; 1518 1519 if (fullRepaint) { 1520 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds)); 1521 if (newBounds != oldBounds) 1522 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds)); 1523 return true; 1524 } 1525 1526 if (newBounds == oldBounds && newOutlineBox == oldOutlineBox) 1527 return false; 1528 1529 LayoutUnit deltaLeft = newBounds.x() - oldBounds.x(); 1530 if (deltaLeft > 0) 1531 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height())); 1532 else if (deltaLeft < 0) 1533 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.x(), newBounds.y(), -deltaLeft, newBounds.height())); 1534 1535 LayoutUnit deltaRight = newBounds.maxX() - oldBounds.maxX(); 1536 if (deltaRight > 0) 1537 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.maxX(), newBounds.y(), deltaRight, newBounds.height())); 1538 else if (deltaRight < 0) 1539 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.maxX(), oldBounds.y(), -deltaRight, oldBounds.height())); 1540 1541 LayoutUnit deltaTop = newBounds.y() - oldBounds.y(); 1542 if (deltaTop > 0) 1543 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.x(), oldBounds.y(), oldBounds.width(), deltaTop)); 1544 else if (deltaTop < 0) 1545 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.x(), newBounds.y(), newBounds.width(), -deltaTop)); 1546 1547 LayoutUnit deltaBottom = newBounds.maxY() - oldBounds.maxY(); 1548 if (deltaBottom > 0) 1549 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(newBounds.x(), oldBounds.maxY(), newBounds.width(), deltaBottom)); 1550 else if (deltaBottom < 0) 1551 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldBounds.x(), newBounds.maxY(), oldBounds.width(), -deltaBottom)); 1552 1553 if (newOutlineBox == oldOutlineBox) 1554 return false; 1555 1556 // We didn't move, but we did change size. Invalidate the delta, which will consist of possibly 1557 // two rectangles (but typically only one). 1558 RenderStyle* outlineStyle = outlineStyleForRepaint(); 1559 LayoutUnit outlineWidth = outlineStyle->outlineSize(); 1560 LayoutBoxExtent insetShadowExtent = style()->getBoxShadowInsetExtent(); 1561 LayoutUnit width = absoluteValue(newOutlineBox.width() - oldOutlineBox.width()); 1562 if (width) { 1563 LayoutUnit shadowLeft; 1564 LayoutUnit shadowRight; 1565 style()->getBoxShadowHorizontalExtent(shadowLeft, shadowRight); 1566 int borderRight = isBox() ? toRenderBox(this)->borderRight() : 0; 1567 LayoutUnit boxWidth = isBox() ? toRenderBox(this)->width() : LayoutUnit(); 1568 LayoutUnit minInsetRightShadowExtent = min<LayoutUnit>(-insetShadowExtent.right(), min<LayoutUnit>(newBounds.width(), oldBounds.width())); 1569 LayoutUnit borderWidth = max<LayoutUnit>(borderRight, max<LayoutUnit>(valueForLength(style()->borderTopRightRadius().width(), boxWidth, v), valueForLength(style()->borderBottomRightRadius().width(), boxWidth, v))); 1570 LayoutUnit decorationsWidth = max<LayoutUnit>(-outlineStyle->outlineOffset(), borderWidth + minInsetRightShadowExtent) + max<LayoutUnit>(outlineWidth, shadowRight); 1571 LayoutRect rightRect(newOutlineBox.x() + min(newOutlineBox.width(), oldOutlineBox.width()) - decorationsWidth, 1572 newOutlineBox.y(), 1573 width + decorationsWidth, 1574 max(newOutlineBox.height(), oldOutlineBox.height())); 1575 LayoutUnit right = min<LayoutUnit>(newBounds.maxX(), oldBounds.maxX()); 1576 if (rightRect.x() < right) { 1577 rightRect.setWidth(min(rightRect.width(), right - rightRect.x())); 1578 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(rightRect)); 1579 } 1580 } 1581 LayoutUnit height = absoluteValue(newOutlineBox.height() - oldOutlineBox.height()); 1582 if (height) { 1583 LayoutUnit shadowTop; 1584 LayoutUnit shadowBottom; 1585 style()->getBoxShadowVerticalExtent(shadowTop, shadowBottom); 1586 int borderBottom = isBox() ? toRenderBox(this)->borderBottom() : 0; 1587 LayoutUnit boxHeight = isBox() ? toRenderBox(this)->height() : LayoutUnit(); 1588 LayoutUnit minInsetBottomShadowExtent = min<LayoutUnit>(-insetShadowExtent.bottom(), min<LayoutUnit>(newBounds.height(), oldBounds.height())); 1589 LayoutUnit borderHeight = max<LayoutUnit>(borderBottom, max<LayoutUnit>(valueForLength(style()->borderBottomLeftRadius().height(), boxHeight, v), valueForLength(style()->borderBottomRightRadius().height(), boxHeight, v))); 1590 LayoutUnit decorationsHeight = max<LayoutUnit>(-outlineStyle->outlineOffset(), borderHeight + minInsetBottomShadowExtent) + max<LayoutUnit>(outlineWidth, shadowBottom); 1591 LayoutRect bottomRect(newOutlineBox.x(), 1592 min(newOutlineBox.maxY(), oldOutlineBox.maxY()) - decorationsHeight, 1593 max(newOutlineBox.width(), oldOutlineBox.width()), 1594 height + decorationsHeight); 1595 LayoutUnit bottom = min(newBounds.maxY(), oldBounds.maxY()); 1596 if (bottomRect.y() < bottom) { 1597 bottomRect.setHeight(min(bottomRect.height(), bottom - bottomRect.y())); 1598 repaintUsingContainer(repaintContainer, pixelSnappedIntRect(bottomRect)); 1599 } 1600 } 1601 return false; 1602 } 1603 1604 void RenderObject::repaintOverflow() 1605 { 1606 } 1607 1608 bool RenderObject::checkForRepaintDuringLayout() const 1609 { 1610 return !document().view()->needsFullRepaint() && !hasLayer() && everHadLayout(); 1611 } 1612 1613 LayoutRect RenderObject::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const 1614 { 1615 LayoutRect r(clippedOverflowRectForRepaint(repaintContainer)); 1616 r.inflate(outlineWidth); 1617 return r; 1618 } 1619 1620 LayoutRect RenderObject::clippedOverflowRectForRepaint(const RenderLayerModelObject*) const 1621 { 1622 ASSERT_NOT_REACHED(); 1623 return LayoutRect(); 1624 } 1625 1626 void RenderObject::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const 1627 { 1628 if (repaintContainer == this) 1629 return; 1630 1631 if (RenderObject* o = parent()) { 1632 if (o->isRenderBlockFlow()) { 1633 RenderBlock* cb = toRenderBlock(o); 1634 if (cb->hasColumns()) 1635 cb->adjustRectForColumns(rect); 1636 } 1637 1638 if (o->hasOverflowClip()) { 1639 RenderBox* boxParent = toRenderBox(o); 1640 boxParent->applyCachedClipAndScrollOffsetForRepaint(rect); 1641 if (rect.isEmpty()) 1642 return; 1643 } 1644 1645 o->computeRectForRepaint(repaintContainer, rect, fixed); 1646 } 1647 } 1648 1649 void RenderObject::computeFloatRectForRepaint(const RenderLayerModelObject*, FloatRect&, bool) const 1650 { 1651 ASSERT_NOT_REACHED(); 1652 } 1653 1654 void RenderObject::dirtyLinesFromChangedChild(RenderObject*) 1655 { 1656 } 1657 1658 #ifndef NDEBUG 1659 1660 void RenderObject::showTreeForThis() const 1661 { 1662 if (node()) 1663 node()->showTreeForThis(); 1664 } 1665 1666 void RenderObject::showRenderTreeForThis() const 1667 { 1668 showRenderTree(this, 0); 1669 } 1670 1671 void RenderObject::showLineTreeForThis() const 1672 { 1673 if (containingBlock()) 1674 containingBlock()->showLineTreeAndMark(0, 0, 0, 0, this); 1675 } 1676 1677 void RenderObject::showRenderObject() const 1678 { 1679 showRenderObject(0); 1680 } 1681 1682 void RenderObject::showRenderObject(int printedCharacters) const 1683 { 1684 // As this function is intended to be used when debugging, the 1685 // this pointer may be 0. 1686 if (!this) { 1687 fputs("(null)\n", stderr); 1688 return; 1689 } 1690 1691 printedCharacters += fprintf(stderr, "%s %p", renderName(), this); 1692 1693 if (node()) { 1694 if (printedCharacters) 1695 for (; printedCharacters < showTreeCharacterOffset; printedCharacters++) 1696 fputc(' ', stderr); 1697 fputc('\t', stderr); 1698 node()->showNode(); 1699 } else 1700 fputc('\n', stderr); 1701 } 1702 1703 void RenderObject::showRenderTreeAndMark(const RenderObject* markedObject1, const char* markedLabel1, const RenderObject* markedObject2, const char* markedLabel2, int depth) const 1704 { 1705 int printedCharacters = 0; 1706 if (markedObject1 == this && markedLabel1) 1707 printedCharacters += fprintf(stderr, "%s", markedLabel1); 1708 if (markedObject2 == this && markedLabel2) 1709 printedCharacters += fprintf(stderr, "%s", markedLabel2); 1710 for (; printedCharacters < depth * 2; printedCharacters++) 1711 fputc(' ', stderr); 1712 1713 showRenderObject(printedCharacters); 1714 if (!this) 1715 return; 1716 1717 for (const RenderObject* child = firstChild(); child; child = child->nextSibling()) 1718 child->showRenderTreeAndMark(markedObject1, markedLabel1, markedObject2, markedLabel2, depth + 1); 1719 } 1720 1721 #endif // NDEBUG 1722 1723 bool RenderObject::isSelectable() const 1724 { 1725 return !isInert() && !(style()->userSelect() == SELECT_NONE && style()->userModify() == READ_ONLY); 1726 } 1727 1728 Color RenderObject::selectionBackgroundColor() const 1729 { 1730 Color backgroundColor; 1731 if (isSelectable()) { 1732 RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(PseudoStyleRequest(SELECTION)); 1733 if (pseudoStyle && resolveColor(pseudoStyle.get(), CSSPropertyBackgroundColor).isValid()) { 1734 backgroundColor = resolveColor(pseudoStyle.get(), CSSPropertyBackgroundColor).blendWithWhite(); 1735 } else { 1736 backgroundColor = frame()->selection().isFocusedAndActive() ? 1737 RenderTheme::theme().activeSelectionBackgroundColor() : 1738 RenderTheme::theme().inactiveSelectionBackgroundColor(); 1739 } 1740 } 1741 1742 return backgroundColor; 1743 } 1744 1745 Color RenderObject::selectionColor(int colorProperty) const 1746 { 1747 // If the element is unselectable, or we are only painting the selection, 1748 // don't override the foreground color with the selection foreground color. 1749 if (!isSelectable() || (frame()->view()->paintBehavior() & PaintBehaviorSelectionOnly)) 1750 return Color::transparent; 1751 1752 Color color; 1753 if (RefPtr<RenderStyle> pseudoStyle = getUncachedPseudoStyle(PseudoStyleRequest(SELECTION))) { 1754 Color selectionColor = resolveColor(pseudoStyle.get(), colorProperty); 1755 color = selectionColor.isValid() ? selectionColor : resolveColor(pseudoStyle.get(), CSSPropertyColor); 1756 } else { 1757 color = frame()->selection().isFocusedAndActive() ? 1758 RenderTheme::theme().activeSelectionForegroundColor() : 1759 RenderTheme::theme().inactiveSelectionForegroundColor(); 1760 } 1761 1762 return color; 1763 } 1764 1765 Color RenderObject::selectionForegroundColor() const 1766 { 1767 return selectionColor(CSSPropertyWebkitTextFillColor); 1768 } 1769 1770 Color RenderObject::selectionEmphasisMarkColor() const 1771 { 1772 return selectionColor(CSSPropertyWebkitTextEmphasisColor); 1773 } 1774 1775 void RenderObject::selectionStartEnd(int& spos, int& epos) const 1776 { 1777 view()->selectionStartEnd(spos, epos); 1778 } 1779 1780 void RenderObject::handleDynamicFloatPositionChange() 1781 { 1782 // We have gone from not affecting the inline status of the parent flow to suddenly 1783 // having an impact. See if there is a mismatch between the parent flow's 1784 // childrenInline() state and our state. 1785 setInline(style()->isDisplayInlineType()); 1786 if (isInline() != parent()->childrenInline()) { 1787 if (!isInline()) 1788 toRenderBoxModelObject(parent())->childBecameNonInline(this); 1789 else { 1790 // An anonymous block must be made to wrap this inline. 1791 RenderBlock* block = toRenderBlock(parent())->createAnonymousBlock(); 1792 RenderObjectChildList* childlist = parent()->virtualChildren(); 1793 childlist->insertChildNode(parent(), block, this); 1794 block->children()->appendChildNode(block, childlist->removeChildNode(parent(), this)); 1795 } 1796 } 1797 } 1798 1799 void RenderObject::setAnimatableStyle(PassRefPtr<RenderStyle> style) 1800 { 1801 if (!isText() && style && !RuntimeEnabledFeatures::webAnimationsCSSEnabled()) { 1802 setStyle(animation().updateAnimations(*this, *style)); 1803 return; 1804 } 1805 setStyle(style); 1806 } 1807 1808 StyleDifference RenderObject::adjustStyleDifference(StyleDifference diff, unsigned contextSensitiveProperties) const 1809 { 1810 // If transform changed, and the layer does not paint into its own separate backing, then we need to do a layout. 1811 // FIXME: The comment above is what the code does, but it is technically not following spec. This means we will 1812 // not to layout for 3d transforms, but we should be invoking a simplified relayout. Is it possible we are avoiding 1813 // doing this for some performance reason at this time? 1814 if (contextSensitiveProperties & ContextSensitivePropertyTransform) { 1815 // Text nodes share style with their parents but transforms don't apply to them, 1816 // hence the !isText() check. 1817 // FIXME: when transforms are taken into account for overflow, we will need to do a layout. 1818 if (!isText() && (!hasLayer() || toRenderLayerModelObject(this)->layer()->compositingState() != PaintsIntoOwnBacking)) { 1819 // We need to set at least SimplifiedLayout, but if PositionedMovementOnly is already set 1820 // then we actually need SimplifiedLayoutAndPositionedMovement. 1821 if (!hasLayer()) 1822 diff = StyleDifferenceLayout; // FIXME: Do this for now since SimplifiedLayout cannot handle updating floating objects lists. 1823 else if (diff < StyleDifferenceLayoutPositionedMovementOnly) 1824 diff = StyleDifferenceSimplifiedLayout; 1825 else if (diff < StyleDifferenceSimplifiedLayout) 1826 diff = StyleDifferenceSimplifiedLayoutAndPositionedMovement; 1827 } else if (diff < StyleDifferenceRecompositeLayer) 1828 diff = StyleDifferenceRecompositeLayer; 1829 } 1830 1831 // If opacity or filters changed, and the layer does not paint into its own separate backing, then we need to repaint (also 1832 // ignoring text nodes) 1833 if (contextSensitiveProperties & ContextSensitivePropertyOpacity && diff <= StyleDifferenceRepaintLayer) { 1834 if (!isText() && (!hasLayer() || toRenderLayerModelObject(this)->layer()->compositingState() != PaintsIntoOwnBacking)) 1835 diff = StyleDifferenceRepaintLayer; 1836 else if (diff < StyleDifferenceRecompositeLayer) 1837 diff = StyleDifferenceRecompositeLayer; 1838 } 1839 if ((contextSensitiveProperties & ContextSensitivePropertyFilter) && hasLayer() && diff <= StyleDifferenceRepaintLayer) { 1840 RenderLayer* layer = toRenderLayerModelObject(this)->layer(); 1841 if (layer->compositingState() != PaintsIntoOwnBacking || layer->paintsWithFilters()) 1842 diff = StyleDifferenceRepaintLayer; 1843 else if (diff < StyleDifferenceRecompositeLayer) 1844 diff = StyleDifferenceRecompositeLayer; 1845 } 1846 1847 // The answer to requiresLayer() for plugins, iframes, and canvas can change without the actual 1848 // style changing, since it depends on whether we decide to composite these elements. When the 1849 // layer status of one of these elements changes, we need to force a layout. 1850 if (diff == StyleDifferenceEqual && style() && isLayerModelObject()) { 1851 if (hasLayer() != toRenderLayerModelObject(this)->requiresLayer()) 1852 diff = StyleDifferenceLayout; 1853 } 1854 1855 // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint. 1856 if (diff == StyleDifferenceRepaintLayer && !hasLayer()) 1857 diff = StyleDifferenceRepaint; 1858 1859 return diff; 1860 } 1861 1862 void RenderObject::setPseudoStyle(PassRefPtr<RenderStyle> pseudoStyle) 1863 { 1864 ASSERT(pseudoStyle->styleType() == BEFORE || pseudoStyle->styleType() == AFTER); 1865 1866 // FIXME: We should consider just making all pseudo items use an inherited style. 1867 1868 // Images are special and must inherit the pseudoStyle so the width and height of 1869 // the pseudo element doesn't change the size of the image. In all other cases we 1870 // can just share the style. 1871 // 1872 // Quotes are also RenderInline, so we need to create an inherited style to avoid 1873 // getting an inline with positioning or an invalid display. 1874 // 1875 if (isImage() || isQuote()) { 1876 RefPtr<RenderStyle> style = RenderStyle::create(); 1877 style->inheritFrom(pseudoStyle.get()); 1878 setStyle(style.release()); 1879 return; 1880 } 1881 1882 setStyle(pseudoStyle); 1883 } 1884 1885 inline bool RenderObject::hasImmediateNonWhitespaceTextChildOrPropertiesDependentOnColor() const 1886 { 1887 for (const RenderObject* r = firstChild(); r; r = r->nextSibling()) { 1888 if (r->isText() && !toRenderText(r)->isAllCollapsibleWhitespace()) 1889 return true; 1890 if (r->style()->hasOutline() || r->style()->hasBorder()) 1891 return true; 1892 } 1893 return false; 1894 } 1895 1896 inline bool RenderObject::shouldRepaintForStyleDifference(StyleDifference diff) const 1897 { 1898 return diff == StyleDifferenceRepaint || (diff == StyleDifferenceRepaintIfTextOrColorChange && hasImmediateNonWhitespaceTextChildOrPropertiesDependentOnColor()); 1899 } 1900 1901 void RenderObject::setStyle(PassRefPtr<RenderStyle> style) 1902 { 1903 if (m_style == style) { 1904 // We need to run through adjustStyleDifference() for iframes, plugins, and canvas so 1905 // style sharing is disabled for them. That should ensure that we never hit this code path. 1906 ASSERT(!isRenderIFrame() && !isEmbeddedObject() && !isCanvas()); 1907 return; 1908 } 1909 1910 StyleDifference diff = StyleDifferenceEqual; 1911 unsigned contextSensitiveProperties = ContextSensitivePropertyNone; 1912 if (m_style) 1913 diff = m_style->diff(style.get(), contextSensitiveProperties); 1914 1915 diff = adjustStyleDifference(diff, contextSensitiveProperties); 1916 1917 styleWillChange(diff, style.get()); 1918 1919 RefPtr<RenderStyle> oldStyle = m_style.release(); 1920 setStyleInternal(style); 1921 1922 updateFillImages(oldStyle ? oldStyle->backgroundLayers() : 0, m_style ? m_style->backgroundLayers() : 0); 1923 updateFillImages(oldStyle ? oldStyle->maskLayers() : 0, m_style ? m_style->maskLayers() : 0); 1924 1925 updateImage(oldStyle ? oldStyle->borderImage().image() : 0, m_style ? m_style->borderImage().image() : 0); 1926 updateImage(oldStyle ? oldStyle->maskBoxImage().image() : 0, m_style ? m_style->maskBoxImage().image() : 0); 1927 1928 updateShapeImage(oldStyle ? oldStyle->shapeInside() : 0, m_style ? m_style->shapeInside() : 0); 1929 updateShapeImage(oldStyle ? oldStyle->shapeOutside() : 0, m_style ? m_style->shapeOutside() : 0); 1930 1931 // We need to ensure that view->maximalOutlineSize() is valid for any repaints that happen 1932 // during styleDidChange (it's used by clippedOverflowRectForRepaint()). 1933 // FIXME: Do this more cleanly. http://crbug.com/273904 1934 if (m_style->outlineWidth() > 0 && m_style->outlineSize() > view()->maximalOutlineSize()) 1935 view()->setMaximalOutlineSize(m_style->outlineSize()); 1936 1937 bool doesNotNeedLayout = !m_parent || isText(); 1938 1939 styleDidChange(diff, oldStyle.get()); 1940 1941 // FIXME: |this| might be destroyed here. This can currently happen for a RenderTextFragment when 1942 // its first-letter block gets an update in RenderTextFragment::styleDidChange. For RenderTextFragment(s), 1943 // we will safely bail out with the doesNotNeedLayout flag. We might want to broaden this condition 1944 // in the future as we move renderer changes out of layout and into style changes. 1945 if (doesNotNeedLayout) 1946 return; 1947 1948 // Now that the layer (if any) has been updated, we need to adjust the diff again, 1949 // check whether we should layout now, and decide if we need to repaint. 1950 StyleDifference updatedDiff = adjustStyleDifference(diff, contextSensitiveProperties); 1951 1952 if (diff <= StyleDifferenceLayoutPositionedMovementOnly) { 1953 if (updatedDiff == StyleDifferenceLayout) 1954 setNeedsLayoutAndPrefWidthsRecalc(); 1955 else if (updatedDiff == StyleDifferenceLayoutPositionedMovementOnly) 1956 setNeedsPositionedMovementLayout(); 1957 else if (updatedDiff == StyleDifferenceSimplifiedLayoutAndPositionedMovement) { 1958 setNeedsPositionedMovementLayout(); 1959 setNeedsSimplifiedNormalFlowLayout(); 1960 } else if (updatedDiff == StyleDifferenceSimplifiedLayout) 1961 setNeedsSimplifiedNormalFlowLayout(); 1962 } 1963 1964 if (updatedDiff == StyleDifferenceRepaintLayer || shouldRepaintForStyleDifference(updatedDiff)) { 1965 // Do a repaint with the new style now, e.g., for example if we go from 1966 // not having an outline to having an outline. 1967 repaint(); 1968 } 1969 } 1970 1971 static inline bool rendererHasBackground(const RenderObject* renderer) 1972 { 1973 return renderer && renderer->hasBackground(); 1974 } 1975 1976 void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) 1977 { 1978 if (m_style) { 1979 // If our z-index changes value or our visibility changes, 1980 // we need to dirty our stacking context's z-order list. 1981 if (newStyle) { 1982 bool visibilityChanged = m_style->visibility() != newStyle->visibility() 1983 || m_style->zIndex() != newStyle->zIndex() 1984 || m_style->hasAutoZIndex() != newStyle->hasAutoZIndex(); 1985 if (visibilityChanged) { 1986 document().setAnnotatedRegionsDirty(true); 1987 if (AXObjectCache* cache = document().existingAXObjectCache()) 1988 cache->childrenChanged(parent()); 1989 } 1990 1991 // Keep layer hierarchy visibility bits up to date if visibility changes. 1992 if (m_style->visibility() != newStyle->visibility()) { 1993 if (RenderLayer* l = enclosingLayer()) { 1994 if (newStyle->visibility() == VISIBLE) 1995 l->setHasVisibleContent(); 1996 else if (l->hasVisibleContent() && (this == l->renderer() || l->renderer()->style()->visibility() != VISIBLE)) { 1997 l->dirtyVisibleContentStatus(); 1998 if (diff > StyleDifferenceRepaintLayer) 1999 repaint(); 2000 } 2001 } 2002 } 2003 } 2004 2005 if (m_parent && (newStyle->outlineSize() < m_style->outlineSize() || shouldRepaintForStyleDifference(diff))) 2006 repaint(); 2007 if (isFloating() && (m_style->floating() != newStyle->floating())) 2008 // For changes in float styles, we need to conceivably remove ourselves 2009 // from the floating objects list. 2010 toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); 2011 else if (isOutOfFlowPositioned() && (m_style->position() != newStyle->position())) 2012 // For changes in positioning styles, we need to conceivably remove ourselves 2013 // from the positioned objects list. 2014 toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); 2015 2016 s_affectsParentBlock = isFloatingOrOutOfFlowPositioned() 2017 && (!newStyle->isFloating() && !newStyle->hasOutOfFlowPosition()) 2018 && parent() && (parent()->isRenderBlockFlow() || parent()->isRenderInline()); 2019 2020 // Clearing these bits is required to avoid leaving stale renderers. 2021 // FIXME: We shouldn't need that hack if our logic was totally correct. 2022 if (diff == StyleDifferenceLayout || diff == StyleDifferenceLayoutPositionedMovementOnly) { 2023 setFloating(false); 2024 clearPositionedState(); 2025 } 2026 } else 2027 s_affectsParentBlock = false; 2028 2029 if (view()->frameView()) { 2030 bool shouldBlitOnFixedBackgroundImage = false; 2031 #if ENABLE(FAST_MOBILE_SCROLLING) 2032 // On low-powered/mobile devices, preventing blitting on a scroll can cause noticeable delays 2033 // when scrolling a page with a fixed background image. As an optimization, assuming there are 2034 // no fixed positoned elements on the page, we can acclerate scrolling (via blitting) if we 2035 // ignore the CSS property "background-attachment: fixed". 2036 shouldBlitOnFixedBackgroundImage = true; 2037 #endif 2038 2039 bool newStyleSlowScroll = newStyle && !shouldBlitOnFixedBackgroundImage && newStyle->hasFixedBackgroundImage(); 2040 bool oldStyleSlowScroll = m_style && !shouldBlitOnFixedBackgroundImage && m_style->hasFixedBackgroundImage(); 2041 2042 bool drawsRootBackground = isRoot() || (isBody() && !rendererHasBackground(document().documentElement()->renderer())); 2043 if (drawsRootBackground && !shouldBlitOnFixedBackgroundImage) { 2044 if (view()->compositor()->supportsFixedRootBackgroundCompositing()) { 2045 if (newStyleSlowScroll && newStyle->hasEntirelyFixedBackground()) 2046 newStyleSlowScroll = false; 2047 2048 if (oldStyleSlowScroll && m_style->hasEntirelyFixedBackground()) 2049 oldStyleSlowScroll = false; 2050 } 2051 } 2052 2053 if (oldStyleSlowScroll != newStyleSlowScroll) { 2054 if (oldStyleSlowScroll) 2055 view()->frameView()->removeSlowRepaintObject(); 2056 if (newStyleSlowScroll) 2057 view()->frameView()->addSlowRepaintObject(); 2058 } 2059 } 2060 } 2061 2062 static bool areNonIdenticalCursorListsEqual(const RenderStyle* a, const RenderStyle* b) 2063 { 2064 ASSERT(a->cursors() != b->cursors()); 2065 return a->cursors() && b->cursors() && *a->cursors() == *b->cursors(); 2066 } 2067 2068 static inline bool areCursorsEqual(const RenderStyle* a, const RenderStyle* b) 2069 { 2070 return a->cursor() == b->cursor() && (a->cursors() == b->cursors() || areNonIdenticalCursorListsEqual(a, b)); 2071 } 2072 2073 void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 2074 { 2075 if (s_affectsParentBlock) 2076 handleDynamicFloatPositionChange(); 2077 2078 if (!m_parent) 2079 return; 2080 2081 if (diff == StyleDifferenceLayout || diff == StyleDifferenceSimplifiedLayout) { 2082 RenderCounter::rendererStyleChanged(*this, oldStyle, m_style.get()); 2083 2084 // If the object already needs layout, then setNeedsLayout won't do 2085 // any work. But if the containing block has changed, then we may need 2086 // to mark the new containing blocks for layout. The change that can 2087 // directly affect the containing block of this object is a change to 2088 // the position style. 2089 if (needsLayout() && oldStyle->position() != m_style->position()) 2090 markContainingBlocksForLayout(); 2091 2092 if (diff == StyleDifferenceLayout) 2093 setNeedsLayoutAndPrefWidthsRecalc(); 2094 else 2095 setNeedsSimplifiedNormalFlowLayout(); 2096 } else if (diff == StyleDifferenceSimplifiedLayoutAndPositionedMovement) { 2097 setNeedsPositionedMovementLayout(); 2098 setNeedsSimplifiedNormalFlowLayout(); 2099 } else if (diff == StyleDifferenceLayoutPositionedMovementOnly) 2100 setNeedsPositionedMovementLayout(); 2101 2102 // Don't check for repaint here; we need to wait until the layer has been 2103 // updated by subclasses before we know if we have to repaint (in setStyle()). 2104 2105 if (oldStyle && !areCursorsEqual(oldStyle, style())) { 2106 if (Frame* frame = this->frame()) 2107 frame->eventHandler().scheduleCursorUpdate(); 2108 } 2109 } 2110 2111 void RenderObject::propagateStyleToAnonymousChildren(bool blockChildrenOnly) 2112 { 2113 // FIXME: We could save this call when the change only affected non-inherited properties. 2114 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 2115 if (!child->isAnonymous() || child->style()->styleType() != NOPSEUDO) 2116 continue; 2117 2118 if (blockChildrenOnly && !child->isRenderBlock()) 2119 continue; 2120 2121 if (child->isRenderFullScreen() || child->isRenderFullScreenPlaceholder()) 2122 continue; 2123 2124 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), child->style()->display()); 2125 if (style()->specifiesColumns()) { 2126 if (child->style()->specifiesColumns()) 2127 newStyle->inheritColumnPropertiesFrom(style()); 2128 if (child->style()->columnSpan()) 2129 newStyle->setColumnSpan(ColumnSpanAll); 2130 } 2131 2132 // Preserve the position style of anonymous block continuations as they can have relative or sticky position when 2133 // they contain block descendants of relative or sticky positioned inlines. 2134 if (child->isInFlowPositioned() && toRenderBlock(child)->isAnonymousBlockContinuation()) 2135 newStyle->setPosition(child->style()->position()); 2136 2137 child->setStyle(newStyle.release()); 2138 } 2139 } 2140 2141 void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers) 2142 { 2143 // Optimize the common case 2144 if (oldLayers && !oldLayers->next() && newLayers && !newLayers->next() && (oldLayers->image() == newLayers->image())) 2145 return; 2146 2147 // Go through the new layers and addClients first, to avoid removing all clients of an image. 2148 for (const FillLayer* currNew = newLayers; currNew; currNew = currNew->next()) { 2149 if (currNew->image()) 2150 currNew->image()->addClient(this); 2151 } 2152 2153 for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) { 2154 if (currOld->image()) 2155 currOld->image()->removeClient(this); 2156 } 2157 } 2158 2159 void RenderObject::updateImage(StyleImage* oldImage, StyleImage* newImage) 2160 { 2161 if (oldImage != newImage) { 2162 if (oldImage) 2163 oldImage->removeClient(this); 2164 if (newImage) 2165 newImage->addClient(this); 2166 } 2167 } 2168 2169 void RenderObject::updateShapeImage(const ShapeValue* oldShapeValue, const ShapeValue* newShapeValue) 2170 { 2171 if (oldShapeValue || newShapeValue) 2172 updateImage(oldShapeValue ? oldShapeValue->image() : 0, newShapeValue ? newShapeValue->image() : 0); 2173 } 2174 2175 LayoutRect RenderObject::viewRect() const 2176 { 2177 return view()->viewRect(); 2178 } 2179 2180 FloatPoint RenderObject::localToAbsolute(const FloatPoint& localPoint, MapCoordinatesFlags mode) const 2181 { 2182 TransformState transformState(TransformState::ApplyTransformDirection, localPoint); 2183 mapLocalToContainer(0, transformState, mode | ApplyContainerFlip); 2184 transformState.flatten(); 2185 2186 return transformState.lastPlanarPoint(); 2187 } 2188 2189 FloatPoint RenderObject::absoluteToLocal(const FloatPoint& containerPoint, MapCoordinatesFlags mode) const 2190 { 2191 TransformState transformState(TransformState::UnapplyInverseTransformDirection, containerPoint); 2192 mapAbsoluteToLocalPoint(mode, transformState); 2193 transformState.flatten(); 2194 2195 return transformState.lastPlanarPoint(); 2196 } 2197 2198 FloatQuad RenderObject::absoluteToLocalQuad(const FloatQuad& quad, MapCoordinatesFlags mode) const 2199 { 2200 TransformState transformState(TransformState::UnapplyInverseTransformDirection, quad.boundingBox().center(), quad); 2201 mapAbsoluteToLocalPoint(mode, transformState); 2202 transformState.flatten(); 2203 return transformState.lastPlanarQuad(); 2204 } 2205 2206 void RenderObject::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const 2207 { 2208 if (repaintContainer == this) 2209 return; 2210 2211 RenderObject* o = parent(); 2212 if (!o) 2213 return; 2214 2215 // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called. 2216 LayoutPoint centerPoint = roundedLayoutPoint(transformState.mappedPoint()); 2217 if (mode & ApplyContainerFlip && o->isBox()) { 2218 if (o->style()->isFlippedBlocksWritingMode()) 2219 transformState.move(toRenderBox(o)->flipForWritingModeIncludingColumns(roundedLayoutPoint(transformState.mappedPoint())) - centerPoint); 2220 mode &= ~ApplyContainerFlip; 2221 } 2222 2223 LayoutSize columnOffset; 2224 o->adjustForColumns(columnOffset, roundedLayoutPoint(transformState.mappedPoint())); 2225 if (!columnOffset.isZero()) 2226 transformState.move(columnOffset); 2227 2228 if (o->hasOverflowClip()) 2229 transformState.move(-toRenderBox(o)->scrolledContentOffset()); 2230 2231 o->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed); 2232 } 2233 2234 const RenderObject* RenderObject::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const 2235 { 2236 ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != this); 2237 2238 RenderObject* container = parent(); 2239 if (!container) 2240 return 0; 2241 2242 // FIXME: this should call offsetFromContainer to share code, but I'm not sure it's ever called. 2243 LayoutSize offset; 2244 if (container->hasOverflowClip()) 2245 offset = -toRenderBox(container)->scrolledContentOffset(); 2246 2247 geometryMap.push(this, offset, hasColumns()); 2248 2249 return container; 2250 } 2251 2252 void RenderObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const 2253 { 2254 RenderObject* o = parent(); 2255 if (o) { 2256 o->mapAbsoluteToLocalPoint(mode, transformState); 2257 if (o->hasOverflowClip()) 2258 transformState.move(toRenderBox(o)->scrolledContentOffset()); 2259 } 2260 } 2261 2262 bool RenderObject::shouldUseTransformFromContainer(const RenderObject* containerObject) const 2263 { 2264 // hasTransform() indicates whether the object has transform, transform-style or perspective. We just care about transform, 2265 // so check the layer's transform directly. 2266 return (hasLayer() && toRenderLayerModelObject(this)->layer()->transform()) || (containerObject && containerObject->style()->hasPerspective()); 2267 } 2268 2269 void RenderObject::getTransformFromContainer(const RenderObject* containerObject, const LayoutSize& offsetInContainer, TransformationMatrix& transform) const 2270 { 2271 transform.makeIdentity(); 2272 transform.translate(offsetInContainer.width(), offsetInContainer.height()); 2273 RenderLayer* layer; 2274 if (hasLayer() && (layer = toRenderLayerModelObject(this)->layer()) && layer->transform()) 2275 transform.multiply(layer->currentTransform()); 2276 2277 if (containerObject && containerObject->hasLayer() && containerObject->style()->hasPerspective()) { 2278 // Perpsective on the container affects us, so we have to factor it in here. 2279 ASSERT(containerObject->hasLayer()); 2280 FloatPoint perspectiveOrigin = toRenderLayerModelObject(containerObject)->layer()->perspectiveOrigin(); 2281 2282 TransformationMatrix perspectiveMatrix; 2283 perspectiveMatrix.applyPerspective(containerObject->style()->perspective()); 2284 2285 transform.translateRight3d(-perspectiveOrigin.x(), -perspectiveOrigin.y(), 0); 2286 transform = perspectiveMatrix * transform; 2287 transform.translateRight3d(perspectiveOrigin.x(), perspectiveOrigin.y(), 0); 2288 } 2289 } 2290 2291 FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, const RenderLayerModelObject* repaintContainer, MapCoordinatesFlags mode, bool* wasFixed) const 2292 { 2293 // Track the point at the center of the quad's bounding box. As mapLocalToContainer() calls offsetFromContainer(), 2294 // it will use that point as the reference point to decide which column's transform to apply in multiple-column blocks. 2295 TransformState transformState(TransformState::ApplyTransformDirection, localQuad.boundingBox().center(), localQuad); 2296 mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip | UseTransforms, wasFixed); 2297 transformState.flatten(); 2298 2299 return transformState.lastPlanarQuad(); 2300 } 2301 2302 FloatPoint RenderObject::localToContainerPoint(const FloatPoint& localPoint, const RenderLayerModelObject* repaintContainer, MapCoordinatesFlags mode, bool* wasFixed) const 2303 { 2304 TransformState transformState(TransformState::ApplyTransformDirection, localPoint); 2305 mapLocalToContainer(repaintContainer, transformState, mode | ApplyContainerFlip | UseTransforms, wasFixed); 2306 transformState.flatten(); 2307 2308 return transformState.lastPlanarPoint(); 2309 } 2310 2311 LayoutSize RenderObject::offsetFromContainer(RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const 2312 { 2313 ASSERT(o == container()); 2314 2315 LayoutSize offset; 2316 2317 o->adjustForColumns(offset, point); 2318 2319 if (o->hasOverflowClip()) 2320 offset -= toRenderBox(o)->scrolledContentOffset(); 2321 2322 if (offsetDependsOnPoint) 2323 *offsetDependsOnPoint = hasColumns() || o->isRenderFlowThread(); 2324 2325 return offset; 2326 } 2327 2328 LayoutSize RenderObject::offsetFromAncestorContainer(RenderObject* container) const 2329 { 2330 LayoutSize offset; 2331 LayoutPoint referencePoint; 2332 const RenderObject* currContainer = this; 2333 do { 2334 RenderObject* nextContainer = currContainer->container(); 2335 ASSERT(nextContainer); // This means we reached the top without finding container. 2336 if (!nextContainer) 2337 break; 2338 ASSERT(!currContainer->hasTransform()); 2339 LayoutSize currentOffset = currContainer->offsetFromContainer(nextContainer, referencePoint); 2340 offset += currentOffset; 2341 referencePoint.move(currentOffset); 2342 currContainer = nextContainer; 2343 } while (currContainer != container); 2344 2345 return offset; 2346 } 2347 2348 LayoutRect RenderObject::localCaretRect(InlineBox*, int, LayoutUnit* extraWidthToEndOfLine) 2349 { 2350 if (extraWidthToEndOfLine) 2351 *extraWidthToEndOfLine = 0; 2352 2353 return LayoutRect(); 2354 } 2355 2356 void RenderObject::computeLayerHitTestRects(LayerHitTestRects& layerRects) const 2357 { 2358 // Figure out what layer our container is in. Any offset (or new layer) for this 2359 // renderer within it's container will be applied in addLayerHitTestRects. 2360 LayoutPoint layerOffset; 2361 const RenderLayer* currentLayer = 0; 2362 2363 if (!hasLayer()) { 2364 RenderObject* container = this->container(); 2365 if (container) { 2366 currentLayer = container->enclosingLayer(); 2367 if (currentLayer && currentLayer->renderer() != container) { 2368 layerOffset.move(container->offsetFromAncestorContainer(currentLayer->renderer())); 2369 // If the layer itself is scrolled, we have to undo the subtraction of its scroll 2370 // offset since we want the offset relative to the scrolling content, not the 2371 // element itself. 2372 if (currentLayer->renderer()->hasOverflowClip()) 2373 layerOffset.move(currentLayer->renderBox()->scrolledContentOffset()); 2374 } 2375 } else { 2376 currentLayer = enclosingLayer(); 2377 } 2378 if (!currentLayer) 2379 return; 2380 } 2381 2382 this->addLayerHitTestRects(layerRects, currentLayer, layerOffset, LayoutRect()); 2383 } 2384 2385 void RenderObject::addLayerHitTestRects(LayerHitTestRects& layerRects, const RenderLayer* currentLayer, const LayoutPoint& layerOffset, const LayoutRect& containerRect) const 2386 { 2387 ASSERT(currentLayer); 2388 ASSERT(currentLayer == this->enclosingLayer()); 2389 2390 // Compute the rects for this renderer only and add them to the results. 2391 // Note that we could avoid passing the offset and instead adjust each result, but this 2392 // seems slightly simpler. 2393 Vector<LayoutRect> ownRects; 2394 LayoutRect newContainerRect; 2395 computeSelfHitTestRects(ownRects, layerOffset); 2396 2397 // When we get to have a lot of rects on a layer, the performance cost of tracking those 2398 // rects outweighs the benefit of doing compositor thread hit testing. 2399 // FIXME: This limit needs to be low due to the O(n^2) algorithm in 2400 // WebLayer::setTouchEventHandlerRegion - crbug.com/300282. 2401 const size_t maxRectsPerLayer = 100; 2402 2403 LayerHitTestRects::iterator iter = layerRects.find(currentLayer); 2404 if (iter == layerRects.end()) 2405 iter = layerRects.add(currentLayer, Vector<LayoutRect>()).iterator; 2406 for (size_t i = 0; i < ownRects.size(); i++) { 2407 if (!containerRect.contains(ownRects[i])) { 2408 iter->value.append(ownRects[i]); 2409 if (iter->value.size() > maxRectsPerLayer) { 2410 // Just mark the entire layer instead, and switch to walking the layer 2411 // tree instead of the render tree. 2412 layerRects.remove(currentLayer); 2413 currentLayer->addLayerHitTestRects(layerRects); 2414 return; 2415 } 2416 if (newContainerRect.isEmpty()) 2417 newContainerRect = ownRects[i]; 2418 } 2419 } 2420 if (newContainerRect.isEmpty()) 2421 newContainerRect = containerRect; 2422 2423 // If it's possible for children to have rects outside our bounds, then we need to descend into 2424 // the children and compute them. 2425 // Ideally there would be other cases where we could detect that children couldn't have rects 2426 // outside our bounds and prune the tree walk. 2427 // Note that we don't use Region here because Union is O(N) - better to just keep a list of 2428 // partially redundant rectangles. If we find examples where this is expensive, then we could 2429 // rewrite Region to be more efficient. See https://bugs.webkit.org/show_bug.cgi?id=100814. 2430 if (!isRenderView()) { 2431 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { 2432 curr->addLayerHitTestRects(layerRects, currentLayer, layerOffset, newContainerRect); 2433 } 2434 } 2435 } 2436 2437 bool RenderObject::isRooted(RenderView** view) const 2438 { 2439 const RenderObject* o = this; 2440 while (o->parent()) 2441 o = o->parent(); 2442 2443 if (!o->isRenderView()) 2444 return false; 2445 2446 if (view) 2447 *view = const_cast<RenderView*>(toRenderView(o)); 2448 2449 return true; 2450 } 2451 2452 RenderObject* RenderObject::rendererForRootBackground() 2453 { 2454 ASSERT(isRoot()); 2455 if (!hasBackground() && node() && isHTMLHtmlElement(node())) { 2456 // Locate the <body> element using the DOM. This is easier than trying 2457 // to crawl around a render tree with potential :before/:after content and 2458 // anonymous blocks created by inline <body> tags etc. We can locate the <body> 2459 // render object very easily via the DOM. 2460 HTMLElement* body = document().body(); 2461 RenderObject* bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; 2462 if (bodyObject) 2463 return bodyObject; 2464 } 2465 2466 return this; 2467 } 2468 2469 RespectImageOrientationEnum RenderObject::shouldRespectImageOrientation() const 2470 { 2471 // Respect the image's orientation if it's being used as a full-page image or it's 2472 // an <img> and the setting to respect it everywhere is set. 2473 return document().isImageDocument() 2474 || (document().settings() && document().settings()->shouldRespectImageOrientation() && node() && node()->hasTagName(HTMLNames::imgTag)) ? RespectImageOrientation : DoNotRespectImageOrientation; 2475 } 2476 2477 bool RenderObject::hasOutlineAnnotation() const 2478 { 2479 return node() && node()->isLink() && document().printing(); 2480 } 2481 2482 bool RenderObject::hasEntirelyFixedBackground() const 2483 { 2484 return m_style->hasEntirelyFixedBackground(); 2485 } 2486 2487 RenderObject* RenderObject::container(const RenderLayerModelObject* repaintContainer, bool* repaintContainerSkipped) const 2488 { 2489 if (repaintContainerSkipped) 2490 *repaintContainerSkipped = false; 2491 2492 // This method is extremely similar to containingBlock(), but with a few notable 2493 // exceptions. 2494 // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when 2495 // the object is not part of the primary document subtree yet. 2496 // (2) For normal flow elements, it just returns the parent. 2497 // (3) For absolute positioned elements, it will return a relative positioned inline. 2498 // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle 2499 // the layout of the positioned object. This does mean that computePositionedLogicalWidth and 2500 // computePositionedLogicalHeight have to use container(). 2501 RenderObject* o = parent(); 2502 2503 if (isText()) 2504 return o; 2505 2506 EPosition pos = m_style->position(); 2507 if (pos == FixedPosition) { 2508 return containerForFixedPosition(repaintContainer, repaintContainerSkipped); 2509 } else if (pos == AbsolutePosition) { 2510 // We technically just want our containing block, but 2511 // we may not have one if we're part of an uninstalled 2512 // subtree. We'll climb as high as we can though. 2513 while (o) { 2514 if (o->style()->position() != StaticPosition) 2515 break; 2516 2517 if (o->canContainFixedPositionObjects()) 2518 break; 2519 2520 if (repaintContainerSkipped && o == repaintContainer) 2521 *repaintContainerSkipped = true; 2522 2523 o = o->parent(); 2524 } 2525 } 2526 2527 return o; 2528 } 2529 2530 bool RenderObject::isSelectionBorder() const 2531 { 2532 SelectionState st = selectionState(); 2533 return st == SelectionStart || st == SelectionEnd || st == SelectionBoth; 2534 } 2535 2536 inline void RenderObject::clearLayoutRootIfNeeded() const 2537 { 2538 if (frame()) { 2539 if (FrameView* view = frame()->view()) { 2540 if (view->layoutRoot() == this) { 2541 if (!documentBeingDestroyed()) 2542 ASSERT_NOT_REACHED(); 2543 // This indicates a failure to layout the child, which is why 2544 // the layout root is still set to |this|. Make sure to clear it 2545 // since we are getting destroyed. 2546 view->clearLayoutRoot(); 2547 } 2548 } 2549 } 2550 } 2551 2552 void RenderObject::willBeDestroyed() 2553 { 2554 // Destroy any leftover anonymous children. 2555 RenderObjectChildList* children = virtualChildren(); 2556 if (children) 2557 children->destroyLeftoverChildren(); 2558 2559 // If this renderer is being autoscrolled, stop the autoscrolling. 2560 if (Frame* frame = this->frame()) { 2561 if (frame->page()) 2562 frame->page()->autoscrollController().stopAutoscrollIfNeeded(this); 2563 frame->animation().cancelAnimations(this); 2564 } 2565 2566 // For accessibility management, notify the parent of the imminent change to its child set. 2567 // We do it now, before remove(), while the parent pointer is still available. 2568 if (AXObjectCache* cache = document().existingAXObjectCache()) 2569 cache->childrenChanged(this->parent()); 2570 2571 remove(); 2572 2573 // The remove() call above may invoke axObjectCache()->childrenChanged() on the parent, which may require the AX render 2574 // object for this renderer. So we remove the AX render object now, after the renderer is removed. 2575 if (AXObjectCache* cache = document().existingAXObjectCache()) 2576 cache->remove(this); 2577 2578 #ifndef NDEBUG 2579 if (!documentBeingDestroyed() && view() && view()->hasRenderNamedFlowThreads()) { 2580 // After remove, the object and the associated information should not be in any flow thread. 2581 const RenderNamedFlowThreadList* flowThreadList = view()->flowThreadController()->renderNamedFlowThreadList(); 2582 for (RenderNamedFlowThreadList::const_iterator iter = flowThreadList->begin(); iter != flowThreadList->end(); ++iter) { 2583 const RenderNamedFlowThread* renderFlowThread = *iter; 2584 ASSERT(!renderFlowThread->hasChild(this)); 2585 ASSERT(!renderFlowThread->hasChildInfo(this)); 2586 } 2587 } 2588 #endif 2589 2590 // If this renderer had a parent, remove should have destroyed any counters 2591 // attached to this renderer and marked the affected other counters for 2592 // reevaluation. This apparently redundant check is here for the case when 2593 // this renderer had no parent at the time remove() was called. 2594 2595 if (hasCounterNodeMap()) 2596 RenderCounter::destroyCounterNodes(*this); 2597 2598 setAncestorLineBoxDirty(false); 2599 2600 clearLayoutRootIfNeeded(); 2601 } 2602 2603 void RenderObject::insertedIntoTree() 2604 { 2605 // FIXME: We should ASSERT(isRooted()) here but generated content makes some out-of-order insertion. 2606 2607 // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children 2608 // and don't have a layer attached to ourselves. 2609 RenderLayer* layer = 0; 2610 if (firstChild() || hasLayer()) { 2611 layer = parent()->enclosingLayer(); 2612 addLayers(layer); 2613 } 2614 2615 // If |this| is visible but this object was not, tell the layer it has some visible content 2616 // that needs to be drawn and layer visibility optimization can't be used 2617 if (parent()->style()->visibility() != VISIBLE && style()->visibility() == VISIBLE && !hasLayer()) { 2618 if (!layer) 2619 layer = parent()->enclosingLayer(); 2620 if (layer) 2621 layer->setHasVisibleContent(); 2622 } 2623 2624 if (!isFloating() && parent()->childrenInline()) 2625 parent()->dirtyLinesFromChangedChild(this); 2626 2627 if (RenderNamedFlowThread* containerFlowThread = parent()->renderNamedFlowThreadWrapper()) 2628 containerFlowThread->addFlowChild(this); 2629 } 2630 2631 void RenderObject::willBeRemovedFromTree() 2632 { 2633 // FIXME: We should ASSERT(isRooted()) but we have some out-of-order removals which would need to be fixed first. 2634 2635 // If we remove a visible child from an invisible parent, we don't know the layer visibility any more. 2636 RenderLayer* layer = 0; 2637 if (parent()->style()->visibility() != VISIBLE && style()->visibility() == VISIBLE && !hasLayer()) { 2638 if ((layer = parent()->enclosingLayer())) 2639 layer->dirtyVisibleContentStatus(); 2640 } 2641 2642 // Keep our layer hierarchy updated. 2643 if (firstChild() || hasLayer()) { 2644 if (!layer) 2645 layer = parent()->enclosingLayer(); 2646 removeLayers(layer); 2647 } 2648 2649 if (isOutOfFlowPositioned() && parent()->childrenInline()) 2650 parent()->dirtyLinesFromChangedChild(this); 2651 2652 removeFromRenderFlowThread(); 2653 2654 if (RenderNamedFlowThread* containerFlowThread = parent()->renderNamedFlowThreadWrapper()) 2655 containerFlowThread->removeFlowChild(this); 2656 2657 // Update cached boundaries in SVG renderers if a child is removed. 2658 parent()->setNeedsBoundariesUpdate(); 2659 } 2660 2661 void RenderObject::removeFromRenderFlowThread() 2662 { 2663 if (flowThreadState() == NotInsideFlowThread) 2664 return; 2665 2666 // Sometimes we remove the element from the flow, but it's not destroyed at that time. 2667 // It's only until later when we actually destroy it and remove all the children from it. 2668 // Currently, that happens for firstLetter elements and list markers. 2669 // Pass in the flow thread so that we don't have to look it up for all the children. 2670 removeFromRenderFlowThreadRecursive(flowThreadContainingBlock()); 2671 } 2672 2673 void RenderObject::removeFromRenderFlowThreadRecursive(RenderFlowThread* renderFlowThread) 2674 { 2675 if (const RenderObjectChildList* children = virtualChildren()) { 2676 for (RenderObject* child = children->firstChild(); child; child = child->nextSibling()) 2677 child->removeFromRenderFlowThreadRecursive(renderFlowThread); 2678 } 2679 2680 RenderFlowThread* localFlowThread = renderFlowThread; 2681 if (flowThreadState() == InsideInFlowThread) 2682 localFlowThread = flowThreadContainingBlock(); // We have to ask. We can't just assume we are in the same flow thread. 2683 if (localFlowThread) 2684 localFlowThread->removeFlowChildInfo(this); 2685 setFlowThreadState(NotInsideFlowThread); 2686 } 2687 2688 void RenderObject::destroyAndCleanupAnonymousWrappers() 2689 { 2690 // If the tree is destroyed, there is no need for a clean-up phase. 2691 if (documentBeingDestroyed()) { 2692 destroy(); 2693 return; 2694 } 2695 2696 RenderObject* destroyRoot = this; 2697 for (RenderObject* destroyRootParent = destroyRoot->parent(); destroyRootParent && destroyRootParent->isAnonymous(); destroyRoot = destroyRootParent, destroyRootParent = destroyRootParent->parent()) { 2698 // Anonymous block continuations are tracked and destroyed elsewhere (see the bottom of RenderBlock::removeChild) 2699 if (destroyRootParent->isRenderBlock() && toRenderBlock(destroyRootParent)->isAnonymousBlockContinuation()) 2700 break; 2701 // Render flow threads are tracked by the FlowThreadController, so we can't destroy them here. 2702 // Column spans are tracked elsewhere. 2703 if (destroyRootParent->isRenderFlowThread() || destroyRootParent->isAnonymousColumnSpanBlock()) 2704 break; 2705 2706 if (destroyRootParent->firstChild() != this || destroyRootParent->lastChild() != this) 2707 break; 2708 } 2709 2710 destroyRoot->destroy(); 2711 2712 // WARNING: |this| is deleted here. 2713 } 2714 2715 void RenderObject::removeShapeImageClient(ShapeValue* shapeValue) 2716 { 2717 if (!shapeValue) 2718 return; 2719 if (StyleImage* shapeImage = shapeValue->image()) 2720 shapeImage->removeClient(this); 2721 } 2722 2723 void RenderObject::destroy() 2724 { 2725 willBeDestroyed(); 2726 postDestroy(); 2727 } 2728 2729 void RenderObject::postDestroy() 2730 { 2731 // It seems ugly that this is not in willBeDestroyed(). 2732 if (m_style) { 2733 for (const FillLayer* bgLayer = m_style->backgroundLayers(); bgLayer; bgLayer = bgLayer->next()) { 2734 if (StyleImage* backgroundImage = bgLayer->image()) 2735 backgroundImage->removeClient(this); 2736 } 2737 2738 for (const FillLayer* maskLayer = m_style->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { 2739 if (StyleImage* maskImage = maskLayer->image()) 2740 maskImage->removeClient(this); 2741 } 2742 2743 if (StyleImage* borderImage = m_style->borderImage().image()) 2744 borderImage->removeClient(this); 2745 2746 if (StyleImage* maskBoxImage = m_style->maskBoxImage().image()) 2747 maskBoxImage->removeClient(this); 2748 2749 removeShapeImageClient(m_style->shapeInside()); 2750 removeShapeImageClient(m_style->shapeOutside()); 2751 } 2752 2753 delete this; 2754 } 2755 2756 PositionWithAffinity RenderObject::positionForPoint(const LayoutPoint&) 2757 { 2758 return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); 2759 } 2760 2761 void RenderObject::updateDragState(bool dragOn) 2762 { 2763 bool valueChanged = (dragOn != isDragging()); 2764 setIsDragging(dragOn); 2765 if (valueChanged && node() && (style()->affectedByDrag() || (node()->isElementNode() && toElement(node())->childrenAffectedByDrag()))) 2766 node()->setNeedsStyleRecalc(); 2767 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) 2768 curr->updateDragState(dragOn); 2769 } 2770 2771 CompositingState RenderObject::compositingState() const 2772 { 2773 return hasLayer() ? toRenderLayerModelObject(this)->layer()->compositingState() : NotComposited; 2774 } 2775 2776 bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestFilter hitTestFilter) 2777 { 2778 bool inside = false; 2779 if (hitTestFilter != HitTestSelf) { 2780 // First test the foreground layer (lines and inlines). 2781 inside = nodeAtPoint(request, result, locationInContainer, accumulatedOffset, HitTestForeground); 2782 2783 // Test floats next. 2784 if (!inside) 2785 inside = nodeAtPoint(request, result, locationInContainer, accumulatedOffset, HitTestFloat); 2786 2787 // Finally test to see if the mouse is in the background (within a child block's background). 2788 if (!inside) 2789 inside = nodeAtPoint(request, result, locationInContainer, accumulatedOffset, HitTestChildBlockBackgrounds); 2790 } 2791 2792 // See if the mouse is inside us but not any of our descendants 2793 if (hitTestFilter != HitTestDescendants && !inside) 2794 inside = nodeAtPoint(request, result, locationInContainer, accumulatedOffset, HitTestBlockBackground); 2795 2796 return inside; 2797 } 2798 2799 void RenderObject::updateHitTestResult(HitTestResult& result, const LayoutPoint& point) 2800 { 2801 if (result.innerNode()) 2802 return; 2803 2804 Node* node = this->node(); 2805 2806 // If we hit the anonymous renderers inside generated content we should 2807 // actually hit the generated content so walk up to the PseudoElement. 2808 if (!node && parent() && parent()->isBeforeOrAfterContent()) { 2809 for (RenderObject* renderer = parent(); renderer && !node; renderer = renderer->parent()) 2810 node = renderer->node(); 2811 } 2812 2813 if (node) { 2814 result.setInnerNode(node); 2815 if (!result.innerNonSharedNode()) 2816 result.setInnerNonSharedNode(node); 2817 result.setLocalPoint(point); 2818 } 2819 } 2820 2821 bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& /*locationInContainer*/, const LayoutPoint& /*accumulatedOffset*/, HitTestAction) 2822 { 2823 return false; 2824 } 2825 2826 void RenderObject::scheduleRelayout() 2827 { 2828 if (isRenderView()) { 2829 FrameView* view = toRenderView(this)->frameView(); 2830 if (view) 2831 view->scheduleRelayout(); 2832 } else { 2833 if (isRooted()) { 2834 if (RenderView* renderView = view()) { 2835 if (FrameView* frameView = renderView->frameView()) 2836 frameView->scheduleRelayoutOfSubtree(this); 2837 } 2838 } 2839 } 2840 } 2841 2842 void RenderObject::layout() 2843 { 2844 ASSERT(needsLayout()); 2845 LayoutRectRecorder recorder(*this); 2846 RenderObject* child = firstChild(); 2847 while (child) { 2848 child->layoutIfNeeded(); 2849 ASSERT(!child->needsLayout()); 2850 child = child->nextSibling(); 2851 } 2852 clearNeedsLayout(); 2853 } 2854 2855 void RenderObject::didLayout(ResourceLoadPriorityOptimizer& priorityModifier) 2856 { 2857 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) 2858 child->didLayout(priorityModifier); 2859 } 2860 2861 void RenderObject::didScroll(ResourceLoadPriorityOptimizer& priorityModifier) 2862 { 2863 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) 2864 child->didScroll(priorityModifier); 2865 } 2866 2867 void RenderObject::forceLayout() 2868 { 2869 setSelfNeedsLayout(true); 2870 setShouldDoFullRepaintAfterLayout(true); 2871 layout(); 2872 } 2873 2874 // FIXME: Does this do anything different than forceLayout given that we don't walk 2875 // the containing block chain. If not, we should change all callers to use forceLayout. 2876 void RenderObject::forceChildLayout() 2877 { 2878 setNormalChildNeedsLayout(true); 2879 layout(); 2880 } 2881 2882 enum StyleCacheState { 2883 Cached, 2884 Uncached 2885 }; 2886 2887 static PassRefPtr<RenderStyle> firstLineStyleForCachedUncachedType(StyleCacheState type, const RenderObject* renderer, RenderStyle* style) 2888 { 2889 const RenderObject* rendererForFirstLineStyle = renderer; 2890 if (renderer->isBeforeOrAfterContent()) 2891 rendererForFirstLineStyle = renderer->parent(); 2892 2893 if (rendererForFirstLineStyle->isRenderBlockFlow() || rendererForFirstLineStyle->isRenderButton()) { 2894 if (RenderBlock* firstLineBlock = rendererForFirstLineStyle->firstLineBlock()) { 2895 if (type == Cached) 2896 return firstLineBlock->getCachedPseudoStyle(FIRST_LINE, style); 2897 return firstLineBlock->getUncachedPseudoStyle(PseudoStyleRequest(FIRST_LINE), style, firstLineBlock == renderer ? style : 0); 2898 } 2899 } else if (!rendererForFirstLineStyle->isAnonymous() && rendererForFirstLineStyle->isRenderInline()) { 2900 RenderStyle* parentStyle = rendererForFirstLineStyle->parent()->firstLineStyle(); 2901 if (parentStyle != rendererForFirstLineStyle->parent()->style()) { 2902 if (type == Cached) { 2903 // A first-line style is in effect. Cache a first-line style for ourselves. 2904 rendererForFirstLineStyle->style()->setHasPseudoStyle(FIRST_LINE_INHERITED); 2905 return rendererForFirstLineStyle->getCachedPseudoStyle(FIRST_LINE_INHERITED, parentStyle); 2906 } 2907 return rendererForFirstLineStyle->getUncachedPseudoStyle(PseudoStyleRequest(FIRST_LINE_INHERITED), parentStyle, style); 2908 } 2909 } 2910 return 0; 2911 } 2912 2913 PassRefPtr<RenderStyle> RenderObject::uncachedFirstLineStyle(RenderStyle* style) const 2914 { 2915 if (!document().styleEngine()->usesFirstLineRules()) 2916 return 0; 2917 2918 ASSERT(!isText()); 2919 2920 return firstLineStyleForCachedUncachedType(Uncached, this, style); 2921 } 2922 2923 RenderStyle* RenderObject::cachedFirstLineStyle() const 2924 { 2925 ASSERT(document().styleEngine()->usesFirstLineRules()); 2926 2927 if (RefPtr<RenderStyle> style = firstLineStyleForCachedUncachedType(Cached, isText() ? parent() : this, m_style.get())) 2928 return style.get(); 2929 2930 return m_style.get(); 2931 } 2932 2933 RenderStyle* RenderObject::getCachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle) const 2934 { 2935 if (pseudo < FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) 2936 return 0; 2937 2938 RenderStyle* cachedStyle = style()->getCachedPseudoStyle(pseudo); 2939 if (cachedStyle) 2940 return cachedStyle; 2941 2942 RefPtr<RenderStyle> result = getUncachedPseudoStyle(PseudoStyleRequest(pseudo), parentStyle); 2943 if (result) 2944 return style()->addCachedPseudoStyle(result.release()); 2945 return 0; 2946 } 2947 2948 PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(const PseudoStyleRequest& pseudoStyleRequest, RenderStyle* parentStyle, RenderStyle* ownStyle) const 2949 { 2950 if (pseudoStyleRequest.pseudoId < FIRST_INTERNAL_PSEUDOID && !ownStyle && !style()->hasPseudoStyle(pseudoStyleRequest.pseudoId)) 2951 return 0; 2952 2953 if (!parentStyle) { 2954 ASSERT(!ownStyle); 2955 parentStyle = style(); 2956 } 2957 2958 // FIXME: This "find nearest element parent" should be a helper function. 2959 Node* n = node(); 2960 while (n && !n->isElementNode()) 2961 n = n->parentNode(); 2962 if (!n) 2963 return 0; 2964 Element* element = toElement(n); 2965 2966 if (pseudoStyleRequest.pseudoId == FIRST_LINE_INHERITED) { 2967 RefPtr<RenderStyle> result = document().ensureStyleResolver().styleForElement(element, parentStyle, DisallowStyleSharing); 2968 result->setStyleType(FIRST_LINE_INHERITED); 2969 return result.release(); 2970 } 2971 2972 return document().ensureStyleResolver().pseudoStyleForElement(element, pseudoStyleRequest, parentStyle); 2973 } 2974 2975 bool RenderObject::hasBlendMode() const 2976 { 2977 return RuntimeEnabledFeatures::cssCompositingEnabled() && style() && style()->hasBlendMode(); 2978 } 2979 2980 static Color decorationColor(const RenderObject* object, RenderStyle* style) 2981 { 2982 Color result; 2983 // Check for text decoration color first. 2984 result = object->resolveColor(style, CSSPropertyTextDecorationColor); 2985 if (result.isValid()) 2986 return result; 2987 if (style->textStrokeWidth() > 0) { 2988 // Prefer stroke color if possible but not if it's fully transparent. 2989 result = object->resolveColor(style, CSSPropertyWebkitTextStrokeColor); 2990 if (result.alpha()) 2991 return result; 2992 } 2993 2994 result = object->resolveColor(style, CSSPropertyWebkitTextFillColor); 2995 return result; 2996 } 2997 2998 void RenderObject::getTextDecorationColors(unsigned decorations, Color& underline, Color& overline, 2999 Color& linethrough, bool quirksMode, bool firstlineStyle) 3000 { 3001 RenderObject* curr = this; 3002 RenderStyle* styleToUse = 0; 3003 unsigned currDecs = TextDecorationNone; 3004 Color resultColor; 3005 do { 3006 styleToUse = curr->style(firstlineStyle); 3007 currDecs = styleToUse->textDecoration(); 3008 currDecs &= decorations; 3009 resultColor = decorationColor(this, styleToUse); 3010 // Parameter 'decorations' is cast as an int to enable the bitwise operations below. 3011 if (currDecs) { 3012 if (currDecs & TextDecorationUnderline) { 3013 decorations &= ~TextDecorationUnderline; 3014 underline = resultColor; 3015 } 3016 if (currDecs & TextDecorationOverline) { 3017 decorations &= ~TextDecorationOverline; 3018 overline = resultColor; 3019 } 3020 if (currDecs & TextDecorationLineThrough) { 3021 decorations &= ~TextDecorationLineThrough; 3022 linethrough = resultColor; 3023 } 3024 } 3025 if (curr->isRubyText()) 3026 return; 3027 curr = curr->parent(); 3028 if (curr && curr->isAnonymousBlock() && toRenderBlock(curr)->continuation()) 3029 curr = toRenderBlock(curr)->continuation(); 3030 } while (curr && decorations && (!quirksMode || !curr->node() || (!isHTMLAnchorElement(curr->node()) && !curr->node()->hasTagName(fontTag)))); 3031 3032 // If we bailed out, use the element we bailed out at (typically a <font> or <a> element). 3033 if (decorations && curr) { 3034 styleToUse = curr->style(firstlineStyle); 3035 resultColor = decorationColor(this, styleToUse); 3036 if (decorations & TextDecorationUnderline) 3037 underline = resultColor; 3038 if (decorations & TextDecorationOverline) 3039 overline = resultColor; 3040 if (decorations & TextDecorationLineThrough) 3041 linethrough = resultColor; 3042 } 3043 } 3044 3045 void RenderObject::addAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) 3046 { 3047 // Convert the style regions to absolute coordinates. 3048 if (style()->visibility() != VISIBLE || !isBox()) 3049 return; 3050 3051 if (style()->getDraggableRegionMode() == DraggableRegionNone) 3052 return; 3053 3054 RenderBox* box = toRenderBox(this); 3055 FloatRect localBounds(FloatPoint(), FloatSize(box->width(), box->height())); 3056 FloatRect absBounds = localToAbsoluteQuad(localBounds).boundingBox(); 3057 3058 AnnotatedRegionValue region; 3059 region.draggable = style()->getDraggableRegionMode() == DraggableRegionDrag; 3060 region.bounds = LayoutRect(absBounds); 3061 regions.append(region); 3062 } 3063 3064 void RenderObject::collectAnnotatedRegions(Vector<AnnotatedRegionValue>& regions) 3065 { 3066 // RenderTexts don't have their own style, they just use their parent's style, 3067 // so we don't want to include them. 3068 if (isText()) 3069 return; 3070 3071 addAnnotatedRegions(regions); 3072 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) 3073 curr->collectAnnotatedRegions(regions); 3074 } 3075 3076 bool RenderObject::willRenderImage(ImageResource*) 3077 { 3078 // Without visibility we won't render (and therefore don't care about animation). 3079 if (style()->visibility() != VISIBLE) 3080 return false; 3081 3082 // We will not render a new image when Active DOM is suspended 3083 if (document().activeDOMObjectsAreSuspended()) 3084 return false; 3085 3086 // If we're not in a window (i.e., we're dormant from being in a background tab) 3087 // then we don't want to render either. 3088 return !document().view()->isOffscreen(); 3089 } 3090 3091 int RenderObject::maximalOutlineSize(PaintPhase p) const 3092 { 3093 if (p != PaintPhaseOutline && p != PaintPhaseSelfOutline && p != PaintPhaseChildOutlines) 3094 return 0; 3095 return view()->maximalOutlineSize(); 3096 } 3097 3098 int RenderObject::caretMinOffset() const 3099 { 3100 return 0; 3101 } 3102 3103 int RenderObject::caretMaxOffset() const 3104 { 3105 if (isReplaced()) 3106 return node() ? max(1U, node()->childNodeCount()) : 1; 3107 if (isHR()) 3108 return 1; 3109 return 0; 3110 } 3111 3112 int RenderObject::previousOffset(int current) const 3113 { 3114 return current - 1; 3115 } 3116 3117 int RenderObject::previousOffsetForBackwardDeletion(int current) const 3118 { 3119 return current - 1; 3120 } 3121 3122 int RenderObject::nextOffset(int current) const 3123 { 3124 return current + 1; 3125 } 3126 3127 void RenderObject::adjustRectForOutlineAndShadow(LayoutRect& rect) const 3128 { 3129 int outlineSize = outlineStyleForRepaint()->outlineSize(); 3130 if (const ShadowList* boxShadow = style()->boxShadow()) { 3131 boxShadow->adjustRectForShadow(rect, outlineSize); 3132 return; 3133 } 3134 3135 rect.inflate(outlineSize); 3136 } 3137 3138 AnimationController& RenderObject::animation() const 3139 { 3140 return frame()->animation(); 3141 } 3142 3143 bool RenderObject::isInert() const 3144 { 3145 const RenderObject* renderer = this; 3146 while (!renderer->node()) 3147 renderer = renderer->parent(); 3148 return renderer->node()->isInert(); 3149 } 3150 3151 void RenderObject::imageChanged(ImageResource* image, const IntRect* rect) 3152 { 3153 imageChanged(static_cast<WrappedImagePtr>(image), rect); 3154 } 3155 3156 RenderObject* RenderObject::hoverAncestor() const 3157 { 3158 // When searching for the hover ancestor and encountering a named flow thread, 3159 // the search will continue with the DOM ancestor of the top-most element 3160 // in the named flow thread. 3161 // See https://code.google.com/p/chromium/issues/detail?id=243278 3162 RenderObject* hoverAncestor = parent(); 3163 3164 // Skip anonymous blocks directly flowed into flow threads as it would 3165 // prevent us from continuing the search on the DOM tree when reaching the named flow thread. 3166 if (hoverAncestor && hoverAncestor->isAnonymousBlock() && hoverAncestor->parent() && hoverAncestor->parent()->isRenderNamedFlowThread()) 3167 hoverAncestor = hoverAncestor->parent(); 3168 3169 if (hoverAncestor && hoverAncestor->isRenderNamedFlowThread()) { 3170 hoverAncestor = 0; 3171 3172 Node* node = this->node(); 3173 if (node) { 3174 Node* domAncestorNode = node->parentNode(); 3175 if (domAncestorNode) 3176 hoverAncestor = domAncestorNode->renderer(); 3177 } 3178 } 3179 3180 return hoverAncestor; 3181 } 3182 3183 Element* RenderObject::offsetParent() const 3184 { 3185 if (isRoot() || isBody()) 3186 return 0; 3187 3188 if (isOutOfFlowPositioned() && style()->position() == FixedPosition) 3189 return 0; 3190 3191 // If A is an area HTML element which has a map HTML element somewhere in the ancestor 3192 // chain return the nearest ancestor map HTML element and stop this algorithm. 3193 // FIXME: Implement! 3194 3195 float effectiveZoom = style()->effectiveZoom(); 3196 Node* node = 0; 3197 for (RenderObject* ancestor = parent(); ancestor; ancestor = ancestor->parent()) { 3198 // Spec: http://www.w3.org/TR/cssom-view/#offset-attributes 3199 3200 // CSS regions specification says that region flows should return the body element as their offsetParent. 3201 if (ancestor->isRenderNamedFlowThread()) 3202 return document().body(); 3203 3204 node = ancestor->node(); 3205 3206 if (!node) 3207 continue; 3208 3209 if (ancestor->isPositioned()) 3210 break; 3211 3212 if (node->hasTagName(HTMLNames::bodyTag)) 3213 break; 3214 3215 if (!isPositioned() && (isHTMLTableElement(node) || node->hasTagName(tdTag) || node->hasTagName(thTag))) 3216 break; 3217 3218 // Webkit specific extension where offsetParent stops at zoom level changes. 3219 if (effectiveZoom != ancestor->style()->effectiveZoom()) 3220 break; 3221 } 3222 3223 return node && node->isElementNode() ? toElement(node) : 0; 3224 } 3225 3226 PositionWithAffinity RenderObject::createPositionWithAffinity(int offset, EAffinity affinity) 3227 { 3228 // If this is a non-anonymous renderer in an editable area, then it's simple. 3229 if (Node* node = nonPseudoNode()) { 3230 if (!node->rendererIsEditable()) { 3231 // If it can be found, we prefer a visually equivalent position that is editable. 3232 Position position = createLegacyEditingPosition(node, offset); 3233 Position candidate = position.downstream(CanCrossEditingBoundary); 3234 if (candidate.deprecatedNode()->rendererIsEditable()) 3235 return PositionWithAffinity(candidate, affinity); 3236 candidate = position.upstream(CanCrossEditingBoundary); 3237 if (candidate.deprecatedNode()->rendererIsEditable()) 3238 return PositionWithAffinity(candidate, affinity); 3239 } 3240 // FIXME: Eliminate legacy editing positions 3241 return PositionWithAffinity(createLegacyEditingPosition(node, offset), affinity); 3242 } 3243 3244 // We don't want to cross the boundary between editable and non-editable 3245 // regions of the document, but that is either impossible or at least 3246 // extremely unlikely in any normal case because we stop as soon as we 3247 // find a single non-anonymous renderer. 3248 3249 // Find a nearby non-anonymous renderer. 3250 RenderObject* child = this; 3251 while (RenderObject* parent = child->parent()) { 3252 // Find non-anonymous content after. 3253 RenderObject* renderer = child; 3254 while ((renderer = renderer->nextInPreOrder(parent))) { 3255 if (Node* node = renderer->nonPseudoNode()) 3256 return PositionWithAffinity(firstPositionInOrBeforeNode(node), DOWNSTREAM); 3257 } 3258 3259 // Find non-anonymous content before. 3260 renderer = child; 3261 while ((renderer = renderer->previousInPreOrder())) { 3262 if (renderer == parent) 3263 break; 3264 if (Node* node = renderer->nonPseudoNode()) 3265 return PositionWithAffinity(lastPositionInOrAfterNode(node), DOWNSTREAM); 3266 } 3267 3268 // Use the parent itself unless it too is anonymous. 3269 if (Node* node = parent->nonPseudoNode()) 3270 return PositionWithAffinity(firstPositionInOrBeforeNode(node), DOWNSTREAM); 3271 3272 // Repeat at the next level up. 3273 child = parent; 3274 } 3275 3276 // Everything was anonymous. Give up. 3277 return PositionWithAffinity(); 3278 } 3279 3280 PositionWithAffinity RenderObject::createPositionWithAffinity(const Position& position) 3281 { 3282 if (position.isNotNull()) 3283 return PositionWithAffinity(position); 3284 3285 ASSERT(!node()); 3286 return createPositionWithAffinity(0, DOWNSTREAM); 3287 } 3288 3289 CursorDirective RenderObject::getCursor(const LayoutPoint&, Cursor&) const 3290 { 3291 return SetCursorBasedOnStyle; 3292 } 3293 3294 bool RenderObject::canUpdateSelectionOnRootLineBoxes() 3295 { 3296 if (needsLayout()) 3297 return false; 3298 3299 RenderBlock* containingBlock = this->containingBlock(); 3300 return containingBlock ? !containingBlock->needsLayout() : true; 3301 } 3302 3303 // We only create "generated" child renderers like one for first-letter if: 3304 // - the firstLetterBlock can have children in the DOM and 3305 // - the block doesn't have any special assumption on its text children. 3306 // This correctly prevents form controls from having such renderers. 3307 bool RenderObject::canHaveGeneratedChildren() const 3308 { 3309 return canHaveChildren(); 3310 } 3311 3312 void RenderObject::setNeedsBoundariesUpdate() 3313 { 3314 if (RenderObject* renderer = parent()) 3315 renderer->setNeedsBoundariesUpdate(); 3316 } 3317 3318 FloatRect RenderObject::objectBoundingBox() const 3319 { 3320 ASSERT_NOT_REACHED(); 3321 return FloatRect(); 3322 } 3323 3324 FloatRect RenderObject::strokeBoundingBox() const 3325 { 3326 ASSERT_NOT_REACHED(); 3327 return FloatRect(); 3328 } 3329 3330 // Returns the smallest rectangle enclosing all of the painted content 3331 // respecting clipping, masking, filters, opacity, stroke-width and markers 3332 FloatRect RenderObject::repaintRectInLocalCoordinates() const 3333 { 3334 ASSERT_NOT_REACHED(); 3335 return FloatRect(); 3336 } 3337 3338 AffineTransform RenderObject::localTransform() const 3339 { 3340 static const AffineTransform identity; 3341 return identity; 3342 } 3343 3344 const AffineTransform& RenderObject::localToParentTransform() const 3345 { 3346 static const AffineTransform identity; 3347 return identity; 3348 } 3349 3350 bool RenderObject::nodeAtFloatPoint(const HitTestRequest&, HitTestResult&, const FloatPoint&, HitTestAction) 3351 { 3352 ASSERT_NOT_REACHED(); 3353 return false; 3354 } 3355 3356 // FIXME: This should really use local coords 3357 // Works on absolute coords - expensive to call 3358 bool RenderObject::isContainedInParentBoundingBox() const 3359 { 3360 if (!parent()) 3361 return false; 3362 3363 IntRect parentRect = parent()->absoluteBoundingBoxRect(); 3364 return parentRect.contains(absoluteBoundingBoxRect()); 3365 } 3366 3367 bool RenderObject::isRelayoutBoundaryForInspector() const 3368 { 3369 return objectIsRelayoutBoundary(this); 3370 } 3371 3372 bool RenderObject::isRenderNamedFlowFragmentContainer() const 3373 { 3374 return isRenderBlockFlow() && toRenderBlockFlow(this)->renderNamedFlowFragment(); 3375 } 3376 3377 } // namespace WebCore 3378 3379 #ifndef NDEBUG 3380 3381 void showTree(const WebCore::RenderObject* object) 3382 { 3383 if (object) 3384 object->showTreeForThis(); 3385 } 3386 3387 void showLineTree(const WebCore::RenderObject* object) 3388 { 3389 if (object) 3390 object->showLineTreeForThis(); 3391 } 3392 3393 void showRenderTree(const WebCore::RenderObject* object1) 3394 { 3395 showRenderTree(object1, 0); 3396 } 3397 3398 void showRenderTree(const WebCore::RenderObject* object1, const WebCore::RenderObject* object2) 3399 { 3400 if (object1) { 3401 const WebCore::RenderObject* root = object1; 3402 while (root->parent()) 3403 root = root->parent(); 3404 root->showRenderTreeAndMark(object1, "*", object2, "-", 0); 3405 } 3406 } 3407 3408 #endif 3409