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