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