1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2007 David Smith (catfish.man (at) gmail.com) 5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 */ 23 24 #include "config.h" 25 #include "core/rendering/RenderBlock.h" 26 27 #include "core/HTMLNames.h" 28 #include "core/accessibility/AXObjectCache.h" 29 #include "core/dom/Document.h" 30 #include "core/dom/Element.h" 31 #include "core/events/OverflowEvent.h" 32 #include "core/dom/shadow/ShadowRoot.h" 33 #include "core/editing/Editor.h" 34 #include "core/editing/FrameSelection.h" 35 #include "core/fetch/ResourceLoadPriorityOptimizer.h" 36 #include "core/frame/FrameView.h" 37 #include "core/frame/LocalFrame.h" 38 #include "core/page/Page.h" 39 #include "core/frame/Settings.h" 40 #include "core/rendering/FastTextAutosizer.h" 41 #include "core/rendering/GraphicsContextAnnotator.h" 42 #include "core/rendering/HitTestLocation.h" 43 #include "core/rendering/HitTestResult.h" 44 #include "core/rendering/InlineIterator.h" 45 #include "core/rendering/InlineTextBox.h" 46 #include "core/rendering/LayoutRepainter.h" 47 #include "core/rendering/PaintInfo.h" 48 #include "core/rendering/RenderCombineText.h" 49 #include "core/rendering/RenderDeprecatedFlexibleBox.h" 50 #include "core/rendering/RenderFlexibleBox.h" 51 #include "core/rendering/RenderFlowThread.h" 52 #include "core/rendering/RenderGrid.h" 53 #include "core/rendering/RenderInline.h" 54 #include "core/rendering/RenderLayer.h" 55 #include "core/rendering/RenderMarquee.h" 56 #include "core/rendering/RenderRegion.h" 57 #include "core/rendering/RenderTableCell.h" 58 #include "core/rendering/RenderTextControl.h" 59 #include "core/rendering/RenderTextFragment.h" 60 #include "core/rendering/RenderTheme.h" 61 #include "core/rendering/RenderView.h" 62 #include "core/rendering/shapes/ShapeOutsideInfo.h" 63 #include "core/rendering/style/ContentData.h" 64 #include "core/rendering/style/RenderStyle.h" 65 #include "platform/geometry/FloatQuad.h" 66 #include "platform/geometry/TransformState.h" 67 #include "platform/graphics/GraphicsContextCullSaver.h" 68 #include "platform/graphics/GraphicsContextStateSaver.h" 69 #include "wtf/StdLibExtras.h" 70 #include "wtf/TemporaryChange.h" 71 72 using namespace std; 73 using namespace WTF; 74 using namespace Unicode; 75 76 namespace WebCore { 77 78 using namespace HTMLNames; 79 80 struct SameSizeAsRenderBlock : public RenderBox { 81 void* pointers[1]; 82 RenderObjectChildList children; 83 RenderLineBoxList lineBoxes; 84 uint32_t bitfields; 85 }; 86 87 struct SameSizeAsRenderBlockRareData { 88 int paginationStrut; 89 int pageLogicalOffset; 90 uint32_t bitfields; 91 }; 92 93 COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), RenderBlock_should_stay_small); 94 COMPILE_ASSERT(sizeof(RenderBlock::RenderBlockRareData) == sizeof(SameSizeAsRenderBlockRareData), RenderBlockRareData_should_stay_small); 95 96 typedef WTF::HashMap<const RenderBox*, OwnPtr<ColumnInfo> > ColumnInfoMap; 97 static ColumnInfoMap* gColumnInfoMap = 0; 98 99 static TrackedDescendantsMap* gPositionedDescendantsMap = 0; 100 static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0; 101 102 static TrackedContainerMap* gPositionedContainerMap = 0; 103 static TrackedContainerMap* gPercentHeightContainerMap = 0; 104 105 typedef WTF::HashMap<RenderBlock*, OwnPtr<ListHashSet<RenderInline*> > > ContinuationOutlineTableMap; 106 107 typedef WTF::HashSet<RenderBlock*> DelayedUpdateScrollInfoSet; 108 static int gDelayUpdateScrollInfo = 0; 109 static DelayedUpdateScrollInfoSet* gDelayedUpdateScrollInfoSet = 0; 110 111 static bool gColumnFlowSplitEnabled = true; 112 113 // This class helps dispatching the 'overflow' event on layout change. overflow can be set on RenderBoxes, yet the existing code 114 // only works on RenderBlocks. If this changes, this class should be shared with other RenderBoxes. 115 class OverflowEventDispatcher { 116 WTF_MAKE_NONCOPYABLE(OverflowEventDispatcher); 117 public: 118 OverflowEventDispatcher(const RenderBlock* block) 119 : m_block(block) 120 , m_hadHorizontalLayoutOverflow(false) 121 , m_hadVerticalLayoutOverflow(false) 122 { 123 m_shouldDispatchEvent = !m_block->isAnonymous() && m_block->hasOverflowClip() && m_block->document().hasListenerType(Document::OVERFLOWCHANGED_LISTENER); 124 if (m_shouldDispatchEvent) { 125 m_hadHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow(); 126 m_hadVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow(); 127 } 128 } 129 130 ~OverflowEventDispatcher() 131 { 132 if (!m_shouldDispatchEvent) 133 return; 134 135 bool hasHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow(); 136 bool hasVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow(); 137 138 bool horizontalLayoutOverflowChanged = hasHorizontalLayoutOverflow != m_hadHorizontalLayoutOverflow; 139 bool verticalLayoutOverflowChanged = hasVerticalLayoutOverflow != m_hadVerticalLayoutOverflow; 140 141 if (!horizontalLayoutOverflowChanged && !verticalLayoutOverflowChanged) 142 return; 143 144 RefPtrWillBeRawPtr<OverflowEvent> event = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow); 145 event->setTarget(m_block->node()); 146 m_block->document().enqueueAnimationFrameEvent(event.release()); 147 } 148 149 private: 150 const RenderBlock* m_block; 151 bool m_shouldDispatchEvent; 152 bool m_hadHorizontalLayoutOverflow; 153 bool m_hadVerticalLayoutOverflow; 154 }; 155 156 RenderBlock::RenderBlock(ContainerNode* node) 157 : RenderBox(node) 158 , m_hasMarginBeforeQuirk(false) 159 , m_hasMarginAfterQuirk(false) 160 , m_beingDestroyed(false) 161 , m_hasMarkupTruncation(false) 162 , m_hasBorderOrPaddingLogicalWidthChanged(false) 163 , m_hasOnlySelfCollapsingChildren(false) 164 { 165 setChildrenInline(true); 166 } 167 168 static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, TrackedDescendantsMap*& descendantMap, TrackedContainerMap*& containerMap) 169 { 170 if (OwnPtr<TrackedRendererListHashSet> descendantSet = descendantMap->take(block)) { 171 TrackedRendererListHashSet::iterator end = descendantSet->end(); 172 for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { 173 TrackedContainerMap::iterator it = containerMap->find(*descendant); 174 ASSERT(it != containerMap->end()); 175 if (it == containerMap->end()) 176 continue; 177 HashSet<RenderBlock*>* containerSet = it->value.get(); 178 ASSERT(containerSet->contains(block)); 179 containerSet->remove(block); 180 if (containerSet->isEmpty()) 181 containerMap->remove(it); 182 } 183 } 184 } 185 186 static void appendImageIfNotNull(Vector<ImageResource*>& imageResources, const StyleImage* styleImage) 187 { 188 if (styleImage && styleImage->cachedImage()) { 189 ImageResource* imageResource = styleImage->cachedImage(); 190 if (imageResource && !imageResource->isLoaded()) 191 imageResources.append(styleImage->cachedImage()); 192 } 193 } 194 195 static void appendLayers(Vector<ImageResource*>& images, const FillLayer* styleLayer) 196 { 197 for (const FillLayer* layer = styleLayer; layer; layer = layer->next()) { 198 appendImageIfNotNull(images, layer->image()); 199 } 200 } 201 202 static void appendImagesFromStyle(Vector<ImageResource*>& images, RenderStyle& blockStyle) 203 { 204 appendLayers(images, blockStyle.backgroundLayers()); 205 appendLayers(images, blockStyle.maskLayers()); 206 207 const ContentData* contentData = blockStyle.contentData(); 208 if (contentData && contentData->isImage()) { 209 const ImageContentData* imageContentData = static_cast<const ImageContentData*>(contentData); 210 appendImageIfNotNull(images, imageContentData->image()); 211 } 212 if (blockStyle.boxReflect()) 213 appendImageIfNotNull(images, blockStyle.boxReflect()->mask().image()); 214 appendImageIfNotNull(images, blockStyle.listStyleImage()); 215 appendImageIfNotNull(images, blockStyle.borderImageSource()); 216 appendImageIfNotNull(images, blockStyle.maskBoxImageSource()); 217 if (blockStyle.shapeOutside()) 218 appendImageIfNotNull(images, blockStyle.shapeOutside()->image()); 219 } 220 221 RenderBlock::~RenderBlock() 222 { 223 if (hasColumns()) 224 gColumnInfoMap->take(this); 225 if (gPercentHeightDescendantsMap) 226 removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap, gPercentHeightContainerMap); 227 if (gPositionedDescendantsMap) 228 removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap, gPositionedContainerMap); 229 } 230 231 void RenderBlock::willBeDestroyed() 232 { 233 // Mark as being destroyed to avoid trouble with merges in removeChild(). 234 m_beingDestroyed = true; 235 236 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will 237 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. 238 children()->destroyLeftoverChildren(); 239 240 // Destroy our continuation before anything other than anonymous children. 241 // The reason we don't destroy it before anonymous children is that they may 242 // have continuations of their own that are anonymous children of our continuation. 243 RenderBoxModelObject* continuation = this->continuation(); 244 if (continuation) { 245 continuation->destroy(); 246 setContinuation(0); 247 } 248 249 if (!documentBeingDestroyed()) { 250 if (firstLineBox()) { 251 // We can't wait for RenderBox::destroy to clear the selection, 252 // because by then we will have nuked the line boxes. 253 // FIXME: The FrameSelection should be responsible for this when it 254 // is notified of DOM mutations. 255 if (isSelectionBorder()) 256 view()->clearSelection(); 257 258 // If we are an anonymous block, then our line boxes might have children 259 // that will outlast this block. In the non-anonymous block case those 260 // children will be destroyed by the time we return from this function. 261 if (isAnonymousBlock()) { 262 for (InlineFlowBox* box = firstLineBox(); box; box = box->nextLineBox()) { 263 while (InlineBox* childBox = box->firstChild()) 264 childBox->remove(); 265 } 266 } 267 } else if (parent()) 268 parent()->dirtyLinesFromChangedChild(this); 269 } 270 271 m_lineBoxes.deleteLineBoxes(); 272 273 if (UNLIKELY(gDelayedUpdateScrollInfoSet != 0)) 274 gDelayedUpdateScrollInfoSet->remove(this); 275 276 if (FastTextAutosizer* textAutosizer = document().fastTextAutosizer()) 277 textAutosizer->destroy(this); 278 279 RenderBox::willBeDestroyed(); 280 } 281 282 void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) 283 { 284 RenderStyle* oldStyle = style(); 285 286 setReplaced(newStyle.isDisplayInlineType()); 287 288 if (oldStyle && parent()) { 289 bool oldStyleIsContainer = oldStyle->position() != StaticPosition || oldStyle->hasTransformRelatedProperty(); 290 bool newStyleIsContainer = newStyle.position() != StaticPosition || newStyle.hasTransformRelatedProperty(); 291 292 if (oldStyleIsContainer && !newStyleIsContainer) { 293 // Clear our positioned objects list. Our absolutely positioned descendants will be 294 // inserted into our containing block's positioned objects list during layout. 295 removePositionedObjects(0, NewContainingBlock); 296 } else if (!oldStyleIsContainer && newStyleIsContainer) { 297 // Remove our absolutely positioned descendants from their current containing block. 298 // They will be inserted into our positioned objects list during layout. 299 RenderObject* cb = parent(); 300 while (cb && (cb->style()->position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) { 301 if (cb->style()->position() == RelativePosition && cb->isInline() && !cb->isReplaced()) { 302 cb = cb->containingBlock(); 303 break; 304 } 305 cb = cb->parent(); 306 } 307 308 if (cb->isRenderBlock()) 309 toRenderBlock(cb)->removePositionedObjects(this, NewContainingBlock); 310 } 311 } 312 313 RenderBox::styleWillChange(diff, newStyle); 314 } 315 316 static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle) 317 { 318 if (newStyle->isHorizontalWritingMode()) 319 return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth() 320 || oldStyle->borderRightWidth() != newStyle->borderRightWidth() 321 || oldStyle->paddingLeft() != newStyle->paddingLeft() 322 || oldStyle->paddingRight() != newStyle->paddingRight(); 323 324 return oldStyle->borderTopWidth() != newStyle->borderTopWidth() 325 || oldStyle->borderBottomWidth() != newStyle->borderBottomWidth() 326 || oldStyle->paddingTop() != newStyle->paddingTop() 327 || oldStyle->paddingBottom() != newStyle->paddingBottom(); 328 } 329 330 void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 331 { 332 RenderBox::styleDidChange(diff, oldStyle); 333 334 RenderStyle* newStyle = style(); 335 336 if (!isAnonymousBlock()) { 337 // Ensure that all of our continuation blocks pick up the new style. 338 for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) { 339 RenderBoxModelObject* nextCont = currCont->continuation(); 340 currCont->setContinuation(0); 341 currCont->setStyle(newStyle); 342 currCont->setContinuation(nextCont); 343 } 344 } 345 346 if (FastTextAutosizer* textAutosizer = document().fastTextAutosizer()) 347 textAutosizer->record(this); 348 349 propagateStyleToAnonymousChildren(true); 350 351 // It's possible for our border/padding to change, but for the overall logical width of the block to 352 // end up being the same. We keep track of this change so in layoutBlock, we can know to set relayoutChildren=true. 353 m_hasBorderOrPaddingLogicalWidthChanged = oldStyle && diff.needsFullLayout() && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, newStyle); 354 355 // If the style has unloaded images, want to notify the ResourceLoadPriorityOptimizer so that 356 // network priorities can be set. 357 Vector<ImageResource*> images; 358 appendImagesFromStyle(images, *newStyle); 359 if (images.isEmpty()) 360 ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->removeRenderObject(this); 361 else 362 ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->addRenderObject(this); 363 } 364 365 void RenderBlock::invalidateTreeAfterLayout(const RenderLayerModelObject& invalidationContainer) 366 { 367 // Note, we don't want to early out here using shouldCheckForInvalidationAfterLayout as 368 // we have to make sure we go through any positioned objects as they won't be seen in 369 // the normal tree walk. 370 371 if (shouldCheckForPaintInvalidationAfterLayout()) 372 RenderBox::invalidateTreeAfterLayout(invalidationContainer); 373 374 // Take care of positioned objects. This is required as LayoutState keeps a single clip rect. 375 if (TrackedRendererListHashSet* positionedObjects = this->positionedObjects()) { 376 TrackedRendererListHashSet::iterator end = positionedObjects->end(); 377 LayoutState state(*this, isTableRow() ? LayoutSize() : locationOffset()); 378 for (TrackedRendererListHashSet::iterator it = positionedObjects->begin(); it != end; ++it) { 379 RenderBox* box = *it; 380 381 // One of the renderers we're skipping over here may be the child's repaint container, 382 // so we can't pass our own repaint container along. 383 const RenderLayerModelObject& repaintContainerForChild = *box->containerForPaintInvalidation(); 384 385 // If the positioned renderer is absolutely positioned and it is inside 386 // a relatively positioend inline element, we need to account for 387 // the inline elements position in LayoutState. 388 if (box->style()->position() == AbsolutePosition) { 389 RenderObject* container = box->container(&repaintContainerForChild, 0); 390 if (container->isInFlowPositioned() && container->isRenderInline()) { 391 // FIXME: We should be able to use layout-state for this. 392 // Currently, we will place absolutly positioned elements inside 393 // relatively positioned inline blocks in the wrong location. crbug.com/371485 394 ForceHorriblySlowRectMapping slowRectMapping(*this); 395 box->invalidateTreeAfterLayout(repaintContainerForChild); 396 continue; 397 } 398 } 399 400 box->invalidateTreeAfterLayout(repaintContainerForChild); 401 } 402 } 403 } 404 405 RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) 406 { 407 if (beforeChild && beforeChild->parent() == this) 408 return this; 409 410 RenderBlock* curr = toRenderBlock(continuation()); 411 RenderBlock* nextToLast = this; 412 RenderBlock* last = this; 413 while (curr) { 414 if (beforeChild && beforeChild->parent() == curr) { 415 if (curr->firstChild() == beforeChild) 416 return last; 417 return curr; 418 } 419 420 nextToLast = last; 421 last = curr; 422 curr = toRenderBlock(curr->continuation()); 423 } 424 425 if (!beforeChild && !last->firstChild()) 426 return nextToLast; 427 return last; 428 } 429 430 void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) 431 { 432 RenderBlock* flow = continuationBefore(beforeChild); 433 ASSERT(!beforeChild || beforeChild->parent()->isAnonymousColumnSpanBlock() || beforeChild->parent()->isRenderBlock()); 434 RenderBoxModelObject* beforeChildParent = 0; 435 if (beforeChild) 436 beforeChildParent = toRenderBoxModelObject(beforeChild->parent()); 437 else { 438 RenderBoxModelObject* cont = flow->continuation(); 439 if (cont) 440 beforeChildParent = cont; 441 else 442 beforeChildParent = flow; 443 } 444 445 if (newChild->isFloatingOrOutOfFlowPositioned()) { 446 beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 447 return; 448 } 449 450 // A continuation always consists of two potential candidates: a block or an anonymous 451 // column span box holding column span children. 452 bool childIsNormal = newChild->isInline() || !newChild->style()->columnSpan(); 453 bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style()->columnSpan(); 454 bool flowIsNormal = flow->isInline() || !flow->style()->columnSpan(); 455 456 if (flow == beforeChildParent) { 457 flow->addChildIgnoringContinuation(newChild, beforeChild); 458 return; 459 } 460 461 // The goal here is to match up if we can, so that we can coalesce and create the 462 // minimal # of continuations needed for the inline. 463 if (childIsNormal == bcpIsNormal) { 464 beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 465 return; 466 } 467 if (flowIsNormal == childIsNormal) { 468 flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append. 469 return; 470 } 471 beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 472 } 473 474 475 void RenderBlock::addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) 476 { 477 ASSERT(!continuation()); // We don't yet support column spans that aren't immediate children of the multi-column block. 478 479 // The goal is to locate a suitable box in which to place our child. 480 RenderBlock* beforeChildParent = 0; 481 if (beforeChild) { 482 RenderObject* curr = beforeChild; 483 while (curr && curr->parent() != this) 484 curr = curr->parent(); 485 beforeChildParent = toRenderBlock(curr); 486 ASSERT(beforeChildParent); 487 ASSERT(beforeChildParent->isAnonymousColumnsBlock() || beforeChildParent->isAnonymousColumnSpanBlock()); 488 } else 489 beforeChildParent = toRenderBlock(lastChild()); 490 491 // If the new child is floating or positioned it can just go in that block. 492 if (newChild->isFloatingOrOutOfFlowPositioned()) { 493 beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); 494 return; 495 } 496 497 // See if the child can be placed in the box. 498 bool newChildHasColumnSpan = newChild->style()->columnSpan() && !newChild->isInline(); 499 bool beforeChildParentHoldsColumnSpans = beforeChildParent->isAnonymousColumnSpanBlock(); 500 501 if (newChildHasColumnSpan == beforeChildParentHoldsColumnSpans) { 502 beforeChildParent->addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); 503 return; 504 } 505 506 if (!beforeChild) { 507 // Create a new block of the correct type. 508 RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock(); 509 children()->appendChildNode(this, newBox); 510 newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0); 511 return; 512 } 513 514 RenderObject* immediateChild = beforeChild; 515 bool isPreviousBlockViable = true; 516 while (immediateChild->parent() != this) { 517 if (isPreviousBlockViable) 518 isPreviousBlockViable = !immediateChild->previousSibling(); 519 immediateChild = immediateChild->parent(); 520 } 521 if (isPreviousBlockViable && immediateChild->previousSibling()) { 522 toRenderBlock(immediateChild->previousSibling())->addChildIgnoringAnonymousColumnBlocks(newChild, 0); // Treat like an append. 523 return; 524 } 525 526 // Split our anonymous blocks. 527 RenderObject* newBeforeChild = splitAnonymousBoxesAroundChild(beforeChild); 528 529 530 // Create a new anonymous box of the appropriate type. 531 RenderBlock* newBox = newChildHasColumnSpan ? createAnonymousColumnSpanBlock() : createAnonymousColumnsBlock(); 532 children()->insertChildNode(this, newBox, newBeforeChild); 533 newBox->addChildIgnoringAnonymousColumnBlocks(newChild, 0); 534 return; 535 } 536 537 RenderBlockFlow* RenderBlock::containingColumnsBlock(bool allowAnonymousColumnBlock) 538 { 539 RenderBlock* firstChildIgnoringAnonymousWrappers = 0; 540 for (RenderObject* curr = this; curr; curr = curr->parent()) { 541 if (!curr->isRenderBlock() || curr->isFloatingOrOutOfFlowPositioned() || curr->isTableCell() || curr->isDocumentElement() || curr->isRenderView() || curr->hasOverflowClip() 542 || curr->isInlineBlockOrInlineTable()) 543 return 0; 544 545 // FIXME: Renderers that do special management of their children (tables, buttons, 546 // lists, flexboxes, etc.) breaks when the flow is split through them. Disabling 547 // multi-column for them to avoid this problem.) 548 if (!curr->isRenderBlockFlow() || curr->isListItem()) 549 return 0; 550 551 RenderBlockFlow* currBlock = toRenderBlockFlow(curr); 552 if (!currBlock->createsAnonymousWrapper()) 553 firstChildIgnoringAnonymousWrappers = currBlock; 554 555 if (currBlock->style()->specifiesColumns() && (allowAnonymousColumnBlock || !currBlock->isAnonymousColumnsBlock())) 556 return toRenderBlockFlow(firstChildIgnoringAnonymousWrappers); 557 558 if (currBlock->isAnonymousColumnSpanBlock()) 559 return 0; 560 } 561 return 0; 562 } 563 564 RenderBlock* RenderBlock::clone() const 565 { 566 RenderBlock* cloneBlock; 567 if (isAnonymousBlock()) { 568 cloneBlock = createAnonymousBlock(); 569 cloneBlock->setChildrenInline(childrenInline()); 570 } 571 else { 572 RenderObject* cloneRenderer = toElement(node())->createRenderer(style()); 573 cloneBlock = toRenderBlock(cloneRenderer); 574 cloneBlock->setStyle(style()); 575 576 // This takes care of setting the right value of childrenInline in case 577 // generated content is added to cloneBlock and 'this' does not have 578 // generated content added yet. 579 cloneBlock->setChildrenInline(cloneBlock->firstChild() ? cloneBlock->firstChild()->isInline() : childrenInline()); 580 } 581 cloneBlock->setFlowThreadState(flowThreadState()); 582 return cloneBlock; 583 } 584 585 void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, 586 RenderBlock* middleBlock, 587 RenderObject* beforeChild, RenderBoxModelObject* oldCont) 588 { 589 // Create a clone of this inline. 590 RenderBlock* cloneBlock = clone(); 591 if (!isAnonymousBlock()) 592 cloneBlock->setContinuation(oldCont); 593 594 if (!beforeChild && isAfterContent(lastChild())) 595 beforeChild = lastChild(); 596 597 // If we are moving inline children from |this| to cloneBlock, then we need 598 // to clear our line box tree. 599 if (beforeChild && childrenInline()) 600 deleteLineBoxTree(); 601 602 // Now take all of the children from beforeChild to the end and remove 603 // them from |this| and place them in the clone. 604 moveChildrenTo(cloneBlock, beforeChild, 0, true); 605 606 // Hook |clone| up as the continuation of the middle block. 607 if (!cloneBlock->isAnonymousBlock()) 608 middleBlock->setContinuation(cloneBlock); 609 610 // We have been reparented and are now under the fromBlock. We need 611 // to walk up our block parent chain until we hit the containing anonymous columns block. 612 // Once we hit the anonymous columns block we're done. 613 RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); 614 RenderBoxModelObject* currChild = this; 615 RenderObject* currChildNextSibling = currChild->nextSibling(); 616 617 while (curr && curr->isDescendantOf(fromBlock) && curr != fromBlock) { 618 ASSERT_WITH_SECURITY_IMPLICATION(curr->isRenderBlock()); 619 620 RenderBlock* blockCurr = toRenderBlock(curr); 621 622 // Create a new clone. 623 RenderBlock* cloneChild = cloneBlock; 624 cloneBlock = blockCurr->clone(); 625 626 // Insert our child clone as the first child. 627 cloneBlock->addChildIgnoringContinuation(cloneChild, 0); 628 629 // Hook the clone up as a continuation of |curr|. Note we do encounter 630 // anonymous blocks possibly as we walk up the block chain. When we split an 631 // anonymous block, there's no need to do any continuation hookup, since we haven't 632 // actually split a real element. 633 if (!blockCurr->isAnonymousBlock()) { 634 oldCont = blockCurr->continuation(); 635 blockCurr->setContinuation(cloneBlock); 636 cloneBlock->setContinuation(oldCont); 637 } 638 639 // Now we need to take all of the children starting from the first child 640 // *after* currChild and append them all to the clone. 641 blockCurr->moveChildrenTo(cloneBlock, currChildNextSibling, 0, true); 642 643 // Keep walking up the chain. 644 currChild = curr; 645 currChildNextSibling = currChild->nextSibling(); 646 curr = toRenderBoxModelObject(curr->parent()); 647 } 648 649 // Now we are at the columns block level. We need to put the clone into the toBlock. 650 toBlock->children()->appendChildNode(toBlock, cloneBlock); 651 652 // Now take all the children after currChild and remove them from the fromBlock 653 // and put them in the toBlock. 654 fromBlock->moveChildrenTo(toBlock, currChildNextSibling, 0, true); 655 } 656 657 void RenderBlock::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, 658 RenderObject* newChild, RenderBoxModelObject* oldCont) 659 { 660 RenderBlock* pre = 0; 661 RenderBlock* block = containingColumnsBlock(); 662 663 // Delete our line boxes before we do the inline split into continuations. 664 block->deleteLineBoxTree(); 665 666 bool madeNewBeforeBlock = false; 667 if (block->isAnonymousColumnsBlock()) { 668 // We can reuse this block and make it the preBlock of the next continuation. 669 pre = block; 670 pre->removePositionedObjects(0); 671 if (block->isRenderBlockFlow()) 672 toRenderBlockFlow(pre)->removeFloatingObjects(); 673 block = toRenderBlock(block->parent()); 674 } else { 675 // No anonymous block available for use. Make one. 676 pre = block->createAnonymousColumnsBlock(); 677 pre->setChildrenInline(false); 678 madeNewBeforeBlock = true; 679 } 680 681 RenderBlock* post = block->createAnonymousColumnsBlock(); 682 post->setChildrenInline(false); 683 684 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); 685 if (madeNewBeforeBlock) 686 block->children()->insertChildNode(block, pre, boxFirst); 687 block->children()->insertChildNode(block, newBlockBox, boxFirst); 688 block->children()->insertChildNode(block, post, boxFirst); 689 block->setChildrenInline(false); 690 691 if (madeNewBeforeBlock) 692 block->moveChildrenTo(pre, boxFirst, 0, true); 693 694 splitBlocks(pre, post, newBlockBox, beforeChild, oldCont); 695 696 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting 697 // time in makeChildrenNonInline by just setting this explicitly up front. 698 newBlockBox->setChildrenInline(false); 699 700 newBlockBox->addChild(newChild); 701 702 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) 703 // get deleted properly. Because objects moves from the pre block into the post block, we want to 704 // make new line boxes instead of leaving the old line boxes around. 705 pre->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); 706 block->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); 707 post->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); 708 } 709 710 void RenderBlock::makeChildrenAnonymousColumnBlocks(RenderObject* beforeChild, RenderBlockFlow* newBlockBox, RenderObject* newChild) 711 { 712 RenderBlockFlow* pre = 0; 713 RenderBlockFlow* post = 0; 714 RenderBlock* block = this; // Eventually block will not just be |this|, but will also be a block nested inside |this|. Assign to a variable 715 // so that we don't have to patch all of the rest of the code later on. 716 717 // Delete the block's line boxes before we do the split. 718 block->deleteLineBoxTree(); 719 720 if (beforeChild && beforeChild->parent() != this) 721 beforeChild = splitAnonymousBoxesAroundChild(beforeChild); 722 723 if (beforeChild != firstChild()) { 724 pre = block->createAnonymousColumnsBlock(); 725 pre->setChildrenInline(block->childrenInline()); 726 } 727 728 if (beforeChild) { 729 post = block->createAnonymousColumnsBlock(); 730 post->setChildrenInline(block->childrenInline()); 731 } 732 733 RenderObject* boxFirst = block->firstChild(); 734 if (pre) 735 block->children()->insertChildNode(block, pre, boxFirst); 736 block->children()->insertChildNode(block, newBlockBox, boxFirst); 737 if (post) 738 block->children()->insertChildNode(block, post, boxFirst); 739 block->setChildrenInline(false); 740 741 // The pre/post blocks always have layers, so we know to always do a full insert/remove (so we pass true as the last argument). 742 block->moveChildrenTo(pre, boxFirst, beforeChild, true); 743 block->moveChildrenTo(post, beforeChild, 0, true); 744 745 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting 746 // time in makeChildrenNonInline by just setting this explicitly up front. 747 newBlockBox->setChildrenInline(false); 748 749 newBlockBox->addChild(newChild); 750 751 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) 752 // get deleted properly. Because objects moved from the pre block into the post block, we want to 753 // make new line boxes instead of leaving the old line boxes around. 754 if (pre) 755 pre->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); 756 block->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); 757 if (post) 758 post->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); 759 } 760 761 RenderBlockFlow* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild) 762 { 763 // FIXME: This function is the gateway for the addition of column-span support. It will 764 // be added to in three stages: 765 // (1) Immediate children of a multi-column block can span. 766 // (2) Nested block-level children with only block-level ancestors between them and the multi-column block can span. 767 // (3) Nested children with block or inline ancestors between them and the multi-column block can span (this is when we 768 // cross the streams and have to cope with both types of continuations mixed together). 769 // This function currently supports (1) and (2). 770 RenderBlockFlow* columnsBlockAncestor = 0; 771 if (!newChild->isText() && newChild->style()->columnSpan() && !newChild->isBeforeOrAfterContent() 772 && !newChild->isFloatingOrOutOfFlowPositioned() && !newChild->isInline() && !isAnonymousColumnSpanBlock()) { 773 columnsBlockAncestor = containingColumnsBlock(false); 774 if (columnsBlockAncestor) { 775 // Make sure that none of the parent ancestors have a continuation. 776 // If yes, we do not want split the block into continuations. 777 RenderObject* curr = this; 778 while (curr && curr != columnsBlockAncestor) { 779 if (curr->isRenderBlock() && toRenderBlock(curr)->continuation()) { 780 columnsBlockAncestor = 0; 781 break; 782 } 783 curr = curr->parent(); 784 } 785 } 786 } 787 return columnsBlockAncestor; 788 } 789 790 void RenderBlock::addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) 791 { 792 if (beforeChild && beforeChild->parent() != this) { 793 RenderObject* beforeChildContainer = beforeChild->parent(); 794 while (beforeChildContainer->parent() != this) 795 beforeChildContainer = beforeChildContainer->parent(); 796 ASSERT(beforeChildContainer); 797 798 if (beforeChildContainer->isAnonymous()) { 799 // If the requested beforeChild is not one of our children, then this is because 800 // there is an anonymous container within this object that contains the beforeChild. 801 RenderObject* beforeChildAnonymousContainer = beforeChildContainer; 802 if (beforeChildAnonymousContainer->isAnonymousBlock() 803 // Full screen renderers and full screen placeholders act as anonymous blocks, not tables: 804 || beforeChildAnonymousContainer->isRenderFullScreen() 805 || beforeChildAnonymousContainer->isRenderFullScreenPlaceholder() 806 ) { 807 // Insert the child into the anonymous block box instead of here. 808 if (newChild->isInline() || newChild->isFloatingOrOutOfFlowPositioned() || beforeChild->parent()->slowFirstChild() != beforeChild) 809 beforeChild->parent()->addChild(newChild, beforeChild); 810 else 811 addChild(newChild, beforeChild->parent()); 812 return; 813 } 814 815 ASSERT(beforeChildAnonymousContainer->isTable()); 816 if (newChild->isTablePart()) { 817 // Insert into the anonymous table. 818 beforeChildAnonymousContainer->addChild(newChild, beforeChild); 819 return; 820 } 821 822 beforeChild = splitAnonymousBoxesAroundChild(beforeChild); 823 824 ASSERT(beforeChild->parent() == this); 825 if (beforeChild->parent() != this) { 826 // We should never reach here. If we do, we need to use the 827 // safe fallback to use the topmost beforeChild container. 828 beforeChild = beforeChildContainer; 829 } 830 } 831 } 832 833 // Check for a spanning element in columns. 834 if (gColumnFlowSplitEnabled && !document().regionBasedColumnsEnabled()) { 835 RenderBlockFlow* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); 836 if (columnsBlockAncestor) { 837 TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); 838 // We are placing a column-span element inside a block. 839 RenderBlockFlow* newBox = createAnonymousColumnSpanBlock(); 840 841 if (columnsBlockAncestor != this && !isRenderFlowThread()) { 842 // We are nested inside a multi-column element and are being split by the span. We have to break up 843 // our block into continuations. 844 RenderBoxModelObject* oldContinuation = continuation(); 845 846 // When we split an anonymous block, there's no need to do any continuation hookup, 847 // since we haven't actually split a real element. 848 if (!isAnonymousBlock()) 849 setContinuation(newBox); 850 851 splitFlow(beforeChild, newBox, newChild, oldContinuation); 852 return; 853 } 854 855 // We have to perform a split of this block's children. This involves creating an anonymous block box to hold 856 // the column-spanning |newChild|. We take all of the children from before |newChild| and put them into 857 // one anonymous columns block, and all of the children after |newChild| go into another anonymous block. 858 makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild); 859 return; 860 } 861 } 862 863 bool madeBoxesNonInline = false; 864 865 // A block has to either have all of its children inline, or all of its children as blocks. 866 // So, if our children are currently inline and a block child has to be inserted, we move all our 867 // inline children into anonymous block boxes. 868 if (childrenInline() && !newChild->isInline() && !newChild->isFloatingOrOutOfFlowPositioned()) { 869 // This is a block with inline content. Wrap the inline content in anonymous blocks. 870 makeChildrenNonInline(beforeChild); 871 madeBoxesNonInline = true; 872 873 if (beforeChild && beforeChild->parent() != this) { 874 beforeChild = beforeChild->parent(); 875 ASSERT(beforeChild->isAnonymousBlock()); 876 ASSERT(beforeChild->parent() == this); 877 } 878 } else if (!childrenInline() && (newChild->isFloatingOrOutOfFlowPositioned() || newChild->isInline())) { 879 // If we're inserting an inline child but all of our children are blocks, then we have to make sure 880 // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise 881 // a new one is created and inserted into our list of children in the appropriate position. 882 RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild(); 883 884 if (afterChild && afterChild->isAnonymousBlock()) { 885 afterChild->addChild(newChild); 886 return; 887 } 888 889 if (newChild->isInline()) { 890 // No suitable existing anonymous box - create a new one. 891 RenderBlock* newBox = createAnonymousBlock(); 892 RenderBox::addChild(newBox, beforeChild); 893 newBox->addChild(newChild); 894 return; 895 } 896 } 897 898 RenderBox::addChild(newChild, beforeChild); 899 900 if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) 901 toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); 902 // this object may be dead here 903 } 904 905 void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) 906 { 907 if (continuation() && !isAnonymousBlock()) 908 addChildToContinuation(newChild, beforeChild); 909 else 910 addChildIgnoringContinuation(newChild, beforeChild); 911 } 912 913 void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) 914 { 915 if (!isAnonymousBlock() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock())) 916 addChildToAnonymousColumnBlocks(newChild, beforeChild); 917 else 918 addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); 919 } 920 921 static void getInlineRun(RenderObject* start, RenderObject* boundary, 922 RenderObject*& inlineRunStart, 923 RenderObject*& inlineRunEnd) 924 { 925 // Beginning at |start| we find the largest contiguous run of inlines that 926 // we can. We denote the run with start and end points, |inlineRunStart| 927 // and |inlineRunEnd|. Note that these two values may be the same if 928 // we encounter only one inline. 929 // 930 // We skip any non-inlines we encounter as long as we haven't found any 931 // inlines yet. 932 // 933 // |boundary| indicates a non-inclusive boundary point. Regardless of whether |boundary| 934 // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered 935 // a non-inline. 936 937 // Start by skipping as many non-inlines as we can. 938 RenderObject * curr = start; 939 bool sawInline; 940 do { 941 while (curr && !(curr->isInline() || curr->isFloatingOrOutOfFlowPositioned())) 942 curr = curr->nextSibling(); 943 944 inlineRunStart = inlineRunEnd = curr; 945 946 if (!curr) 947 return; // No more inline children to be found. 948 949 sawInline = curr->isInline(); 950 951 curr = curr->nextSibling(); 952 while (curr && (curr->isInline() || curr->isFloatingOrOutOfFlowPositioned()) && (curr != boundary)) { 953 inlineRunEnd = curr; 954 if (curr->isInline()) 955 sawInline = true; 956 curr = curr->nextSibling(); 957 } 958 } while (!sawInline); 959 } 960 961 void RenderBlock::deleteLineBoxTree() 962 { 963 m_lineBoxes.deleteLineBoxTree(); 964 965 if (AXObjectCache* cache = document().existingAXObjectCache()) 966 cache->recomputeIsIgnored(this); 967 } 968 969 void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) 970 { 971 // makeChildrenNonInline takes a block whose children are *all* inline and it 972 // makes sure that inline children are coalesced under anonymous 973 // blocks. If |insertionPoint| is defined, then it represents the insertion point for 974 // the new block child that is causing us to have to wrap all the inlines. This 975 // means that we cannot coalesce inlines before |insertionPoint| with inlines following 976 // |insertionPoint|, because the new child is going to be inserted in between the inlines, 977 // splitting them. 978 ASSERT(isInlineBlockOrInlineTable() || !isInline()); 979 ASSERT(!insertionPoint || insertionPoint->parent() == this); 980 981 setChildrenInline(false); 982 983 RenderObject *child = firstChild(); 984 if (!child) 985 return; 986 987 deleteLineBoxTree(); 988 989 while (child) { 990 RenderObject *inlineRunStart, *inlineRunEnd; 991 getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd); 992 993 if (!inlineRunStart) 994 break; 995 996 child = inlineRunEnd->nextSibling(); 997 998 RenderBlock* block = createAnonymousBlock(); 999 children()->insertChildNode(this, block, inlineRunStart); 1000 moveChildrenTo(block, inlineRunStart, child); 1001 } 1002 1003 #ifndef NDEBUG 1004 for (RenderObject *c = firstChild(); c; c = c->nextSibling()) 1005 ASSERT(!c->isInline()); 1006 #endif 1007 1008 paintInvalidationForWholeRenderer(); 1009 } 1010 1011 void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) 1012 { 1013 ASSERT(child->isAnonymousBlock()); 1014 ASSERT(!child->childrenInline()); 1015 1016 if (child->continuation() || (child->firstChild() && (child->isAnonymousColumnSpanBlock() || child->isAnonymousColumnsBlock()))) 1017 return; 1018 1019 RenderObject* firstAnChild = child->m_children.firstChild(); 1020 RenderObject* lastAnChild = child->m_children.lastChild(); 1021 if (firstAnChild) { 1022 RenderObject* o = firstAnChild; 1023 while (o) { 1024 o->setParent(this); 1025 o = o->nextSibling(); 1026 } 1027 firstAnChild->setPreviousSibling(child->previousSibling()); 1028 lastAnChild->setNextSibling(child->nextSibling()); 1029 if (child->previousSibling()) 1030 child->previousSibling()->setNextSibling(firstAnChild); 1031 if (child->nextSibling()) 1032 child->nextSibling()->setPreviousSibling(lastAnChild); 1033 1034 if (child == m_children.firstChild()) 1035 m_children.setFirstChild(firstAnChild); 1036 if (child == m_children.lastChild()) 1037 m_children.setLastChild(lastAnChild); 1038 } else { 1039 if (child == m_children.firstChild()) 1040 m_children.setFirstChild(child->nextSibling()); 1041 if (child == m_children.lastChild()) 1042 m_children.setLastChild(child->previousSibling()); 1043 1044 if (child->previousSibling()) 1045 child->previousSibling()->setNextSibling(child->nextSibling()); 1046 if (child->nextSibling()) 1047 child->nextSibling()->setPreviousSibling(child->previousSibling()); 1048 } 1049 1050 child->children()->setFirstChild(0); 1051 child->m_next = 0; 1052 1053 // Remove all the information in the flow thread associated with the leftover anonymous block. 1054 child->removeFromRenderFlowThread(); 1055 1056 // RenderGrid keeps track of its children, we must notify it about changes in the tree. 1057 if (child->parent()->isRenderGrid()) 1058 toRenderGrid(child->parent())->dirtyGrid(); 1059 1060 child->setParent(0); 1061 child->setPreviousSibling(0); 1062 child->setNextSibling(0); 1063 1064 child->destroy(); 1065 } 1066 1067 static bool canMergeContiguousAnonymousBlocks(RenderObject* oldChild, RenderObject* prev, RenderObject* next) 1068 { 1069 if (oldChild->documentBeingDestroyed() || oldChild->isInline() || oldChild->virtualContinuation()) 1070 return false; 1071 1072 if ((prev && (!prev->isAnonymousBlock() || toRenderBlock(prev)->continuation() || toRenderBlock(prev)->beingDestroyed())) 1073 || (next && (!next->isAnonymousBlock() || toRenderBlock(next)->continuation() || toRenderBlock(next)->beingDestroyed()))) 1074 return false; 1075 1076 if ((prev && (prev->isRubyRun() || prev->isRubyBase())) 1077 || (next && (next->isRubyRun() || next->isRubyBase()))) 1078 return false; 1079 1080 if (!prev || !next) 1081 return true; 1082 1083 // Make sure the types of the anonymous blocks match up. 1084 return prev->isAnonymousColumnsBlock() == next->isAnonymousColumnsBlock() 1085 && prev->isAnonymousColumnSpanBlock() == next->isAnonymousColumnSpanBlock(); 1086 } 1087 1088 void RenderBlock::collapseAnonymousBlockChild(RenderBlock* parent, RenderBlock* child) 1089 { 1090 // It's possible that this block's destruction may have been triggered by the 1091 // child's removal. Just bail if the anonymous child block is already being 1092 // destroyed. See crbug.com/282088 1093 if (child->beingDestroyed()) 1094 return; 1095 parent->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); 1096 parent->setChildrenInline(child->childrenInline()); 1097 RenderObject* nextSibling = child->nextSibling(); 1098 1099 RenderFlowThread* childFlowThread = child->flowThreadContainingBlock(); 1100 CurrentRenderFlowThreadMaintainer flowThreadMaintainer(childFlowThread); 1101 1102 parent->children()->removeChildNode(parent, child, child->hasLayer()); 1103 child->moveAllChildrenTo(parent, nextSibling, child->hasLayer()); 1104 // Explicitly delete the child's line box tree, or the special anonymous 1105 // block handling in willBeDestroyed will cause problems. 1106 child->deleteLineBoxTree(); 1107 child->destroy(); 1108 } 1109 1110 void RenderBlock::removeChild(RenderObject* oldChild) 1111 { 1112 // No need to waste time in merging or removing empty anonymous blocks. 1113 // We can just bail out if our document is getting destroyed. 1114 if (documentBeingDestroyed()) { 1115 RenderBox::removeChild(oldChild); 1116 return; 1117 } 1118 1119 // This protects against column split flows when anonymous blocks are getting merged. 1120 TemporaryChange<bool> columnFlowSplitEnabled(gColumnFlowSplitEnabled, false); 1121 1122 // If this child is a block, and if our previous and next siblings are 1123 // both anonymous blocks with inline content, then we can go ahead and 1124 // fold the inline content back together. 1125 RenderObject* prev = oldChild->previousSibling(); 1126 RenderObject* next = oldChild->nextSibling(); 1127 bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next); 1128 if (canMergeAnonymousBlocks && prev && next) { 1129 prev->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); 1130 RenderBlockFlow* nextBlock = toRenderBlockFlow(next); 1131 RenderBlockFlow* prevBlock = toRenderBlockFlow(prev); 1132 1133 if (prev->childrenInline() != next->childrenInline()) { 1134 RenderBlock* inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock; 1135 RenderBlock* blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock; 1136 1137 // Place the inline children block inside of the block children block instead of deleting it. 1138 // In order to reuse it, we have to reset it to just be a generic anonymous block. Make sure 1139 // to clear out inherited column properties by just making a new style, and to also clear the 1140 // column span flag if it is set. 1141 ASSERT(!inlineChildrenBlock->continuation()); 1142 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK); 1143 // Cache this value as it might get changed in setStyle() call. 1144 bool inlineChildrenBlockHasLayer = inlineChildrenBlock->hasLayer(); 1145 inlineChildrenBlock->setStyle(newStyle); 1146 children()->removeChildNode(this, inlineChildrenBlock, inlineChildrenBlockHasLayer); 1147 1148 // Now just put the inlineChildrenBlock inside the blockChildrenBlock. 1149 blockChildrenBlock->children()->insertChildNode(blockChildrenBlock, inlineChildrenBlock, prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : 0, 1150 inlineChildrenBlockHasLayer || blockChildrenBlock->hasLayer()); 1151 next->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(); 1152 1153 // inlineChildrenBlock got reparented to blockChildrenBlock, so it is no longer a child 1154 // of "this". we null out prev or next so that is not used later in the function. 1155 if (inlineChildrenBlock == prevBlock) 1156 prev = 0; 1157 else 1158 next = 0; 1159 } else { 1160 // Take all the children out of the |next| block and put them in 1161 // the |prev| block. 1162 nextBlock->moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); 1163 1164 // Delete the now-empty block's lines and nuke it. 1165 nextBlock->deleteLineBoxTree(); 1166 nextBlock->destroy(); 1167 next = 0; 1168 } 1169 } 1170 1171 RenderBox::removeChild(oldChild); 1172 1173 RenderObject* child = prev ? prev : next; 1174 if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && canCollapseAnonymousBlockChild()) { 1175 // The removal has knocked us down to containing only a single anonymous 1176 // box. We can go ahead and pull the content right back up into our 1177 // box. 1178 collapseAnonymousBlockChild(this, toRenderBlock(child)); 1179 } else if (((prev && prev->isAnonymousBlock()) || (next && next->isAnonymousBlock())) && canCollapseAnonymousBlockChild()) { 1180 // It's possible that the removal has knocked us down to a single anonymous 1181 // block with pseudo-style element siblings (e.g. first-letter). If these 1182 // are floating, then we need to pull the content up also. 1183 RenderBlock* anonymousBlock = toRenderBlock((prev && prev->isAnonymousBlock()) ? prev : next); 1184 if ((anonymousBlock->previousSibling() || anonymousBlock->nextSibling()) 1185 && (!anonymousBlock->previousSibling() || (anonymousBlock->previousSibling()->style()->styleType() != NOPSEUDO && anonymousBlock->previousSibling()->isFloating() && !anonymousBlock->previousSibling()->previousSibling())) 1186 && (!anonymousBlock->nextSibling() || (anonymousBlock->nextSibling()->style()->styleType() != NOPSEUDO && anonymousBlock->nextSibling()->isFloating() && !anonymousBlock->nextSibling()->nextSibling()))) { 1187 collapseAnonymousBlockChild(this, anonymousBlock); 1188 } 1189 } 1190 1191 if (!firstChild()) { 1192 // If this was our last child be sure to clear out our line boxes. 1193 if (childrenInline()) 1194 deleteLineBoxTree(); 1195 1196 // If we are an empty anonymous block in the continuation chain, 1197 // we need to remove ourself and fix the continuation chain. 1198 if (!beingDestroyed() && isAnonymousBlockContinuation() && !oldChild->isListMarker()) { 1199 RenderObject* containingBlockIgnoringAnonymous = containingBlock(); 1200 while (containingBlockIgnoringAnonymous && containingBlockIgnoringAnonymous->isAnonymous()) 1201 containingBlockIgnoringAnonymous = containingBlockIgnoringAnonymous->containingBlock(); 1202 for (RenderObject* curr = this; curr; curr = curr->previousInPreOrder(containingBlockIgnoringAnonymous)) { 1203 if (curr->virtualContinuation() != this) 1204 continue; 1205 1206 // Found our previous continuation. We just need to point it to 1207 // |this|'s next continuation. 1208 RenderBoxModelObject* nextContinuation = continuation(); 1209 if (curr->isRenderInline()) 1210 toRenderInline(curr)->setContinuation(nextContinuation); 1211 else if (curr->isRenderBlock()) 1212 toRenderBlock(curr)->setContinuation(nextContinuation); 1213 else 1214 ASSERT_NOT_REACHED(); 1215 1216 break; 1217 } 1218 setContinuation(0); 1219 destroy(); 1220 } 1221 } 1222 } 1223 1224 bool RenderBlock::isSelfCollapsingBlock() const 1225 { 1226 // We are not self-collapsing if we 1227 // (a) have a non-zero height according to layout (an optimization to avoid wasting time) 1228 // (b) are a table, 1229 // (c) have border/padding, 1230 // (d) have a min-height 1231 // (e) have specified that one of our margins can't collapse using a CSS extension 1232 // (f) establish a new block formatting context. 1233 1234 // The early exit must be done before we check for clean layout. 1235 // We should be able to give a quick answer if the box is a relayout boundary. 1236 // Being a relayout boundary implies a block formatting context, and also 1237 // our internal layout shouldn't affect our container in any way. 1238 if (createsBlockFormattingContext()) 1239 return false; 1240 1241 // Placeholder elements are not laid out until the dimensions of their parent text control are known, so they 1242 // don't get layout until their parent has had layout - this is unique in the layout tree and means 1243 // when we call isSelfCollapsingBlock on them we find that they still need layout. 1244 ASSERT(!needsLayout() || (node() && node()->isElementNode() && toElement(node())->shadowPseudoId() == "-webkit-input-placeholder")); 1245 1246 if (logicalHeight() > 0 1247 || isTable() || borderAndPaddingLogicalHeight() 1248 || style()->logicalMinHeight().isPositive() 1249 || style()->marginBeforeCollapse() == MSEPARATE || style()->marginAfterCollapse() == MSEPARATE) 1250 return false; 1251 1252 Length logicalHeightLength = style()->logicalHeight(); 1253 bool hasAutoHeight = logicalHeightLength.isAuto(); 1254 if (logicalHeightLength.isPercent() && !document().inQuirksMode()) { 1255 hasAutoHeight = true; 1256 for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) { 1257 if (cb->style()->logicalHeight().isFixed() || cb->isTableCell()) 1258 hasAutoHeight = false; 1259 } 1260 } 1261 1262 // If the height is 0 or auto, then whether or not we are a self-collapsing block depends 1263 // on whether we have content that is all self-collapsing or not. 1264 if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercent()) && logicalHeightLength.isZero())) { 1265 // If the block has inline children, see if we generated any line boxes. If we have any 1266 // line boxes, then we can't be self-collapsing, since we have content. 1267 if (childrenInline()) 1268 return !firstLineBox(); 1269 1270 // Whether or not we collapse is dependent on whether all our normal flow children 1271 // are also self-collapsing. 1272 if (m_hasOnlySelfCollapsingChildren) 1273 return true; 1274 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { 1275 if (child->isFloatingOrOutOfFlowPositioned()) 1276 continue; 1277 if (!child->isSelfCollapsingBlock()) 1278 return false; 1279 } 1280 return true; 1281 } 1282 return false; 1283 } 1284 1285 void RenderBlock::startDelayUpdateScrollInfo() 1286 { 1287 if (gDelayUpdateScrollInfo == 0) { 1288 ASSERT(!gDelayedUpdateScrollInfoSet); 1289 gDelayedUpdateScrollInfoSet = new DelayedUpdateScrollInfoSet; 1290 } 1291 ASSERT(gDelayedUpdateScrollInfoSet); 1292 ++gDelayUpdateScrollInfo; 1293 } 1294 1295 void RenderBlock::finishDelayUpdateScrollInfo() 1296 { 1297 --gDelayUpdateScrollInfo; 1298 ASSERT(gDelayUpdateScrollInfo >= 0); 1299 if (gDelayUpdateScrollInfo == 0) { 1300 ASSERT(gDelayedUpdateScrollInfoSet); 1301 1302 OwnPtr<DelayedUpdateScrollInfoSet> infoSet(adoptPtr(gDelayedUpdateScrollInfoSet)); 1303 gDelayedUpdateScrollInfoSet = 0; 1304 1305 for (DelayedUpdateScrollInfoSet::iterator it = infoSet->begin(); it != infoSet->end(); ++it) { 1306 RenderBlock* block = *it; 1307 if (block->hasOverflowClip()) { 1308 block->layer()->scrollableArea()->updateAfterLayout(); 1309 } 1310 } 1311 } 1312 } 1313 1314 void RenderBlock::updateScrollInfoAfterLayout() 1315 { 1316 if (hasOverflowClip()) { 1317 if (style()->isFlippedBlocksWritingMode()) { 1318 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937 1319 // Workaround for now. We cannot delay the scroll info for overflow 1320 // for items with opposite writing directions, as the contents needs 1321 // to overflow in that direction 1322 layer()->scrollableArea()->updateAfterLayout(); 1323 return; 1324 } 1325 1326 if (gDelayUpdateScrollInfo) 1327 gDelayedUpdateScrollInfoSet->add(this); 1328 else 1329 layer()->scrollableArea()->updateAfterLayout(); 1330 } 1331 } 1332 1333 void RenderBlock::layout() 1334 { 1335 OverflowEventDispatcher dispatcher(this); 1336 1337 // Update our first letter info now. 1338 updateFirstLetter(); 1339 1340 // Table cells call layoutBlock directly, so don't add any logic here. Put code into 1341 // layoutBlock(). 1342 layoutBlock(false); 1343 1344 // It's safe to check for control clip here, since controls can never be table cells. 1345 // If we have a lightweight clip, there can never be any overflow from children. 1346 if (hasControlClip() && m_overflow) 1347 clearLayoutOverflow(); 1348 1349 invalidateBackgroundObscurationStatus(); 1350 } 1351 1352 bool RenderBlock::updateImageLoadingPriorities() 1353 { 1354 Vector<ImageResource*> images; 1355 appendImagesFromStyle(images, *style()); 1356 1357 if (images.isEmpty()) 1358 return false; 1359 1360 LayoutRect viewBounds = viewRect(); 1361 LayoutRect objectBounds = absoluteContentBox(); 1362 // The object bounds might be empty right now, so intersects will fail since it doesn't deal 1363 // with empty rects. Use LayoutRect::contains in that case. 1364 bool isVisible; 1365 if (!objectBounds.isEmpty()) 1366 isVisible = viewBounds.intersects(objectBounds); 1367 else 1368 isVisible = viewBounds.contains(objectBounds); 1369 1370 ResourceLoadPriorityOptimizer::VisibilityStatus status = isVisible ? 1371 ResourceLoadPriorityOptimizer::Visible : ResourceLoadPriorityOptimizer::NotVisible; 1372 1373 LayoutRect screenArea; 1374 if (!objectBounds.isEmpty()) { 1375 screenArea = viewBounds; 1376 screenArea.intersect(objectBounds); 1377 } 1378 1379 for (Vector<ImageResource*>::iterator it = images.begin(), end = images.end(); it != end; ++it) 1380 ResourceLoadPriorityOptimizer::resourceLoadPriorityOptimizer()->notifyImageResourceVisibility(*it, status, screenArea); 1381 1382 return true; 1383 } 1384 1385 void RenderBlock::computeRegionRangeForBlock(RenderFlowThread* flowThread) 1386 { 1387 if (flowThread) 1388 flowThread->setRegionRangeForBox(this, offsetFromLogicalTopOfFirstPage()); 1389 } 1390 1391 bool RenderBlock::updateLogicalWidthAndColumnWidth() 1392 { 1393 LayoutUnit oldWidth = logicalWidth(); 1394 LayoutUnit oldColumnWidth = desiredColumnWidth(); 1395 1396 updateLogicalWidth(); 1397 calcColumnWidth(); 1398 1399 bool hasBorderOrPaddingLogicalWidthChanged = m_hasBorderOrPaddingLogicalWidthChanged; 1400 m_hasBorderOrPaddingLogicalWidthChanged = false; 1401 1402 return oldWidth != logicalWidth() || oldColumnWidth != desiredColumnWidth() || hasBorderOrPaddingLogicalWidthChanged; 1403 } 1404 1405 void RenderBlock::layoutBlock(bool) 1406 { 1407 ASSERT_NOT_REACHED(); 1408 clearNeedsLayout(); 1409 } 1410 1411 void RenderBlock::addOverflowFromChildren() 1412 { 1413 if (!hasColumns()) { 1414 if (childrenInline()) 1415 toRenderBlockFlow(this)->addOverflowFromInlineChildren(); 1416 else 1417 addOverflowFromBlockChildren(); 1418 } else { 1419 ColumnInfo* colInfo = columnInfo(); 1420 if (columnCount(colInfo)) { 1421 LayoutRect lastRect = columnRectAt(colInfo, columnCount(colInfo) - 1); 1422 addLayoutOverflow(lastRect); 1423 addContentsVisualOverflow(lastRect); 1424 } 1425 } 1426 } 1427 1428 void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool) 1429 { 1430 m_overflow.clear(); 1431 1432 // Add overflow from children. 1433 addOverflowFromChildren(); 1434 1435 // Add in the overflow from positioned objects. 1436 addOverflowFromPositionedObjects(); 1437 1438 if (hasOverflowClip()) { 1439 // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins 1440 // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always 1441 // be considered reachable. 1442 LayoutRect clientRect(noOverflowRect()); 1443 LayoutRect rectToApply; 1444 if (isHorizontalWritingMode()) 1445 rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, max<LayoutUnit>(0, oldClientAfterEdge - clientRect.y())); 1446 else 1447 rectToApply = LayoutRect(clientRect.x(), clientRect.y(), max<LayoutUnit>(0, oldClientAfterEdge - clientRect.x()), 1); 1448 addLayoutOverflow(rectToApply); 1449 if (hasRenderOverflow()) 1450 m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge); 1451 } 1452 1453 addVisualEffectOverflow(); 1454 1455 addVisualOverflowFromTheme(); 1456 } 1457 1458 void RenderBlock::addOverflowFromBlockChildren() 1459 { 1460 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { 1461 if (!child->isFloatingOrOutOfFlowPositioned()) 1462 addOverflowFromChild(child); 1463 } 1464 } 1465 1466 void RenderBlock::addOverflowFromPositionedObjects() 1467 { 1468 TrackedRendererListHashSet* positionedDescendants = positionedObjects(); 1469 if (!positionedDescendants) 1470 return; 1471 1472 RenderBox* positionedObject; 1473 TrackedRendererListHashSet::iterator end = positionedDescendants->end(); 1474 for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { 1475 positionedObject = *it; 1476 1477 // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content. 1478 if (positionedObject->style()->position() != FixedPosition) 1479 addOverflowFromChild(positionedObject, LayoutSize(positionedObject->x(), positionedObject->y())); 1480 } 1481 } 1482 1483 void RenderBlock::addVisualOverflowFromTheme() 1484 { 1485 if (!style()->hasAppearance()) 1486 return; 1487 1488 IntRect inflatedRect = pixelSnappedBorderBoxRect(); 1489 RenderTheme::theme().adjustRepaintRect(this, inflatedRect); 1490 addVisualOverflow(inflatedRect); 1491 } 1492 1493 bool RenderBlock::createsBlockFormattingContext() const 1494 { 1495 return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || isFlexItemIncludingDeprecated() 1496 || style()->specifiesColumns() || isRenderFlowThread() || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isDocumentElement() || style()->columnSpan(); 1497 } 1498 1499 void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox* child) 1500 { 1501 // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into 1502 // an auto value. Add a method to determine this, so that we can avoid the relayout. 1503 if (relayoutChildren || (child->hasRelativeLogicalHeight() && !isRenderView())) 1504 child->setChildNeedsLayout(MarkOnlyThis); 1505 1506 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. 1507 if (relayoutChildren && child->needsPreferredWidthsRecalculation()) 1508 child->setPreferredLogicalWidthsDirty(MarkOnlyThis); 1509 } 1510 1511 void RenderBlock::simplifiedNormalFlowLayout() 1512 { 1513 if (childrenInline()) { 1514 ListHashSet<RootInlineBox*> lineBoxes; 1515 for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { 1516 RenderObject* o = walker.current(); 1517 if (!o->isOutOfFlowPositioned() && (o->isReplaced() || o->isFloating())) { 1518 o->layoutIfNeeded(); 1519 if (toRenderBox(o)->inlineBoxWrapper()) { 1520 RootInlineBox& box = toRenderBox(o)->inlineBoxWrapper()->root(); 1521 lineBoxes.add(&box); 1522 } 1523 } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) { 1524 o->clearNeedsLayout(); 1525 } 1526 } 1527 1528 // FIXME: Glyph overflow will get lost in this case, but not really a big deal. 1529 GlyphOverflowAndFallbackFontsMap textBoxDataMap; 1530 for (ListHashSet<RootInlineBox*>::const_iterator it = lineBoxes.begin(); it != lineBoxes.end(); ++it) { 1531 RootInlineBox* box = *it; 1532 box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap); 1533 } 1534 } else { 1535 for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) { 1536 if (!box->isOutOfFlowPositioned()) 1537 box->layoutIfNeeded(); 1538 } 1539 } 1540 } 1541 1542 bool RenderBlock::simplifiedLayout() 1543 { 1544 // Check if we need to do a full layout. 1545 if (normalChildNeedsLayout() || selfNeedsLayout()) 1546 return false; 1547 1548 // Check that we actually need to do a simplified layout. 1549 if (!posChildNeedsLayout() && !(needsSimplifiedNormalFlowLayout() || needsPositionedMovementLayout())) 1550 return false; 1551 1552 1553 { 1554 // LayoutState needs this deliberate scope to pop before repaint 1555 LayoutState state(*this, locationOffset()); 1556 1557 if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly()) 1558 return false; 1559 1560 FastTextAutosizer::LayoutScope fastTextAutosizerLayoutScope(this); 1561 1562 // Lay out positioned descendants or objects that just need to recompute overflow. 1563 if (needsSimplifiedNormalFlowLayout()) 1564 simplifiedNormalFlowLayout(); 1565 1566 // Lay out our positioned objects if our positioned child bit is set. 1567 // Also, if an absolute position element inside a relative positioned container moves, and the absolute element has a fixed position 1568 // child, neither the fixed element nor its container learn of the movement since posChildNeedsLayout() is only marked as far as the 1569 // relative positioned container. So if we can have fixed pos objects in our positioned objects list check if any of them 1570 // are statically positioned and thus need to move with their absolute ancestors. 1571 bool canContainFixedPosObjects = canContainFixedPositionObjects(); 1572 if (posChildNeedsLayout() || needsPositionedMovementLayout() || canContainFixedPosObjects) 1573 layoutPositionedObjects(false, needsPositionedMovementLayout() ? ForcedLayoutAfterContainingBlockMoved : (!posChildNeedsLayout() && canContainFixedPosObjects ? LayoutOnlyFixedPositionedObjects : DefaultLayout)); 1574 1575 // Recompute our overflow information. 1576 // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only 1577 // updating our overflow if we either used to have overflow or if the new temporary object has overflow. 1578 // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and 1579 // lowestPosition on every relayout so it's not a regression. 1580 // computeOverflow expects the bottom edge before we clamp our height. Since this information isn't available during 1581 // simplifiedLayout, we cache the value in m_overflow. 1582 LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom(); 1583 computeOverflow(oldClientAfterEdge, true); 1584 } 1585 1586 updateLayerTransformAfterLayout(); 1587 1588 updateScrollInfoAfterLayout(); 1589 1590 clearNeedsLayout(); 1591 return true; 1592 } 1593 1594 void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderObject* child, SubtreeLayoutScope& layoutScope) 1595 { 1596 if (child->style()->position() != FixedPosition) 1597 return; 1598 1599 bool hasStaticBlockPosition = child->style()->hasStaticBlockPosition(isHorizontalWritingMode()); 1600 bool hasStaticInlinePosition = child->style()->hasStaticInlinePosition(isHorizontalWritingMode()); 1601 if (!hasStaticBlockPosition && !hasStaticInlinePosition) 1602 return; 1603 1604 RenderObject* o = child->parent(); 1605 while (o && !o->isRenderView() && o->style()->position() != AbsolutePosition) 1606 o = o->parent(); 1607 if (o->style()->position() != AbsolutePosition) 1608 return; 1609 1610 RenderBox* box = toRenderBox(child); 1611 if (hasStaticInlinePosition) { 1612 LogicalExtentComputedValues computedValues; 1613 box->computeLogicalWidth(computedValues); 1614 LayoutUnit newLeft = computedValues.m_position; 1615 if (newLeft != box->logicalLeft()) 1616 layoutScope.setChildNeedsLayout(child); 1617 } else if (hasStaticBlockPosition) { 1618 LayoutUnit oldTop = box->logicalTop(); 1619 box->updateLogicalHeight(); 1620 if (box->logicalTop() != oldTop) 1621 layoutScope.setChildNeedsLayout(child); 1622 } 1623 } 1624 1625 LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild(RenderBox* child) const 1626 { 1627 // A margin has three types: fixed, percentage, and auto (variable). 1628 // Auto and percentage margins become 0 when computing min/max width. 1629 // Fixed margins can be added in as is. 1630 Length marginLeft = child->style()->marginStartUsing(style()); 1631 Length marginRight = child->style()->marginEndUsing(style()); 1632 LayoutUnit margin = 0; 1633 if (marginLeft.isFixed()) 1634 margin += marginLeft.value(); 1635 if (marginRight.isFixed()) 1636 margin += marginRight.value(); 1637 return margin; 1638 } 1639 1640 void RenderBlock::layoutPositionedObjects(bool relayoutChildren, PositionedLayoutBehavior info) 1641 { 1642 TrackedRendererListHashSet* positionedDescendants = positionedObjects(); 1643 if (!positionedDescendants) 1644 return; 1645 1646 if (hasColumns()) 1647 view()->layoutState()->clearPaginationInformation(); // Positioned objects are not part of the column flow, so they don't paginate with the columns. 1648 1649 RenderBox* r; 1650 TrackedRendererListHashSet::iterator end = positionedDescendants->end(); 1651 for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { 1652 r = *it; 1653 1654 // FIXME: this should only be set from clearNeedsLayout crbug.com/361250 1655 r->setLayoutDidGetCalled(true); 1656 1657 SubtreeLayoutScope layoutScope(*r); 1658 // A fixed position element with an absolute positioned ancestor has no way of knowing if the latter has changed position. So 1659 // if this is a fixed position element, mark it for layout if it has an abspos ancestor and needs to move with that ancestor, i.e. 1660 // it has static position. 1661 markFixedPositionObjectForLayoutIfNeeded(r, layoutScope); 1662 if (info == LayoutOnlyFixedPositionedObjects) { 1663 r->layoutIfNeeded(); 1664 continue; 1665 } 1666 1667 // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the 1668 // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned 1669 // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is 1670 // positioned explicitly) this should not incur a performance penalty. 1671 if (relayoutChildren || (r->style()->hasStaticBlockPosition(isHorizontalWritingMode()) && r->parent() != this)) 1672 layoutScope.setChildNeedsLayout(r); 1673 1674 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. 1675 if (relayoutChildren && r->needsPreferredWidthsRecalculation()) 1676 r->setPreferredLogicalWidthsDirty(MarkOnlyThis); 1677 1678 if (!r->needsLayout()) 1679 r->markForPaginationRelayoutIfNeeded(layoutScope); 1680 1681 // If we are paginated or in a line grid, go ahead and compute a vertical position for our object now. 1682 // If it's wrong we'll lay out again. 1683 LayoutUnit oldLogicalTop = 0; 1684 bool needsBlockDirectionLocationSetBeforeLayout = r->needsLayout() && view()->layoutState()->needsBlockDirectionLocationSetBeforeLayout(); 1685 if (needsBlockDirectionLocationSetBeforeLayout) { 1686 if (isHorizontalWritingMode() == r->isHorizontalWritingMode()) 1687 r->updateLogicalHeight(); 1688 else 1689 r->updateLogicalWidth(); 1690 oldLogicalTop = logicalTopForChild(r); 1691 } 1692 1693 // FIXME: We should be able to do a r->setNeedsPositionedMovementLayout() here instead of a full layout. Need 1694 // to investigate why it does not trigger the correct invalidations in that case. crbug.com/350756 1695 if (info == ForcedLayoutAfterContainingBlockMoved) 1696 r->setNeedsLayoutAndFullPaintInvalidation(); 1697 1698 r->layoutIfNeeded(); 1699 1700 // Lay out again if our estimate was wrong. 1701 if (needsBlockDirectionLocationSetBeforeLayout && logicalTopForChild(r) != oldLogicalTop) 1702 r->forceChildLayout(); 1703 } 1704 1705 if (hasColumns()) 1706 view()->layoutState()->setColumnInfo(columnInfo()); // FIXME: Kind of gross. We just put this back into the layout state so that pop() will work. 1707 } 1708 1709 void RenderBlock::markPositionedObjectsForLayout() 1710 { 1711 TrackedRendererListHashSet* positionedDescendants = positionedObjects(); 1712 if (positionedDescendants) { 1713 RenderBox* r; 1714 TrackedRendererListHashSet::iterator end = positionedDescendants->end(); 1715 for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { 1716 r = *it; 1717 r->setChildNeedsLayout(); 1718 } 1719 } 1720 } 1721 1722 void RenderBlock::markForPaginationRelayoutIfNeeded(SubtreeLayoutScope& layoutScope) 1723 { 1724 ASSERT(!needsLayout()); 1725 if (needsLayout()) 1726 return; 1727 1728 if (view()->layoutState()->pageLogicalHeightChanged() || (view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(*this, logicalTop()) != pageLogicalOffset())) 1729 layoutScope.setChildNeedsLayout(this); 1730 } 1731 1732 void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1733 { 1734 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this); 1735 1736 LayoutPoint adjustedPaintOffset = paintOffset + location(); 1737 1738 PaintPhase phase = paintInfo.phase; 1739 1740 LayoutRect overflowBox; 1741 // Check if we need to do anything at all. 1742 // FIXME: Could eliminate the isDocumentElement() check if we fix background painting so that the RenderView 1743 // paints the root's background. 1744 if (!isDocumentElement()) { 1745 overflowBox = overflowRectForPaintRejection(); 1746 flipForWritingMode(overflowBox); 1747 overflowBox.moveBy(adjustedPaintOffset); 1748 if (!overflowBox.intersects(paintInfo.rect)) 1749 return; 1750 } 1751 1752 // There are some cases where not all clipped visual overflow is accounted for. 1753 // FIXME: reduce the number of such cases. 1754 ContentsClipBehavior contentsClipBehavior = ForceContentsClip; 1755 if (hasOverflowClip() && !hasControlClip() && !(shouldPaintSelectionGaps() && phase == PaintPhaseForeground) && !hasCaret()) 1756 contentsClipBehavior = SkipContentsClipIfPossible; 1757 1758 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, contentsClipBehavior); 1759 { 1760 GraphicsContextCullSaver cullSaver(*paintInfo.context); 1761 // Cull if we have more than one child and we didn't already clip. 1762 bool shouldCull = document().settings()->containerCullingEnabled() && !pushedClip && !isDocumentElement() 1763 && firstChild() && lastChild() && firstChild() != lastChild(); 1764 if (shouldCull) 1765 cullSaver.cull(overflowBox); 1766 1767 paintObject(paintInfo, adjustedPaintOffset); 1768 } 1769 if (pushedClip) 1770 popContentsClip(paintInfo, phase, adjustedPaintOffset); 1771 1772 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with 1773 // z-index. We paint after we painted the background/border, so that the scrollbars will 1774 // sit above the background/border. 1775 if (hasOverflowClip() && style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(this) && !paintInfo.paintRootBackgroundOnly()) 1776 layer()->scrollableArea()->paintOverflowControls(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.rect, false /* paintingOverlayControls */); 1777 } 1778 1779 void RenderBlock::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1780 { 1781 if (paintInfo.context->paintingDisabled()) 1782 return; 1783 1784 const Color& ruleColor = resolveColor(CSSPropertyWebkitColumnRuleColor); 1785 bool ruleTransparent = style()->columnRuleIsTransparent(); 1786 EBorderStyle ruleStyle = style()->columnRuleStyle(); 1787 LayoutUnit ruleThickness = style()->columnRuleWidth(); 1788 LayoutUnit colGap = columnGap(); 1789 bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; 1790 if (!renderRule) 1791 return; 1792 1793 ColumnInfo* colInfo = columnInfo(); 1794 unsigned colCount = columnCount(colInfo); 1795 1796 bool antialias = shouldAntialiasLines(paintInfo.context); 1797 1798 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { 1799 bool leftToRight = style()->isLeftToRightDirection(); 1800 LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth(); 1801 LayoutUnit ruleAdd = logicalLeftOffsetForContent(); 1802 LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth(); 1803 LayoutUnit inlineDirectionSize = colInfo->desiredColumnWidth(); 1804 BoxSide boxSide = isHorizontalWritingMode() 1805 ? leftToRight ? BSLeft : BSRight 1806 : leftToRight ? BSTop : BSBottom; 1807 1808 for (unsigned i = 0; i < colCount; i++) { 1809 // Move to the next position. 1810 if (leftToRight) { 1811 ruleLogicalLeft += inlineDirectionSize + colGap / 2; 1812 currLogicalLeftOffset += inlineDirectionSize + colGap; 1813 } else { 1814 ruleLogicalLeft -= (inlineDirectionSize + colGap / 2); 1815 currLogicalLeftOffset -= (inlineDirectionSize + colGap); 1816 } 1817 1818 // Now paint the column rule. 1819 if (i < colCount - 1) { 1820 LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft(); 1821 LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth(); 1822 LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd; 1823 LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness; 1824 IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom); 1825 drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); 1826 } 1827 1828 ruleLogicalLeft = currLogicalLeftOffset; 1829 } 1830 } else { 1831 bool topToBottom = !style()->isFlippedBlocksWritingMode(); 1832 LayoutUnit ruleLeft = isHorizontalWritingMode() 1833 ? borderLeft() + paddingLeft() 1834 : colGap / 2 - colGap - ruleThickness / 2 + borderBefore() + paddingBefore(); 1835 LayoutUnit ruleWidth = isHorizontalWritingMode() ? contentWidth() : ruleThickness; 1836 LayoutUnit ruleTop = isHorizontalWritingMode() 1837 ? colGap / 2 - colGap - ruleThickness / 2 + borderBefore() + paddingBefore() 1838 : borderStart() + paddingStart(); 1839 LayoutUnit ruleHeight = isHorizontalWritingMode() ? ruleThickness : contentHeight(); 1840 LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight); 1841 1842 if (!topToBottom) { 1843 if (isHorizontalWritingMode()) 1844 ruleRect.setY(height() - ruleRect.maxY()); 1845 else 1846 ruleRect.setX(width() - ruleRect.maxX()); 1847 } 1848 1849 ruleRect.moveBy(paintOffset); 1850 1851 BoxSide boxSide = isHorizontalWritingMode() 1852 ? topToBottom ? BSTop : BSBottom 1853 : topToBottom ? BSLeft : BSRight; 1854 1855 LayoutSize step(0, topToBottom ? colInfo->columnHeight() + colGap : -(colInfo->columnHeight() + colGap)); 1856 if (!isHorizontalWritingMode()) 1857 step = step.transposedSize(); 1858 1859 for (unsigned i = 1; i < colCount; i++) { 1860 ruleRect.move(step); 1861 IntRect pixelSnappedRuleRect = pixelSnappedIntRect(ruleRect); 1862 drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); 1863 } 1864 } 1865 } 1866 1867 void RenderBlock::paintColumnContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset, bool paintingFloats) 1868 { 1869 // We need to do multiple passes, breaking up our child painting into strips. 1870 GraphicsContext* context = paintInfo.context; 1871 ColumnInfo* colInfo = columnInfo(); 1872 unsigned colCount = columnCount(colInfo); 1873 if (!colCount) 1874 return; 1875 LayoutUnit currLogicalTopOffset = 0; 1876 LayoutUnit colGap = columnGap(); 1877 for (unsigned i = 0; i < colCount; i++) { 1878 // For each rect, we clip to the rect, and then we adjust our coords. 1879 LayoutRect colRect = columnRectAt(colInfo, i); 1880 flipForWritingMode(colRect); 1881 LayoutUnit logicalLeftOffset = (isHorizontalWritingMode() ? colRect.x() : colRect.y()) - logicalLeftOffsetForContent(); 1882 LayoutSize offset = isHorizontalWritingMode() ? LayoutSize(logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, logicalLeftOffset); 1883 if (colInfo->progressionAxis() == ColumnInfo::BlockAxis) { 1884 if (isHorizontalWritingMode()) 1885 offset.expand(0, colRect.y() - borderTop() - paddingTop()); 1886 else 1887 offset.expand(colRect.x() - borderLeft() - paddingLeft(), 0); 1888 } 1889 colRect.moveBy(paintOffset); 1890 PaintInfo info(paintInfo); 1891 info.rect.intersect(enclosingIntRect(colRect)); 1892 1893 if (!info.rect.isEmpty()) { 1894 GraphicsContextStateSaver stateSaver(*context); 1895 LayoutRect clipRect(colRect); 1896 1897 if (i < colCount - 1) { 1898 if (isHorizontalWritingMode()) 1899 clipRect.expand(colGap / 2, 0); 1900 else 1901 clipRect.expand(0, colGap / 2); 1902 } 1903 // Each strip pushes a clip, since column boxes are specified as being 1904 // like overflow:hidden. 1905 // FIXME: Content and column rules that extend outside column boxes at the edges of the multi-column element 1906 // are clipped according to the 'overflow' property. 1907 context->clip(enclosingIntRect(clipRect)); 1908 1909 // Adjust our x and y when painting. 1910 LayoutPoint adjustedPaintOffset = paintOffset + offset; 1911 if (paintingFloats) 1912 paintFloats(info, adjustedPaintOffset, paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip); 1913 else 1914 paintContents(info, adjustedPaintOffset); 1915 } 1916 1917 LayoutUnit blockDelta = (isHorizontalWritingMode() ? colRect.height() : colRect.width()); 1918 if (style()->isFlippedBlocksWritingMode()) 1919 currLogicalTopOffset += blockDelta; 1920 else 1921 currLogicalTopOffset -= blockDelta; 1922 } 1923 } 1924 1925 void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1926 { 1927 // Avoid painting descendants of the root element when stylesheets haven't loaded. This eliminates FOUC. 1928 // It's ok not to draw, because later on, when all the stylesheets do load, styleResolverChanged() on the Document 1929 // will do a full repaint. 1930 if (document().didLayoutWithPendingStylesheets() && !isRenderView()) 1931 return; 1932 1933 if (childrenInline()) 1934 m_lineBoxes.paint(this, paintInfo, paintOffset); 1935 else { 1936 PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? PaintPhaseOutline : paintInfo.phase; 1937 newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChildBlockBackground : newPhase; 1938 1939 // We don't paint our own background, but we do let the kids paint their backgrounds. 1940 PaintInfo paintInfoForChild(paintInfo); 1941 paintInfoForChild.phase = newPhase; 1942 paintInfoForChild.updatePaintingRootForChildren(this); 1943 paintChildren(paintInfoForChild, paintOffset); 1944 } 1945 } 1946 1947 void RenderBlock::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1948 { 1949 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) 1950 paintChild(child, paintInfo, paintOffset); 1951 } 1952 1953 void RenderBlock::paintChild(RenderBox* child, PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1954 { 1955 LayoutPoint childPoint = flipForWritingModeForChild(child, paintOffset); 1956 if (!child->hasSelfPaintingLayer() && !child->isFloating()) 1957 child->paint(paintInfo, childPoint); 1958 } 1959 1960 void RenderBlock::paintChildAsInlineBlock(RenderBox* child, PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1961 { 1962 LayoutPoint childPoint = flipForWritingModeForChild(child, paintOffset); 1963 if (!child->hasSelfPaintingLayer() && !child->isFloating()) 1964 paintAsInlineBlock(child, paintInfo, childPoint); 1965 } 1966 1967 void RenderBlock::paintAsInlineBlock(RenderObject* renderer, PaintInfo& paintInfo, const LayoutPoint& childPoint) 1968 { 1969 if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection) 1970 return; 1971 1972 // Paint all phases atomically, as though the element established its own 1973 // stacking context. (See Appendix E.2, section 7.2.1.4 on 1974 // inline block/table/replaced elements in the CSS2.1 specification.) 1975 // This is also used by other elements (e.g. flex items and grid items). 1976 bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip; 1977 PaintInfo info(paintInfo); 1978 info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; 1979 renderer->paint(info, childPoint); 1980 if (!preservePhase) { 1981 info.phase = PaintPhaseChildBlockBackgrounds; 1982 renderer->paint(info, childPoint); 1983 info.phase = PaintPhaseFloat; 1984 renderer->paint(info, childPoint); 1985 info.phase = PaintPhaseForeground; 1986 renderer->paint(info, childPoint); 1987 info.phase = PaintPhaseOutline; 1988 renderer->paint(info, childPoint); 1989 } 1990 } 1991 1992 static inline bool caretBrowsingEnabled(const Frame* frame) 1993 { 1994 Settings* settings = frame->settings(); 1995 return settings && settings->caretBrowsingEnabled(); 1996 } 1997 1998 static inline bool hasCursorCaret(const FrameSelection& selection, const RenderBlock* block, bool caretBrowsing) 1999 { 2000 return selection.caretRenderer() == block && (selection.rendererIsEditable() || caretBrowsing); 2001 } 2002 2003 static inline bool hasDragCaret(const DragCaretController& dragCaretController, const RenderBlock* block, bool caretBrowsing) 2004 { 2005 return dragCaretController.caretRenderer() == block && (dragCaretController.isContentEditable() || caretBrowsing); 2006 } 2007 2008 bool RenderBlock::hasCaret() const 2009 { 2010 bool caretBrowsing = caretBrowsingEnabled(frame()); 2011 return hasCursorCaret(frame()->selection(), this, caretBrowsing) 2012 || hasDragCaret(frame()->page()->dragCaretController(), this, caretBrowsing); 2013 } 2014 2015 void RenderBlock::paintCarets(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 2016 { 2017 bool caretBrowsing = caretBrowsingEnabled(frame()); 2018 2019 FrameSelection& selection = frame()->selection(); 2020 if (hasCursorCaret(selection, this, caretBrowsing)) { 2021 selection.paintCaret(paintInfo.context, paintOffset, paintInfo.rect); 2022 } 2023 2024 DragCaretController& dragCaretController = frame()->page()->dragCaretController(); 2025 if (hasDragCaret(dragCaretController, this, caretBrowsing)) { 2026 dragCaretController.paintDragCaret(frame(), paintInfo.context, paintOffset, paintInfo.rect); 2027 } 2028 } 2029 2030 void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 2031 { 2032 PaintPhase paintPhase = paintInfo.phase; 2033 2034 // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div). 2035 LayoutPoint scrolledOffset = paintOffset; 2036 if (hasOverflowClip()) 2037 scrolledOffset.move(-scrolledContentOffset()); 2038 2039 // 1. paint background, borders etc 2040 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style()->visibility() == VISIBLE) { 2041 if (hasBoxDecorations()) 2042 paintBoxDecorations(paintInfo, paintOffset); 2043 if (hasColumns() && !paintInfo.paintRootBackgroundOnly()) 2044 paintColumnRules(paintInfo, scrolledOffset); 2045 } 2046 2047 if (paintPhase == PaintPhaseMask && style()->visibility() == VISIBLE) { 2048 paintMask(paintInfo, paintOffset); 2049 return; 2050 } 2051 2052 if (paintPhase == PaintPhaseClippingMask && style()->visibility() == VISIBLE) { 2053 paintClippingMask(paintInfo, paintOffset); 2054 return; 2055 } 2056 2057 // We're done. We don't bother painting any children. 2058 if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackgroundOnly()) 2059 return; 2060 2061 // 2. paint contents 2062 if (paintPhase != PaintPhaseSelfOutline) { 2063 if (hasColumns()) 2064 paintColumnContents(paintInfo, scrolledOffset); 2065 else 2066 paintContents(paintInfo, scrolledOffset); 2067 } 2068 2069 // 3. paint selection 2070 // FIXME: Make this work with multi column layouts. For now don't fill gaps. 2071 bool isPrinting = document().printing(); 2072 if (!isPrinting && !hasColumns()) 2073 paintSelection(paintInfo, scrolledOffset); // Fill in gaps in selection on lines and between blocks. 2074 2075 // 4. paint floats. 2076 if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) { 2077 if (hasColumns()) 2078 paintColumnContents(paintInfo, scrolledOffset, true); 2079 else 2080 paintFloats(paintInfo, scrolledOffset, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); 2081 } 2082 2083 // 5. paint outline. 2084 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) 2085 paintOutline(paintInfo, LayoutRect(paintOffset, size())); 2086 2087 // 6. paint continuation outlines. 2088 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) { 2089 RenderInline* inlineCont = inlineElementContinuation(); 2090 if (inlineCont && inlineCont->hasOutline() && inlineCont->style()->visibility() == VISIBLE) { 2091 RenderInline* inlineRenderer = toRenderInline(inlineCont->node()->renderer()); 2092 RenderBlock* cb = containingBlock(); 2093 2094 bool inlineEnclosedInSelfPaintingLayer = false; 2095 for (RenderBoxModelObject* box = inlineRenderer; box != cb; box = box->parent()->enclosingBoxModelObject()) { 2096 if (box->hasSelfPaintingLayer()) { 2097 inlineEnclosedInSelfPaintingLayer = true; 2098 break; 2099 } 2100 } 2101 2102 // Do not add continuations for outline painting by our containing block if we are a relative positioned 2103 // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on renderers in its continuation table being 2104 // in the same layer. 2105 if (!inlineEnclosedInSelfPaintingLayer && !hasLayer()) 2106 cb->addContinuationWithOutline(inlineRenderer); 2107 else if (!inlineRenderer->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && hasLayer())) 2108 inlineRenderer->paintOutline(paintInfo, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location()); 2109 } 2110 paintContinuationOutlines(paintInfo, paintOffset); 2111 } 2112 2113 // 7. paint caret. 2114 // If the caret's node's render object's containing block is this block, and the paint action is PaintPhaseForeground, 2115 // then paint the caret. 2116 if (paintPhase == PaintPhaseForeground) { 2117 paintCarets(paintInfo, paintOffset); 2118 } 2119 } 2120 2121 RenderInline* RenderBlock::inlineElementContinuation() const 2122 { 2123 RenderBoxModelObject* continuation = this->continuation(); 2124 return continuation && continuation->isInline() ? toRenderInline(continuation) : 0; 2125 } 2126 2127 RenderBlock* RenderBlock::blockElementContinuation() const 2128 { 2129 RenderBoxModelObject* currentContinuation = continuation(); 2130 if (!currentContinuation || currentContinuation->isInline()) 2131 return 0; 2132 RenderBlock* nextContinuation = toRenderBlock(currentContinuation); 2133 if (nextContinuation->isAnonymousBlock()) 2134 return nextContinuation->blockElementContinuation(); 2135 return nextContinuation; 2136 } 2137 2138 static ContinuationOutlineTableMap* continuationOutlineTable() 2139 { 2140 DEFINE_STATIC_LOCAL(ContinuationOutlineTableMap, table, ()); 2141 return &table; 2142 } 2143 2144 void RenderBlock::addContinuationWithOutline(RenderInline* flow) 2145 { 2146 // We can't make this work if the inline is in a layer. We'll just rely on the broken 2147 // way of painting. 2148 ASSERT(!flow->layer() && !flow->isInlineElementContinuation()); 2149 2150 ContinuationOutlineTableMap* table = continuationOutlineTable(); 2151 ListHashSet<RenderInline*>* continuations = table->get(this); 2152 if (!continuations) { 2153 continuations = new ListHashSet<RenderInline*>; 2154 table->set(this, adoptPtr(continuations)); 2155 } 2156 2157 continuations->add(flow); 2158 } 2159 2160 bool RenderBlock::paintsContinuationOutline(RenderInline* flow) 2161 { 2162 ContinuationOutlineTableMap* table = continuationOutlineTable(); 2163 if (table->isEmpty()) 2164 return false; 2165 2166 ListHashSet<RenderInline*>* continuations = table->get(this); 2167 if (!continuations) 2168 return false; 2169 2170 return continuations->contains(flow); 2171 } 2172 2173 void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& paintOffset) 2174 { 2175 ContinuationOutlineTableMap* table = continuationOutlineTable(); 2176 if (table->isEmpty()) 2177 return; 2178 2179 OwnPtr<ListHashSet<RenderInline*> > continuations = table->take(this); 2180 if (!continuations) 2181 return; 2182 2183 LayoutPoint accumulatedPaintOffset = paintOffset; 2184 // Paint each continuation outline. 2185 ListHashSet<RenderInline*>::iterator end = continuations->end(); 2186 for (ListHashSet<RenderInline*>::iterator it = continuations->begin(); it != end; ++it) { 2187 // Need to add in the coordinates of the intervening blocks. 2188 RenderInline* flow = *it; 2189 RenderBlock* block = flow->containingBlock(); 2190 for ( ; block && block != this; block = block->containingBlock()) 2191 accumulatedPaintOffset.moveBy(block->location()); 2192 ASSERT(block); 2193 flow->paintOutline(info, accumulatedPaintOffset); 2194 } 2195 } 2196 2197 bool RenderBlock::shouldPaintSelectionGaps() const 2198 { 2199 return selectionState() != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot(); 2200 } 2201 2202 bool RenderBlock::isSelectionRoot() const 2203 { 2204 if (isPseudoElement()) 2205 return false; 2206 ASSERT(node() || isAnonymous()); 2207 2208 // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases. 2209 if (isTable()) 2210 return false; 2211 2212 if (isBody() || isDocumentElement() || hasOverflowClip() 2213 || isPositioned() || isFloating() 2214 || isTableCell() || isInlineBlockOrInlineTable() 2215 || hasTransform() || hasReflection() || hasMask() || isWritingModeRoot() 2216 || isRenderFlowThread() || isFlexItemIncludingDeprecated()) 2217 return true; 2218 2219 if (view() && view()->selectionStart()) { 2220 Node* startElement = view()->selectionStart()->node(); 2221 if (startElement && startElement->rootEditableElement() == node()) 2222 return true; 2223 } 2224 2225 return false; 2226 } 2227 2228 GapRects RenderBlock::selectionGapRectsForRepaint(const RenderLayerModelObject* repaintContainer) 2229 { 2230 ASSERT(!needsLayout()); 2231 2232 if (!shouldPaintSelectionGaps()) 2233 return GapRects(); 2234 2235 TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint()); 2236 mapLocalToContainer(repaintContainer, transformState, ApplyContainerFlip | UseTransforms); 2237 LayoutPoint offsetFromRepaintContainer = roundedLayoutPoint(transformState.mappedPoint()); 2238 2239 if (hasOverflowClip()) 2240 offsetFromRepaintContainer -= scrolledContentOffset(); 2241 2242 LayoutUnit lastTop = 0; 2243 LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop); 2244 LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop); 2245 2246 return selectionGaps(this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight); 2247 } 2248 2249 void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 2250 { 2251 if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) { 2252 LayoutUnit lastTop = 0; 2253 LayoutUnit lastLeft = logicalLeftSelectionOffset(this, lastTop); 2254 LayoutUnit lastRight = logicalRightSelectionOffset(this, lastTop); 2255 GraphicsContextStateSaver stateSaver(*paintInfo.context); 2256 2257 LayoutRect gapRectsBounds = selectionGaps(this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, &paintInfo); 2258 if (!gapRectsBounds.isEmpty()) { 2259 RenderLayer* layer = enclosingLayer(); 2260 gapRectsBounds.moveBy(-paintOffset); 2261 if (!hasLayer()) { 2262 LayoutRect localBounds(gapRectsBounds); 2263 flipForWritingMode(localBounds); 2264 gapRectsBounds = localToContainerQuad(FloatRect(localBounds), layer->renderer()).enclosingBoundingBox(); 2265 if (layer->renderer()->hasOverflowClip()) 2266 gapRectsBounds.move(layer->renderBox()->scrolledContentOffset()); 2267 } 2268 layer->addBlockSelectionGapsBounds(gapRectsBounds); 2269 } 2270 } 2271 } 2272 2273 static void clipOutPositionedObjects(const PaintInfo* paintInfo, const LayoutPoint& offset, TrackedRendererListHashSet* positionedObjects) 2274 { 2275 if (!positionedObjects) 2276 return; 2277 2278 TrackedRendererListHashSet::const_iterator end = positionedObjects->end(); 2279 for (TrackedRendererListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) { 2280 RenderBox* r = *it; 2281 paintInfo->context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height())); 2282 } 2283 } 2284 2285 LayoutUnit RenderBlock::blockDirectionOffset(const LayoutSize& offsetFromBlock) const 2286 { 2287 return isHorizontalWritingMode() ? offsetFromBlock.height() : offsetFromBlock.width(); 2288 } 2289 2290 LayoutUnit RenderBlock::inlineDirectionOffset(const LayoutSize& offsetFromBlock) const 2291 { 2292 return isHorizontalWritingMode() ? offsetFromBlock.width() : offsetFromBlock.height(); 2293 } 2294 2295 LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPhysicalPosition, const LayoutRect& logicalRect) 2296 { 2297 LayoutRect result; 2298 if (isHorizontalWritingMode()) 2299 result = logicalRect; 2300 else 2301 result = LayoutRect(logicalRect.y(), logicalRect.x(), logicalRect.height(), logicalRect.width()); 2302 flipForWritingMode(result); 2303 result.moveBy(rootBlockPhysicalPosition); 2304 return result; 2305 } 2306 2307 GapRects RenderBlock::selectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 2308 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) 2309 { 2310 // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore. 2311 // Clip out floating and positioned objects when painting selection gaps. 2312 if (paintInfo) { 2313 // Note that we don't clip out overflow for positioned objects. We just stick to the border box. 2314 LayoutRect flippedBlockRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height()); 2315 rootBlock->flipForWritingMode(flippedBlockRect); 2316 flippedBlockRect.moveBy(rootBlockPhysicalPosition); 2317 clipOutPositionedObjects(paintInfo, flippedBlockRect.location(), positionedObjects()); 2318 if (isBody() || isDocumentElement()) // The <body> must make sure to examine its containingBlock's positioned objects. 2319 for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock()) 2320 clipOutPositionedObjects(paintInfo, LayoutPoint(cb->x(), cb->y()), cb->positionedObjects()); // FIXME: Not right for flipped writing modes. 2321 clipOutFloatingObjects(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock); 2322 } 2323 2324 // FIXME: overflow: auto/scroll regions need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is 2325 // fixed). 2326 GapRects result; 2327 if (!isRenderBlockFlow() && !isFlexibleBoxIncludingDeprecated()) // FIXME: Make multi-column selection gap filling work someday. 2328 return result; 2329 2330 if (hasColumns() || hasTransform() || style()->columnSpan()) { 2331 // FIXME: We should learn how to gap fill multiple columns and transforms eventually. 2332 lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalHeight(); 2333 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight()); 2334 lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight()); 2335 return result; 2336 } 2337 2338 if (childrenInline()) 2339 result = toRenderBlockFlow(this)->inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); 2340 else 2341 result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo); 2342 2343 // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. 2344 if (rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) 2345 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, 2346 logicalHeight(), paintInfo)); 2347 return result; 2348 } 2349 2350 GapRects RenderBlock::blockSelectionGaps(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 2351 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const PaintInfo* paintInfo) 2352 { 2353 GapRects result; 2354 2355 // Go ahead and jump right to the first block child that contains some selected objects. 2356 RenderBox* curr; 2357 for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { } 2358 2359 for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) { 2360 SelectionState childState = curr->selectionState(); 2361 if (childState == SelectionBoth || childState == SelectionEnd) 2362 sawSelectionEnd = true; 2363 2364 if (curr->isFloatingOrOutOfFlowPositioned()) 2365 continue; // We must be a normal flow object in order to even be considered. 2366 2367 if (curr->isInFlowPositioned() && curr->hasLayer()) { 2368 // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element. 2369 // Just disregard it completely. 2370 LayoutSize relOffset = curr->layer()->offsetForInFlowPosition(); 2371 if (relOffset.width() || relOffset.height()) 2372 continue; 2373 } 2374 2375 bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this. 2376 bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone); 2377 if (fillBlockGaps) { 2378 // We need to fill the vertical gap above this object. 2379 if (childState == SelectionEnd || childState == SelectionInside) 2380 // Fill the gap above the object. 2381 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, 2382 curr->logicalTop(), paintInfo)); 2383 2384 // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past* 2385 // our object. We know this if the selection did not end inside our object. 2386 if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd)) 2387 childState = SelectionNone; 2388 2389 // Fill side gaps on this object based off its state. 2390 bool leftGap, rightGap; 2391 getSelectionGapInfo(childState, leftGap, rightGap); 2392 2393 if (leftGap) 2394 result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); 2395 if (rightGap) 2396 result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), paintInfo)); 2397 2398 // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as 2399 // they can without bumping into floating or positioned objects. Ideally they will go right up 2400 // to the border of the root selection block. 2401 lastLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + curr->logicalBottom(); 2402 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom()); 2403 lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom()); 2404 } else if (childState != SelectionNone) 2405 // We must be a block that has some selected object inside it. Go ahead and recur. 2406 result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()), 2407 lastLogicalTop, lastLogicalLeft, lastLogicalRight, paintInfo)); 2408 } 2409 return result; 2410 } 2411 2412 LayoutRect RenderBlock::blockSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 2413 LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const PaintInfo* paintInfo) 2414 { 2415 LayoutUnit logicalTop = lastLogicalTop; 2416 LayoutUnit logicalHeight = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalBottom - logicalTop; 2417 if (logicalHeight <= 0) 2418 return LayoutRect(); 2419 2420 // Get the selection offsets for the bottom of the gap 2421 LayoutUnit logicalLeft = max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom)); 2422 LayoutUnit logicalRight = min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom)); 2423 LayoutUnit logicalWidth = logicalRight - logicalLeft; 2424 if (logicalWidth <= 0) 2425 return LayoutRect(); 2426 2427 LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight)); 2428 if (paintInfo) 2429 paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selectionBackgroundColor()); 2430 return gapRect; 2431 } 2432 2433 LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 2434 RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) 2435 { 2436 LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop; 2437 LayoutUnit rootBlockLogicalLeft = max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight)); 2438 LayoutUnit rootBlockLogicalRight = min(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + floorToInt(logicalLeft), min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight))); 2439 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; 2440 if (rootBlockLogicalWidth <= 0) 2441 return LayoutRect(); 2442 2443 LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); 2444 if (paintInfo) 2445 paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selObj->selectionBackgroundColor()); 2446 return gapRect; 2447 } 2448 2449 LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock* rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 2450 RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const PaintInfo* paintInfo) 2451 { 2452 LayoutUnit rootBlockLogicalTop = rootBlock->blockDirectionOffset(offsetFromRootBlock) + logicalTop; 2453 LayoutUnit rootBlockLogicalLeft = max(rootBlock->inlineDirectionOffset(offsetFromRootBlock) + floorToInt(logicalRight), max(logicalLeftSelectionOffset(rootBlock, logicalTop), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight))); 2454 LayoutUnit rootBlockLogicalRight = min(logicalRightSelectionOffset(rootBlock, logicalTop), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight)); 2455 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; 2456 if (rootBlockLogicalWidth <= 0) 2457 return LayoutRect(); 2458 2459 LayoutRect gapRect = rootBlock->logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); 2460 if (paintInfo) 2461 paintInfo->context->fillRect(pixelSnappedIntRect(gapRect), selObj->selectionBackgroundColor()); 2462 return gapRect; 2463 } 2464 2465 void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap) 2466 { 2467 bool ltr = style()->isLeftToRightDirection(); 2468 leftGap = (state == RenderObject::SelectionInside) || 2469 (state == RenderObject::SelectionEnd && ltr) || 2470 (state == RenderObject::SelectionStart && !ltr); 2471 rightGap = (state == RenderObject::SelectionInside) || 2472 (state == RenderObject::SelectionStart && ltr) || 2473 (state == RenderObject::SelectionEnd && !ltr); 2474 } 2475 2476 LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) 2477 { 2478 // The border can potentially be further extended by our containingBlock(). 2479 if (rootBlock != this) 2480 return containingBlock()->logicalLeftSelectionOffset(rootBlock, position + logicalTop()); 2481 return logicalLeftOffsetForContent(); 2482 } 2483 2484 LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) 2485 { 2486 // The border can potentially be further extended by our containingBlock(). 2487 if (rootBlock != this) 2488 return containingBlock()->logicalRightSelectionOffset(rootBlock, position + logicalTop()); 2489 return logicalRightOffsetForContent(); 2490 } 2491 2492 RenderBlock* RenderBlock::blockBeforeWithinSelectionRoot(LayoutSize& offset) const 2493 { 2494 if (isSelectionRoot()) 2495 return 0; 2496 2497 const RenderObject* object = this; 2498 RenderObject* sibling; 2499 do { 2500 sibling = object->previousSibling(); 2501 while (sibling && (!sibling->isRenderBlock() || toRenderBlock(sibling)->isSelectionRoot())) 2502 sibling = sibling->previousSibling(); 2503 2504 offset -= LayoutSize(toRenderBlock(object)->logicalLeft(), toRenderBlock(object)->logicalTop()); 2505 object = object->parent(); 2506 } while (!sibling && object && object->isRenderBlock() && !toRenderBlock(object)->isSelectionRoot()); 2507 2508 if (!sibling) 2509 return 0; 2510 2511 RenderBlock* beforeBlock = toRenderBlock(sibling); 2512 2513 offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); 2514 2515 RenderObject* child = beforeBlock->lastChild(); 2516 while (child && child->isRenderBlock()) { 2517 beforeBlock = toRenderBlock(child); 2518 offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); 2519 child = beforeBlock->lastChild(); 2520 } 2521 return beforeBlock; 2522 } 2523 2524 void RenderBlock::insertIntoTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap) 2525 { 2526 if (!descendantsMap) { 2527 descendantsMap = new TrackedDescendantsMap; 2528 containerMap = new TrackedContainerMap; 2529 } 2530 2531 TrackedRendererListHashSet* descendantSet = descendantsMap->get(this); 2532 if (!descendantSet) { 2533 descendantSet = new TrackedRendererListHashSet; 2534 descendantsMap->set(this, adoptPtr(descendantSet)); 2535 } 2536 bool added = descendantSet->add(descendant).isNewEntry; 2537 if (!added) { 2538 ASSERT(containerMap->get(descendant)); 2539 ASSERT(containerMap->get(descendant)->contains(this)); 2540 return; 2541 } 2542 2543 HashSet<RenderBlock*>* containerSet = containerMap->get(descendant); 2544 if (!containerSet) { 2545 containerSet = new HashSet<RenderBlock*>; 2546 containerMap->set(descendant, adoptPtr(containerSet)); 2547 } 2548 ASSERT(!containerSet->contains(this)); 2549 containerSet->add(this); 2550 } 2551 2552 void RenderBlock::removeFromTrackedRendererMaps(RenderBox* descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap) 2553 { 2554 if (!descendantsMap) 2555 return; 2556 2557 OwnPtr<HashSet<RenderBlock*> > containerSet = containerMap->take(descendant); 2558 if (!containerSet) 2559 return; 2560 2561 HashSet<RenderBlock*>::iterator end = containerSet->end(); 2562 for (HashSet<RenderBlock*>::iterator it = containerSet->begin(); it != end; ++it) { 2563 RenderBlock* container = *it; 2564 2565 // FIXME: Disabling this assert temporarily until we fix the layout 2566 // bugs associated with positioned objects not properly cleared from 2567 // their ancestor chain before being moved. See webkit bug 93766. 2568 // ASSERT(descendant->isDescendantOf(container)); 2569 2570 TrackedDescendantsMap::iterator descendantsMapIterator = descendantsMap->find(container); 2571 ASSERT(descendantsMapIterator != descendantsMap->end()); 2572 if (descendantsMapIterator == descendantsMap->end()) 2573 continue; 2574 TrackedRendererListHashSet* descendantSet = descendantsMapIterator->value.get(); 2575 ASSERT(descendantSet->contains(descendant)); 2576 descendantSet->remove(descendant); 2577 if (descendantSet->isEmpty()) 2578 descendantsMap->remove(descendantsMapIterator); 2579 } 2580 } 2581 2582 TrackedRendererListHashSet* RenderBlock::positionedObjects() const 2583 { 2584 if (gPositionedDescendantsMap) 2585 return gPositionedDescendantsMap->get(this); 2586 return 0; 2587 } 2588 2589 void RenderBlock::insertPositionedObject(RenderBox* o) 2590 { 2591 ASSERT(!isAnonymousBlock()); 2592 2593 if (o->isRenderFlowThread()) 2594 return; 2595 2596 insertIntoTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap); 2597 } 2598 2599 void RenderBlock::removePositionedObject(RenderBox* o) 2600 { 2601 removeFromTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap); 2602 } 2603 2604 void RenderBlock::removePositionedObjects(RenderBlock* o, ContainingBlockState containingBlockState) 2605 { 2606 TrackedRendererListHashSet* positionedDescendants = positionedObjects(); 2607 if (!positionedDescendants) 2608 return; 2609 2610 RenderBox* r; 2611 2612 TrackedRendererListHashSet::iterator end = positionedDescendants->end(); 2613 2614 Vector<RenderBox*, 16> deadObjects; 2615 2616 for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { 2617 r = *it; 2618 if (!o || r->isDescendantOf(o)) { 2619 if (containingBlockState == NewContainingBlock) 2620 r->setChildNeedsLayout(MarkOnlyThis); 2621 2622 // It is parent blocks job to add positioned child to positioned objects list of its containing block 2623 // Parent layout needs to be invalidated to ensure this happens. 2624 RenderObject* p = r->parent(); 2625 while (p && !p->isRenderBlock()) 2626 p = p->parent(); 2627 if (p) 2628 p->setChildNeedsLayout(); 2629 2630 deadObjects.append(r); 2631 } 2632 } 2633 2634 for (unsigned i = 0; i < deadObjects.size(); i++) 2635 removePositionedObject(deadObjects.at(i)); 2636 } 2637 2638 void RenderBlock::addPercentHeightDescendant(RenderBox* descendant) 2639 { 2640 insertIntoTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap); 2641 } 2642 2643 void RenderBlock::removePercentHeightDescendant(RenderBox* descendant) 2644 { 2645 removeFromTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap); 2646 } 2647 2648 TrackedRendererListHashSet* RenderBlock::percentHeightDescendants() const 2649 { 2650 return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0; 2651 } 2652 2653 bool RenderBlock::hasPercentHeightContainerMap() 2654 { 2655 return gPercentHeightContainerMap; 2656 } 2657 2658 bool RenderBlock::hasPercentHeightDescendant(RenderBox* descendant) 2659 { 2660 // We don't null check gPercentHeightContainerMap since the caller 2661 // already ensures this and we need to call this function on every 2662 // descendant in clearPercentHeightDescendantsFrom(). 2663 ASSERT(gPercentHeightContainerMap); 2664 return gPercentHeightContainerMap->contains(descendant); 2665 } 2666 2667 void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants(SubtreeLayoutScope& layoutScope) 2668 { 2669 if (!gPercentHeightDescendantsMap) 2670 return; 2671 2672 TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this); 2673 if (!descendants) 2674 return; 2675 2676 TrackedRendererListHashSet::iterator end = descendants->end(); 2677 for (TrackedRendererListHashSet::iterator it = descendants->begin(); it != end; ++it) { 2678 RenderBox* box = *it; 2679 while (box != this) { 2680 if (box->normalChildNeedsLayout()) 2681 break; 2682 layoutScope.setChildNeedsLayout(box); 2683 box = box->containingBlock(); 2684 ASSERT(box); 2685 if (!box) 2686 break; 2687 } 2688 } 2689 } 2690 2691 void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox* descendant) 2692 { 2693 // We query the map directly, rather than looking at style's 2694 // logicalHeight()/logicalMinHeight()/logicalMaxHeight() since those 2695 // can change with writing mode/directional changes. 2696 if (!hasPercentHeightContainerMap()) 2697 return; 2698 2699 if (!hasPercentHeightDescendant(descendant)) 2700 return; 2701 2702 removePercentHeightDescendant(descendant); 2703 } 2704 2705 void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox* parent) 2706 { 2707 ASSERT(gPercentHeightContainerMap); 2708 for (RenderObject* curr = parent->slowFirstChild(); curr; curr = curr->nextInPreOrder(parent)) { 2709 if (!curr->isBox()) 2710 continue; 2711 2712 RenderBox* box = toRenderBox(curr); 2713 if (!hasPercentHeightDescendant(box)) 2714 continue; 2715 2716 removePercentHeightDescendant(box); 2717 } 2718 } 2719 2720 LayoutUnit RenderBlock::textIndentOffset() const 2721 { 2722 LayoutUnit cw = 0; 2723 if (style()->textIndent().isPercent()) 2724 cw = containingBlock()->availableLogicalWidth(); 2725 return minimumValueForLength(style()->textIndent(), cw); 2726 } 2727 2728 void RenderBlock::markLinesDirtyInBlockRange(LayoutUnit logicalTop, LayoutUnit logicalBottom, RootInlineBox* highest) 2729 { 2730 if (logicalTop >= logicalBottom) 2731 return; 2732 2733 RootInlineBox* lowestDirtyLine = lastRootBox(); 2734 RootInlineBox* afterLowest = lowestDirtyLine; 2735 while (lowestDirtyLine && lowestDirtyLine->lineBottomWithLeading() >= logicalBottom && logicalBottom < LayoutUnit::max()) { 2736 afterLowest = lowestDirtyLine; 2737 lowestDirtyLine = lowestDirtyLine->prevRootBox(); 2738 } 2739 2740 while (afterLowest && afterLowest != highest && (afterLowest->lineBottomWithLeading() >= logicalTop || afterLowest->lineBottomWithLeading() < 0)) { 2741 afterLowest->markDirty(); 2742 afterLowest = afterLowest->prevRootBox(); 2743 } 2744 } 2745 2746 bool RenderBlock::avoidsFloats() const 2747 { 2748 // Floats can't intrude into our box if we have a non-auto column count or width. 2749 return RenderBox::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth(); 2750 } 2751 2752 bool RenderBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset) 2753 { 2754 if (!scrollsOverflow()) 2755 return false; 2756 2757 return layer()->scrollableArea()->hitTestOverflowControls(result, roundedIntPoint(locationInContainer - toLayoutSize(accumulatedOffset))); 2758 } 2759 2760 Node* RenderBlock::nodeForHitTest() const 2761 { 2762 // If we are in the margins of block elements that are part of a 2763 // continuation we're actually still inside the enclosing element 2764 // that was split. Use the appropriate inner node. 2765 return isAnonymousBlockContinuation() ? continuation()->node() : node(); 2766 } 2767 2768 bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) 2769 { 2770 LayoutPoint adjustedLocation(accumulatedOffset + location()); 2771 LayoutSize localOffset = toLayoutSize(adjustedLocation); 2772 2773 if (!isRenderView()) { 2774 // Check if we need to do anything at all. 2775 // If we have clipping, then we can't have any spillout. 2776 LayoutRect overflowBox = hasOverflowClip() ? borderBoxRect() : visualOverflowRect(); 2777 flipForWritingMode(overflowBox); 2778 overflowBox.moveBy(adjustedLocation); 2779 if (!locationInContainer.intersects(overflowBox)) 2780 return false; 2781 } 2782 2783 if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) 2784 && visibleToHitTestRequest(request) 2785 && isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) { 2786 updateHitTestResult(result, locationInContainer.point() - localOffset); 2787 // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet. 2788 if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer)) 2789 return true; 2790 } 2791 2792 if (style()->clipPath()) { 2793 switch (style()->clipPath()->type()) { 2794 case ClipPathOperation::SHAPE: { 2795 ShapeClipPathOperation* clipPath = toShapeClipPathOperation(style()->clipPath()); 2796 // FIXME: handle marginBox etc. 2797 if (!clipPath->path(borderBoxRect()).contains(locationInContainer.point() - localOffset, clipPath->windRule())) 2798 return false; 2799 break; 2800 } 2801 case ClipPathOperation::REFERENCE: 2802 // FIXME: handle REFERENCE 2803 break; 2804 } 2805 } 2806 2807 // If we have clipping, then we can't have any spillout. 2808 bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); 2809 bool useClip = (hasControlClip() || useOverflowClip); 2810 bool checkChildren = !useClip; 2811 if (!checkChildren) { 2812 if (hasControlClip()) { 2813 checkChildren = locationInContainer.intersects(controlClipRect(adjustedLocation)); 2814 } else { 2815 LayoutRect clipRect = overflowClipRect(adjustedLocation, IncludeOverlayScrollbarSize); 2816 if (style()->hasBorderRadius()) 2817 checkChildren = locationInContainer.intersects(style()->getRoundedBorderFor(clipRect)); 2818 else 2819 checkChildren = locationInContainer.intersects(clipRect); 2820 } 2821 } 2822 if (checkChildren) { 2823 // Hit test descendants first. 2824 LayoutSize scrolledOffset(localOffset); 2825 if (hasOverflowClip()) 2826 scrolledOffset -= scrolledContentOffset(); 2827 2828 // Hit test contents if we don't have columns. 2829 if (!hasColumns()) { 2830 if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) { 2831 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset)); 2832 return true; 2833 } 2834 if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset))) 2835 return true; 2836 } else if (hitTestColumns(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) { 2837 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset)); 2838 return true; 2839 } 2840 } 2841 2842 // Check if the point is outside radii. 2843 if (style()->hasBorderRadius()) { 2844 LayoutRect borderRect = borderBoxRect(); 2845 borderRect.moveBy(adjustedLocation); 2846 RoundedRect border = style()->getRoundedBorderFor(borderRect); 2847 if (!locationInContainer.intersects(border)) 2848 return false; 2849 } 2850 2851 // Now hit test our background 2852 if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) { 2853 LayoutRect boundsRect(adjustedLocation, size()); 2854 if (visibleToHitTestRequest(request) && locationInContainer.intersects(boundsRect)) { 2855 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset)); 2856 if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer, boundsRect)) 2857 return true; 2858 } 2859 } 2860 2861 return false; 2862 } 2863 2864 class ColumnRectIterator { 2865 WTF_MAKE_NONCOPYABLE(ColumnRectIterator); 2866 public: 2867 ColumnRectIterator(const RenderBlock& block) 2868 : m_block(block) 2869 , m_colInfo(block.columnInfo()) 2870 , m_direction(m_block.style()->isFlippedBlocksWritingMode() ? 1 : -1) 2871 , m_isHorizontal(block.isHorizontalWritingMode()) 2872 , m_logicalLeft(block.logicalLeftOffsetForContent()) 2873 { 2874 int colCount = m_colInfo->columnCount(); 2875 m_colIndex = colCount - 1; 2876 m_currLogicalTopOffset = colCount * m_colInfo->columnHeight() * m_direction; 2877 update(); 2878 } 2879 2880 void advance() 2881 { 2882 ASSERT(hasMore()); 2883 m_colIndex--; 2884 update(); 2885 } 2886 2887 LayoutRect columnRect() const { return m_colRect; } 2888 bool hasMore() const { return m_colIndex >= 0; } 2889 2890 void adjust(LayoutSize& offset) const 2891 { 2892 LayoutUnit currLogicalLeftOffset = (m_isHorizontal ? m_colRect.x() : m_colRect.y()) - m_logicalLeft; 2893 offset += m_isHorizontal ? LayoutSize(currLogicalLeftOffset, m_currLogicalTopOffset) : LayoutSize(m_currLogicalTopOffset, currLogicalLeftOffset); 2894 if (m_colInfo->progressionAxis() == ColumnInfo::BlockAxis) { 2895 if (m_isHorizontal) 2896 offset.expand(0, m_colRect.y() - m_block.borderTop() - m_block.paddingTop()); 2897 else 2898 offset.expand(m_colRect.x() - m_block.borderLeft() - m_block.paddingLeft(), 0); 2899 } 2900 } 2901 2902 private: 2903 void update() 2904 { 2905 if (m_colIndex < 0) 2906 return; 2907 2908 m_colRect = m_block.columnRectAt(const_cast<ColumnInfo*>(m_colInfo), m_colIndex); 2909 m_block.flipForWritingMode(m_colRect); 2910 m_currLogicalTopOffset -= (m_isHorizontal ? m_colRect.height() : m_colRect.width()) * m_direction; 2911 } 2912 2913 const RenderBlock& m_block; 2914 const ColumnInfo* const m_colInfo; 2915 const int m_direction; 2916 const bool m_isHorizontal; 2917 const LayoutUnit m_logicalLeft; 2918 int m_colIndex; 2919 LayoutUnit m_currLogicalTopOffset; 2920 LayoutRect m_colRect; 2921 }; 2922 2923 bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) 2924 { 2925 // We need to do multiple passes, breaking up our hit testing into strips. 2926 if (!hasColumns()) 2927 return false; 2928 2929 for (ColumnRectIterator it(*this); it.hasMore(); it.advance()) { 2930 LayoutRect hitRect = locationInContainer.boundingBox(); 2931 LayoutRect colRect = it.columnRect(); 2932 colRect.moveBy(accumulatedOffset); 2933 if (locationInContainer.intersects(colRect)) { 2934 // The point is inside this column. 2935 // Adjust accumulatedOffset to change where we hit test. 2936 LayoutSize offset; 2937 it.adjust(offset); 2938 LayoutPoint finalLocation = accumulatedOffset + offset; 2939 if (!result.isRectBasedTest() || colRect.contains(hitRect)) 2940 return hitTestContents(request, result, locationInContainer, finalLocation, hitTestAction) || (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, finalLocation)); 2941 2942 hitTestContents(request, result, locationInContainer, finalLocation, hitTestAction); 2943 } 2944 } 2945 2946 return false; 2947 } 2948 2949 void RenderBlock::adjustForColumnRect(LayoutSize& offset, const LayoutPoint& locationInContainer) const 2950 { 2951 for (ColumnRectIterator it(*this); it.hasMore(); it.advance()) { 2952 LayoutRect colRect = it.columnRect(); 2953 if (colRect.contains(locationInContainer)) { 2954 it.adjust(offset); 2955 return; 2956 } 2957 } 2958 } 2959 2960 bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) 2961 { 2962 if (childrenInline() && !isTable()) { 2963 // We have to hit-test our line boxes. 2964 if (m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction)) 2965 return true; 2966 } else { 2967 // Hit test our children. 2968 HitTestAction childHitTest = hitTestAction; 2969 if (hitTestAction == HitTestChildBlockBackgrounds) 2970 childHitTest = HitTestChildBlockBackground; 2971 for (RenderBox* child = lastChildBox(); child; child = child->previousSiblingBox()) { 2972 LayoutPoint childPoint = flipForWritingModeForChild(child, accumulatedOffset); 2973 if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, locationInContainer, childPoint, childHitTest)) 2974 return true; 2975 } 2976 } 2977 2978 return false; 2979 } 2980 2981 Position RenderBlock::positionForBox(InlineBox *box, bool start) const 2982 { 2983 if (!box) 2984 return Position(); 2985 2986 if (!box->renderer().nonPseudoNode()) 2987 return createLegacyEditingPosition(nonPseudoNode(), start ? caretMinOffset() : caretMaxOffset()); 2988 2989 if (!box->isInlineTextBox()) 2990 return createLegacyEditingPosition(box->renderer().nonPseudoNode(), start ? box->renderer().caretMinOffset() : box->renderer().caretMaxOffset()); 2991 2992 InlineTextBox* textBox = toInlineTextBox(box); 2993 return createLegacyEditingPosition(box->renderer().nonPseudoNode(), start ? textBox->start() : textBox->start() + textBox->len()); 2994 } 2995 2996 static inline bool isEditingBoundary(RenderObject* ancestor, RenderObject* child) 2997 { 2998 ASSERT(!ancestor || ancestor->nonPseudoNode()); 2999 ASSERT(child && child->nonPseudoNode()); 3000 return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor->parent()->isRenderView()) 3001 || ancestor->nonPseudoNode()->rendererIsEditable() == child->nonPseudoNode()->rendererIsEditable(); 3002 } 3003 3004 // FIXME: This function should go on RenderObject as an instance method. Then 3005 // all cases in which positionForPoint recurs could call this instead to 3006 // prevent crossing editable boundaries. This would require many tests. 3007 static PositionWithAffinity positionForPointRespectingEditingBoundaries(RenderBlock* parent, RenderBox* child, const LayoutPoint& pointInParentCoordinates) 3008 { 3009 LayoutPoint childLocation = child->location(); 3010 if (child->isInFlowPositioned()) 3011 childLocation += child->offsetForInFlowPosition(); 3012 3013 // FIXME: This is wrong if the child's writing-mode is different from the parent's. 3014 LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation)); 3015 3016 // If this is an anonymous renderer, we just recur normally 3017 Node* childNode = child->nonPseudoNode(); 3018 if (!childNode) 3019 return child->positionForPoint(pointInChildCoordinates); 3020 3021 // Otherwise, first make sure that the editability of the parent and child agree. 3022 // If they don't agree, then we return a visible position just before or after the child 3023 RenderObject* ancestor = parent; 3024 while (ancestor && !ancestor->nonPseudoNode()) 3025 ancestor = ancestor->parent(); 3026 3027 // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal 3028 if (isEditingBoundary(ancestor, child)) 3029 return child->positionForPoint(pointInChildCoordinates); 3030 3031 // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child 3032 LayoutUnit childMiddle = parent->logicalWidthForChild(child) / 2; 3033 LayoutUnit logicalLeft = parent->isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y(); 3034 if (logicalLeft < childMiddle) 3035 return ancestor->createPositionWithAffinity(childNode->nodeIndex(), DOWNSTREAM); 3036 return ancestor->createPositionWithAffinity(childNode->nodeIndex() + 1, UPSTREAM); 3037 } 3038 3039 PositionWithAffinity RenderBlock::positionForPointWithInlineChildren(const LayoutPoint& pointInLogicalContents) 3040 { 3041 ASSERT(childrenInline()); 3042 3043 if (!firstRootBox()) 3044 return createPositionWithAffinity(0, DOWNSTREAM); 3045 3046 bool linesAreFlipped = style()->isFlippedLinesWritingMode(); 3047 bool blocksAreFlipped = style()->isFlippedBlocksWritingMode(); 3048 3049 // look for the closest line box in the root box which is at the passed-in y coordinate 3050 InlineBox* closestBox = 0; 3051 RootInlineBox* firstRootBoxWithChildren = 0; 3052 RootInlineBox* lastRootBoxWithChildren = 0; 3053 for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { 3054 if (!root->firstLeafChild()) 3055 continue; 3056 if (!firstRootBoxWithChildren) 3057 firstRootBoxWithChildren = root; 3058 3059 if (!linesAreFlipped && root->isFirstAfterPageBreak() && (pointInLogicalContents.y() < root->lineTopWithLeading() 3060 || (blocksAreFlipped && pointInLogicalContents.y() == root->lineTopWithLeading()))) 3061 break; 3062 3063 lastRootBoxWithChildren = root; 3064 3065 // check if this root line box is located at this y coordinate 3066 if (pointInLogicalContents.y() < root->selectionBottom() || (blocksAreFlipped && pointInLogicalContents.y() == root->selectionBottom())) { 3067 if (linesAreFlipped) { 3068 RootInlineBox* nextRootBoxWithChildren = root->nextRootBox(); 3069 while (nextRootBoxWithChildren && !nextRootBoxWithChildren->firstLeafChild()) 3070 nextRootBoxWithChildren = nextRootBoxWithChildren->nextRootBox(); 3071 3072 if (nextRootBoxWithChildren && nextRootBoxWithChildren->isFirstAfterPageBreak() && (pointInLogicalContents.y() > nextRootBoxWithChildren->lineTopWithLeading() 3073 || (!blocksAreFlipped && pointInLogicalContents.y() == nextRootBoxWithChildren->lineTopWithLeading()))) 3074 continue; 3075 } 3076 closestBox = root->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x()); 3077 if (closestBox) 3078 break; 3079 } 3080 } 3081 3082 bool moveCaretToBoundary = document().frame()->editor().behavior().shouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom(); 3083 3084 if (!moveCaretToBoundary && !closestBox && lastRootBoxWithChildren) { 3085 // y coordinate is below last root line box, pretend we hit it 3086 closestBox = lastRootBoxWithChildren->closestLeafChildForLogicalLeftPosition(pointInLogicalContents.x()); 3087 } 3088 3089 if (closestBox) { 3090 if (moveCaretToBoundary) { 3091 LayoutUnit firstRootBoxWithChildrenTop = min<LayoutUnit>(firstRootBoxWithChildren->selectionTop(), firstRootBoxWithChildren->logicalTop()); 3092 if (pointInLogicalContents.y() < firstRootBoxWithChildrenTop 3093 || (blocksAreFlipped && pointInLogicalContents.y() == firstRootBoxWithChildrenTop)) { 3094 InlineBox* box = firstRootBoxWithChildren->firstLeafChild(); 3095 if (box->isLineBreak()) { 3096 if (InlineBox* newBox = box->nextLeafChildIgnoringLineBreak()) 3097 box = newBox; 3098 } 3099 // y coordinate is above first root line box, so return the start of the first 3100 return PositionWithAffinity(positionForBox(box, true), DOWNSTREAM); 3101 } 3102 } 3103 3104 // pass the box a top position that is inside it 3105 LayoutPoint point(pointInLogicalContents.x(), closestBox->root().blockDirectionPointInLine()); 3106 if (!isHorizontalWritingMode()) 3107 point = point.transposedPoint(); 3108 if (closestBox->renderer().isReplaced()) 3109 return positionForPointRespectingEditingBoundaries(this, &toRenderBox(closestBox->renderer()), point); 3110 return closestBox->renderer().positionForPoint(point); 3111 } 3112 3113 if (lastRootBoxWithChildren) { 3114 // We hit this case for Mac behavior when the Y coordinate is below the last box. 3115 ASSERT(moveCaretToBoundary); 3116 InlineBox* logicallyLastBox; 3117 if (lastRootBoxWithChildren->getLogicalEndBoxWithNode(logicallyLastBox)) 3118 return PositionWithAffinity(positionForBox(logicallyLastBox, false), DOWNSTREAM); 3119 } 3120 3121 // Can't reach this. We have a root line box, but it has no kids. 3122 // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text 3123 // seems to hit this code path. 3124 return createPositionWithAffinity(0, DOWNSTREAM); 3125 } 3126 3127 static inline bool isChildHitTestCandidate(RenderBox* box) 3128 { 3129 return box->height() && box->style()->visibility() == VISIBLE && !box->isFloatingOrOutOfFlowPositioned(); 3130 } 3131 3132 PositionWithAffinity RenderBlock::positionForPoint(const LayoutPoint& point) 3133 { 3134 if (isTable()) 3135 return RenderBox::positionForPoint(point); 3136 3137 if (isReplaced()) { 3138 // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode. 3139 LayoutUnit pointLogicalLeft = isHorizontalWritingMode() ? point.x() : point.y(); 3140 LayoutUnit pointLogicalTop = isHorizontalWritingMode() ? point.y() : point.x(); 3141 3142 if (pointLogicalLeft < 0) 3143 return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); 3144 if (pointLogicalLeft >= logicalWidth()) 3145 return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM); 3146 if (pointLogicalTop < 0) 3147 return createPositionWithAffinity(caretMinOffset(), DOWNSTREAM); 3148 if (pointLogicalTop >= logicalHeight()) 3149 return createPositionWithAffinity(caretMaxOffset(), DOWNSTREAM); 3150 } 3151 3152 LayoutPoint pointInContents = point; 3153 offsetForContents(pointInContents); 3154 LayoutPoint pointInLogicalContents(pointInContents); 3155 if (!isHorizontalWritingMode()) 3156 pointInLogicalContents = pointInLogicalContents.transposedPoint(); 3157 3158 if (childrenInline()) 3159 return positionForPointWithInlineChildren(pointInLogicalContents); 3160 3161 RenderBox* lastCandidateBox = lastChildBox(); 3162 while (lastCandidateBox && !isChildHitTestCandidate(lastCandidateBox)) 3163 lastCandidateBox = lastCandidateBox->previousSiblingBox(); 3164 3165 bool blocksAreFlipped = style()->isFlippedBlocksWritingMode(); 3166 if (lastCandidateBox) { 3167 if (pointInLogicalContents.y() > logicalTopForChild(lastCandidateBox) 3168 || (!blocksAreFlipped && pointInLogicalContents.y() == logicalTopForChild(lastCandidateBox))) 3169 return positionForPointRespectingEditingBoundaries(this, lastCandidateBox, pointInContents); 3170 3171 for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { 3172 if (!isChildHitTestCandidate(childBox)) 3173 continue; 3174 LayoutUnit childLogicalBottom = logicalTopForChild(childBox) + logicalHeightForChild(childBox); 3175 // We hit child if our click is above the bottom of its padding box (like IE6/7 and FF3). 3176 if (isChildHitTestCandidate(childBox) && (pointInLogicalContents.y() < childLogicalBottom 3177 || (blocksAreFlipped && pointInLogicalContents.y() == childLogicalBottom))) 3178 return positionForPointRespectingEditingBoundaries(this, childBox, pointInContents); 3179 } 3180 } 3181 3182 // We only get here if there are no hit test candidate children below the click. 3183 return RenderBox::positionForPoint(point); 3184 } 3185 3186 void RenderBlock::offsetForContents(LayoutPoint& offset) const 3187 { 3188 offset = flipForWritingMode(offset); 3189 3190 if (hasOverflowClip()) 3191 offset += scrolledContentOffset(); 3192 3193 if (hasColumns()) 3194 adjustPointToColumnContents(offset); 3195 3196 offset = flipForWritingMode(offset); 3197 } 3198 3199 LayoutUnit RenderBlock::availableLogicalWidth() const 3200 { 3201 // If we have multiple columns, then the available logical width is reduced to our column width. 3202 if (hasColumns()) 3203 return desiredColumnWidth(); 3204 return RenderBox::availableLogicalWidth(); 3205 } 3206 3207 int RenderBlock::columnGap() const 3208 { 3209 if (style()->hasNormalColumnGap()) 3210 return style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins. 3211 return static_cast<int>(style()->columnGap()); 3212 } 3213 3214 void RenderBlock::calcColumnWidth() 3215 { 3216 if (document().regionBasedColumnsEnabled()) 3217 return; 3218 3219 // Calculate our column width and column count. 3220 // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744 3221 unsigned desiredColumnCount = 1; 3222 LayoutUnit desiredColumnWidth = contentLogicalWidth(); 3223 3224 // For now, we don't support multi-column layouts when printing, since we have to do a lot of work for proper pagination. 3225 if (document().paginated() || !style()->specifiesColumns()) { 3226 setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); 3227 return; 3228 } 3229 3230 LayoutUnit availWidth = desiredColumnWidth; 3231 LayoutUnit colGap = columnGap(); 3232 LayoutUnit colWidth = max<LayoutUnit>(1, LayoutUnit(style()->columnWidth())); 3233 int colCount = max<int>(1, style()->columnCount()); 3234 3235 if (style()->hasAutoColumnWidth() && !style()->hasAutoColumnCount()) { 3236 desiredColumnCount = colCount; 3237 desiredColumnWidth = max<LayoutUnit>(0, (availWidth - ((desiredColumnCount - 1) * colGap)) / desiredColumnCount); 3238 } else if (!style()->hasAutoColumnWidth() && style()->hasAutoColumnCount()) { 3239 desiredColumnCount = max<LayoutUnit>(1, (availWidth + colGap) / (colWidth + colGap)); 3240 desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap; 3241 } else { 3242 desiredColumnCount = max<LayoutUnit>(min<LayoutUnit>(colCount, (availWidth + colGap) / (colWidth + colGap)), 1); 3243 desiredColumnWidth = ((availWidth + colGap) / desiredColumnCount) - colGap; 3244 } 3245 setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); 3246 } 3247 3248 bool RenderBlock::requiresColumns(int desiredColumnCount) const 3249 { 3250 // Paged overflow is treated as multicol here, unless this element was the one that got its 3251 // overflow propagated to the viewport. 3252 bool isPaginated = style()->isOverflowPaged() && node() != document().viewportDefiningElement(); 3253 3254 return firstChild() 3255 && (desiredColumnCount != 1 || !style()->hasAutoColumnWidth() || isPaginated) 3256 && !firstChild()->isAnonymousColumnsBlock() 3257 && !firstChild()->isAnonymousColumnSpanBlock(); 3258 } 3259 3260 void RenderBlock::setDesiredColumnCountAndWidth(int count, LayoutUnit width) 3261 { 3262 bool destroyColumns = !requiresColumns(count); 3263 if (destroyColumns) { 3264 if (hasColumns()) { 3265 gColumnInfoMap->take(this); 3266 setHasColumns(false); 3267 } 3268 } else { 3269 ColumnInfo* info; 3270 if (hasColumns()) 3271 info = gColumnInfoMap->get(this); 3272 else { 3273 if (!gColumnInfoMap) 3274 gColumnInfoMap = new ColumnInfoMap; 3275 info = new ColumnInfo; 3276 gColumnInfoMap->add(this, adoptPtr(info)); 3277 setHasColumns(true); 3278 } 3279 info->setDesiredColumnWidth(width); 3280 if (style()->isOverflowPaged()) { 3281 info->setDesiredColumnCount(1); 3282 info->setProgressionAxis(style()->hasInlinePaginationAxis() ? ColumnInfo::InlineAxis : ColumnInfo::BlockAxis); 3283 } else { 3284 info->setDesiredColumnCount(count); 3285 info->setProgressionAxis(ColumnInfo::InlineAxis); 3286 } 3287 } 3288 } 3289 3290 LayoutUnit RenderBlock::desiredColumnWidth() const 3291 { 3292 if (!hasColumns()) 3293 return contentLogicalWidth(); 3294 return gColumnInfoMap->get(this)->desiredColumnWidth(); 3295 } 3296 3297 ColumnInfo* RenderBlock::columnInfo() const 3298 { 3299 if (!hasColumns()) 3300 return 0; 3301 return gColumnInfoMap->get(this); 3302 } 3303 3304 unsigned RenderBlock::columnCount(ColumnInfo* colInfo) const 3305 { 3306 ASSERT(hasColumns()); 3307 ASSERT(gColumnInfoMap->get(this) == colInfo); 3308 return colInfo->columnCount(); 3309 } 3310 3311 LayoutRect RenderBlock::columnRectAt(ColumnInfo* colInfo, unsigned index) const 3312 { 3313 ASSERT(hasColumns() && gColumnInfoMap->get(this) == colInfo); 3314 3315 // Compute the appropriate rect based off our information. 3316 LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth(); 3317 LayoutUnit colLogicalHeight = colInfo->columnHeight(); 3318 LayoutUnit colLogicalTop = borderBefore() + paddingBefore(); 3319 LayoutUnit colLogicalLeft = logicalLeftOffsetForContent(); 3320 LayoutUnit colGap = columnGap(); 3321 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { 3322 if (style()->isLeftToRightDirection()) 3323 colLogicalLeft += index * (colLogicalWidth + colGap); 3324 else 3325 colLogicalLeft += contentLogicalWidth() - colLogicalWidth - index * (colLogicalWidth + colGap); 3326 } else { 3327 colLogicalTop += index * (colLogicalHeight + colGap); 3328 } 3329 3330 if (isHorizontalWritingMode()) 3331 return LayoutRect(colLogicalLeft, colLogicalTop, colLogicalWidth, colLogicalHeight); 3332 return LayoutRect(colLogicalTop, colLogicalLeft, colLogicalHeight, colLogicalWidth); 3333 } 3334 3335 void RenderBlock::adjustPointToColumnContents(LayoutPoint& point) const 3336 { 3337 // Just bail if we have no columns. 3338 if (!hasColumns()) 3339 return; 3340 3341 ColumnInfo* colInfo = columnInfo(); 3342 if (!columnCount(colInfo)) 3343 return; 3344 3345 // Determine which columns we intersect. 3346 LayoutUnit colGap = columnGap(); 3347 LayoutUnit halfColGap = colGap / 2; 3348 LayoutPoint columnPoint(columnRectAt(colInfo, 0).location()); 3349 LayoutUnit logicalOffset = 0; 3350 for (unsigned i = 0; i < colInfo->columnCount(); i++) { 3351 // Add in half the column gap to the left and right of the rect. 3352 LayoutRect colRect = columnRectAt(colInfo, i); 3353 flipForWritingMode(colRect); 3354 if (isHorizontalWritingMode() == (colInfo->progressionAxis() == ColumnInfo::InlineAxis)) { 3355 LayoutRect gapAndColumnRect(colRect.x() - halfColGap, colRect.y(), colRect.width() + colGap, colRect.height()); 3356 if (point.x() >= gapAndColumnRect.x() && point.x() < gapAndColumnRect.maxX()) { 3357 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { 3358 // FIXME: The clamping that follows is not completely right for right-to-left 3359 // content. 3360 // Clamp everything above the column to its top left. 3361 if (point.y() < gapAndColumnRect.y()) 3362 point = gapAndColumnRect.location(); 3363 // Clamp everything below the column to the next column's top left. If there is 3364 // no next column, this still maps to just after this column. 3365 else if (point.y() >= gapAndColumnRect.maxY()) { 3366 point = gapAndColumnRect.location(); 3367 point.move(0, gapAndColumnRect.height()); 3368 } 3369 } else { 3370 if (point.x() < colRect.x()) 3371 point.setX(colRect.x()); 3372 else if (point.x() >= colRect.maxX()) 3373 point.setX(colRect.maxX() - 1); 3374 } 3375 3376 // We're inside the column. Translate the x and y into our column coordinate space. 3377 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) 3378 point.move(columnPoint.x() - colRect.x(), (!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset)); 3379 else 3380 point.move((!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset) - colRect.x() + borderLeft() + paddingLeft(), 0); 3381 return; 3382 } 3383 3384 // Move to the next position. 3385 logicalOffset += colInfo->progressionAxis() == ColumnInfo::InlineAxis ? colRect.height() : colRect.width(); 3386 } else { 3387 LayoutRect gapAndColumnRect(colRect.x(), colRect.y() - halfColGap, colRect.width(), colRect.height() + colGap); 3388 if (point.y() >= gapAndColumnRect.y() && point.y() < gapAndColumnRect.maxY()) { 3389 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { 3390 // FIXME: The clamping that follows is not completely right for right-to-left 3391 // content. 3392 // Clamp everything above the column to its top left. 3393 if (point.x() < gapAndColumnRect.x()) 3394 point = gapAndColumnRect.location(); 3395 // Clamp everything below the column to the next column's top left. If there is 3396 // no next column, this still maps to just after this column. 3397 else if (point.x() >= gapAndColumnRect.maxX()) { 3398 point = gapAndColumnRect.location(); 3399 point.move(gapAndColumnRect.width(), 0); 3400 } 3401 } else { 3402 if (point.y() < colRect.y()) 3403 point.setY(colRect.y()); 3404 else if (point.y() >= colRect.maxY()) 3405 point.setY(colRect.maxY() - 1); 3406 } 3407 3408 // We're inside the column. Translate the x and y into our column coordinate space. 3409 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) 3410 point.move((!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset), columnPoint.y() - colRect.y()); 3411 else 3412 point.move(0, (!style()->isFlippedBlocksWritingMode() ? logicalOffset : -logicalOffset) - colRect.y() + borderTop() + paddingTop()); 3413 return; 3414 } 3415 3416 // Move to the next position. 3417 logicalOffset += colInfo->progressionAxis() == ColumnInfo::InlineAxis ? colRect.width() : colRect.height(); 3418 } 3419 } 3420 } 3421 3422 void RenderBlock::adjustRectForColumns(LayoutRect& r) const 3423 { 3424 // Just bail if we have no columns. 3425 if (!hasColumns()) 3426 return; 3427 3428 ColumnInfo* colInfo = columnInfo(); 3429 3430 // Determine which columns we intersect. 3431 unsigned colCount = columnCount(colInfo); 3432 if (!colCount) 3433 return; 3434 3435 // Begin with a result rect that is empty. 3436 LayoutRect result; 3437 3438 bool isHorizontal = isHorizontalWritingMode(); 3439 LayoutUnit beforeBorderPadding = borderBefore() + paddingBefore(); 3440 LayoutUnit colHeight = colInfo->columnHeight(); 3441 if (!colHeight) 3442 return; 3443 3444 LayoutUnit startOffset = max(isHorizontal ? r.y() : r.x(), beforeBorderPadding); 3445 LayoutUnit endOffset = max(min<LayoutUnit>(isHorizontal ? r.maxY() : r.maxX(), beforeBorderPadding + colCount * colHeight), beforeBorderPadding); 3446 3447 // FIXME: Can overflow on fast/block/float/float-not-removed-from-next-sibling4.html, see https://bugs.webkit.org/show_bug.cgi?id=68744 3448 unsigned startColumn = (startOffset - beforeBorderPadding) / colHeight; 3449 unsigned endColumn = (endOffset - beforeBorderPadding) / colHeight; 3450 3451 if (startColumn == endColumn) { 3452 // The rect is fully contained within one column. Adjust for our offsets 3453 // and repaint only that portion. 3454 LayoutUnit logicalLeftOffset = logicalLeftOffsetForContent(); 3455 LayoutRect colRect = columnRectAt(colInfo, startColumn); 3456 LayoutRect repaintRect = r; 3457 3458 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) { 3459 if (isHorizontal) 3460 repaintRect.move(colRect.x() - logicalLeftOffset, - static_cast<int>(startColumn) * colHeight); 3461 else 3462 repaintRect.move(- static_cast<int>(startColumn) * colHeight, colRect.y() - logicalLeftOffset); 3463 } else { 3464 if (isHorizontal) 3465 repaintRect.move(0, colRect.y() - startColumn * colHeight - beforeBorderPadding); 3466 else 3467 repaintRect.move(colRect.x() - startColumn * colHeight - beforeBorderPadding, 0); 3468 } 3469 repaintRect.intersect(colRect); 3470 result.unite(repaintRect); 3471 } else { 3472 // We span multiple columns. We can just unite the start and end column to get the final 3473 // repaint rect. 3474 result.unite(columnRectAt(colInfo, startColumn)); 3475 result.unite(columnRectAt(colInfo, endColumn)); 3476 } 3477 3478 r = result; 3479 } 3480 3481 LayoutPoint RenderBlock::flipForWritingModeIncludingColumns(const LayoutPoint& point) const 3482 { 3483 ASSERT(hasColumns()); 3484 if (!hasColumns() || !style()->isFlippedBlocksWritingMode()) 3485 return point; 3486 ColumnInfo* colInfo = columnInfo(); 3487 LayoutUnit columnLogicalHeight = colInfo->columnHeight(); 3488 LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight(); 3489 if (isHorizontalWritingMode()) 3490 return LayoutPoint(point.x(), expandedLogicalHeight - point.y()); 3491 return LayoutPoint(expandedLogicalHeight - point.x(), point.y()); 3492 } 3493 3494 void RenderBlock::adjustStartEdgeForWritingModeIncludingColumns(LayoutRect& rect) const 3495 { 3496 ASSERT(hasColumns()); 3497 if (!hasColumns() || !style()->isFlippedBlocksWritingMode()) 3498 return; 3499 3500 ColumnInfo* colInfo = columnInfo(); 3501 LayoutUnit columnLogicalHeight = colInfo->columnHeight(); 3502 LayoutUnit expandedLogicalHeight = borderBefore() + paddingBefore() + columnCount(colInfo) * columnLogicalHeight + borderAfter() + paddingAfter() + scrollbarLogicalHeight(); 3503 3504 if (isHorizontalWritingMode()) 3505 rect.setY(expandedLogicalHeight - rect.maxY()); 3506 else 3507 rect.setX(expandedLogicalHeight - rect.maxX()); 3508 } 3509 3510 LayoutSize RenderBlock::columnOffset(const LayoutPoint& point) const 3511 { 3512 if (!hasColumns()) 3513 return LayoutSize(); 3514 3515 ColumnInfo* colInfo = columnInfo(); 3516 3517 LayoutUnit logicalLeft = logicalLeftOffsetForContent(); 3518 unsigned colCount = columnCount(colInfo); 3519 LayoutUnit colLogicalWidth = colInfo->desiredColumnWidth(); 3520 LayoutUnit colLogicalHeight = colInfo->columnHeight(); 3521 3522 for (unsigned i = 0; i < colCount; ++i) { 3523 // Compute the edges for a given column in the block progression direction. 3524 LayoutRect sliceRect = LayoutRect(logicalLeft, borderBefore() + paddingBefore() + i * colLogicalHeight, colLogicalWidth, colLogicalHeight); 3525 if (!isHorizontalWritingMode()) 3526 sliceRect = sliceRect.transposedRect(); 3527 3528 LayoutUnit logicalOffset = i * colLogicalHeight; 3529 3530 // Now we're in the same coordinate space as the point. See if it is inside the rectangle. 3531 if (isHorizontalWritingMode()) { 3532 if (point.y() >= sliceRect.y() && point.y() < sliceRect.maxY()) { 3533 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) 3534 return LayoutSize(columnRectAt(colInfo, i).x() - logicalLeft, -logicalOffset); 3535 return LayoutSize(0, columnRectAt(colInfo, i).y() - logicalOffset - borderBefore() - paddingBefore()); 3536 } 3537 } else { 3538 if (point.x() >= sliceRect.x() && point.x() < sliceRect.maxX()) { 3539 if (colInfo->progressionAxis() == ColumnInfo::InlineAxis) 3540 return LayoutSize(-logicalOffset, columnRectAt(colInfo, i).y() - logicalLeft); 3541 return LayoutSize(columnRectAt(colInfo, i).x() - logicalOffset - borderBefore() - paddingBefore(), 0); 3542 } 3543 } 3544 } 3545 3546 return LayoutSize(); 3547 } 3548 3549 void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const 3550 { 3551 if (childrenInline()) { 3552 // FIXME: Remove this const_cast. 3553 toRenderBlockFlow(const_cast<RenderBlock*>(this))->computeInlinePreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); 3554 } else { 3555 computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); 3556 } 3557 3558 maxLogicalWidth = max(minLogicalWidth, maxLogicalWidth); 3559 3560 adjustIntrinsicLogicalWidthsForColumns(minLogicalWidth, maxLogicalWidth); 3561 3562 // A horizontal marquee with inline children has no minimum width. 3563 if (childrenInline() && isMarquee() && toRenderMarquee(this)->isHorizontal()) 3564 minLogicalWidth = 0; 3565 3566 if (isTableCell()) { 3567 Length tableCellWidth = toRenderTableCell(this)->styleOrColLogicalWidth(); 3568 if (tableCellWidth.isFixed() && tableCellWidth.value() > 0) 3569 maxLogicalWidth = max(minLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(tableCellWidth.value())); 3570 } 3571 3572 int scrollbarWidth = instrinsicScrollbarLogicalWidth(); 3573 maxLogicalWidth += scrollbarWidth; 3574 minLogicalWidth += scrollbarWidth; 3575 } 3576 3577 void RenderBlock::computePreferredLogicalWidths() 3578 { 3579 ASSERT(preferredLogicalWidthsDirty()); 3580 3581 updateFirstLetter(); 3582 3583 m_minPreferredLogicalWidth = 0; 3584 m_maxPreferredLogicalWidth = 0; 3585 3586 // FIXME: The isFixed() calls here should probably be checking for isSpecified since you 3587 // should be able to use percentage, calc or viewport relative values for width. 3588 RenderStyle* styleToUse = style(); 3589 if (!isTableCell() && styleToUse->logicalWidth().isFixed() && styleToUse->logicalWidth().value() >= 0 3590 && !(isDeprecatedFlexItem() && !styleToUse->logicalWidth().intValue())) 3591 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalWidth().value()); 3592 else 3593 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 3594 3595 if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) { 3596 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); 3597 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value())); 3598 } 3599 3600 if (styleToUse->logicalMaxWidth().isFixed()) { 3601 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); 3602 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value())); 3603 } 3604 3605 // Table layout uses integers, ceil the preferred widths to ensure that they can contain the contents. 3606 if (isTableCell()) { 3607 m_minPreferredLogicalWidth = m_minPreferredLogicalWidth.ceil(); 3608 m_maxPreferredLogicalWidth = m_maxPreferredLogicalWidth.ceil(); 3609 } 3610 3611 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); 3612 m_minPreferredLogicalWidth += borderAndPadding; 3613 m_maxPreferredLogicalWidth += borderAndPadding; 3614 3615 clearPreferredLogicalWidthsDirty(); 3616 } 3617 3618 void RenderBlock::adjustIntrinsicLogicalWidthsForColumns(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const 3619 { 3620 if (!style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth()) { 3621 // The min/max intrinsic widths calculated really tell how much space elements need when 3622 // laid out inside the columns. In order to eventually end up with the desired column width, 3623 // we need to convert them to values pertaining to the multicol container. 3624 int columnCount = style()->hasAutoColumnCount() ? 1 : style()->columnCount(); 3625 LayoutUnit columnWidth; 3626 LayoutUnit gapExtra = (columnCount - 1) * columnGap(); 3627 if (style()->hasAutoColumnWidth()) { 3628 minLogicalWidth = minLogicalWidth * columnCount + gapExtra; 3629 } else { 3630 columnWidth = style()->columnWidth(); 3631 minLogicalWidth = min(minLogicalWidth, columnWidth); 3632 } 3633 // FIXME: If column-count is auto here, we should resolve it to calculate the maximum 3634 // intrinsic width, instead of pretending that it's 1. The only way to do that is by 3635 // performing a layout pass, but this is not an appropriate time or place for layout. The 3636 // good news is that if height is unconstrained and there are no explicit breaks, the 3637 // resolved column-count really should be 1. 3638 maxLogicalWidth = max(maxLogicalWidth, columnWidth) * columnCount + gapExtra; 3639 } 3640 } 3641 3642 void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const 3643 { 3644 RenderStyle* styleToUse = style(); 3645 bool nowrap = styleToUse->whiteSpace() == NOWRAP; 3646 3647 RenderObject* child = firstChild(); 3648 RenderBlock* containingBlock = this->containingBlock(); 3649 LayoutUnit floatLeftWidth = 0, floatRightWidth = 0; 3650 while (child) { 3651 // Positioned children don't affect the min/max width 3652 if (child->isOutOfFlowPositioned()) { 3653 child = child->nextSibling(); 3654 continue; 3655 } 3656 3657 RenderStyle* childStyle = child->style(); 3658 if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) { 3659 LayoutUnit floatTotalWidth = floatLeftWidth + floatRightWidth; 3660 if (childStyle->clear() & CLEFT) { 3661 maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth); 3662 floatLeftWidth = 0; 3663 } 3664 if (childStyle->clear() & CRIGHT) { 3665 maxLogicalWidth = max(floatTotalWidth, maxLogicalWidth); 3666 floatRightWidth = 0; 3667 } 3668 } 3669 3670 // A margin basically has three types: fixed, percentage, and auto (variable). 3671 // Auto and percentage margins simply become 0 when computing min/max width. 3672 // Fixed margins can be added in as is. 3673 Length startMarginLength = childStyle->marginStartUsing(styleToUse); 3674 Length endMarginLength = childStyle->marginEndUsing(styleToUse); 3675 LayoutUnit margin = 0; 3676 LayoutUnit marginStart = 0; 3677 LayoutUnit marginEnd = 0; 3678 if (startMarginLength.isFixed()) 3679 marginStart += startMarginLength.value(); 3680 if (endMarginLength.isFixed()) 3681 marginEnd += endMarginLength.value(); 3682 margin = marginStart + marginEnd; 3683 3684 LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth; 3685 if (child->isBox() && child->isHorizontalWritingMode() != isHorizontalWritingMode()) { 3686 RenderBox* childBox = toRenderBox(child); 3687 LogicalExtentComputedValues computedValues; 3688 childBox->computeLogicalHeight(childBox->borderAndPaddingLogicalHeight(), 0, computedValues); 3689 childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = computedValues.m_extent; 3690 } else { 3691 childMinPreferredLogicalWidth = child->minPreferredLogicalWidth(); 3692 childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth(); 3693 } 3694 3695 LayoutUnit w = childMinPreferredLogicalWidth + margin; 3696 minLogicalWidth = max(w, minLogicalWidth); 3697 3698 // IE ignores tables for calculation of nowrap. Makes some sense. 3699 if (nowrap && !child->isTable()) 3700 maxLogicalWidth = max(w, maxLogicalWidth); 3701 3702 w = childMaxPreferredLogicalWidth + margin; 3703 3704 if (!child->isFloating()) { 3705 if (child->isBox() && toRenderBox(child)->avoidsFloats()) { 3706 // Determine a left and right max value based off whether or not the floats can fit in the 3707 // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin 3708 // is smaller than the float width. 3709 bool ltr = containingBlock ? containingBlock->style()->isLeftToRightDirection() : styleToUse->isLeftToRightDirection(); 3710 LayoutUnit marginLogicalLeft = ltr ? marginStart : marginEnd; 3711 LayoutUnit marginLogicalRight = ltr ? marginEnd : marginStart; 3712 LayoutUnit maxLeft = marginLogicalLeft > 0 ? max(floatLeftWidth, marginLogicalLeft) : floatLeftWidth + marginLogicalLeft; 3713 LayoutUnit maxRight = marginLogicalRight > 0 ? max(floatRightWidth, marginLogicalRight) : floatRightWidth + marginLogicalRight; 3714 w = childMaxPreferredLogicalWidth + maxLeft + maxRight; 3715 w = max(w, floatLeftWidth + floatRightWidth); 3716 } 3717 else 3718 maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth); 3719 floatLeftWidth = floatRightWidth = 0; 3720 } 3721 3722 if (child->isFloating()) { 3723 if (childStyle->floating() == LeftFloat) 3724 floatLeftWidth += w; 3725 else 3726 floatRightWidth += w; 3727 } else 3728 maxLogicalWidth = max(w, maxLogicalWidth); 3729 3730 child = child->nextSibling(); 3731 } 3732 3733 // Always make sure these values are non-negative. 3734 minLogicalWidth = max<LayoutUnit>(0, minLogicalWidth); 3735 maxLogicalWidth = max<LayoutUnit>(0, maxLogicalWidth); 3736 3737 maxLogicalWidth = max(floatLeftWidth + floatRightWidth, maxLogicalWidth); 3738 } 3739 3740 bool RenderBlock::hasLineIfEmpty() const 3741 { 3742 if (!node()) 3743 return false; 3744 3745 if (node()->isRootEditableElement()) 3746 return true; 3747 3748 if (node()->isShadowRoot() && isHTMLInputElement(*toShadowRoot(node())->host())) 3749 return true; 3750 3751 return false; 3752 } 3753 3754 LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const 3755 { 3756 // Inline blocks are replaced elements. Otherwise, just pass off to 3757 // the base class. If we're being queried as though we're the root line 3758 // box, then the fact that we're an inline-block is irrelevant, and we behave 3759 // just like a block. 3760 if (isReplaced() && linePositionMode == PositionOnContainingLine) 3761 return RenderBox::lineHeight(firstLine, direction, linePositionMode); 3762 3763 RenderStyle* s = style(firstLine && document().styleEngine()->usesFirstLineRules()); 3764 return s->computedLineHeight(); 3765 } 3766 3767 int RenderBlock::beforeMarginInLineDirection(LineDirectionMode direction) const 3768 { 3769 return direction == HorizontalLine ? marginTop() : marginRight(); 3770 } 3771 3772 int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const 3773 { 3774 // Inline blocks are replaced elements. Otherwise, just pass off to 3775 // the base class. If we're being queried as though we're the root line 3776 // box, then the fact that we're an inline-block is irrelevant, and we behave 3777 // just like a block. 3778 if (isInline() && linePositionMode == PositionOnContainingLine) { 3779 // For "leaf" theme objects, let the theme decide what the baseline position is. 3780 // FIXME: Might be better to have a custom CSS property instead, so that if the theme 3781 // is turned off, checkboxes/radios will still have decent baselines. 3782 // FIXME: Need to patch form controls to deal with vertical lines. 3783 if (style()->hasAppearance() && !RenderTheme::theme().isControlContainer(style()->appearance())) 3784 return RenderTheme::theme().baselinePosition(this); 3785 3786 // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in 3787 // the normal flow. We make an exception for marquees, since their baselines are meaningless 3788 // (the content inside them moves). This matches WinIE as well, which just bottom-aligns them. 3789 // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled 3790 // vertically (e.g., an overflow:hidden block that has had scrollTop moved). 3791 bool ignoreBaseline = (layer() && layer()->scrollableArea() && (isMarquee() || (direction == HorizontalLine ? (layer()->scrollableArea()->verticalScrollbar() || layer()->scrollableArea()->scrollYOffset()) 3792 : (layer()->scrollableArea()->horizontalScrollbar() || layer()->scrollableArea()->scrollXOffset())))) || (isWritingModeRoot() && !isRubyRun()); 3793 3794 int baselinePos = ignoreBaseline ? -1 : inlineBlockBaseline(direction); 3795 3796 if (isDeprecatedFlexibleBox()) { 3797 // Historically, we did this check for all baselines. But we can't 3798 // remove this code from deprecated flexbox, because it effectively 3799 // breaks -webkit-line-clamp, which is used in the wild -- we would 3800 // calculate the baseline as if -webkit-line-clamp wasn't used. 3801 // For simplicity, we use this for all uses of deprecated flexbox. 3802 LayoutUnit bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth(); 3803 if (baselinePos > bottomOfContent) 3804 baselinePos = -1; 3805 } 3806 if (baselinePos != -1) 3807 return beforeMarginInLineDirection(direction) + baselinePos; 3808 3809 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); 3810 } 3811 3812 // If we're not replaced, we'll only get called with PositionOfInteriorLineBoxes. 3813 // Note that inline-block counts as replaced here. 3814 ASSERT(linePositionMode == PositionOfInteriorLineBoxes); 3815 3816 const FontMetrics& fontMetrics = style(firstLine)->fontMetrics(); 3817 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2; 3818 } 3819 3820 LayoutUnit RenderBlock::minLineHeightForReplacedRenderer(bool isFirstLine, LayoutUnit replacedHeight) const 3821 { 3822 if (!document().inNoQuirksMode() && replacedHeight) 3823 return replacedHeight; 3824 3825 if (!(style(isFirstLine)->lineBoxContain() & LineBoxContainBlock)) 3826 return 0; 3827 3828 return std::max<LayoutUnit>(replacedHeight, lineHeight(isFirstLine, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); 3829 } 3830 3831 int RenderBlock::firstLineBoxBaseline() const 3832 { 3833 if (isWritingModeRoot() && !isRubyRun()) 3834 return -1; 3835 3836 if (childrenInline()) { 3837 if (firstLineBox()) 3838 return firstLineBox()->logicalTop() + style(true)->fontMetrics().ascent(firstRootBox()->baselineType()); 3839 else 3840 return -1; 3841 } 3842 else { 3843 for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) { 3844 if (!curr->isFloatingOrOutOfFlowPositioned()) { 3845 int result = curr->firstLineBoxBaseline(); 3846 if (result != -1) 3847 return curr->logicalTop() + result; // Translate to our coordinate space. 3848 } 3849 } 3850 } 3851 3852 return -1; 3853 } 3854 3855 int RenderBlock::inlineBlockBaseline(LineDirectionMode direction) const 3856 { 3857 if (!style()->isOverflowVisible()) { 3858 // We are not calling RenderBox::baselinePosition here because the caller should add the margin-top/margin-right, not us. 3859 return direction == HorizontalLine ? height() + m_marginBox.bottom() : width() + m_marginBox.left(); 3860 } 3861 3862 return lastLineBoxBaseline(direction); 3863 } 3864 3865 int RenderBlock::lastLineBoxBaseline(LineDirectionMode lineDirection) const 3866 { 3867 if (isWritingModeRoot() && !isRubyRun()) 3868 return -1; 3869 3870 if (childrenInline()) { 3871 if (!firstLineBox() && hasLineIfEmpty()) { 3872 const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics(); 3873 return fontMetrics.ascent() 3874 + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2 3875 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); 3876 } 3877 if (lastLineBox()) 3878 return lastLineBox()->logicalTop() + style(lastLineBox() == firstLineBox())->fontMetrics().ascent(lastRootBox()->baselineType()); 3879 return -1; 3880 } else { 3881 bool haveNormalFlowChild = false; 3882 for (RenderBox* curr = lastChildBox(); curr; curr = curr->previousSiblingBox()) { 3883 if (!curr->isFloatingOrOutOfFlowPositioned()) { 3884 haveNormalFlowChild = true; 3885 int result = curr->inlineBlockBaseline(lineDirection); 3886 if (result != -1) 3887 return curr->logicalTop() + result; // Translate to our coordinate space. 3888 } 3889 } 3890 if (!haveNormalFlowChild && hasLineIfEmpty()) { 3891 const FontMetrics& fontMetrics = firstLineStyle()->fontMetrics(); 3892 return fontMetrics.ascent() 3893 + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2 3894 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); 3895 } 3896 } 3897 3898 return -1; 3899 } 3900 3901 RenderBlock* RenderBlock::firstLineBlock() const 3902 { 3903 RenderBlock* firstLineBlock = const_cast<RenderBlock*>(this); 3904 bool hasPseudo = false; 3905 while (true) { 3906 hasPseudo = firstLineBlock->style()->hasPseudoStyle(FIRST_LINE); 3907 if (hasPseudo) 3908 break; 3909 RenderObject* parentBlock = firstLineBlock->parent(); 3910 // We include isRenderButton in this check because buttons are 3911 // implemented using flex box but should still support first-line. The 3912 // flex box spec requires that flex box does not support first-line, 3913 // though. 3914 // FIXME: Remove when buttons are implemented with align-items instead 3915 // of flexbox. 3916 if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() 3917 || !parentBlock 3918 || (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton())) 3919 break; 3920 ASSERT_WITH_SECURITY_IMPLICATION(parentBlock->isRenderBlock()); 3921 if (toRenderBlock(parentBlock)->firstChild() != firstLineBlock) 3922 break; 3923 firstLineBlock = toRenderBlock(parentBlock); 3924 } 3925 3926 if (!hasPseudo) 3927 return 0; 3928 3929 return firstLineBlock; 3930 } 3931 3932 static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer) 3933 { 3934 RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, firstLetterContainer->firstLineStyle()); 3935 // Force inline display (except for floating first-letters). 3936 pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); 3937 // CSS2 says first-letter can't be positioned. 3938 pseudoStyle->setPosition(StaticPosition); 3939 return pseudoStyle; 3940 } 3941 3942 // CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter 3943 // "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" (Pe), 3944 // "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included" 3945 static inline bool isPunctuationForFirstLetter(UChar c) 3946 { 3947 CharCategory charCategory = category(c); 3948 return charCategory == Punctuation_Open 3949 || charCategory == Punctuation_Close 3950 || charCategory == Punctuation_InitialQuote 3951 || charCategory == Punctuation_FinalQuote 3952 || charCategory == Punctuation_Other; 3953 } 3954 3955 static inline bool isSpaceForFirstLetter(UChar c) 3956 { 3957 return isSpaceOrNewline(c) || c == noBreakSpace; 3958 } 3959 3960 static inline RenderObject* findFirstLetterBlock(RenderBlock* start) 3961 { 3962 RenderObject* firstLetterBlock = start; 3963 while (true) { 3964 // We include isRenderButton in these two checks because buttons are 3965 // implemented using flex box but should still support first-letter. 3966 // The flex box spec requires that flex box does not support 3967 // first-letter, though. 3968 // FIXME: Remove when buttons are implemented with align-items instead 3969 // of flexbox. 3970 bool canHaveFirstLetterRenderer = firstLetterBlock->style()->hasPseudoStyle(FIRST_LETTER) 3971 && firstLetterBlock->canHaveGeneratedChildren() 3972 && (!firstLetterBlock->isFlexibleBox() || firstLetterBlock->isRenderButton()); 3973 if (canHaveFirstLetterRenderer) 3974 return firstLetterBlock; 3975 3976 RenderObject* parentBlock = firstLetterBlock->parent(); 3977 if (firstLetterBlock->isReplaced() || !parentBlock 3978 || (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton())) { 3979 return 0; 3980 } 3981 ASSERT(parentBlock->isRenderBlock()); 3982 if (toRenderBlock(parentBlock)->firstChild() != firstLetterBlock) 3983 return 0; 3984 firstLetterBlock = parentBlock; 3985 } 3986 3987 return 0; 3988 } 3989 3990 void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderObject* currentChild) 3991 { 3992 RenderObject* firstLetter = currentChild->parent(); 3993 RenderObject* firstLetterContainer = firstLetter->parent(); 3994 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); 3995 ASSERT(firstLetter->isFloating() || firstLetter->isInline()); 3996 3997 ForceHorriblySlowRectMapping slowRectMapping(*this); 3998 3999 if (RenderStyle::stylePropagationDiff(firstLetter->style(), pseudoStyle) == Reattach) { 4000 // The first-letter renderer needs to be replaced. Create a new renderer of the right type. 4001 RenderBoxModelObject* newFirstLetter; 4002 if (pseudoStyle->display() == INLINE) 4003 newFirstLetter = RenderInline::createAnonymous(&document()); 4004 else 4005 newFirstLetter = RenderBlockFlow::createAnonymous(&document()); 4006 newFirstLetter->setStyle(pseudoStyle); 4007 4008 // Move the first letter into the new renderer. 4009 while (RenderObject* child = firstLetter->slowFirstChild()) { 4010 if (child->isText()) 4011 toRenderText(child)->removeAndDestroyTextBoxes(); 4012 firstLetter->removeChild(child); 4013 newFirstLetter->addChild(child, 0); 4014 } 4015 4016 RenderObject* nextSibling = firstLetter->nextSibling(); 4017 if (RenderTextFragment* remainingText = toRenderBoxModelObject(firstLetter)->firstLetterRemainingText()) { 4018 ASSERT(remainingText->isAnonymous() || remainingText->node()->renderer() == remainingText); 4019 // Replace the old renderer with the new one. 4020 remainingText->setFirstLetter(newFirstLetter); 4021 newFirstLetter->setFirstLetterRemainingText(remainingText); 4022 } 4023 // To prevent removal of single anonymous block in RenderBlock::removeChild and causing 4024 // |nextSibling| to go stale, we remove the old first letter using removeChildNode first. 4025 firstLetterContainer->virtualChildren()->removeChildNode(firstLetterContainer, firstLetter); 4026 firstLetter->destroy(); 4027 firstLetter = newFirstLetter; 4028 firstLetterContainer->addChild(firstLetter, nextSibling); 4029 } else 4030 firstLetter->setStyle(pseudoStyle); 4031 4032 for (RenderObject* genChild = firstLetter->slowFirstChild(); genChild; genChild = genChild->nextSibling()) { 4033 if (genChild->isText()) 4034 genChild->setStyle(pseudoStyle); 4035 } 4036 } 4037 4038 static inline unsigned firstLetterLength(const String& text) 4039 { 4040 unsigned length = 0; 4041 unsigned textLength = text.length(); 4042 4043 // Account for leading spaces first. 4044 while (length < textLength && isSpaceForFirstLetter(text[length])) 4045 length++; 4046 4047 // Now account for leading punctuation. 4048 while (length < textLength && isPunctuationForFirstLetter(text[length])) 4049 length++; 4050 4051 // Bail if we didn't find a letter before the end of the text or before a space. 4052 if (isSpaceForFirstLetter(text[length]) || (textLength && length == textLength)) 4053 return 0; 4054 4055 // Account the next character for first letter. 4056 length++; 4057 4058 // Keep looking allowed punctuation for the :first-letter. 4059 for (unsigned scanLength = length; scanLength < textLength; ++scanLength) { 4060 UChar c = text[scanLength]; 4061 4062 if (!isPunctuationForFirstLetter(c)) 4063 break; 4064 4065 length = scanLength + 1; 4066 } 4067 4068 // FIXME: If textLength is 0, length may still be 1! 4069 return length; 4070 } 4071 4072 void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, RenderObject* currentChild, unsigned length) 4073 { 4074 ASSERT(length && currentChild->isText()); 4075 4076 RenderObject* firstLetterContainer = currentChild->parent(); 4077 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); 4078 RenderBoxModelObject* firstLetter = 0; 4079 if (pseudoStyle->display() == INLINE) 4080 firstLetter = RenderInline::createAnonymous(&document()); 4081 else 4082 firstLetter = RenderBlockFlow::createAnonymous(&document()); 4083 firstLetter->setStyle(pseudoStyle); 4084 4085 // FIXME: The first letter code should not modify the render tree during 4086 // layout. crbug.com/370458 4087 DeprecatedDisableModifyRenderTreeStructureAsserts disabler; 4088 4089 firstLetterContainer->addChild(firstLetter, currentChild); 4090 RenderText* textObj = toRenderText(currentChild); 4091 4092 // The original string is going to be either a generated content string or a DOM node's 4093 // string. We want the original string before it got transformed in case first-letter has 4094 // no text-transform or a different text-transform applied to it. 4095 String oldText = textObj->originalText(); 4096 ASSERT(oldText.impl()); 4097 4098 // Construct a text fragment for the text after the first letter. 4099 // This text fragment might be empty. 4100 RenderTextFragment* remainingText = 4101 new RenderTextFragment(textObj->node() ? textObj->node() : &textObj->document(), oldText.impl(), length, oldText.length() - length); 4102 remainingText->setStyle(textObj->style()); 4103 if (remainingText->node()) 4104 remainingText->node()->setRenderer(remainingText); 4105 4106 firstLetterContainer->addChild(remainingText, textObj); 4107 firstLetterContainer->removeChild(textObj); 4108 remainingText->setFirstLetter(firstLetter); 4109 firstLetter->setFirstLetterRemainingText(remainingText); 4110 4111 // construct text fragment for the first letter 4112 RenderTextFragment* letter = 4113 new RenderTextFragment(remainingText->node() ? remainingText->node() : &remainingText->document(), oldText.impl(), 0, length); 4114 letter->setStyle(pseudoStyle); 4115 firstLetter->addChild(letter); 4116 4117 textObj->destroy(); 4118 } 4119 4120 void RenderBlock::updateFirstLetter() 4121 { 4122 if (!document().styleEngine()->usesFirstLetterRules()) 4123 return; 4124 // Don't recur 4125 if (style()->styleType() == FIRST_LETTER) 4126 return; 4127 4128 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find 4129 // an efficient way to check for that situation though before implementing anything. 4130 RenderObject* firstLetterBlock = findFirstLetterBlock(this); 4131 if (!firstLetterBlock) 4132 return; 4133 4134 // Drill into inlines looking for our first text child. 4135 RenderObject* currChild = firstLetterBlock->slowFirstChild(); 4136 unsigned length = 0; 4137 while (currChild) { 4138 if (currChild->isText()) { 4139 // FIXME: If there is leading punctuation in a different RenderText than 4140 // the first letter, we'll not apply the correct style to it. 4141 length = firstLetterLength(toRenderText(currChild)->originalText()); 4142 if (length) 4143 break; 4144 currChild = currChild->nextSibling(); 4145 } else if (currChild->isListMarker()) { 4146 currChild = currChild->nextSibling(); 4147 } else if (currChild->isFloatingOrOutOfFlowPositioned()) { 4148 if (currChild->style()->styleType() == FIRST_LETTER) { 4149 currChild = currChild->slowFirstChild(); 4150 break; 4151 } 4152 currChild = currChild->nextSibling(); 4153 } else if (currChild->isReplaced() || currChild->isRenderButton() || currChild->isMenuList()) { 4154 break; 4155 } else if (currChild->style()->hasPseudoStyle(FIRST_LETTER) && currChild->canHaveGeneratedChildren()) { 4156 // We found a lower-level node with first-letter, which supersedes the higher-level style 4157 firstLetterBlock = currChild; 4158 currChild = currChild->slowFirstChild(); 4159 } else { 4160 currChild = currChild->slowFirstChild(); 4161 } 4162 } 4163 4164 if (!currChild) 4165 return; 4166 4167 // If the child already has style, then it has already been created, so we just want 4168 // to update it. 4169 if (currChild->parent()->style()->styleType() == FIRST_LETTER) { 4170 updateFirstLetterStyle(firstLetterBlock, currChild); 4171 return; 4172 } 4173 4174 // FIXME: This black-list of disallowed RenderText subclasses is fragile. 4175 // Should counter be on this list? What about RenderTextFragment? 4176 if (!currChild->isText() || currChild->isBR() || toRenderText(currChild)->isWordBreak()) 4177 return; 4178 4179 // Our layout state is not valid for the repaints we are going to trigger by 4180 // adding and removing children of firstLetterContainer. 4181 ForceHorriblySlowRectMapping slowRectMapping(*this); 4182 4183 createFirstLetterRenderer(firstLetterBlock, currChild, length); 4184 } 4185 4186 // Helper methods for obtaining the last line, computing line counts and heights for line counts 4187 // (crawling into blocks). 4188 static bool shouldCheckLines(RenderObject* obj) 4189 { 4190 return !obj->isFloatingOrOutOfFlowPositioned() 4191 && obj->isRenderBlock() && obj->style()->height().isAuto() 4192 && (!obj->isDeprecatedFlexibleBox() || obj->style()->boxOrient() == VERTICAL); 4193 } 4194 4195 static int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, int& count) 4196 { 4197 if (block->style()->visibility() == VISIBLE) { 4198 if (block->childrenInline()) { 4199 for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) { 4200 if (++count == l) 4201 return box->lineBottom() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : LayoutUnit()); 4202 } 4203 } 4204 else { 4205 RenderBox* normalFlowChildWithoutLines = 0; 4206 for (RenderBox* obj = block->firstChildBox(); obj; obj = obj->nextSiblingBox()) { 4207 if (shouldCheckLines(obj)) { 4208 int result = getHeightForLineCount(toRenderBlock(obj), l, false, count); 4209 if (result != -1) 4210 return result + obj->y() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : LayoutUnit()); 4211 } else if (!obj->isFloatingOrOutOfFlowPositioned()) 4212 normalFlowChildWithoutLines = obj; 4213 } 4214 if (normalFlowChildWithoutLines && l == 0) 4215 return normalFlowChildWithoutLines->y() + normalFlowChildWithoutLines->height(); 4216 } 4217 } 4218 4219 return -1; 4220 } 4221 4222 RootInlineBox* RenderBlock::lineAtIndex(int i) const 4223 { 4224 ASSERT(i >= 0); 4225 4226 if (style()->visibility() != VISIBLE) 4227 return 0; 4228 4229 if (childrenInline()) { 4230 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) 4231 if (!i--) 4232 return box; 4233 } else { 4234 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { 4235 if (!shouldCheckLines(child)) 4236 continue; 4237 if (RootInlineBox* box = toRenderBlock(child)->lineAtIndex(i)) 4238 return box; 4239 } 4240 } 4241 4242 return 0; 4243 } 4244 4245 int RenderBlock::lineCount(const RootInlineBox* stopRootInlineBox, bool* found) const 4246 { 4247 int count = 0; 4248 4249 if (style()->visibility() == VISIBLE) { 4250 if (childrenInline()) 4251 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { 4252 count++; 4253 if (box == stopRootInlineBox) { 4254 if (found) 4255 *found = true; 4256 break; 4257 } 4258 } 4259 else 4260 for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) 4261 if (shouldCheckLines(obj)) { 4262 bool recursiveFound = false; 4263 count += toRenderBlock(obj)->lineCount(stopRootInlineBox, &recursiveFound); 4264 if (recursiveFound) { 4265 if (found) 4266 *found = true; 4267 break; 4268 } 4269 } 4270 } 4271 return count; 4272 } 4273 4274 int RenderBlock::heightForLineCount(int l) 4275 { 4276 int count = 0; 4277 return getHeightForLineCount(this, l, true, count); 4278 } 4279 4280 void RenderBlock::adjustForBorderFit(LayoutUnit x, LayoutUnit& left, LayoutUnit& right) const 4281 { 4282 // We don't deal with relative positioning. Our assumption is that you shrink to fit the lines without accounting 4283 // for either overflow or translations via relative positioning. 4284 if (style()->visibility() == VISIBLE) { 4285 if (childrenInline()) { 4286 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { 4287 if (box->firstChild()) 4288 left = min(left, x + static_cast<LayoutUnit>(box->firstChild()->x())); 4289 if (box->lastChild()) 4290 right = max(right, x + static_cast<LayoutUnit>(ceilf(box->lastChild()->logicalRight()))); 4291 } 4292 } else { 4293 for (RenderBox* obj = firstChildBox(); obj; obj = obj->nextSiblingBox()) { 4294 if (!obj->isFloatingOrOutOfFlowPositioned()) { 4295 if (obj->isRenderBlockFlow() && !obj->hasOverflowClip()) 4296 toRenderBlock(obj)->adjustForBorderFit(x + obj->x(), left, right); 4297 else if (obj->style()->visibility() == VISIBLE) { 4298 // We are a replaced element or some kind of non-block-flow object. 4299 left = min(left, x + obj->x()); 4300 right = max(right, x + obj->x() + obj->width()); 4301 } 4302 } 4303 } 4304 } 4305 } 4306 } 4307 4308 void RenderBlock::fitBorderToLinesIfNeeded() 4309 { 4310 if (style()->borderFit() == BorderFitBorder || hasOverrideWidth()) 4311 return; 4312 4313 // Walk any normal flow lines to snugly fit. 4314 LayoutUnit left = LayoutUnit::max(); 4315 LayoutUnit right = LayoutUnit::min(); 4316 LayoutUnit oldWidth = contentWidth(); 4317 adjustForBorderFit(0, left, right); 4318 4319 // Clamp to our existing edges. We can never grow. We only shrink. 4320 LayoutUnit leftEdge = borderLeft() + paddingLeft(); 4321 LayoutUnit rightEdge = leftEdge + oldWidth; 4322 left = min(rightEdge, max(leftEdge, left)); 4323 right = max(left, min(rightEdge, right)); 4324 4325 LayoutUnit newContentWidth = right - left; 4326 if (newContentWidth == oldWidth) 4327 return; 4328 4329 setOverrideLogicalContentWidth(newContentWidth); 4330 layoutBlock(false); 4331 clearOverrideLogicalContentWidth(); 4332 } 4333 4334 void RenderBlock::clearTruncation() 4335 { 4336 if (style()->visibility() == VISIBLE) { 4337 if (childrenInline() && hasMarkupTruncation()) { 4338 setHasMarkupTruncation(false); 4339 for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) 4340 box->clearTruncation(); 4341 } else { 4342 for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) { 4343 if (shouldCheckLines(obj)) 4344 toRenderBlock(obj)->clearTruncation(); 4345 } 4346 } 4347 } 4348 } 4349 4350 void RenderBlock::setPaginationStrut(LayoutUnit strut) 4351 { 4352 if (!m_rareData) { 4353 if (!strut) 4354 return; 4355 m_rareData = adoptPtr(new RenderBlockRareData()); 4356 } 4357 m_rareData->m_paginationStrut = strut; 4358 } 4359 4360 void RenderBlock::setPageLogicalOffset(LayoutUnit logicalOffset) 4361 { 4362 if (!m_rareData) { 4363 if (!logicalOffset) 4364 return; 4365 m_rareData = adoptPtr(new RenderBlockRareData()); 4366 } 4367 m_rareData->m_pageLogicalOffset = logicalOffset; 4368 } 4369 4370 void RenderBlock::setBreakAtLineToAvoidWidow(int lineToBreak) 4371 { 4372 ASSERT(lineToBreak >= 0); 4373 if (!m_rareData) 4374 m_rareData = adoptPtr(new RenderBlockRareData()); 4375 4376 ASSERT(!m_rareData->m_didBreakAtLineToAvoidWidow); 4377 m_rareData->m_lineBreakToAvoidWidow = lineToBreak; 4378 } 4379 4380 void RenderBlock::setDidBreakAtLineToAvoidWidow() 4381 { 4382 ASSERT(!shouldBreakAtLineToAvoidWidow()); 4383 4384 // This function should be called only after a break was applied to avoid widows 4385 // so assert |m_rareData| exists. 4386 ASSERT(m_rareData); 4387 4388 m_rareData->m_didBreakAtLineToAvoidWidow = true; 4389 } 4390 4391 void RenderBlock::clearDidBreakAtLineToAvoidWidow() 4392 { 4393 if (!m_rareData) 4394 return; 4395 4396 m_rareData->m_didBreakAtLineToAvoidWidow = false; 4397 } 4398 4399 void RenderBlock::clearShouldBreakAtLineToAvoidWidow() const 4400 { 4401 ASSERT(shouldBreakAtLineToAvoidWidow()); 4402 if (!m_rareData) 4403 return; 4404 4405 m_rareData->m_lineBreakToAvoidWidow = -1; 4406 } 4407 4408 void RenderBlock::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const 4409 { 4410 // For blocks inside inlines, we go ahead and include margins so that we run right up to the 4411 // inline boxes above and below us (thus getting merged with them to form a single irregular 4412 // shape). 4413 if (isAnonymousBlockContinuation()) { 4414 // FIXME: This is wrong for block-flows that are horizontal. 4415 // https://bugs.webkit.org/show_bug.cgi?id=46781 4416 rects.append(pixelSnappedIntRect(accumulatedOffset.x(), accumulatedOffset.y() - collapsedMarginBefore(), 4417 width(), height() + collapsedMarginBefore() + collapsedMarginAfter())); 4418 continuation()->absoluteRects(rects, accumulatedOffset - toLayoutSize(location() + 4419 inlineElementContinuation()->containingBlock()->location())); 4420 } else 4421 rects.append(pixelSnappedIntRect(accumulatedOffset, size())); 4422 } 4423 4424 void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const 4425 { 4426 // For blocks inside inlines, we go ahead and include margins so that we run right up to the 4427 // inline boxes above and below us (thus getting merged with them to form a single irregular 4428 // shape). 4429 if (isAnonymousBlockContinuation()) { 4430 // FIXME: This is wrong for block-flows that are horizontal. 4431 // https://bugs.webkit.org/show_bug.cgi?id=46781 4432 FloatRect localRect(0, -collapsedMarginBefore().toFloat(), 4433 width().toFloat(), (height() + collapsedMarginBefore() + collapsedMarginAfter()).toFloat()); 4434 quads.append(localToAbsoluteQuad(localRect, 0 /* mode */, wasFixed)); 4435 continuation()->absoluteQuads(quads, wasFixed); 4436 } else { 4437 quads.append(RenderBox::localToAbsoluteQuad(FloatRect(0, 0, width().toFloat(), height().toFloat()), 0 /* mode */, wasFixed)); 4438 } 4439 } 4440 4441 LayoutRect RenderBlock::rectWithOutlineForPaintInvalidation(const RenderLayerModelObject* paintInvalidationContainer, LayoutUnit outlineWidth) const 4442 { 4443 LayoutRect r(RenderBox::rectWithOutlineForPaintInvalidation(paintInvalidationContainer, outlineWidth)); 4444 if (isAnonymousBlockContinuation()) 4445 r.inflateY(collapsedMarginBefore()); // FIXME: This is wrong for block-flows that are horizontal. 4446 return r; 4447 } 4448 4449 RenderObject* RenderBlock::hoverAncestor() const 4450 { 4451 return isAnonymousBlockContinuation() ? continuation() : RenderBox::hoverAncestor(); 4452 } 4453 4454 void RenderBlock::updateDragState(bool dragOn) 4455 { 4456 RenderBox::updateDragState(dragOn); 4457 if (continuation()) 4458 continuation()->updateDragState(dragOn); 4459 } 4460 4461 RenderStyle* RenderBlock::outlineStyleForPaintInvalidation() const 4462 { 4463 return isAnonymousBlockContinuation() ? continuation()->style() : style(); 4464 } 4465 4466 void RenderBlock::childBecameNonInline(RenderObject*) 4467 { 4468 makeChildrenNonInline(); 4469 if (isAnonymousBlock() && parent() && parent()->isRenderBlock()) 4470 toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); 4471 // |this| may be dead here 4472 } 4473 4474 void RenderBlock::updateHitTestResult(HitTestResult& result, const LayoutPoint& point) 4475 { 4476 if (result.innerNode()) 4477 return; 4478 4479 if (Node* n = nodeForHitTest()) { 4480 result.setInnerNode(n); 4481 if (!result.innerNonSharedNode()) 4482 result.setInnerNonSharedNode(n); 4483 result.setLocalPoint(point); 4484 } 4485 } 4486 4487 LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine) 4488 { 4489 // Do the normal calculation in most cases. 4490 if (firstChild()) 4491 return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine); 4492 4493 LayoutRect caretRect = localCaretRectForEmptyElement(width(), textIndentOffset()); 4494 4495 if (extraWidthToEndOfLine) { 4496 if (isRenderBlock()) { 4497 *extraWidthToEndOfLine = width() - caretRect.maxX(); 4498 } else { 4499 // FIXME: This code looks wrong. 4500 // myRight and containerRight are set up, but then clobbered. 4501 // So *extraWidthToEndOfLine will always be 0 here. 4502 4503 LayoutUnit myRight = caretRect.maxX(); 4504 // FIXME: why call localToAbsoluteForContent() twice here, too? 4505 FloatPoint absRightPoint = localToAbsolute(FloatPoint(myRight.toFloat(), 0)); 4506 4507 LayoutUnit containerRight = containingBlock()->x() + containingBlockLogicalWidthForContent(); 4508 FloatPoint absContainerPoint = localToAbsolute(FloatPoint(containerRight.toFloat(), 0)); 4509 4510 *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x(); 4511 } 4512 } 4513 4514 return caretRect; 4515 } 4516 4517 void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) 4518 { 4519 // For blocks inside inlines, we go ahead and include margins so that we run right up to the 4520 // inline boxes above and below us (thus getting merged with them to form a single irregular 4521 // shape). 4522 if (inlineElementContinuation()) { 4523 // FIXME: This check really isn't accurate. 4524 bool nextInlineHasLineBox = inlineElementContinuation()->firstLineBox(); 4525 // FIXME: This is wrong. The principal renderer may not be the continuation preceding this block. 4526 // FIXME: This is wrong for block-flows that are horizontal. 4527 // https://bugs.webkit.org/show_bug.cgi?id=46781 4528 bool prevInlineHasLineBox = toRenderInline(inlineElementContinuation()->node()->renderer())->firstLineBox(); 4529 LayoutUnit topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : LayoutUnit(); 4530 LayoutUnit bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : LayoutUnit(); 4531 LayoutRect rect(additionalOffset.x(), additionalOffset.y() - topMargin, width(), height() + topMargin + bottomMargin); 4532 if (!rect.isEmpty()) 4533 rects.append(pixelSnappedIntRect(rect)); 4534 } else if (width() && height()) 4535 rects.append(pixelSnappedIntRect(additionalOffset, size())); 4536 4537 if (!hasOverflowClip() && !hasControlClip()) { 4538 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { 4539 LayoutUnit top = max<LayoutUnit>(curr->lineTop(), curr->top()); 4540 LayoutUnit bottom = min<LayoutUnit>(curr->lineBottom(), curr->top() + curr->height()); 4541 LayoutRect rect(additionalOffset.x() + curr->x(), additionalOffset.y() + top, curr->width(), bottom - top); 4542 if (!rect.isEmpty()) 4543 rects.append(pixelSnappedIntRect(rect)); 4544 } 4545 4546 addChildFocusRingRects(rects, additionalOffset, paintContainer); 4547 } 4548 4549 if (inlineElementContinuation()) 4550 inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location()), paintContainer); 4551 } 4552 4553 void RenderBlock::computeSelfHitTestRects(Vector<LayoutRect>& rects, const LayoutPoint& layerOffset) const 4554 { 4555 RenderBox::computeSelfHitTestRects(rects, layerOffset); 4556 4557 if (hasHorizontalLayoutOverflow() || hasVerticalLayoutOverflow()) { 4558 for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { 4559 LayoutUnit top = max<LayoutUnit>(curr->lineTop(), curr->top()); 4560 LayoutUnit bottom = min<LayoutUnit>(curr->lineBottom(), curr->top() + curr->height()); 4561 LayoutRect rect(layerOffset.x() + curr->x(), layerOffset.y() + top, curr->width(), bottom - top); 4562 // It's common for this rect to be entirely contained in our box, so exclude that simple case. 4563 if (!rect.isEmpty() && (rects.isEmpty() || !rects[0].contains(rect))) 4564 rects.append(rect); 4565 } 4566 } 4567 } 4568 4569 RenderBox* RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const 4570 { 4571 if (isAnonymousColumnsBlock()) 4572 return createAnonymousColumnsWithParentRenderer(parent); 4573 if (isAnonymousColumnSpanBlock()) 4574 return createAnonymousColumnSpanWithParentRenderer(parent); 4575 return createAnonymousWithParentRendererAndDisplay(parent, style()->display()); 4576 } 4577 4578 LayoutUnit RenderBlock::nextPageLogicalTop(LayoutUnit logicalOffset, PageBoundaryRule pageBoundaryRule) const 4579 { 4580 LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); 4581 if (!pageLogicalHeight) 4582 return logicalOffset; 4583 4584 // The logicalOffset is in our coordinate space. We can add in our pushed offset. 4585 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset); 4586 if (pageBoundaryRule == ExcludePageBoundary) 4587 return logicalOffset + (remainingLogicalHeight ? remainingLogicalHeight : pageLogicalHeight); 4588 return logicalOffset + remainingLogicalHeight; 4589 } 4590 4591 LayoutUnit RenderBlock::pageLogicalTopForOffset(LayoutUnit offset) const 4592 { 4593 RenderView* renderView = view(); 4594 LayoutUnit firstPageLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->pageOffset().height() : renderView->layoutState()->pageOffset().width(); 4595 LayoutUnit blockLogicalTop = isHorizontalWritingMode() ? renderView->layoutState()->layoutOffset().height() : renderView->layoutState()->layoutOffset().width(); 4596 4597 LayoutUnit cumulativeOffset = offset + blockLogicalTop; 4598 RenderFlowThread* flowThread = flowThreadContainingBlock(); 4599 if (!flowThread) { 4600 LayoutUnit pageLogicalHeight = renderView->layoutState()->pageLogicalHeight(); 4601 if (!pageLogicalHeight) 4602 return 0; 4603 return cumulativeOffset - roundToInt(cumulativeOffset - firstPageLogicalTop) % roundToInt(pageLogicalHeight); 4604 } 4605 return flowThread->pageLogicalTopForOffset(cumulativeOffset); 4606 } 4607 4608 LayoutUnit RenderBlock::pageLogicalHeightForOffset(LayoutUnit offset) const 4609 { 4610 RenderView* renderView = view(); 4611 RenderFlowThread* flowThread = flowThreadContainingBlock(); 4612 if (!flowThread) 4613 return renderView->layoutState()->pageLogicalHeight(); 4614 return flowThread->pageLogicalHeightForOffset(offset + offsetFromLogicalTopOfFirstPage()); 4615 } 4616 4617 LayoutUnit RenderBlock::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) const 4618 { 4619 RenderView* renderView = view(); 4620 offset += offsetFromLogicalTopOfFirstPage(); 4621 4622 RenderFlowThread* flowThread = flowThreadContainingBlock(); 4623 if (!flowThread) { 4624 LayoutUnit pageLogicalHeight = renderView->layoutState()->pageLogicalHeight(); 4625 LayoutUnit remainingHeight = pageLogicalHeight - intMod(offset, pageLogicalHeight); 4626 if (pageBoundaryRule == IncludePageBoundary) { 4627 // If includeBoundaryPoint is true the line exactly on the top edge of a 4628 // column will act as being part of the previous column. 4629 remainingHeight = intMod(remainingHeight, pageLogicalHeight); 4630 } 4631 return remainingHeight; 4632 } 4633 4634 return flowThread->pageRemainingLogicalHeightForOffset(offset, pageBoundaryRule); 4635 } 4636 4637 LayoutUnit RenderBlock::adjustForUnsplittableChild(RenderBox* child, LayoutUnit logicalOffset, bool includeMargins) 4638 { 4639 bool checkColumnBreaks = view()->layoutState()->isPaginatingColumns() || flowThreadContainingBlock(); 4640 bool checkPageBreaks = !checkColumnBreaks && view()->layoutState()->pageLogicalHeight(); 4641 bool isUnsplittable = child->isUnsplittableForPagination() || (checkColumnBreaks && child->style()->columnBreakInside() == PBAVOID) 4642 || (checkPageBreaks && child->style()->pageBreakInside() == PBAVOID); 4643 if (!isUnsplittable) 4644 return logicalOffset; 4645 LayoutUnit childLogicalHeight = logicalHeightForChild(child) + (includeMargins ? marginBeforeForChild(child) + marginAfterForChild(child) : LayoutUnit()); 4646 LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); 4647 updateMinimumPageHeight(logicalOffset, childLogicalHeight); 4648 if (!pageLogicalHeight || childLogicalHeight > pageLogicalHeight) 4649 return logicalOffset; 4650 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary); 4651 if (remainingLogicalHeight < childLogicalHeight) 4652 return logicalOffset + remainingLogicalHeight; 4653 return logicalOffset; 4654 } 4655 4656 void RenderBlock::setPageBreak(LayoutUnit offset, LayoutUnit spaceShortage) 4657 { 4658 if (RenderFlowThread* flowThread = flowThreadContainingBlock()) 4659 flowThread->setPageBreak(offsetFromLogicalTopOfFirstPage() + offset, spaceShortage); 4660 } 4661 4662 void RenderBlock::updateMinimumPageHeight(LayoutUnit offset, LayoutUnit minHeight) 4663 { 4664 if (RenderFlowThread* flowThread = flowThreadContainingBlock()) 4665 flowThread->updateMinimumPageHeight(offsetFromLogicalTopOfFirstPage() + offset, minHeight); 4666 else if (ColumnInfo* colInfo = view()->layoutState()->columnInfo()) 4667 colInfo->updateMinimumColumnHeight(minHeight); 4668 } 4669 4670 static inline LayoutUnit calculateMinimumPageHeight(RenderStyle* renderStyle, RootInlineBox* lastLine, LayoutUnit lineTop, LayoutUnit lineBottom) 4671 { 4672 // We may require a certain minimum number of lines per page in order to satisfy 4673 // orphans and widows, and that may affect the minimum page height. 4674 unsigned lineCount = max<unsigned>(renderStyle->hasAutoOrphans() ? 1 : renderStyle->orphans(), renderStyle->hasAutoWidows() ? 1 : renderStyle->widows()); 4675 if (lineCount > 1) { 4676 RootInlineBox* line = lastLine; 4677 for (unsigned i = 1; i < lineCount && line->prevRootBox(); i++) 4678 line = line->prevRootBox(); 4679 4680 // FIXME: Paginating using line overflow isn't all fine. See FIXME in 4681 // adjustLinePositionForPagination() for more details. 4682 LayoutRect overflow = line->logicalVisualOverflowRect(line->lineTop(), line->lineBottom()); 4683 lineTop = min(line->lineTopWithLeading(), overflow.y()); 4684 } 4685 return lineBottom - lineTop; 4686 } 4687 4688 void RenderBlock::adjustLinePositionForPagination(RootInlineBox* lineBox, LayoutUnit& delta, RenderFlowThread* flowThread) 4689 { 4690 // FIXME: For now we paginate using line overflow. This ensures that lines don't overlap at all when we 4691 // put a strut between them for pagination purposes. However, this really isn't the desired rendering, since 4692 // the line on the top of the next page will appear too far down relative to the same kind of line at the top 4693 // of the first column. 4694 // 4695 // The rendering we would like to see is one where the lineTopWithLeading is at the top of the column, and any line overflow 4696 // simply spills out above the top of the column. This effect would match what happens at the top of the first column. 4697 // We can't achieve this rendering, however, until we stop columns from clipping to the column bounds (thus allowing 4698 // for overflow to occur), and then cache visible overflow for each column rect. 4699 // 4700 // Furthermore, the paint we have to do when a column has overflow has to be special. We need to exclude 4701 // content that paints in a previous column (and content that paints in the following column). 4702 // 4703 // For now we'll at least honor the lineTopWithLeading when paginating if it is above the logical top overflow. This will 4704 // at least make positive leading work in typical cases. 4705 // 4706 // FIXME: Another problem with simply moving lines is that the available line width may change (because of floats). 4707 // Technically if the location we move the line to has a different line width than our old position, then we need to dirty the 4708 // line and all following lines. 4709 LayoutRect logicalVisualOverflow = lineBox->logicalVisualOverflowRect(lineBox->lineTop(), lineBox->lineBottom()); 4710 LayoutUnit logicalOffset = min(lineBox->lineTopWithLeading(), logicalVisualOverflow.y()); 4711 LayoutUnit logicalBottom = max(lineBox->lineBottomWithLeading(), logicalVisualOverflow.maxY()); 4712 LayoutUnit lineHeight = logicalBottom - logicalOffset; 4713 updateMinimumPageHeight(logicalOffset, calculateMinimumPageHeight(style(), lineBox, logicalOffset, logicalBottom)); 4714 logicalOffset += delta; 4715 lineBox->setPaginationStrut(0); 4716 lineBox->setIsFirstAfterPageBreak(false); 4717 LayoutUnit pageLogicalHeight = pageLogicalHeightForOffset(logicalOffset); 4718 bool hasUniformPageLogicalHeight = !flowThread || flowThread->regionsHaveUniformLogicalHeight(); 4719 // If lineHeight is greater than pageLogicalHeight, but logicalVisualOverflow.height() still fits, we are 4720 // still going to add a strut, so that the visible overflow fits on a single page. 4721 if (!pageLogicalHeight || (hasUniformPageLogicalHeight && logicalVisualOverflow.height() > pageLogicalHeight)) 4722 // FIXME: In case the line aligns with the top of the page (or it's slightly shifted downwards) it will not be marked as the first line in the page. 4723 // From here, the fix is not straightforward because it's not easy to always determine when the current line is the first in the page. 4724 return; 4725 LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalOffset, ExcludePageBoundary); 4726 4727 int lineIndex = lineCount(lineBox); 4728 if (remainingLogicalHeight < lineHeight || (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineIndex)) { 4729 if (shouldBreakAtLineToAvoidWidow() && lineBreakToAvoidWidow() == lineIndex) { 4730 clearShouldBreakAtLineToAvoidWidow(); 4731 setDidBreakAtLineToAvoidWidow(); 4732 } 4733 if (lineHeight > pageLogicalHeight) { 4734 // Split the top margin in order to avoid splitting the visible part of the line. 4735 remainingLogicalHeight -= min(lineHeight - pageLogicalHeight, max<LayoutUnit>(0, logicalVisualOverflow.y() - lineBox->lineTopWithLeading())); 4736 } 4737 LayoutUnit totalLogicalHeight = lineHeight + max<LayoutUnit>(0, logicalOffset); 4738 LayoutUnit pageLogicalHeightAtNewOffset = hasUniformPageLogicalHeight ? pageLogicalHeight : pageLogicalHeightForOffset(logicalOffset + remainingLogicalHeight); 4739 setPageBreak(logicalOffset, lineHeight - remainingLogicalHeight); 4740 if (((lineBox == firstRootBox() && totalLogicalHeight < pageLogicalHeightAtNewOffset) || (!style()->hasAutoOrphans() && style()->orphans() >= lineIndex)) 4741 && !isOutOfFlowPositioned() && !isTableCell()) 4742 setPaginationStrut(remainingLogicalHeight + max<LayoutUnit>(0, logicalOffset)); 4743 else { 4744 delta += remainingLogicalHeight; 4745 lineBox->setPaginationStrut(remainingLogicalHeight); 4746 lineBox->setIsFirstAfterPageBreak(true); 4747 } 4748 } else if (remainingLogicalHeight == pageLogicalHeight) { 4749 // We're at the very top of a page or column. 4750 if (lineBox != firstRootBox()) 4751 lineBox->setIsFirstAfterPageBreak(true); 4752 if (lineBox != firstRootBox() || offsetFromLogicalTopOfFirstPage()) 4753 setPageBreak(logicalOffset, lineHeight); 4754 } 4755 } 4756 4757 LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const 4758 { 4759 LayoutState* layoutState = view()->layoutState(); 4760 if (layoutState && !layoutState->isPaginated()) 4761 return 0; 4762 4763 RenderFlowThread* flowThread = flowThreadContainingBlock(); 4764 if (flowThread) 4765 return flowThread->offsetFromLogicalTopOfFirstRegion(this); 4766 4767 if (layoutState) { 4768 ASSERT(layoutState->renderer() == this); 4769 4770 LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset(); 4771 return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width(); 4772 } 4773 4774 ASSERT_NOT_REACHED(); 4775 return 0; 4776 } 4777 4778 LayoutUnit RenderBlock::collapsedMarginBeforeForChild(const RenderBox* child) const 4779 { 4780 // If the child has the same directionality as we do, then we can just return its 4781 // collapsed margin. 4782 if (!child->isWritingModeRoot()) 4783 return child->collapsedMarginBefore(); 4784 4785 // The child has a different directionality. If the child is parallel, then it's just 4786 // flipped relative to us. We can use the collapsed margin for the opposite edge. 4787 if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) 4788 return child->collapsedMarginAfter(); 4789 4790 // The child is perpendicular to us, which means its margins don't collapse but are on the 4791 // "logical left/right" sides of the child box. We can just return the raw margin in this case. 4792 return marginBeforeForChild(child); 4793 } 4794 4795 LayoutUnit RenderBlock::collapsedMarginAfterForChild(const RenderBox* child) const 4796 { 4797 // If the child has the same directionality as we do, then we can just return its 4798 // collapsed margin. 4799 if (!child->isWritingModeRoot()) 4800 return child->collapsedMarginAfter(); 4801 4802 // The child has a different directionality. If the child is parallel, then it's just 4803 // flipped relative to us. We can use the collapsed margin for the opposite edge. 4804 if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) 4805 return child->collapsedMarginBefore(); 4806 4807 // The child is perpendicular to us, which means its margins don't collapse but are on the 4808 // "logical left/right" side of the child box. We can just return the raw margin in this case. 4809 return marginAfterForChild(child); 4810 } 4811 4812 bool RenderBlock::hasMarginBeforeQuirk(const RenderBox* child) const 4813 { 4814 // If the child has the same directionality as we do, then we can just return its 4815 // margin quirk. 4816 if (!child->isWritingModeRoot()) 4817 return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk(); 4818 4819 // The child has a different directionality. If the child is parallel, then it's just 4820 // flipped relative to us. We can use the opposite edge. 4821 if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) 4822 return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk(); 4823 4824 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about 4825 // whether or not authors specified quirky ems, since they're an implementation detail. 4826 return false; 4827 } 4828 4829 bool RenderBlock::hasMarginAfterQuirk(const RenderBox* child) const 4830 { 4831 // If the child has the same directionality as we do, then we can just return its 4832 // margin quirk. 4833 if (!child->isWritingModeRoot()) 4834 return child->isRenderBlock() ? toRenderBlock(child)->hasMarginAfterQuirk() : child->style()->hasMarginAfterQuirk(); 4835 4836 // The child has a different directionality. If the child is parallel, then it's just 4837 // flipped relative to us. We can use the opposite edge. 4838 if (child->isHorizontalWritingMode() == isHorizontalWritingMode()) 4839 return child->isRenderBlock() ? toRenderBlock(child)->hasMarginBeforeQuirk() : child->style()->hasMarginBeforeQuirk(); 4840 4841 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about 4842 // whether or not authors specified quirky ems, since they're an implementation detail. 4843 return false; 4844 } 4845 4846 const char* RenderBlock::renderName() const 4847 { 4848 if (isBody()) 4849 return "RenderBody"; // FIXME: Temporary hack until we know that the regression tests pass. 4850 4851 if (isFloating()) 4852 return "RenderBlock (floating)"; 4853 if (isOutOfFlowPositioned()) 4854 return "RenderBlock (positioned)"; 4855 if (isAnonymousColumnsBlock()) 4856 return "RenderBlock (anonymous multi-column)"; 4857 if (isAnonymousColumnSpanBlock()) 4858 return "RenderBlock (anonymous multi-column span)"; 4859 if (isAnonymousBlock()) 4860 return "RenderBlock (anonymous)"; 4861 // FIXME: Temporary hack while the new generated content system is being implemented. 4862 if (isPseudoElement()) 4863 return "RenderBlock (generated)"; 4864 if (isAnonymous()) 4865 return "RenderBlock (generated)"; 4866 if (isRelPositioned()) 4867 return "RenderBlock (relative positioned)"; 4868 if (isStickyPositioned()) 4869 return "RenderBlock (sticky positioned)"; 4870 return "RenderBlock"; 4871 } 4872 4873 RenderBlock* RenderBlock::createAnonymousWithParentRendererAndDisplay(const RenderObject* parent, EDisplay display) 4874 { 4875 // FIXME: Do we need to convert all our inline displays to block-type in the anonymous logic ? 4876 EDisplay newDisplay; 4877 RenderBlock* newBox = 0; 4878 if (display == BOX || display == INLINE_BOX) { 4879 // FIXME: Remove this case once we have eliminated all internal users of old flexbox 4880 newBox = RenderDeprecatedFlexibleBox::createAnonymous(&parent->document()); 4881 newDisplay = BOX; 4882 } else if (display == FLEX || display == INLINE_FLEX) { 4883 newBox = RenderFlexibleBox::createAnonymous(&parent->document()); 4884 newDisplay = FLEX; 4885 } else { 4886 newBox = RenderBlockFlow::createAnonymous(&parent->document()); 4887 newDisplay = BLOCK; 4888 } 4889 4890 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), newDisplay); 4891 newBox->setStyle(newStyle.release()); 4892 return newBox; 4893 } 4894 4895 RenderBlockFlow* RenderBlock::createAnonymousColumnsWithParentRenderer(const RenderObject* parent) 4896 { 4897 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK); 4898 newStyle->inheritColumnPropertiesFrom(parent->style()); 4899 4900 RenderBlockFlow* newBox = RenderBlockFlow::createAnonymous(&parent->document()); 4901 newBox->setStyle(newStyle.release()); 4902 return newBox; 4903 } 4904 4905 RenderBlockFlow* RenderBlock::createAnonymousColumnSpanWithParentRenderer(const RenderObject* parent) 4906 { 4907 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), BLOCK); 4908 newStyle->setColumnSpan(ColumnSpanAll); 4909 4910 RenderBlockFlow* newBox = RenderBlockFlow::createAnonymous(&parent->document()); 4911 newBox->setStyle(newStyle.release()); 4912 return newBox; 4913 } 4914 4915 static bool recalcNormalFlowChildOverflowIfNeeded(RenderObject* renderer) 4916 { 4917 if (renderer->isOutOfFlowPositioned() || !renderer->needsOverflowRecalcAfterStyleChange()) 4918 return false; 4919 4920 ASSERT(renderer->isRenderBlock()); 4921 return toRenderBlock(renderer)->recalcOverflowAfterStyleChange(); 4922 } 4923 4924 bool RenderBlock::recalcChildOverflowAfterStyleChange() 4925 { 4926 ASSERT(childNeedsOverflowRecalcAfterStyleChange()); 4927 setChildNeedsOverflowRecalcAfterStyleChange(false); 4928 4929 bool childrenOverflowChanged = false; 4930 4931 if (childrenInline()) { 4932 ListHashSet<RootInlineBox*> lineBoxes; 4933 for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) { 4934 RenderObject* renderer = walker.current(); 4935 if (recalcNormalFlowChildOverflowIfNeeded(renderer)) { 4936 childrenOverflowChanged = true; 4937 if (InlineBox* inlineBoxWrapper = toRenderBlock(renderer)->inlineBoxWrapper()) 4938 lineBoxes.add(&inlineBoxWrapper->root()); 4939 } 4940 } 4941 4942 // FIXME: Glyph overflow will get lost in this case, but not really a big deal. 4943 GlyphOverflowAndFallbackFontsMap textBoxDataMap; 4944 for (ListHashSet<RootInlineBox*>::const_iterator it = lineBoxes.begin(); it != lineBoxes.end(); ++it) { 4945 RootInlineBox* box = *it; 4946 box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap); 4947 } 4948 } else { 4949 for (RenderBox* box = firstChildBox(); box; box = box->nextSiblingBox()) { 4950 if (recalcNormalFlowChildOverflowIfNeeded(box)) 4951 childrenOverflowChanged = true; 4952 } 4953 } 4954 4955 TrackedRendererListHashSet* positionedDescendants = positionedObjects(); 4956 if (!positionedDescendants) 4957 return childrenOverflowChanged; 4958 4959 TrackedRendererListHashSet::iterator end = positionedDescendants->end(); 4960 for (TrackedRendererListHashSet::iterator it = positionedDescendants->begin(); it != end; ++it) { 4961 RenderBox* box = *it; 4962 4963 if (!box->needsOverflowRecalcAfterStyleChange()) 4964 continue; 4965 RenderBlock* block = toRenderBlock(box); 4966 if (!block->recalcOverflowAfterStyleChange() || box->style()->position() == FixedPosition) 4967 continue; 4968 4969 childrenOverflowChanged = true; 4970 } 4971 return childrenOverflowChanged; 4972 } 4973 4974 bool RenderBlock::recalcOverflowAfterStyleChange() 4975 { 4976 ASSERT(needsOverflowRecalcAfterStyleChange()); 4977 4978 bool childrenOverflowChanged = false; 4979 if (childNeedsOverflowRecalcAfterStyleChange()) 4980 childrenOverflowChanged = recalcChildOverflowAfterStyleChange(); 4981 4982 if (!selfNeedsOverflowRecalcAfterStyleChange() && !childrenOverflowChanged) 4983 return false; 4984 4985 setSelfNeedsOverflowRecalcAfterStyleChange(false); 4986 // If the current block needs layout, overflow will be recalculated during 4987 // layout time anyway. We can safely exit here. 4988 if (needsLayout()) 4989 return false; 4990 4991 LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom(); 4992 computeOverflow(oldClientAfterEdge, true); 4993 4994 if (hasOverflowClip()) 4995 layer()->scrollableArea()->updateAfterOverflowRecalc(); 4996 4997 return !hasOverflowClip(); 4998 } 4999 5000 #ifndef NDEBUG 5001 void RenderBlock::checkPositionedObjectsNeedLayout() 5002 { 5003 if (!gPositionedDescendantsMap) 5004 return; 5005 5006 if (TrackedRendererListHashSet* positionedDescendantSet = positionedObjects()) { 5007 TrackedRendererListHashSet::const_iterator end = positionedDescendantSet->end(); 5008 for (TrackedRendererListHashSet::const_iterator it = positionedDescendantSet->begin(); it != end; ++it) { 5009 RenderBox* currBox = *it; 5010 ASSERT(!currBox->needsLayout()); 5011 } 5012 } 5013 } 5014 5015 void RenderBlock::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj) const 5016 { 5017 showRenderObject(); 5018 for (const RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) 5019 root->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, 1); 5020 } 5021 5022 #endif 5023 5024 } // namespace WebCore 5025