1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * (C) 2007 David Smith (catfish.man@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 "RenderBlock.h" 26 27#include "AXObjectCache.h" 28#include "Document.h" 29#include "Editor.h" 30#include "Element.h" 31#include "FloatQuad.h" 32#include "Frame.h" 33#include "FrameSelection.h" 34#include "FrameView.h" 35#include "GraphicsContext.h" 36#include "HTMLInputElement.h" 37#include "HTMLNames.h" 38#include "HitTestLocation.h" 39#include "HitTestResult.h" 40#include "InlineElementBox.h" 41#include "InlineIterator.h" 42#include "InlineTextBox.h" 43#include "LayoutRepainter.h" 44#include "LogicalSelectionOffsetCaches.h" 45#include "OverflowEvent.h" 46#include "Page.h" 47#include "PaintInfo.h" 48#include "RenderBlockFlow.h" 49#include "RenderBoxRegionInfo.h" 50#include "RenderCombineText.h" 51#include "RenderDeprecatedFlexibleBox.h" 52#include "RenderFlexibleBox.h" 53#include "RenderInline.h" 54#include "RenderIterator.h" 55#include "RenderLayer.h" 56#include "RenderNamedFlowFragment.h" 57#include "RenderNamedFlowThread.h" 58#include "RenderRegion.h" 59#include "RenderTableCell.h" 60#include "RenderTextFragment.h" 61#include "RenderTheme.h" 62#include "RenderView.h" 63#include "SVGTextRunRenderingContext.h" 64#include "Settings.h" 65#include "ShadowRoot.h" 66#include "TransformState.h" 67#include <wtf/NeverDestroyed.h> 68#include <wtf/StackStats.h> 69#include <wtf/TemporaryChange.h> 70 71#if ENABLE(CSS_SHAPES) 72#include "ShapeOutsideInfo.h" 73#endif 74 75using namespace WTF; 76using namespace Unicode; 77 78namespace WebCore { 79 80using namespace HTMLNames; 81 82struct SameSizeAsRenderBlock : public RenderBox { 83 uint32_t bitfields; 84}; 85 86COMPILE_ASSERT(sizeof(RenderBlock) == sizeof(SameSizeAsRenderBlock), RenderBlock_should_stay_small); 87 88static TrackedDescendantsMap* gPositionedDescendantsMap = 0; 89static TrackedDescendantsMap* gPercentHeightDescendantsMap = 0; 90 91static TrackedContainerMap* gPositionedContainerMap = 0; 92static TrackedContainerMap* gPercentHeightContainerMap = 0; 93 94typedef HashMap<RenderBlock*, std::unique_ptr<ListHashSet<RenderInline*>>> ContinuationOutlineTableMap; 95 96struct UpdateScrollInfoAfterLayoutTransaction { 97 UpdateScrollInfoAfterLayoutTransaction(const RenderView& view) 98 : nestedCount(0) 99 , view(&view) 100 { 101 } 102 103 int nestedCount; 104 const RenderView* view; 105 HashSet<RenderBlock*> blocks; 106}; 107 108typedef Vector<UpdateScrollInfoAfterLayoutTransaction> DelayedUpdateScrollInfoStack; 109static std::unique_ptr<DelayedUpdateScrollInfoStack>& updateScrollInfoAfterLayoutTransactionStack() 110{ 111 static NeverDestroyed<std::unique_ptr<DelayedUpdateScrollInfoStack>> delayedUpdatedScrollInfoStack; 112 return delayedUpdatedScrollInfoStack; 113} 114 115// Allocated only when some of these fields have non-default values 116 117struct RenderBlockRareData { 118 WTF_MAKE_NONCOPYABLE(RenderBlockRareData); WTF_MAKE_FAST_ALLOCATED; 119public: 120 RenderBlockRareData() 121 : m_paginationStrut(0) 122 , m_pageLogicalOffset(0) 123 { 124 } 125 126 LayoutUnit m_paginationStrut; 127 LayoutUnit m_pageLogicalOffset; 128}; 129 130typedef HashMap<const RenderBlock*, std::unique_ptr<RenderBlockRareData>> RenderBlockRareDataMap; 131static RenderBlockRareDataMap* gRareDataMap = 0; 132 133// This class helps dispatching the 'overflow' event on layout change. overflow can be set on RenderBoxes, yet the existing code 134// only works on RenderBlocks. If this change, this class should be shared with other RenderBoxes. 135class OverflowEventDispatcher { 136 WTF_MAKE_NONCOPYABLE(OverflowEventDispatcher); 137public: 138 OverflowEventDispatcher(const RenderBlock* block) 139 : m_block(block) 140 , m_hadHorizontalLayoutOverflow(false) 141 , m_hadVerticalLayoutOverflow(false) 142 { 143 m_shouldDispatchEvent = !m_block->isAnonymous() && m_block->hasOverflowClip() && m_block->document().hasListenerType(Document::OVERFLOWCHANGED_LISTENER); 144 if (m_shouldDispatchEvent) { 145 m_hadHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow(); 146 m_hadVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow(); 147 } 148 } 149 150 ~OverflowEventDispatcher() 151 { 152 if (!m_shouldDispatchEvent) 153 return; 154 155 bool hasHorizontalLayoutOverflow = m_block->hasHorizontalLayoutOverflow(); 156 bool hasVerticalLayoutOverflow = m_block->hasVerticalLayoutOverflow(); 157 158 bool horizontalLayoutOverflowChanged = hasHorizontalLayoutOverflow != m_hadHorizontalLayoutOverflow; 159 bool verticalLayoutOverflowChanged = hasVerticalLayoutOverflow != m_hadVerticalLayoutOverflow; 160 if (!horizontalLayoutOverflowChanged && !verticalLayoutOverflowChanged) 161 return; 162 163 RefPtr<OverflowEvent> overflowEvent = OverflowEvent::create(horizontalLayoutOverflowChanged, hasHorizontalLayoutOverflow, verticalLayoutOverflowChanged, hasVerticalLayoutOverflow); 164 overflowEvent->setTarget(m_block->element()); 165 m_block->document().enqueueOverflowEvent(overflowEvent.release()); 166 } 167 168private: 169 const RenderBlock* m_block; 170 bool m_shouldDispatchEvent; 171 bool m_hadHorizontalLayoutOverflow; 172 bool m_hadVerticalLayoutOverflow; 173}; 174 175RenderBlock::RenderBlock(Element& element, PassRef<RenderStyle> style, unsigned baseTypeFlags) 176 : RenderBox(element, WTF::move(style), baseTypeFlags | RenderBlockFlag) 177 , m_lineHeight(-1) 178 , m_hasMarginBeforeQuirk(false) 179 , m_hasMarginAfterQuirk(false) 180 , m_beingDestroyed(false) 181 , m_hasMarkupTruncation(false) 182 , m_hasBorderOrPaddingLogicalWidthChanged(false) 183 , m_lineLayoutPath(UndeterminedPath) 184{ 185} 186 187RenderBlock::RenderBlock(Document& document, PassRef<RenderStyle> style, unsigned baseTypeFlags) 188 : RenderBox(document, WTF::move(style), baseTypeFlags | RenderBlockFlag) 189 , m_lineHeight(-1) 190 , m_hasMarginBeforeQuirk(false) 191 , m_hasMarginAfterQuirk(false) 192 , m_beingDestroyed(false) 193 , m_hasMarkupTruncation(false) 194 , m_hasBorderOrPaddingLogicalWidthChanged(false) 195 , m_lineLayoutPath(UndeterminedPath) 196{ 197} 198 199static void removeBlockFromDescendantAndContainerMaps(RenderBlock* block, TrackedDescendantsMap*& descendantMap, TrackedContainerMap*& containerMap) 200{ 201 if (std::unique_ptr<TrackedRendererListHashSet> descendantSet = descendantMap->take(block)) { 202 TrackedRendererListHashSet::iterator end = descendantSet->end(); 203 for (TrackedRendererListHashSet::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { 204 TrackedContainerMap::iterator it = containerMap->find(*descendant); 205 ASSERT(it != containerMap->end()); 206 if (it == containerMap->end()) 207 continue; 208 HashSet<RenderBlock*>* containerSet = it->value.get(); 209 ASSERT(containerSet->contains(block)); 210 containerSet->remove(block); 211 if (containerSet->isEmpty()) 212 containerMap->remove(it); 213 } 214 } 215} 216 217RenderBlock::~RenderBlock() 218{ 219 if (gRareDataMap) 220 gRareDataMap->remove(this); 221 if (gPercentHeightDescendantsMap) 222 removeBlockFromDescendantAndContainerMaps(this, gPercentHeightDescendantsMap, gPercentHeightContainerMap); 223 if (gPositionedDescendantsMap) 224 removeBlockFromDescendantAndContainerMaps(this, gPositionedDescendantsMap, gPositionedContainerMap); 225} 226 227bool RenderBlock::hasRareData() const 228{ 229 return gRareDataMap ? gRareDataMap->contains(this) : false; 230} 231 232void RenderBlock::willBeDestroyed() 233{ 234 // Mark as being destroyed to avoid trouble with merges in removeChild(). 235 m_beingDestroyed = true; 236 237 // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will 238 // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. 239 destroyLeftoverChildren(); 240 241 // Destroy our continuation before anything other than anonymous children. 242 // The reason we don't destroy it before anonymous children is that they may 243 // have continuations of their own that are anonymous children of our continuation. 244 RenderBoxModelObject* continuation = this->continuation(); 245 if (continuation) { 246 continuation->destroy(); 247 setContinuation(0); 248 } 249 250 if (!documentBeingDestroyed()) { 251 if (parent()) 252 parent()->dirtyLinesFromChangedChild(this); 253 } 254 255 removeFromUpdateScrollInfoAfterLayoutTransaction(); 256 257 RenderBox::willBeDestroyed(); 258} 259 260void RenderBlock::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) 261{ 262 const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr; 263 264 setReplaced(newStyle.isDisplayInlineType()); 265 266 if (oldStyle && parent() && diff == StyleDifferenceLayout && oldStyle->position() != newStyle.position()) { 267 if (newStyle.position() == StaticPosition) 268 // Clear our positioned objects list. Our absolutely positioned descendants will be 269 // inserted into our containing block's positioned objects list during layout. 270 removePositionedObjects(0, NewContainingBlock); 271 else if (oldStyle->position() == StaticPosition) { 272 // Remove our absolutely positioned descendants from their current containing block. 273 // They will be inserted into our positioned objects list during layout. 274 auto cb = parent(); 275 while (cb && (cb->style().position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) { 276 if (cb->style().position() == RelativePosition && cb->isInline() && !cb->isReplaced()) { 277 cb = cb->containingBlock(); 278 break; 279 } 280 cb = cb->parent(); 281 } 282 283 if (cb->isRenderBlock()) 284 toRenderBlock(cb)->removePositionedObjects(this, NewContainingBlock); 285 } 286 } 287 288 RenderBox::styleWillChange(diff, newStyle); 289} 290 291static bool borderOrPaddingLogicalWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle) 292{ 293 if (newStyle->isHorizontalWritingMode()) 294 return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth() 295 || oldStyle->borderRightWidth() != newStyle->borderRightWidth() 296 || oldStyle->paddingLeft() != newStyle->paddingLeft() 297 || oldStyle->paddingRight() != newStyle->paddingRight(); 298 299 return oldStyle->borderTopWidth() != newStyle->borderTopWidth() 300 || oldStyle->borderBottomWidth() != newStyle->borderBottomWidth() 301 || oldStyle->paddingTop() != newStyle->paddingTop() 302 || oldStyle->paddingBottom() != newStyle->paddingBottom(); 303} 304 305void RenderBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 306{ 307 RenderBox::styleDidChange(diff, oldStyle); 308 309 RenderStyle& newStyle = style(); 310 311 if (!isAnonymousBlock()) { 312 // Ensure that all of our continuation blocks pick up the new style. 313 for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) { 314 RenderBoxModelObject* nextCont = currCont->continuation(); 315 currCont->setContinuation(0); 316 currCont->setStyle(newStyle); 317 currCont->setContinuation(nextCont); 318 } 319 } 320 321 propagateStyleToAnonymousChildren(PropagateToBlockChildrenOnly); 322 m_lineHeight = -1; 323 324 // It's possible for our border/padding to change, but for the overall logical width of the block to 325 // end up being the same. We keep track of this change so in layoutBlock, we can know to set relayoutChildren=true. 326 m_hasBorderOrPaddingLogicalWidthChanged = oldStyle && diff == StyleDifferenceLayout && needsLayout() && borderOrPaddingLogicalWidthChanged(oldStyle, &newStyle); 327} 328 329RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) 330{ 331 if (beforeChild && beforeChild->parent() == this) 332 return this; 333 334 RenderBlock* curr = toRenderBlock(continuation()); 335 RenderBlock* nextToLast = this; 336 RenderBlock* last = this; 337 while (curr) { 338 if (beforeChild && beforeChild->parent() == curr) { 339 if (curr->firstChild() == beforeChild) 340 return last; 341 return curr; 342 } 343 344 nextToLast = last; 345 last = curr; 346 curr = toRenderBlock(curr->continuation()); 347 } 348 349 if (!beforeChild && !last->firstChild()) 350 return nextToLast; 351 return last; 352} 353 354void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) 355{ 356 RenderBlock* flow = continuationBefore(beforeChild); 357 ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock()); 358 RenderBoxModelObject* beforeChildParent = 0; 359 if (beforeChild) 360 beforeChildParent = toRenderBoxModelObject(beforeChild->parent()); 361 else { 362 RenderBoxModelObject* cont = flow->continuation(); 363 if (cont) 364 beforeChildParent = cont; 365 else 366 beforeChildParent = flow; 367 } 368 369 if (newChild->isFloatingOrOutOfFlowPositioned()) { 370 beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 371 return; 372 } 373 374 bool childIsNormal = newChild->isInline() || !newChild->style().columnSpan(); 375 bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style().columnSpan(); 376 bool flowIsNormal = flow->isInline() || !flow->style().columnSpan(); 377 378 if (flow == beforeChildParent) { 379 flow->addChildIgnoringContinuation(newChild, beforeChild); 380 return; 381 } 382 383 // The goal here is to match up if we can, so that we can coalesce and create the 384 // minimal # of continuations needed for the inline. 385 if (childIsNormal == bcpIsNormal) { 386 beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 387 return; 388 } 389 if (flowIsNormal == childIsNormal) { 390 flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append. 391 return; 392 } 393 beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 394} 395 396RenderPtr<RenderBlock> RenderBlock::clone() const 397{ 398 RenderPtr<RenderBlock> cloneBlock; 399 if (isAnonymousBlock()) { 400 cloneBlock = RenderPtr<RenderBlock>(createAnonymousBlock()); 401 cloneBlock->setChildrenInline(childrenInline()); 402 } else { 403 cloneBlock = static_pointer_cast<RenderBlock>(element()->createElementRenderer(style())); 404 cloneBlock->initializeStyle(); 405 406 // This takes care of setting the right value of childrenInline in case 407 // generated content is added to cloneBlock and 'this' does not have 408 // generated content added yet. 409 cloneBlock->setChildrenInline(cloneBlock->firstChild() ? cloneBlock->firstChild()->isInline() : childrenInline()); 410 } 411 cloneBlock->setFlowThreadState(flowThreadState()); 412 return cloneBlock; 413} 414 415void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, 416 RenderBlock* middleBlock, 417 RenderObject* beforeChild, RenderBoxModelObject* oldCont) 418{ 419 // Create a clone of this inline. 420 RenderPtr<RenderBlock> cloneBlock = clone(); 421 if (!isAnonymousBlock()) 422 cloneBlock->setContinuation(oldCont); 423 424 if (!beforeChild && isAfterContent(lastChild())) 425 beforeChild = lastChild(); 426 427 // If we are moving inline children from |this| to cloneBlock, then we need 428 // to clear our line box tree. 429 if (beforeChild && childrenInline()) 430 deleteLines(); 431 432 // Now take all of the children from beforeChild to the end and remove 433 // them from |this| and place them in the clone. 434 moveChildrenTo(cloneBlock.get(), beforeChild, 0, true); 435 436 // Hook |clone| up as the continuation of the middle block. 437 if (!cloneBlock->isAnonymousBlock()) 438 middleBlock->setContinuation(cloneBlock.get()); 439 440 // We have been reparented and are now under the fromBlock. We need 441 // to walk up our block parent chain until we hit the containing anonymous columns block. 442 // Once we hit the anonymous columns block we're done. 443 RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); 444 RenderBoxModelObject* currChild = this; 445 RenderObject* currChildNextSibling = currChild->nextSibling(); 446 447 while (curr && curr->isDescendantOf(fromBlock) && curr != fromBlock) { 448 RenderBlock* blockCurr = toRenderBlock(curr); 449 450 // Create a new clone. 451 RenderPtr<RenderBlock> cloneChild = WTF::move(cloneBlock); 452 cloneBlock = blockCurr->clone(); 453 454 // Insert our child clone as the first child. 455 cloneBlock->addChildIgnoringContinuation(cloneChild.leakPtr(), 0); 456 457 // Hook the clone up as a continuation of |curr|. Note we do encounter 458 // anonymous blocks possibly as we walk up the block chain. When we split an 459 // anonymous block, there's no need to do any continuation hookup, since we haven't 460 // actually split a real element. 461 if (!blockCurr->isAnonymousBlock()) { 462 oldCont = blockCurr->continuation(); 463 blockCurr->setContinuation(cloneBlock.get()); 464 cloneBlock->setContinuation(oldCont); 465 } 466 467 // Now we need to take all of the children starting from the first child 468 // *after* currChild and append them all to the clone. 469 blockCurr->moveChildrenTo(cloneBlock.get(), currChildNextSibling, 0, true); 470 471 // Keep walking up the chain. 472 currChild = curr; 473 currChildNextSibling = currChild->nextSibling(); 474 curr = toRenderBoxModelObject(curr->parent()); 475 } 476 477 // Now we are at the columns block level. We need to put the clone into the toBlock. 478 toBlock->insertChildInternal(cloneBlock.leakPtr(), nullptr, NotifyChildren); 479 480 // Now take all the children after currChild and remove them from the fromBlock 481 // and put them in the toBlock. 482 if (currChildNextSibling && currChildNextSibling->parent() == fromBlock) 483 fromBlock->moveChildrenTo(toBlock, currChildNextSibling, 0, true); 484} 485 486void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) 487{ 488 if (continuation() && !isAnonymousBlock()) 489 addChildToContinuation(newChild, beforeChild); 490 else 491 addChildIgnoringContinuation(newChild, beforeChild); 492} 493 494void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) 495{ 496 if (beforeChild && beforeChild->parent() != this) { 497 RenderElement* beforeChildContainer = beforeChild->parent(); 498 while (beforeChildContainer->parent() != this) 499 beforeChildContainer = beforeChildContainer->parent(); 500 ASSERT(beforeChildContainer); 501 502 if (beforeChildContainer->isAnonymous()) { 503 // If the requested beforeChild is not one of our children, then this is because 504 // there is an anonymous container within this object that contains the beforeChild. 505 RenderElement* beforeChildAnonymousContainer = beforeChildContainer; 506 if (beforeChildAnonymousContainer->isAnonymousBlock() 507#if ENABLE(FULLSCREEN_API) 508 // Full screen renderers and full screen placeholders act as anonymous blocks, not tables: 509 || beforeChildAnonymousContainer->isRenderFullScreen() 510 || beforeChildAnonymousContainer->isRenderFullScreenPlaceholder() 511#endif 512 ) { 513 // Insert the child into the anonymous block box instead of here. 514 if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild) 515 beforeChild->parent()->addChild(newChild, beforeChild); 516 else 517 addChild(newChild, beforeChild->parent()); 518 return; 519 } 520 521 ASSERT(beforeChildAnonymousContainer->isTable()); 522 if (newChild->isTablePart()) { 523 // Insert into the anonymous table. 524 beforeChildAnonymousContainer->addChild(newChild, beforeChild); 525 return; 526 } 527 528 beforeChild = splitAnonymousBoxesAroundChild(beforeChild); 529 530 ASSERT(beforeChild->parent() == this); 531 if (beforeChild->parent() != this) { 532 // We should never reach here. If we do, we need to use the 533 // safe fallback to use the topmost beforeChild container. 534 beforeChild = beforeChildContainer; 535 } 536 } 537 } 538 539 bool madeBoxesNonInline = false; 540 541 // A block has to either have all of its children inline, or all of its children as blocks. 542 // So, if our children are currently inline and a block child has to be inserted, we move all our 543 // inline children into anonymous block boxes. 544 if (childrenInline() && !newChild->isInline() && !newChild->isFloatingOrOutOfFlowPositioned()) { 545 // This is a block with inline content. Wrap the inline content in anonymous blocks. 546 makeChildrenNonInline(beforeChild); 547 madeBoxesNonInline = true; 548 549 if (beforeChild && beforeChild->parent() != this) { 550 beforeChild = beforeChild->parent(); 551 ASSERT(beforeChild->isAnonymousBlock()); 552 ASSERT(beforeChild->parent() == this); 553 } 554 } else if (!childrenInline() && (newChild->isFloatingOrOutOfFlowPositioned() || newChild->isInline())) { 555 // If we're inserting an inline child but all of our children are blocks, then we have to make sure 556 // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise 557 // a new one is created and inserted into our list of children in the appropriate position. 558 RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild(); 559 560 if (afterChild && afterChild->isAnonymousBlock()) { 561 toRenderBlock(afterChild)->addChild(newChild); 562 return; 563 } 564 565 if (newChild->isInline()) { 566 // No suitable existing anonymous box - create a new one. 567 RenderBlock* newBox = createAnonymousBlock(); 568 RenderBox::addChild(newBox, beforeChild); 569 newBox->addChild(newChild); 570 return; 571 } 572 } 573 574 invalidateLineLayoutPath(); 575 576 RenderBox::addChild(newChild, beforeChild); 577 578 if (madeBoxesNonInline && parent() && isAnonymousBlock() && parent()->isRenderBlock()) 579 toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); 580 // this object may be dead here 581} 582 583static void getInlineRun(RenderObject* start, RenderObject* boundary, 584 RenderObject*& inlineRunStart, 585 RenderObject*& inlineRunEnd) 586{ 587 // Beginning at |start| we find the largest contiguous run of inlines that 588 // we can. We denote the run with start and end points, |inlineRunStart| 589 // and |inlineRunEnd|. Note that these two values may be the same if 590 // we encounter only one inline. 591 // 592 // We skip any non-inlines we encounter as long as we haven't found any 593 // inlines yet. 594 // 595 // |boundary| indicates a non-inclusive boundary point. Regardless of whether |boundary| 596 // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered 597 // a non-inline. 598 599 // Start by skipping as many non-inlines as we can. 600 RenderObject * curr = start; 601 bool sawInline; 602 do { 603 while (curr && !(curr->isInline() || curr->isFloatingOrOutOfFlowPositioned())) 604 curr = curr->nextSibling(); 605 606 inlineRunStart = inlineRunEnd = curr; 607 608 if (!curr) 609 return; // No more inline children to be found. 610 611 sawInline = curr->isInline(); 612 613 curr = curr->nextSibling(); 614 while (curr && (curr->isInline() || curr->isFloatingOrOutOfFlowPositioned()) && (curr != boundary)) { 615 inlineRunEnd = curr; 616 if (curr->isInline()) 617 sawInline = true; 618 curr = curr->nextSibling(); 619 } 620 } while (!sawInline); 621} 622 623void RenderBlock::deleteLines() 624{ 625 if (AXObjectCache* cache = document().existingAXObjectCache()) 626 cache->recomputeIsIgnored(this); 627} 628 629void RenderBlock::makeChildrenNonInline(RenderObject* insertionPoint) 630{ 631 // makeChildrenNonInline takes a block whose children are *all* inline and it 632 // makes sure that inline children are coalesced under anonymous 633 // blocks. If |insertionPoint| is defined, then it represents the insertion point for 634 // the new block child that is causing us to have to wrap all the inlines. This 635 // means that we cannot coalesce inlines before |insertionPoint| with inlines following 636 // |insertionPoint|, because the new child is going to be inserted in between the inlines, 637 // splitting them. 638 ASSERT(isInlineBlockOrInlineTable() || !isInline()); 639 ASSERT(!insertionPoint || insertionPoint->parent() == this); 640 641 setChildrenInline(false); 642 643 RenderObject* child = firstChild(); 644 if (!child) 645 return; 646 647 deleteLines(); 648 649 while (child) { 650 RenderObject* inlineRunStart; 651 RenderObject* inlineRunEnd; 652 getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd); 653 654 if (!inlineRunStart) 655 break; 656 657 child = inlineRunEnd->nextSibling(); 658 659 RenderBlock* block = createAnonymousBlock(); 660 insertChildInternal(block, inlineRunStart, NotifyChildren); 661 moveChildrenTo(block, inlineRunStart, child); 662 } 663 664#ifndef NDEBUG 665 for (RenderObject* c = firstChild(); c; c = c->nextSibling()) 666 ASSERT(!c->isInline()); 667#endif 668 669 repaint(); 670} 671 672void RenderBlock::removeLeftoverAnonymousBlock(RenderBlock* child) 673{ 674 ASSERT(child->isAnonymousBlock()); 675 ASSERT(!child->childrenInline()); 676 677 if (child->continuation()) 678 return; 679 680 RenderObject* firstAnChild = child->firstChild(); 681 RenderObject* lastAnChild = child->lastChild(); 682 if (firstAnChild) { 683 RenderObject* o = firstAnChild; 684 while (o) { 685 o->setParent(this); 686 o = o->nextSibling(); 687 } 688 firstAnChild->setPreviousSibling(child->previousSibling()); 689 lastAnChild->setNextSibling(child->nextSibling()); 690 if (child->previousSibling()) 691 child->previousSibling()->setNextSibling(firstAnChild); 692 if (child->nextSibling()) 693 child->nextSibling()->setPreviousSibling(lastAnChild); 694 695 if (child == firstChild()) 696 setFirstChild(firstAnChild); 697 if (child == lastChild()) 698 setLastChild(lastAnChild); 699 } else { 700 if (child == firstChild()) 701 setFirstChild(child->nextSibling()); 702 if (child == lastChild()) 703 setLastChild(child->previousSibling()); 704 705 if (child->previousSibling()) 706 child->previousSibling()->setNextSibling(child->nextSibling()); 707 if (child->nextSibling()) 708 child->nextSibling()->setPreviousSibling(child->previousSibling()); 709 } 710 711 child->setFirstChild(0); 712 child->m_next = 0; 713 714 // Remove all the information in the flow thread associated with the leftover anonymous block. 715 child->removeFromRenderFlowThread(); 716 717 child->setParent(0); 718 child->setPreviousSibling(0); 719 child->setNextSibling(0); 720 721 child->destroy(); 722} 723 724static bool canMergeAnonymousBlock(RenderBlock* anonymousBlock) 725{ 726 if (anonymousBlock->beingDestroyed() || anonymousBlock->continuation()) 727 return false; 728 if (anonymousBlock->isRubyRun() || anonymousBlock->isRubyBase()) 729 return false; 730 return true; 731} 732 733static bool canMergeContiguousAnonymousBlocks(RenderObject& oldChild, RenderObject* previous, RenderObject* next) 734{ 735 if (oldChild.documentBeingDestroyed() || oldChild.isInline() || oldChild.virtualContinuation()) 736 return false; 737 738 if (previous) { 739 if (!previous->isAnonymousBlock()) 740 return false; 741 RenderBlock* previousAnonymousBlock = toRenderBlock(previous); 742 if (!canMergeAnonymousBlock(previousAnonymousBlock)) 743 return false; 744 } 745 if (next) { 746 if (!next->isAnonymousBlock()) 747 return false; 748 RenderBlock* nextAnonymousBlock = toRenderBlock(next); 749 if (!canMergeAnonymousBlock(nextAnonymousBlock)) 750 return false; 751 } 752 return true; 753} 754 755void RenderBlock::collapseAnonymousBoxChild(RenderBlock* parent, RenderBlock* child) 756{ 757 parent->setNeedsLayoutAndPrefWidthsRecalc(); 758 parent->setChildrenInline(child->childrenInline()); 759 RenderObject* nextSibling = child->nextSibling(); 760 761 RenderFlowThread* childFlowThread = child->flowThreadContainingBlock(); 762 CurrentRenderFlowThreadMaintainer flowThreadMaintainer(childFlowThread); 763 if (childFlowThread && childFlowThread->isRenderNamedFlowThread()) 764 toRenderNamedFlowThread(childFlowThread)->removeFlowChildInfo(child); 765 766 parent->removeChildInternal(*child, child->hasLayer() ? NotifyChildren : DontNotifyChildren); 767 child->moveAllChildrenTo(parent, nextSibling, child->hasLayer()); 768 // Delete the now-empty block's lines and nuke it. 769 child->deleteLines(); 770 child->destroy(); 771} 772 773RenderObject* RenderBlock::removeChild(RenderObject& oldChild) 774{ 775 // No need to waste time in merging or removing empty anonymous blocks. 776 // We can just bail out if our document is getting destroyed. 777 if (documentBeingDestroyed()) 778 return RenderBox::removeChild(oldChild); 779 780 // If this child is a block, and if our previous and next siblings are 781 // both anonymous blocks with inline content, then we can go ahead and 782 // fold the inline content back together. 783 RenderObject* prev = oldChild.previousSibling(); 784 RenderObject* next = oldChild.nextSibling(); 785 bool canMergeAnonymousBlocks = canMergeContiguousAnonymousBlocks(oldChild, prev, next); 786 if (canMergeAnonymousBlocks && prev && next) { 787 prev->setNeedsLayoutAndPrefWidthsRecalc(); 788 RenderBlock* nextBlock = toRenderBlock(next); 789 RenderBlock* prevBlock = toRenderBlock(prev); 790 791 if (prev->childrenInline() != next->childrenInline()) { 792 RenderBlock* inlineChildrenBlock = prev->childrenInline() ? prevBlock : nextBlock; 793 RenderBlock* blockChildrenBlock = prev->childrenInline() ? nextBlock : prevBlock; 794 795 // Place the inline children block inside of the block children block instead of deleting it. 796 // In order to reuse it, we have to reset it to just be a generic anonymous block. Make sure 797 // to clear out inherited column properties by just making a new style, and to also clear the 798 // column span flag if it is set. 799 ASSERT(!inlineChildrenBlock->continuation()); 800 // Cache this value as it might get changed in setStyle() call. 801 bool inlineChildrenBlockHasLayer = inlineChildrenBlock->hasLayer(); 802 inlineChildrenBlock->setStyle(RenderStyle::createAnonymousStyleWithDisplay(&style(), BLOCK)); 803 removeChildInternal(*inlineChildrenBlock, inlineChildrenBlockHasLayer ? NotifyChildren : DontNotifyChildren); 804 805 // Now just put the inlineChildrenBlock inside the blockChildrenBlock. 806 RenderObject* beforeChild = prev == inlineChildrenBlock ? blockChildrenBlock->firstChild() : nullptr; 807 blockChildrenBlock->insertChildInternal(inlineChildrenBlock, beforeChild, 808 (inlineChildrenBlockHasLayer || blockChildrenBlock->hasLayer()) ? NotifyChildren : DontNotifyChildren); 809 next->setNeedsLayoutAndPrefWidthsRecalc(); 810 811 // inlineChildrenBlock got reparented to blockChildrenBlock, so it is no longer a child 812 // of "this". we null out prev or next so that is not used later in the function. 813 if (inlineChildrenBlock == prevBlock) 814 prev = 0; 815 else 816 next = 0; 817 } else { 818 // Take all the children out of the |next| block and put them in 819 // the |prev| block. 820 nextBlock->moveAllChildrenIncludingFloatsTo(prevBlock, nextBlock->hasLayer() || prevBlock->hasLayer()); 821 822 // Delete the now-empty block's lines and nuke it. 823 nextBlock->deleteLines(); 824 nextBlock->destroy(); 825 next = 0; 826 } 827 } 828 829 invalidateLineLayoutPath(); 830 831 RenderObject* nextSibling = RenderBox::removeChild(oldChild); 832 833 RenderObject* child = prev ? prev : next; 834 if (canMergeAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && canCollapseAnonymousBlockChild()) { 835 // The removal has knocked us down to containing only a single anonymous 836 // box. We can go ahead and pull the content right back up into our 837 // box. 838 collapseAnonymousBoxChild(this, toRenderBlock(child)); 839 nextSibling = nullptr; 840 } else if (((prev && prev->isAnonymousBlock()) || (next && next->isAnonymousBlock())) && canCollapseAnonymousBlockChild()) { 841 // It's possible that the removal has knocked us down to a single anonymous 842 // block with pseudo-style element siblings (e.g. first-letter). If these 843 // are floating, then we need to pull the content up also. 844 RenderBlock* anonBlock = toRenderBlock((prev && prev->isAnonymousBlock()) ? prev : next); 845 if ((anonBlock->previousSibling() || anonBlock->nextSibling()) 846 && (!anonBlock->previousSibling() || (anonBlock->previousSibling()->style().styleType() != NOPSEUDO && anonBlock->previousSibling()->isFloating() && !anonBlock->previousSibling()->previousSibling())) 847 && (!anonBlock->nextSibling() || (anonBlock->nextSibling()->style().styleType() != NOPSEUDO && anonBlock->nextSibling()->isFloating() && !anonBlock->nextSibling()->nextSibling()))) { 848 collapseAnonymousBoxChild(this, anonBlock); 849 nextSibling = nullptr; 850 } 851 } 852 853 if (!firstChild()) { 854 nextSibling = nullptr; 855 856 // If this was our last child be sure to clear out our line boxes. 857 if (childrenInline()) 858 deleteLines(); 859 860 // If we are an empty anonymous block in the continuation chain, 861 // we need to remove ourself and fix the continuation chain. 862 if (!beingDestroyed() && isAnonymousBlockContinuation() && !oldChild.isListMarker()) { 863 auto containingBlockIgnoringAnonymous = containingBlock(); 864 while (containingBlockIgnoringAnonymous && containingBlockIgnoringAnonymous->isAnonymousBlock()) 865 containingBlockIgnoringAnonymous = containingBlockIgnoringAnonymous->containingBlock(); 866 for (RenderObject* curr = this; curr; curr = curr->previousInPreOrder(containingBlockIgnoringAnonymous)) { 867 if (curr->virtualContinuation() != this) 868 continue; 869 870 // Found our previous continuation. We just need to point it to 871 // |this|'s next continuation. 872 RenderBoxModelObject* nextContinuation = continuation(); 873 if (curr->isRenderInline()) 874 toRenderInline(curr)->setContinuation(nextContinuation); 875 else if (curr->isRenderBlock()) 876 toRenderBlock(curr)->setContinuation(nextContinuation); 877 else 878 ASSERT_NOT_REACHED(); 879 880 break; 881 } 882 setContinuation(0); 883 destroy(); 884 } 885 } 886 887 return nextSibling; 888} 889 890bool RenderBlock::isSelfCollapsingBlock() const 891{ 892 // We are not self-collapsing if we 893 // (a) have a non-zero height according to layout (an optimization to avoid wasting time) 894 // (b) are a table, 895 // (c) have border/padding, 896 // (d) have a min-height 897 // (e) have specified that one of our margins can't collapse using a CSS extension 898 if (logicalHeight() > 0 899 || isTable() || borderAndPaddingLogicalHeight() 900 || style().logicalMinHeight().isPositive() 901 || style().marginBeforeCollapse() == MSEPARATE || style().marginAfterCollapse() == MSEPARATE) 902 return false; 903 904 Length logicalHeightLength = style().logicalHeight(); 905 bool hasAutoHeight = logicalHeightLength.isAuto(); 906 if (logicalHeightLength.isPercent() && !document().inQuirksMode()) { 907 hasAutoHeight = true; 908 for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) { 909 if (cb->style().logicalHeight().isFixed() || cb->isTableCell()) 910 hasAutoHeight = false; 911 } 912 } 913 914 // If the height is 0 or auto, then whether or not we are a self-collapsing block depends 915 // on whether we have content that is all self-collapsing or not. 916 if (hasAutoHeight || ((logicalHeightLength.isFixed() || logicalHeightLength.isPercent()) && logicalHeightLength.isZero())) { 917 // If the block has inline children, see if we generated any line boxes. If we have any 918 // line boxes, then we can't be self-collapsing, since we have content. 919 if (childrenInline()) 920 return !hasLines(); 921 922 // Whether or not we collapse is dependent on whether all our normal flow children 923 // are also self-collapsing. 924 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { 925 if (child->isFloatingOrOutOfFlowPositioned()) 926 continue; 927 if (!child->isSelfCollapsingBlock()) 928 return false; 929 } 930 return true; 931 } 932 return false; 933} 934 935static inline UpdateScrollInfoAfterLayoutTransaction* currentUpdateScrollInfoAfterLayoutTransaction() 936{ 937 if (!updateScrollInfoAfterLayoutTransactionStack()) 938 return nullptr; 939 return &updateScrollInfoAfterLayoutTransactionStack()->last(); 940} 941 942void RenderBlock::beginUpdateScrollInfoAfterLayoutTransaction() 943{ 944 if (!updateScrollInfoAfterLayoutTransactionStack()) 945 updateScrollInfoAfterLayoutTransactionStack() = std::make_unique<DelayedUpdateScrollInfoStack>(); 946 if (updateScrollInfoAfterLayoutTransactionStack()->isEmpty() || currentUpdateScrollInfoAfterLayoutTransaction()->view != &view()) 947 updateScrollInfoAfterLayoutTransactionStack()->append(UpdateScrollInfoAfterLayoutTransaction(view())); 948 ++currentUpdateScrollInfoAfterLayoutTransaction()->nestedCount; 949} 950 951void RenderBlock::endAndCommitUpdateScrollInfoAfterLayoutTransaction() 952{ 953 UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); 954 ASSERT(transaction); 955 ASSERT(transaction->view == &view()); 956 if (--transaction->nestedCount) 957 return; 958 959 // Calling RenderLayer::updateScrollInfoAfterLayout() may cause its associated block to layout again and 960 // updates its scroll info (i.e. call RenderBlock::updateScrollInfoAfterLayout()). We remove |transaction| 961 // from the transaction stack to ensure that all subsequent calls to RenderBlock::updateScrollInfoAfterLayout() 962 // are dispatched immediately. That is, to ensure that such subsequent calls aren't added to |transaction| 963 // while we are processing it. 964 Vector<RenderBlock*> blocksToUpdate; 965 copyToVector(transaction->blocks, blocksToUpdate); 966 updateScrollInfoAfterLayoutTransactionStack()->removeLast(); 967 if (updateScrollInfoAfterLayoutTransactionStack()->isEmpty()) 968 updateScrollInfoAfterLayoutTransactionStack() = nullptr; 969 970 for (auto* block : blocksToUpdate) { 971 ASSERT(block->hasOverflowClip()); 972 block->layer()->updateScrollInfoAfterLayout(); 973 block->clearLayoutOverflow(); 974 } 975} 976 977void RenderBlock::removeFromUpdateScrollInfoAfterLayoutTransaction() 978{ 979 if (UNLIKELY(updateScrollInfoAfterLayoutTransactionStack().get() != 0)) { 980 UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); 981 ASSERT(transaction); 982 ASSERT(transaction->view == &view()); 983 transaction->blocks.remove(this); 984 } 985} 986 987void RenderBlock::updateScrollInfoAfterLayout() 988{ 989 if (hasOverflowClip()) { 990 if (style().isFlippedBlocksWritingMode()) { 991 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=97937 992 // Workaround for now. We cannot delay the scroll info for overflow 993 // for items with opposite writing directions, as the contents needs 994 // to overflow in that direction 995 layer()->updateScrollInfoAfterLayout(); 996 return; 997 } 998 999 UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); 1000 if (transaction && transaction->view == &view()) { 1001 transaction->blocks.add(this); 1002 return; 1003 } 1004 layer()->updateScrollInfoAfterLayout(); 1005 } 1006} 1007 1008void RenderBlock::layout() 1009{ 1010 StackStats::LayoutCheckPoint layoutCheckPoint; 1011 OverflowEventDispatcher dispatcher(this); 1012 1013 // Update our first letter info now. 1014 updateFirstLetter(); 1015 1016 // Table cells call layoutBlock directly, so don't add any logic here. Put code into 1017 // layoutBlock(). 1018 layoutBlock(false); 1019 1020 // It's safe to check for control clip here, since controls can never be table cells. 1021 // If we have a lightweight clip, there can never be any overflow from children. 1022 UpdateScrollInfoAfterLayoutTransaction* transaction = currentUpdateScrollInfoAfterLayoutTransaction(); 1023 bool isDelayingUpdateScrollInfoAfterLayoutInView = transaction && transaction->view == &view(); 1024 if (hasControlClip() && m_overflow && !isDelayingUpdateScrollInfoAfterLayoutInView) 1025 clearLayoutOverflow(); 1026 1027 invalidateBackgroundObscurationStatus(); 1028} 1029 1030static RenderBlockRareData* getRareData(const RenderBlock* block) 1031{ 1032 return gRareDataMap ? gRareDataMap->get(block) : 0; 1033} 1034 1035static RenderBlockRareData& ensureRareData(const RenderBlock* block) 1036{ 1037 if (!gRareDataMap) 1038 gRareDataMap = new RenderBlockRareDataMap; 1039 1040 auto& rareData = gRareDataMap->add(block, nullptr).iterator->value; 1041 if (!rareData) 1042 rareData = std::make_unique<RenderBlockRareData>(); 1043 return *rareData.get(); 1044} 1045 1046#if ENABLE(CSS_SHAPES) 1047void RenderBlock::imageChanged(WrappedImagePtr image, const IntRect*) 1048{ 1049 RenderBox::imageChanged(image); 1050 1051 if (!parent() || !everHadLayout()) 1052 return; 1053} 1054#endif 1055 1056void RenderBlock::preparePaginationBeforeBlockLayout(bool& relayoutChildren) 1057{ 1058 // Regions changing widths can force us to relayout our children. 1059 RenderFlowThread* flowThread = flowThreadContainingBlock(); 1060 if (flowThread) 1061 flowThread->logicalWidthChangedInRegionsForBlock(this, relayoutChildren); 1062} 1063 1064bool RenderBlock::recomputeLogicalWidth() 1065{ 1066 LayoutUnit oldWidth = logicalWidth(); 1067 1068 updateLogicalWidth(); 1069 1070 bool hasBorderOrPaddingLogicalWidthChanged = m_hasBorderOrPaddingLogicalWidthChanged; 1071 m_hasBorderOrPaddingLogicalWidthChanged = false; 1072 1073 return oldWidth != logicalWidth() || hasBorderOrPaddingLogicalWidthChanged; 1074} 1075 1076void RenderBlock::layoutBlock(bool, LayoutUnit) 1077{ 1078 ASSERT_NOT_REACHED(); 1079 clearNeedsLayout(); 1080} 1081 1082void RenderBlock::addOverflowFromChildren() 1083{ 1084 if (childrenInline()) 1085 addOverflowFromInlineChildren(); 1086 else 1087 addOverflowFromBlockChildren(); 1088 1089 // If this block is flowed inside a flow thread, make sure its overflow is propagated to the containing regions. 1090 if (m_overflow) { 1091 if (RenderFlowThread* containingFlowThread = flowThreadContainingBlock()) 1092 containingFlowThread->addRegionsVisualOverflow(this, m_overflow->visualOverflowRect()); 1093 } 1094} 1095 1096void RenderBlock::computeOverflow(LayoutUnit oldClientAfterEdge, bool) 1097{ 1098 clearOverflow(); 1099 1100 // Add overflow from children. 1101 addOverflowFromChildren(); 1102 1103 // Add in the overflow from positioned objects. 1104 addOverflowFromPositionedObjects(); 1105 1106 if (hasOverflowClip()) { 1107 // When we have overflow clip, propagate the original spillout since it will include collapsed bottom margins 1108 // and bottom padding. Set the axis we don't care about to be 1, since we want this overflow to always 1109 // be considered reachable. 1110 LayoutRect clientRect(flippedClientBoxRect()); 1111 LayoutRect rectToApply; 1112 if (isHorizontalWritingMode()) 1113 rectToApply = LayoutRect(clientRect.x(), clientRect.y(), 1, std::max<LayoutUnit>(0, oldClientAfterEdge - clientRect.y())); 1114 else 1115 rectToApply = LayoutRect(clientRect.x(), clientRect.y(), std::max<LayoutUnit>(0, oldClientAfterEdge - clientRect.x()), 1); 1116 addLayoutOverflow(rectToApply); 1117 if (hasRenderOverflow()) 1118 m_overflow->setLayoutClientAfterEdge(oldClientAfterEdge); 1119 } 1120 1121 // Add visual overflow from box-shadow and border-image-outset. 1122 addVisualEffectOverflow(); 1123 1124 // Add visual overflow from theme. 1125 addVisualOverflowFromTheme(); 1126} 1127 1128void RenderBlock::clearLayoutOverflow() 1129{ 1130 if (!m_overflow) 1131 return; 1132 1133 if (visualOverflowRect() == borderBoxRect()) { 1134 // FIXME: Implement complete solution for regions overflow. 1135 clearOverflow(); 1136 return; 1137 } 1138 1139 m_overflow->setLayoutOverflow(borderBoxRect()); 1140} 1141 1142void RenderBlock::addOverflowFromBlockChildren() 1143{ 1144 for (auto child = firstChildBox(); child; child = child->nextSiblingBox()) { 1145 if (!child->isFloatingOrOutOfFlowPositioned()) 1146 addOverflowFromChild(child); 1147 } 1148} 1149 1150void RenderBlock::addOverflowFromPositionedObjects() 1151{ 1152 TrackedRendererListHashSet* positionedDescendants = positionedObjects(); 1153 if (!positionedDescendants) 1154 return; 1155 1156 for (auto it = positionedDescendants->begin(), end = positionedDescendants->end(); it != end; ++it) { 1157 RenderBox* positionedObject = *it; 1158 1159 // Fixed positioned elements don't contribute to layout overflow, since they don't scroll with the content. 1160 if (positionedObject->style().position() != FixedPosition) { 1161 LayoutUnit x = positionedObject->x(); 1162 if (style().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) 1163 x -= verticalScrollbarWidth(); 1164 addOverflowFromChild(positionedObject, LayoutSize(x, positionedObject->y())); 1165 } 1166 } 1167} 1168 1169void RenderBlock::addVisualOverflowFromTheme() 1170{ 1171 if (!style().hasAppearance()) 1172 return; 1173 1174 FloatRect inflatedRect = borderBoxRect(); 1175 theme().adjustRepaintRect(*this, inflatedRect); 1176 addVisualOverflow(pixelSnappedIntRect(LayoutRect(inflatedRect))); 1177 1178 if (RenderFlowThread* flowThread = flowThreadContainingBlock()) 1179 flowThread->addRegionsVisualOverflowFromTheme(this); 1180} 1181 1182bool RenderBlock::expandsToEncloseOverhangingFloats() const 1183{ 1184 return isInlineBlockOrInlineTable() || isFloatingOrOutOfFlowPositioned() || hasOverflowClip() || (parent() && parent()->isFlexibleBoxIncludingDeprecated()) 1185 || isTableCell() || isTableCaption() || isFieldset() || isWritingModeRoot() || isRoot() || isRenderFlowThread(); 1186} 1187 1188LayoutUnit RenderBlock::computeStartPositionDeltaForChildAvoidingFloats(const RenderBox& child, LayoutUnit childMarginStart, RenderRegion* region) 1189{ 1190 LayoutUnit startPosition = startOffsetForContent(region); 1191 1192 // Add in our start margin. 1193 LayoutUnit oldPosition = startPosition + childMarginStart; 1194 LayoutUnit newPosition = oldPosition; 1195 1196 LayoutUnit blockOffset = logicalTopForChild(child); 1197 if (region) 1198 blockOffset = std::max(blockOffset, blockOffset + (region->logicalTopForFlowThreadContent() - offsetFromLogicalTopOfFirstPage())); 1199 1200 LayoutUnit startOff = startOffsetForLineInRegion(blockOffset, false, region, logicalHeightForChild(child)); 1201 1202 if (style().textAlign() != WEBKIT_CENTER && !child.style().marginStartUsing(&style()).isAuto()) { 1203 if (childMarginStart < 0) 1204 startOff += childMarginStart; 1205 newPosition = std::max(newPosition, startOff); // Let the float sit in the child's margin if it can fit. 1206 } else if (startOff != startPosition) 1207 newPosition = startOff + childMarginStart; 1208 1209 return newPosition - oldPosition; 1210} 1211 1212void RenderBlock::setLogicalLeftForChild(RenderBox& child, LayoutUnit logicalLeft, ApplyLayoutDeltaMode applyDelta) 1213{ 1214 if (isHorizontalWritingMode()) { 1215 if (applyDelta == ApplyLayoutDelta) 1216 view().addLayoutDelta(LayoutSize(child.x() - logicalLeft, 0)); 1217 child.setX(logicalLeft); 1218 } else { 1219 if (applyDelta == ApplyLayoutDelta) 1220 view().addLayoutDelta(LayoutSize(0, child.y() - logicalLeft)); 1221 child.setY(logicalLeft); 1222 } 1223} 1224 1225void RenderBlock::setLogicalTopForChild(RenderBox& child, LayoutUnit logicalTop, ApplyLayoutDeltaMode applyDelta) 1226{ 1227 if (isHorizontalWritingMode()) { 1228 if (applyDelta == ApplyLayoutDelta) 1229 view().addLayoutDelta(LayoutSize(0, child.y() - logicalTop)); 1230 child.setY(logicalTop); 1231 } else { 1232 if (applyDelta == ApplyLayoutDelta) 1233 view().addLayoutDelta(LayoutSize(child.x() - logicalTop, 0)); 1234 child.setX(logicalTop); 1235 } 1236} 1237 1238void RenderBlock::updateBlockChildDirtyBitsBeforeLayout(bool relayoutChildren, RenderBox& child) 1239{ 1240 // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into 1241 // an auto value. Add a method to determine this, so that we can avoid the relayout. 1242 if (relayoutChildren || (child.hasRelativeLogicalHeight() && !isRenderView())) 1243 child.setChildNeedsLayout(MarkOnlyThis); 1244 1245 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. 1246 if (relayoutChildren && child.needsPreferredWidthsRecalculation()) 1247 child.setPreferredLogicalWidthsDirty(true, MarkOnlyThis); 1248} 1249 1250void RenderBlock::dirtyForLayoutFromPercentageHeightDescendants() 1251{ 1252 if (!gPercentHeightDescendantsMap) 1253 return; 1254 1255 TrackedRendererListHashSet* descendants = gPercentHeightDescendantsMap->get(this); 1256 if (!descendants) 1257 return; 1258 1259 for (auto it = descendants->begin(), end = descendants->end(); it != end; ++it) { 1260 RenderBox* box = *it; 1261 while (box != this) { 1262 if (box->normalChildNeedsLayout()) 1263 break; 1264 box->setChildNeedsLayout(MarkOnlyThis); 1265 1266 // If the width of an image is affected by the height of a child (e.g., an image with an aspect ratio), 1267 // then we have to dirty preferred widths, since even enclosing blocks can become dirty as a result. 1268 // (A horizontal flexbox that contains an inline image wrapped in an anonymous block for example.) 1269 if (box->hasAspectRatio()) 1270 box->setPreferredLogicalWidthsDirty(true); 1271 1272 box = box->containingBlock(); 1273 ASSERT(box); 1274 if (!box) 1275 break; 1276 } 1277 } 1278} 1279 1280void RenderBlock::simplifiedNormalFlowLayout() 1281{ 1282 if (childrenInline()) { 1283 ListHashSet<RootInlineBox*> lineBoxes; 1284 for (InlineWalker walker(*this); !walker.atEnd(); walker.advance()) { 1285 RenderObject* o = walker.current(); 1286 if (!o->isOutOfFlowPositioned() && (o->isReplaced() || o->isFloating())) { 1287 RenderBox& box = toRenderBox(*o); 1288 box.layoutIfNeeded(); 1289 if (box.inlineBoxWrapper()) 1290 lineBoxes.add(&box.inlineBoxWrapper()->root()); 1291 } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) 1292 o->clearNeedsLayout(); 1293 } 1294 1295 // FIXME: Glyph overflow will get lost in this case, but not really a big deal. 1296 // FIXME: Find a way to invalidate the knownToHaveNoOverflow flag on the InlineBoxes. 1297 GlyphOverflowAndFallbackFontsMap textBoxDataMap; 1298 for (auto it = lineBoxes.begin(), end = lineBoxes.end(); it != end; ++it) { 1299 RootInlineBox* box = *it; 1300 box->computeOverflow(box->lineTop(), box->lineBottom(), textBoxDataMap); 1301 } 1302 } else { 1303 for (auto box = firstChildBox(); box; box = box->nextSiblingBox()) { 1304 if (!box->isOutOfFlowPositioned()) 1305 box->layoutIfNeeded(); 1306 } 1307 } 1308} 1309 1310bool RenderBlock::simplifiedLayout() 1311{ 1312 if ((!posChildNeedsLayout() && !needsSimplifiedNormalFlowLayout()) || normalChildNeedsLayout() || selfNeedsLayout()) 1313 return false; 1314 1315 LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode()); 1316 1317 if (needsPositionedMovementLayout() && !tryLayoutDoingPositionedMovementOnly()) 1318 return false; 1319 1320 // Lay out positioned descendants or objects that just need to recompute overflow. 1321 if (needsSimplifiedNormalFlowLayout()) 1322 simplifiedNormalFlowLayout(); 1323 1324 // Make sure a forced break is applied after the content if we are a flow thread in a simplified layout. 1325 // This ensures the size information is correctly computed for the last auto-height region receiving content. 1326 if (isRenderFlowThread()) 1327 toRenderFlowThread(this)->applyBreakAfterContent(clientLogicalBottom()); 1328 1329 // Lay out our positioned objects if our positioned child bit is set. 1330 // Also, if an absolute position element inside a relative positioned container moves, and the absolute element has a fixed position 1331 // child, neither the fixed element nor its container learn of the movement since posChildNeedsLayout() is only marked as far as the 1332 // relative positioned container. So if we can have fixed pos objects in our positioned objects list check if any of them 1333 // are statically positioned and thus need to move with their absolute ancestors. 1334 bool canContainFixedPosObjects = canContainFixedPositionObjects(); 1335 if (posChildNeedsLayout() || canContainFixedPosObjects) 1336 layoutPositionedObjects(false, !posChildNeedsLayout() && canContainFixedPosObjects); 1337 1338 // Recompute our overflow information. 1339 // FIXME: We could do better here by computing a temporary overflow object from layoutPositionedObjects and only 1340 // updating our overflow if we either used to have overflow or if the new temporary object has overflow. 1341 // For now just always recompute overflow. This is no worse performance-wise than the old code that called rightmostPosition and 1342 // lowestPosition on every relayout so it's not a regression. 1343 // computeOverflow expects the bottom edge before we clamp our height. Since this information isn't available during 1344 // simplifiedLayout, we cache the value in m_overflow. 1345 LayoutUnit oldClientAfterEdge = hasRenderOverflow() ? m_overflow->layoutClientAfterEdge() : clientLogicalBottom(); 1346 computeOverflow(oldClientAfterEdge, true); 1347 1348 statePusher.pop(); 1349 1350 updateLayerTransform(); 1351 1352 updateScrollInfoAfterLayout(); 1353 1354 clearNeedsLayout(); 1355 return true; 1356} 1357 1358void RenderBlock::markFixedPositionObjectForLayoutIfNeeded(RenderObject& child) 1359{ 1360 if (child.style().position() != FixedPosition) 1361 return; 1362 1363 bool hasStaticBlockPosition = child.style().hasStaticBlockPosition(isHorizontalWritingMode()); 1364 bool hasStaticInlinePosition = child.style().hasStaticInlinePosition(isHorizontalWritingMode()); 1365 if (!hasStaticBlockPosition && !hasStaticInlinePosition) 1366 return; 1367 1368 auto o = child.parent(); 1369 while (o && !o->isRenderView() && o->style().position() != AbsolutePosition) 1370 o = o->parent(); 1371 if (o->style().position() != AbsolutePosition) 1372 return; 1373 1374 RenderBox& box = toRenderBox(child); 1375 if (hasStaticInlinePosition) { 1376 LogicalExtentComputedValues computedValues; 1377 box.computeLogicalWidthInRegion(computedValues); 1378 LayoutUnit newLeft = computedValues.m_position; 1379 if (newLeft != box.logicalLeft()) 1380 box.setChildNeedsLayout(MarkOnlyThis); 1381 } else if (hasStaticBlockPosition) { 1382 LayoutUnit oldTop = box.logicalTop(); 1383 box.updateLogicalHeight(); 1384 if (box.logicalTop() != oldTop) 1385 box.setChildNeedsLayout(MarkOnlyThis); 1386 } 1387} 1388 1389LayoutUnit RenderBlock::marginIntrinsicLogicalWidthForChild(RenderBox& child) const 1390{ 1391 // A margin has three types: fixed, percentage, and auto (variable). 1392 // Auto and percentage margins become 0 when computing min/max width. 1393 // Fixed margins can be added in as is. 1394 Length marginLeft = child.style().marginStartUsing(&style()); 1395 Length marginRight = child.style().marginEndUsing(&style()); 1396 LayoutUnit margin = 0; 1397 if (marginLeft.isFixed()) 1398 margin += marginLeft.value(); 1399 if (marginRight.isFixed()) 1400 margin += marginRight.value(); 1401 return margin; 1402} 1403 1404void RenderBlock::layoutPositionedObjects(bool relayoutChildren, bool fixedPositionObjectsOnly) 1405{ 1406 TrackedRendererListHashSet* positionedDescendants = positionedObjects(); 1407 if (!positionedDescendants) 1408 return; 1409 1410 for (auto it = positionedDescendants->begin(), end = positionedDescendants->end(); it != end; ++it) { 1411 RenderBox& r = **it; 1412 1413 estimateRegionRangeForBoxChild(r); 1414 1415 // A fixed position element with an absolute positioned ancestor has no way of knowing if the latter has changed position. So 1416 // 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. 1417 // it has static position. 1418 markFixedPositionObjectForLayoutIfNeeded(r); 1419 if (fixedPositionObjectsOnly) { 1420 r.layoutIfNeeded(); 1421 continue; 1422 } 1423 1424 // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the 1425 // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned 1426 // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is 1427 // positioned explicitly) this should not incur a performance penalty. 1428 if (relayoutChildren || (r.style().hasStaticBlockPosition(isHorizontalWritingMode()) && r.parent() != this)) 1429 r.setChildNeedsLayout(MarkOnlyThis); 1430 1431 // If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths. 1432 if (relayoutChildren && r.needsPreferredWidthsRecalculation()) 1433 r.setPreferredLogicalWidthsDirty(true, MarkOnlyThis); 1434 1435 if (!r.needsLayout()) 1436 r.markForPaginationRelayoutIfNeeded(); 1437 1438 // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width 1439 // and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout. 1440 if (r.needsPositionedMovementLayoutOnly() && r.tryLayoutDoingPositionedMovementOnly()) 1441 r.clearNeedsLayout(); 1442 1443 // If we are paginated or in a line grid, go ahead and compute a vertical position for our object now. 1444 // If it's wrong we'll lay out again. 1445 LayoutUnit oldLogicalTop = 0; 1446 bool needsBlockDirectionLocationSetBeforeLayout = r.needsLayout() && view().layoutState()->needsBlockDirectionLocationSetBeforeLayout(); 1447 if (needsBlockDirectionLocationSetBeforeLayout) { 1448 if (isHorizontalWritingMode() == r.isHorizontalWritingMode()) 1449 r.updateLogicalHeight(); 1450 else 1451 r.updateLogicalWidth(); 1452 oldLogicalTop = logicalTopForChild(r); 1453 } 1454 1455 r.layoutIfNeeded(); 1456 1457 // Lay out again if our estimate was wrong. 1458 if (needsBlockDirectionLocationSetBeforeLayout && logicalTopForChild(r) != oldLogicalTop) { 1459 r.setChildNeedsLayout(MarkOnlyThis); 1460 r.layoutIfNeeded(); 1461 } 1462 1463 if (updateRegionRangeForBoxChild(r)) { 1464 r.setNeedsLayout(MarkOnlyThis); 1465 r.layoutIfNeeded(); 1466 } 1467 } 1468} 1469 1470void RenderBlock::markPositionedObjectsForLayout() 1471{ 1472 TrackedRendererListHashSet* positionedDescendants = positionedObjects(); 1473 if (!positionedDescendants) 1474 return; 1475 1476 for (auto it = positionedDescendants->begin(), end = positionedDescendants->end(); it != end; ++it) { 1477 RenderBox* r = *it; 1478 r->setChildNeedsLayout(); 1479 } 1480} 1481 1482void RenderBlock::markForPaginationRelayoutIfNeeded() 1483{ 1484 ASSERT(!needsLayout()); 1485 if (needsLayout()) 1486 return; 1487 1488 if (view().layoutState()->pageLogicalHeightChanged() || (view().layoutState()->pageLogicalHeight() && view().layoutState()->pageLogicalOffset(this, logicalTop()) != pageLogicalOffset())) 1489 setChildNeedsLayout(MarkOnlyThis); 1490} 1491 1492void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1493{ 1494 LayoutPoint adjustedPaintOffset = paintOffset + location(); 1495 1496 PaintPhase phase = paintInfo.phase; 1497 1498 RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); 1499 // Check our region range to make sure we need to be painting in this region. 1500 if (namedFlowFragment && !namedFlowFragment->flowThread()->objectShouldFragmentInFlowRegion(this, namedFlowFragment)) 1501 return; 1502 1503 // Check if we need to do anything at all. 1504 // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView 1505 // paints the root's background. 1506 if (!isRoot()) { 1507 LayoutRect overflowBox = overflowRectForPaintRejection(namedFlowFragment); 1508 flipForWritingMode(overflowBox); 1509 overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); 1510 overflowBox.moveBy(adjustedPaintOffset); 1511 if (!overflowBox.intersects(paintInfo.rect) 1512#if PLATFORM(IOS) 1513 // FIXME: This may be applicable to non-iOS ports. 1514 && (!hasLayer() || !layer()->isComposited()) 1515#endif 1516 ) 1517 return; 1518 } 1519 1520 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset); 1521 paintObject(paintInfo, adjustedPaintOffset); 1522 if (pushedClip) 1523 popContentsClip(paintInfo, phase, adjustedPaintOffset); 1524 1525 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with 1526 // z-index. We paint after we painted the background/border, so that the scrollbars will 1527 // sit above the background/border. 1528 if (hasOverflowClip() && style().visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(*this) && !paintInfo.paintRootBackgroundOnly()) 1529 layer()->paintOverflowControls(paintInfo.context, roundedIntPoint(adjustedPaintOffset), pixelSnappedIntRect(paintInfo.rect)); 1530} 1531 1532void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1533{ 1534 // Avoid painting descendants of the root element when stylesheets haven't loaded. This eliminates FOUC. 1535 // It's ok not to draw, because later on, when all the stylesheets do load, styleResolverChanged() on the Document 1536 // will do a full repaint. 1537 if (document().didLayoutWithPendingStylesheets() && !isRenderView()) 1538 return; 1539 1540 if (childrenInline()) 1541 paintInlineChildren(paintInfo, paintOffset); 1542 else { 1543 PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? PaintPhaseOutline : paintInfo.phase; 1544 newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChildBlockBackground : newPhase; 1545 1546 // We don't paint our own background, but we do let the kids paint their backgrounds. 1547 PaintInfo paintInfoForChild(paintInfo); 1548 paintInfoForChild.phase = newPhase; 1549 paintInfoForChild.updateSubtreePaintRootForChildren(this); 1550 1551 // FIXME: Paint-time pagination is obsolete and is now only used by embedded WebViews inside AppKit 1552 // NSViews. Do not add any more code for this. 1553 bool usePrintRect = !view().printRect().isEmpty(); 1554 paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect); 1555 } 1556} 1557 1558void RenderBlock::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) 1559{ 1560 for (auto child = firstChildBox(); child; child = child->nextSiblingBox()) { 1561 if (!paintChild(*child, paintInfo, paintOffset, paintInfoForChild, usePrintRect)) 1562 return; 1563 } 1564} 1565 1566bool RenderBlock::paintChild(RenderBox& child, PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) 1567{ 1568 // Check for page-break-before: always, and if it's set, break and bail. 1569 bool checkBeforeAlways = !childrenInline() && (usePrintRect && child.style().pageBreakBefore() == PBALWAYS); 1570 LayoutUnit absoluteChildY = paintOffset.y() + child.y(); 1571 if (checkBeforeAlways 1572 && absoluteChildY > paintInfo.rect.y() 1573 && absoluteChildY < paintInfo.rect.maxY()) { 1574 view().setBestTruncatedAt(absoluteChildY, this, true); 1575 return false; 1576 } 1577 1578 if (!child.isFloating() && child.isReplaced() && usePrintRect && child.height() <= view().printRect().height()) { 1579 // Paginate block-level replaced elements. 1580 if (absoluteChildY + child.height() > view().printRect().maxY()) { 1581 if (absoluteChildY < view().truncatedAt()) 1582 view().setBestTruncatedAt(absoluteChildY, &child); 1583 // If we were able to truncate, don't paint. 1584 if (absoluteChildY >= view().truncatedAt()) 1585 return false; 1586 } 1587 } 1588 1589 LayoutPoint childPoint = flipForWritingModeForChild(&child, paintOffset); 1590 if (!child.hasSelfPaintingLayer() && !child.isFloating()) 1591 child.paint(paintInfoForChild, childPoint); 1592 1593 // Check for page-break-after: always, and if it's set, break and bail. 1594 bool checkAfterAlways = !childrenInline() && (usePrintRect && child.style().pageBreakAfter() == PBALWAYS); 1595 if (checkAfterAlways 1596 && (absoluteChildY + child.height()) > paintInfo.rect.y() 1597 && (absoluteChildY + child.height()) < paintInfo.rect.maxY()) { 1598 view().setBestTruncatedAt(absoluteChildY + child.height() + std::max<LayoutUnit>(0, child.collapsedMarginAfter()), this, true); 1599 return false; 1600 } 1601 1602 return true; 1603} 1604 1605 1606void RenderBlock::paintCaret(PaintInfo& paintInfo, const LayoutPoint& paintOffset, CaretType type) 1607{ 1608 // Paint the caret if the FrameSelection says so or if caret browsing is enabled 1609 bool caretBrowsing = frame().settings().caretBrowsingEnabled(); 1610 RenderObject* caretPainter; 1611 bool isContentEditable; 1612 if (type == CursorCaret) { 1613 caretPainter = frame().selection().caretRendererWithoutUpdatingLayout(); 1614 isContentEditable = frame().selection().selection().hasEditableStyle(); 1615 } else { 1616 caretPainter = frame().page()->dragCaretController().caretRenderer(); 1617 isContentEditable = frame().page()->dragCaretController().isContentEditable(); 1618 } 1619 1620 if (caretPainter == this && (isContentEditable || caretBrowsing)) { 1621 if (type == CursorCaret) 1622 frame().selection().paintCaret(paintInfo.context, paintOffset, paintInfo.rect); 1623 else 1624 frame().page()->dragCaretController().paintDragCaret(&frame(), paintInfo.context, paintOffset, paintInfo.rect); 1625 } 1626} 1627 1628void RenderBlock::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1629{ 1630 PaintPhase paintPhase = paintInfo.phase; 1631 1632 // 1. paint background, borders etc 1633 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style().visibility() == VISIBLE) { 1634 if (hasBoxDecorations()) { 1635 bool didClipToRegion = false; 1636 1637 RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); 1638 if (paintInfo.paintContainer && namedFlowFragment && paintInfo.paintContainer->isRenderNamedFlowThread()) { 1639 // If this box goes beyond the current region, then make sure not to overflow the region. 1640 // This (overflowing region X altough also fragmented to region X+1) could happen when one of this box's children 1641 // overflows region X and is an unsplittable element (like an image). 1642 // The same applies for a box overflowing the top of region X when that box is also fragmented in region X-1. 1643 1644 paintInfo.context->save(); 1645 didClipToRegion = true; 1646 1647 paintInfo.context->clip(toRenderNamedFlowThread(paintInfo.paintContainer)->decorationsClipRectForBoxInNamedFlowFragment(*this, *namedFlowFragment)); 1648 } 1649 1650 paintBoxDecorations(paintInfo, paintOffset); 1651 1652 if (didClipToRegion) 1653 paintInfo.context->restore(); 1654 } 1655 } 1656 1657 if (paintPhase == PaintPhaseMask && style().visibility() == VISIBLE) { 1658 paintMask(paintInfo, paintOffset); 1659 return; 1660 } 1661 1662 // If just painting the root background, then return. 1663 if (paintInfo.paintRootBackgroundOnly()) 1664 return; 1665 1666 // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div). 1667 LayoutPoint scrolledOffset = paintOffset; 1668 scrolledOffset.move(-scrolledContentOffset()); 1669 1670 // Column rules need to account for scrolling and clipping. 1671 // FIXME: Clipping of column rules does not work. We will need a separate paint phase for column rules I suspect in order to get 1672 // clipping correct (since it has to paint as background but is still considered "contents"). 1673 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && style().visibility() == VISIBLE) 1674 paintColumnRules(paintInfo, scrolledOffset); 1675 1676 // Done with backgrounds, borders and column rules. 1677 if (paintPhase == PaintPhaseBlockBackground) 1678 return; 1679 1680 // 2. paint contents 1681 if (paintPhase != PaintPhaseSelfOutline) 1682 paintContents(paintInfo, scrolledOffset); 1683 1684 // 3. paint selection 1685 // FIXME: Make this work with multi column layouts. For now don't fill gaps. 1686 bool isPrinting = document().printing(); 1687 if (!isPrinting) 1688 paintSelection(paintInfo, scrolledOffset); // Fill in gaps in selection on lines and between blocks. 1689 1690 // 4. paint floats. 1691 if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) 1692 paintFloats(paintInfo, scrolledOffset, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); 1693 1694 // 5. paint outline. 1695 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style().visibility() == VISIBLE) 1696 paintOutline(paintInfo, LayoutRect(paintOffset, size())); 1697 1698 // 6. paint continuation outlines. 1699 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) { 1700 RenderInline* inlineCont = inlineElementContinuation(); 1701 if (inlineCont && inlineCont->hasOutline() && inlineCont->style().visibility() == VISIBLE) { 1702 RenderInline* inlineRenderer = toRenderInline(inlineCont->element()->renderer()); 1703 RenderBlock* cb = containingBlock(); 1704 1705 bool inlineEnclosedInSelfPaintingLayer = false; 1706 for (RenderBoxModelObject* box = inlineRenderer; box != cb; box = &box->parent()->enclosingBoxModelObject()) { 1707 if (box->hasSelfPaintingLayer()) { 1708 inlineEnclosedInSelfPaintingLayer = true; 1709 break; 1710 } 1711 } 1712 1713 // Do not add continuations for outline painting by our containing block if we are a relative positioned 1714 // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on renderers in its continuation table being 1715 // in the same layer. 1716 if (!inlineEnclosedInSelfPaintingLayer && !hasLayer()) 1717 cb->addContinuationWithOutline(inlineRenderer); 1718 else if (!inlineRenderer->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && hasLayer())) 1719 inlineRenderer->paintOutline(paintInfo, paintOffset - locationOffset() + inlineRenderer->containingBlock()->location()); 1720 } 1721 paintContinuationOutlines(paintInfo, paintOffset); 1722 } 1723 1724 // 7. paint caret. 1725 // If the caret's node's render object's containing block is this block, and the paint action is PaintPhaseForeground, 1726 // then paint the caret. 1727 if (paintPhase == PaintPhaseForeground) { 1728 paintCaret(paintInfo, paintOffset, CursorCaret); 1729 paintCaret(paintInfo, paintOffset, DragCaret); 1730 } 1731} 1732 1733RenderInline* RenderBlock::inlineElementContinuation() const 1734{ 1735 RenderBoxModelObject* continuation = this->continuation(); 1736 return continuation && continuation->isRenderInline() ? toRenderInline(continuation) : 0; 1737} 1738 1739RenderBlock* RenderBlock::blockElementContinuation() const 1740{ 1741 RenderBoxModelObject* currentContinuation = continuation(); 1742 if (!currentContinuation || currentContinuation->isInline()) 1743 return 0; 1744 RenderBlock* nextContinuation = toRenderBlock(currentContinuation); 1745 if (nextContinuation->isAnonymousBlock()) 1746 return nextContinuation->blockElementContinuation(); 1747 return nextContinuation; 1748} 1749 1750static ContinuationOutlineTableMap* continuationOutlineTable() 1751{ 1752 DEPRECATED_DEFINE_STATIC_LOCAL(ContinuationOutlineTableMap, table, ()); 1753 return &table; 1754} 1755 1756void RenderBlock::addContinuationWithOutline(RenderInline* flow) 1757{ 1758 // We can't make this work if the inline is in a layer. We'll just rely on the broken 1759 // way of painting. 1760 ASSERT(!flow->layer() && !flow->isInlineElementContinuation()); 1761 1762 ContinuationOutlineTableMap* table = continuationOutlineTable(); 1763 ListHashSet<RenderInline*>* continuations = table->get(this); 1764 if (!continuations) { 1765 continuations = new ListHashSet<RenderInline*>; 1766 table->set(this, std::unique_ptr<ListHashSet<RenderInline*>>(continuations)); 1767 } 1768 1769 continuations->add(flow); 1770} 1771 1772bool RenderBlock::paintsContinuationOutline(RenderInline* flow) 1773{ 1774 ContinuationOutlineTableMap* table = continuationOutlineTable(); 1775 if (table->isEmpty()) 1776 return false; 1777 1778 ListHashSet<RenderInline*>* continuations = table->get(this); 1779 if (!continuations) 1780 return false; 1781 1782 return continuations->contains(flow); 1783} 1784 1785void RenderBlock::paintContinuationOutlines(PaintInfo& info, const LayoutPoint& paintOffset) 1786{ 1787 ContinuationOutlineTableMap* table = continuationOutlineTable(); 1788 if (table->isEmpty()) 1789 return; 1790 1791 std::unique_ptr<ListHashSet<RenderInline*>> continuations = table->take(this); 1792 if (!continuations) 1793 return; 1794 1795 LayoutPoint accumulatedPaintOffset = paintOffset; 1796 // Paint each continuation outline. 1797 ListHashSet<RenderInline*>::iterator end = continuations->end(); 1798 for (ListHashSet<RenderInline*>::iterator it = continuations->begin(); it != end; ++it) { 1799 // Need to add in the coordinates of the intervening blocks. 1800 RenderInline* flow = *it; 1801 RenderBlock* block = flow->containingBlock(); 1802 for ( ; block && block != this; block = block->containingBlock()) 1803 accumulatedPaintOffset.moveBy(block->location()); 1804 ASSERT(block); 1805 flow->paintOutline(info, accumulatedPaintOffset); 1806 } 1807} 1808 1809bool RenderBlock::shouldPaintSelectionGaps() const 1810{ 1811 return selectionState() != SelectionNone && style().visibility() == VISIBLE && isSelectionRoot(); 1812} 1813 1814bool RenderBlock::isSelectionRoot() const 1815{ 1816 if (isPseudoElement()) 1817 return false; 1818 ASSERT(element() || isAnonymous()); 1819 1820 // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases. 1821 if (isTable()) 1822 return false; 1823 1824 if (isBody() || isRoot() || hasOverflowClip() 1825 || isPositioned() || isFloating() 1826 || isTableCell() || isInlineBlockOrInlineTable() 1827 || hasTransform() || hasReflection() || hasMask() || isWritingModeRoot() 1828 || isRenderFlowThread() || style().columnSpan() == ColumnSpanAll) 1829 return true; 1830 1831 if (view().selectionStart()) { 1832 Node* startElement = view().selectionStart()->node(); 1833 if (startElement && startElement->rootEditableElement() == element()) 1834 return true; 1835 } 1836 1837 return false; 1838} 1839 1840GapRects RenderBlock::selectionGapRectsForRepaint(const RenderLayerModelObject* repaintContainer) 1841{ 1842 ASSERT(!needsLayout()); 1843 1844 if (!shouldPaintSelectionGaps()) 1845 return GapRects(); 1846 1847 TransformState transformState(TransformState::ApplyTransformDirection, FloatPoint()); 1848 mapLocalToContainer(repaintContainer, transformState, ApplyContainerFlip | UseTransforms); 1849 LayoutPoint offsetFromRepaintContainer = roundedLayoutPoint(transformState.mappedPoint()) - scrolledContentOffset(); 1850 1851 LogicalSelectionOffsetCaches cache(*this); 1852 LayoutUnit lastTop = 0; 1853 LayoutUnit lastLeft = logicalLeftSelectionOffset(*this, lastTop, cache); 1854 LayoutUnit lastRight = logicalRightSelectionOffset(*this, lastTop, cache); 1855 1856 return selectionGaps(*this, offsetFromRepaintContainer, IntSize(), lastTop, lastLeft, lastRight, cache); 1857} 1858 1859void RenderBlock::paintSelection(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1860{ 1861#if ENABLE(TEXT_SELECTION) 1862 if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) { 1863 LogicalSelectionOffsetCaches cache(*this); 1864 LayoutUnit lastTop = 0; 1865 LayoutUnit lastLeft = logicalLeftSelectionOffset(*this, lastTop, cache); 1866 LayoutUnit lastRight = logicalRightSelectionOffset(*this, lastTop, cache); 1867 GraphicsContextStateSaver stateSaver(*paintInfo.context); 1868 1869 LayoutRect gapRectsBounds = selectionGaps(*this, paintOffset, LayoutSize(), lastTop, lastLeft, lastRight, cache, &paintInfo); 1870 if (!gapRectsBounds.isEmpty()) { 1871 if (RenderLayer* layer = enclosingLayer()) { 1872 gapRectsBounds.moveBy(-paintOffset); 1873 if (!hasLayer()) { 1874 LayoutRect localBounds(gapRectsBounds); 1875 flipForWritingMode(localBounds); 1876 gapRectsBounds = localToContainerQuad(FloatRect(localBounds), &layer->renderer()).enclosingBoundingBox(); 1877 if (layer->renderer().isBox()) 1878 gapRectsBounds.move(layer->renderBox()->scrolledContentOffset()); 1879 } 1880 layer->addBlockSelectionGapsBounds(gapRectsBounds); 1881 } 1882 } 1883 } 1884#else 1885 UNUSED_PARAM(paintInfo); 1886 UNUSED_PARAM(paintOffset); 1887#endif 1888} 1889 1890static void clipOutPositionedObjects(const PaintInfo* paintInfo, const LayoutPoint& offset, TrackedRendererListHashSet* positionedObjects) 1891{ 1892 if (!positionedObjects) 1893 return; 1894 1895 TrackedRendererListHashSet::const_iterator end = positionedObjects->end(); 1896 for (TrackedRendererListHashSet::const_iterator it = positionedObjects->begin(); it != end; ++it) { 1897 RenderBox* r = *it; 1898 paintInfo->context->clipOut(IntRect(offset.x() + r->x(), offset.y() + r->y(), r->width(), r->height())); 1899 } 1900} 1901 1902LayoutUnit blockDirectionOffset(RenderBlock& rootBlock, const LayoutSize& offsetFromRootBlock) 1903{ 1904 return rootBlock.isHorizontalWritingMode() ? offsetFromRootBlock.height() : offsetFromRootBlock.width(); 1905} 1906 1907LayoutUnit inlineDirectionOffset(RenderBlock& rootBlock, const LayoutSize& offsetFromRootBlock) 1908{ 1909 return rootBlock.isHorizontalWritingMode() ? offsetFromRootBlock.width() : offsetFromRootBlock.height(); 1910} 1911 1912LayoutRect RenderBlock::logicalRectToPhysicalRect(const LayoutPoint& rootBlockPhysicalPosition, const LayoutRect& logicalRect) 1913{ 1914 LayoutRect result; 1915 if (isHorizontalWritingMode()) 1916 result = logicalRect; 1917 else 1918 result = LayoutRect(logicalRect.y(), logicalRect.x(), logicalRect.height(), logicalRect.width()); 1919 flipForWritingMode(result); 1920 result.moveBy(rootBlockPhysicalPosition); 1921 return result; 1922} 1923 1924GapRects RenderBlock::selectionGaps(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 1925 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) 1926{ 1927 // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore. 1928 // Clip out floating and positioned objects when painting selection gaps. 1929 if (paintInfo) { 1930 // Note that we don't clip out overflow for positioned objects. We just stick to the border box. 1931 LayoutRect flippedBlockRect(offsetFromRootBlock.width(), offsetFromRootBlock.height(), width(), height()); 1932 rootBlock.flipForWritingMode(flippedBlockRect); 1933 flippedBlockRect.moveBy(rootBlockPhysicalPosition); 1934 clipOutPositionedObjects(paintInfo, flippedBlockRect.location(), positionedObjects()); 1935 if (isBody() || isRoot()) // The <body> must make sure to examine its containingBlock's positioned objects. 1936 for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock()) 1937 clipOutPositionedObjects(paintInfo, LayoutPoint(cb->x(), cb->y()), cb->positionedObjects()); // FIXME: Not right for flipped writing modes. 1938 clipOutFloatingObjects(rootBlock, paintInfo, rootBlockPhysicalPosition, offsetFromRootBlock); 1939 } 1940 1941 // 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 1942 // fixed). 1943 GapRects result; 1944 if (!isRenderBlockFlow()) // FIXME: Make multi-column selection gap filling work someday. 1945 return result; 1946 1947 if (hasTransform() || style().columnSpan() == ColumnSpanAll || isInFlowRenderFlowThread()) { 1948 // FIXME: We should learn how to gap fill multiple columns and transforms eventually. 1949 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalHeight(); 1950 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, logicalHeight(), cache); 1951 lastLogicalRight = logicalRightSelectionOffset(rootBlock, logicalHeight(), cache); 1952 return result; 1953 } 1954 1955 RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); 1956 if (paintInfo && namedFlowFragment && paintInfo->paintContainer->isRenderFlowThread()) { 1957 // Make sure the current object is actually flowed into the region being painted. 1958 if (!toRenderFlowThread(paintInfo->paintContainer)->objectShouldFragmentInFlowRegion(this, namedFlowFragment)) 1959 return result; 1960 } 1961 1962 if (childrenInline()) 1963 result = inlineSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo); 1964 else 1965 result = blockSelectionGaps(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, lastLogicalTop, lastLogicalLeft, lastLogicalRight, cache, paintInfo); 1966 1967 // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. 1968 if (&rootBlock == this && (selectionState() != SelectionBoth && selectionState() != SelectionEnd)) { 1969 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, 1970 lastLogicalTop, lastLogicalLeft, lastLogicalRight, logicalHeight(), cache, paintInfo)); 1971 } 1972 1973 return result; 1974} 1975 1976GapRects RenderBlock::inlineSelectionGaps(RenderBlock&, const LayoutPoint&, const LayoutSize&, LayoutUnit&, LayoutUnit&, LayoutUnit&, const LogicalSelectionOffsetCaches&, const PaintInfo*) 1977{ 1978 ASSERT_NOT_REACHED(); 1979 return GapRects(); 1980} 1981 1982GapRects RenderBlock::blockSelectionGaps(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 1983 LayoutUnit& lastLogicalTop, LayoutUnit& lastLogicalLeft, LayoutUnit& lastLogicalRight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) 1984{ 1985 GapRects result; 1986 1987 // Go ahead and jump right to the first block child that contains some selected objects. 1988 RenderBox* curr; 1989 for (curr = firstChildBox(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSiblingBox()) { } 1990 1991 if (!curr) 1992 return result; 1993 1994 LogicalSelectionOffsetCaches childCache(*this, cache); 1995 1996 for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSiblingBox()) { 1997 SelectionState childState = curr->selectionState(); 1998 if (childState == SelectionBoth || childState == SelectionEnd) 1999 sawSelectionEnd = true; 2000 2001 if (curr->isFloatingOrOutOfFlowPositioned()) 2002 continue; // We must be a normal flow object in order to even be considered. 2003 2004 if (curr->isInFlowPositioned() && curr->hasLayer()) { 2005 // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element. 2006 // Just disregard it completely. 2007 LayoutSize relOffset = curr->layer()->offsetForInFlowPosition(); 2008 if (relOffset.width() || relOffset.height()) 2009 continue; 2010 } 2011 2012 bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this. 2013 bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone); 2014 if (fillBlockGaps) { 2015 // We need to fill the vertical gap above this object. 2016 if (childState == SelectionEnd || childState == SelectionInside) { 2017 // Fill the gap above the object. 2018 result.uniteCenter(blockSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, 2019 lastLogicalTop, lastLogicalLeft, lastLogicalRight, curr->logicalTop(), cache, paintInfo)); 2020 } 2021 2022 // 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* 2023 // our object. We know this if the selection did not end inside our object. 2024 if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd)) 2025 childState = SelectionNone; 2026 2027 // Fill side gaps on this object based off its state. 2028 bool leftGap, rightGap; 2029 getSelectionGapInfo(childState, leftGap, rightGap); 2030 2031 if (leftGap) 2032 result.uniteLeft(logicalLeftSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalLeft(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo)); 2033 if (rightGap) 2034 result.uniteRight(logicalRightSelectionGap(rootBlock, rootBlockPhysicalPosition, offsetFromRootBlock, this, curr->logicalRight(), curr->logicalTop(), curr->logicalHeight(), cache, paintInfo)); 2035 2036 // Update lastLogicalTop to be just underneath the object. lastLogicalLeft and lastLogicalRight extend as far as 2037 // they can without bumping into floating or positioned objects. Ideally they will go right up 2038 // to the border of the root selection block. 2039 lastLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + curr->logicalBottom(); 2040 lastLogicalLeft = logicalLeftSelectionOffset(rootBlock, curr->logicalBottom(), cache); 2041 lastLogicalRight = logicalRightSelectionOffset(rootBlock, curr->logicalBottom(), cache); 2042 } else if (childState != SelectionNone) { 2043 // We must be a block that has some selected object inside it. Go ahead and recur. 2044 result.unite(toRenderBlock(curr)->selectionGaps(rootBlock, rootBlockPhysicalPosition, LayoutSize(offsetFromRootBlock.width() + curr->x(), offsetFromRootBlock.height() + curr->y()), 2045 lastLogicalTop, lastLogicalLeft, lastLogicalRight, childCache, paintInfo)); 2046 } 2047 } 2048 return result; 2049} 2050 2051LayoutRect RenderBlock::blockSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 2052 LayoutUnit lastLogicalTop, LayoutUnit lastLogicalLeft, LayoutUnit lastLogicalRight, LayoutUnit logicalBottom, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) 2053{ 2054 LayoutUnit logicalTop = lastLogicalTop; 2055 LayoutUnit logicalHeight = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalBottom - logicalTop; 2056 if (logicalHeight <= 0) 2057 return LayoutRect(); 2058 2059 // Get the selection offsets for the bottom of the gap 2060 LayoutUnit logicalLeft = std::max(lastLogicalLeft, logicalLeftSelectionOffset(rootBlock, logicalBottom, cache)); 2061 LayoutUnit logicalRight = std::min(lastLogicalRight, logicalRightSelectionOffset(rootBlock, logicalBottom, cache)); 2062 LayoutUnit logicalWidth = logicalRight - logicalLeft; 2063 if (logicalWidth <= 0) 2064 return LayoutRect(); 2065 2066 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(logicalLeft, logicalTop, logicalWidth, logicalHeight)); 2067 if (paintInfo) 2068 paintInfo->context->fillRect(pixelSnappedForPainting(gapRect, document().deviceScaleFactor()), selectionBackgroundColor(), style().colorSpace()); 2069 return gapRect; 2070} 2071 2072LayoutRect RenderBlock::logicalLeftSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 2073 RenderObject* selObj, LayoutUnit logicalLeft, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) 2074{ 2075 LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; 2076 LayoutUnit rootBlockLogicalLeft = std::max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)); 2077 LayoutUnit rootBlockLogicalRight = std::min(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalLeft, 2078 std::min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache))); 2079 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; 2080 if (rootBlockLogicalWidth <= 0) 2081 return LayoutRect(); 2082 2083 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); 2084 if (paintInfo) 2085 paintInfo->context->fillRect(pixelSnappedForPainting(gapRect, document().deviceScaleFactor()), selObj->selectionBackgroundColor(), selObj->style().colorSpace()); 2086 return gapRect; 2087} 2088 2089LayoutRect RenderBlock::logicalRightSelectionGap(RenderBlock& rootBlock, const LayoutPoint& rootBlockPhysicalPosition, const LayoutSize& offsetFromRootBlock, 2090 RenderObject* selObj, LayoutUnit logicalRight, LayoutUnit logicalTop, LayoutUnit logicalHeight, const LogicalSelectionOffsetCaches& cache, const PaintInfo* paintInfo) 2091{ 2092 LayoutUnit rootBlockLogicalTop = blockDirectionOffset(rootBlock, offsetFromRootBlock) + logicalTop; 2093 LayoutUnit rootBlockLogicalLeft = std::max(inlineDirectionOffset(rootBlock, offsetFromRootBlock) + logicalRight, 2094 std::max(logicalLeftSelectionOffset(rootBlock, logicalTop, cache), logicalLeftSelectionOffset(rootBlock, logicalTop + logicalHeight, cache))); 2095 LayoutUnit rootBlockLogicalRight = std::min(logicalRightSelectionOffset(rootBlock, logicalTop, cache), logicalRightSelectionOffset(rootBlock, logicalTop + logicalHeight, cache)); 2096 LayoutUnit rootBlockLogicalWidth = rootBlockLogicalRight - rootBlockLogicalLeft; 2097 if (rootBlockLogicalWidth <= 0) 2098 return LayoutRect(); 2099 2100 LayoutRect gapRect = rootBlock.logicalRectToPhysicalRect(rootBlockPhysicalPosition, LayoutRect(rootBlockLogicalLeft, rootBlockLogicalTop, rootBlockLogicalWidth, logicalHeight)); 2101 if (paintInfo) 2102 paintInfo->context->fillRect(pixelSnappedForPainting(gapRect, document().deviceScaleFactor()), selObj->selectionBackgroundColor(), selObj->style().colorSpace()); 2103 return gapRect; 2104} 2105 2106void RenderBlock::getSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap) 2107{ 2108 bool ltr = style().isLeftToRightDirection(); 2109 leftGap = (state == RenderObject::SelectionInside) || 2110 (state == RenderObject::SelectionEnd && ltr) || 2111 (state == RenderObject::SelectionStart && !ltr); 2112 rightGap = (state == RenderObject::SelectionInside) || 2113 (state == RenderObject::SelectionStart && ltr) || 2114 (state == RenderObject::SelectionEnd && !ltr); 2115} 2116 2117LayoutUnit RenderBlock::logicalLeftSelectionOffset(RenderBlock& rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache) 2118{ 2119 LayoutUnit logicalLeft = logicalLeftOffsetForLine(position, false); 2120 if (logicalLeft == logicalLeftOffsetForContent()) { 2121 if (&rootBlock != this) // The border can potentially be further extended by our containingBlock(). 2122 return cache.containingBlockInfo(*this).logicalLeftSelectionOffset(rootBlock, position + logicalTop()); 2123 return logicalLeft; 2124 } 2125 2126 RenderBlock* cb = this; 2127 const LogicalSelectionOffsetCaches* currentCache = &cache; 2128 while (cb != &rootBlock) { 2129 logicalLeft += cb->logicalLeft(); 2130 2131 ASSERT(currentCache); 2132 auto info = currentCache->containingBlockInfo(*cb); 2133 cb = info.block(); 2134 currentCache = info.cache(); 2135 } 2136 return logicalLeft; 2137} 2138 2139LayoutUnit RenderBlock::logicalRightSelectionOffset(RenderBlock& rootBlock, LayoutUnit position, const LogicalSelectionOffsetCaches& cache) 2140{ 2141 LayoutUnit logicalRight = logicalRightOffsetForLine(position, false); 2142 if (logicalRight == logicalRightOffsetForContent()) { 2143 if (&rootBlock != this) // The border can potentially be further extended by our containingBlock(). 2144 return cache.containingBlockInfo(*this).logicalRightSelectionOffset(rootBlock, position + logicalTop()); 2145 return logicalRight; 2146 } 2147 2148 RenderBlock* cb = this; 2149 const LogicalSelectionOffsetCaches* currentCache = &cache; 2150 while (cb != &rootBlock) { 2151 logicalRight += cb->logicalLeft(); 2152 2153 ASSERT(currentCache); 2154 auto info = currentCache->containingBlockInfo(*cb); 2155 cb = info.block(); 2156 currentCache = info.cache(); 2157 } 2158 return logicalRight; 2159} 2160 2161RenderBlock* RenderBlock::blockBeforeWithinSelectionRoot(LayoutSize& offset) const 2162{ 2163 if (isSelectionRoot()) 2164 return 0; 2165 2166 const RenderElement* object = this; 2167 RenderObject* sibling; 2168 do { 2169 sibling = object->previousSibling(); 2170 while (sibling && (!sibling->isRenderBlock() || toRenderBlock(sibling)->isSelectionRoot())) 2171 sibling = sibling->previousSibling(); 2172 2173 offset -= LayoutSize(toRenderBlock(object)->logicalLeft(), toRenderBlock(object)->logicalTop()); 2174 object = object->parent(); 2175 } while (!sibling && object && object->isRenderBlock() && !toRenderBlock(object)->isSelectionRoot()); 2176 2177 if (!sibling) 2178 return 0; 2179 2180 RenderBlock* beforeBlock = toRenderBlock(sibling); 2181 2182 offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); 2183 2184 RenderObject* child = beforeBlock->lastChild(); 2185 while (child && child->isRenderBlock()) { 2186 beforeBlock = toRenderBlock(child); 2187 offset += LayoutSize(beforeBlock->logicalLeft(), beforeBlock->logicalTop()); 2188 child = beforeBlock->lastChild(); 2189 } 2190 return beforeBlock; 2191} 2192 2193void RenderBlock::insertIntoTrackedRendererMaps(RenderBox& descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap) 2194{ 2195 if (!descendantsMap) { 2196 descendantsMap = new TrackedDescendantsMap; 2197 containerMap = new TrackedContainerMap; 2198 } 2199 2200 TrackedRendererListHashSet* descendantSet = descendantsMap->get(this); 2201 if (!descendantSet) { 2202 descendantSet = new TrackedRendererListHashSet; 2203 descendantsMap->set(this, std::unique_ptr<TrackedRendererListHashSet>(descendantSet)); 2204 } 2205 bool added = descendantSet->add(&descendant).isNewEntry; 2206 if (!added) { 2207 ASSERT(containerMap->get(&descendant)); 2208 ASSERT(containerMap->get(&descendant)->contains(this)); 2209 return; 2210 } 2211 2212 HashSet<RenderBlock*>* containerSet = containerMap->get(&descendant); 2213 if (!containerSet) { 2214 containerSet = new HashSet<RenderBlock*>; 2215 containerMap->set(&descendant, std::unique_ptr<HashSet<RenderBlock*>>(containerSet)); 2216 } 2217 ASSERT(!containerSet->contains(this)); 2218 containerSet->add(this); 2219} 2220 2221void RenderBlock::removeFromTrackedRendererMaps(RenderBox& descendant, TrackedDescendantsMap*& descendantsMap, TrackedContainerMap*& containerMap) 2222{ 2223 if (!descendantsMap) 2224 return; 2225 2226 std::unique_ptr<HashSet<RenderBlock*>> containerSet = containerMap->take(&descendant); 2227 if (!containerSet) 2228 return; 2229 2230 for (auto it = containerSet->begin(), end = containerSet->end(); it != end; ++it) { 2231 RenderBlock* container = *it; 2232 2233 // FIXME: Disabling this assert temporarily until we fix the layout 2234 // bugs associated with positioned objects not properly cleared from 2235 // their ancestor chain before being moved. See webkit bug 93766. 2236 // ASSERT(descendant->isDescendantOf(container)); 2237 2238 TrackedDescendantsMap::iterator descendantsMapIterator = descendantsMap->find(container); 2239 ASSERT(descendantsMapIterator != descendantsMap->end()); 2240 if (descendantsMapIterator == descendantsMap->end()) 2241 continue; 2242 TrackedRendererListHashSet* descendantSet = descendantsMapIterator->value.get(); 2243 ASSERT(descendantSet->contains(&descendant)); 2244 descendantSet->remove(&descendant); 2245 if (descendantSet->isEmpty()) 2246 descendantsMap->remove(descendantsMapIterator); 2247 } 2248} 2249 2250TrackedRendererListHashSet* RenderBlock::positionedObjects() const 2251{ 2252 if (gPositionedDescendantsMap) 2253 return gPositionedDescendantsMap->get(this); 2254 return 0; 2255} 2256 2257void RenderBlock::insertPositionedObject(RenderBox& o) 2258{ 2259 ASSERT(!isAnonymousBlock()); 2260 2261 if (o.isRenderFlowThread()) 2262 return; 2263 2264 insertIntoTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap); 2265} 2266 2267void RenderBlock::removePositionedObject(RenderBox& o) 2268{ 2269 removeFromTrackedRendererMaps(o, gPositionedDescendantsMap, gPositionedContainerMap); 2270} 2271 2272void RenderBlock::removePositionedObjects(RenderBlock* o, ContainingBlockState containingBlockState) 2273{ 2274 TrackedRendererListHashSet* positionedDescendants = positionedObjects(); 2275 if (!positionedDescendants) 2276 return; 2277 2278 Vector<RenderBox*, 16> deadObjects; 2279 2280 for (auto it = positionedDescendants->begin(), end = positionedDescendants->end(); it != end; ++it) { 2281 RenderBox* r = *it; 2282 if (!o || r->isDescendantOf(o)) { 2283 if (containingBlockState == NewContainingBlock) 2284 r->setChildNeedsLayout(MarkOnlyThis); 2285 2286 // It is parent blocks job to add positioned child to positioned objects list of its containing block 2287 // Parent layout needs to be invalidated to ensure this happens. 2288 RenderElement* p = r->parent(); 2289 while (p && !p->isRenderBlock()) 2290 p = p->parent(); 2291 if (p) 2292 p->setChildNeedsLayout(); 2293 2294 deadObjects.append(r); 2295 } 2296 } 2297 2298 for (unsigned i = 0; i < deadObjects.size(); i++) 2299 removePositionedObject(*deadObjects.at(i)); 2300} 2301 2302void RenderBlock::addPercentHeightDescendant(RenderBox& descendant) 2303{ 2304 insertIntoTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap); 2305} 2306 2307void RenderBlock::removePercentHeightDescendant(RenderBox& descendant) 2308{ 2309 removeFromTrackedRendererMaps(descendant, gPercentHeightDescendantsMap, gPercentHeightContainerMap); 2310} 2311 2312TrackedRendererListHashSet* RenderBlock::percentHeightDescendants() const 2313{ 2314 return gPercentHeightDescendantsMap ? gPercentHeightDescendantsMap->get(this) : 0; 2315} 2316 2317bool RenderBlock::hasPercentHeightContainerMap() 2318{ 2319 return gPercentHeightContainerMap; 2320} 2321 2322bool RenderBlock::hasPercentHeightDescendant(RenderBox& descendant) 2323{ 2324 // We don't null check gPercentHeightContainerMap since the caller 2325 // already ensures this and we need to call this function on every 2326 // descendant in clearPercentHeightDescendantsFrom(). 2327 ASSERT(gPercentHeightContainerMap); 2328 return gPercentHeightContainerMap->contains(&descendant); 2329} 2330 2331void RenderBlock::removePercentHeightDescendantIfNeeded(RenderBox& descendant) 2332{ 2333 // We query the map directly, rather than looking at style's 2334 // logicalHeight()/logicalMinHeight()/logicalMaxHeight() since those 2335 // can change with writing mode/directional changes. 2336 if (!hasPercentHeightContainerMap()) 2337 return; 2338 2339 if (!hasPercentHeightDescendant(descendant)) 2340 return; 2341 2342 removePercentHeightDescendant(descendant); 2343} 2344 2345void RenderBlock::clearPercentHeightDescendantsFrom(RenderBox& parent) 2346{ 2347 ASSERT(gPercentHeightContainerMap); 2348 for (RenderObject* curr = parent.firstChild(); curr; curr = curr->nextInPreOrder(&parent)) { 2349 if (!curr->isBox()) 2350 continue; 2351 2352 RenderBox& box = toRenderBox(*curr); 2353 if (!hasPercentHeightDescendant(box)) 2354 continue; 2355 2356 removePercentHeightDescendant(box); 2357 } 2358} 2359 2360LayoutUnit RenderBlock::textIndentOffset() const 2361{ 2362 LayoutUnit cw = 0; 2363 if (style().textIndent().isPercent()) 2364 cw = containingBlock()->availableLogicalWidth(); 2365 return minimumValueForLength(style().textIndent(), cw); 2366} 2367 2368LayoutUnit RenderBlock::logicalLeftOffsetForContent(RenderRegion* region) const 2369{ 2370 LayoutUnit logicalLeftOffset = style().isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); 2371 if (!region) 2372 return logicalLeftOffset; 2373 LayoutRect boxRect = borderBoxRectInRegion(region); 2374 return logicalLeftOffset + (isHorizontalWritingMode() ? boxRect.x() : boxRect.y()); 2375} 2376 2377LayoutUnit RenderBlock::logicalRightOffsetForContent(RenderRegion* region) const 2378{ 2379 LayoutUnit logicalRightOffset = style().isHorizontalWritingMode() ? borderLeft() + paddingLeft() : borderTop() + paddingTop(); 2380 logicalRightOffset += availableLogicalWidth(); 2381 if (!region) 2382 return logicalRightOffset; 2383 LayoutRect boxRect = borderBoxRectInRegion(region); 2384 return logicalRightOffset - (logicalWidth() - (isHorizontalWritingMode() ? boxRect.maxX() : boxRect.maxY())); 2385} 2386 2387LayoutUnit RenderBlock::adjustLogicalLeftOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const 2388{ 2389 LayoutUnit left = offsetFromFloats; 2390 2391 if (applyTextIndent && style().isLeftToRightDirection()) 2392 left += textIndentOffset(); 2393 2394 if (style().lineAlign() == LineAlignNone) 2395 return left; 2396 2397 // Push in our left offset so that it is aligned with the character grid. 2398 LayoutState* layoutState = view().layoutState(); 2399 if (!layoutState) 2400 return left; 2401 2402 RenderBlock* lineGrid = layoutState->lineGrid(); 2403 if (!lineGrid || lineGrid->style().writingMode() != style().writingMode()) 2404 return left; 2405 2406 // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge? 2407 float maxCharWidth = lineGrid->style().font().primaryFont()->maxCharWidth(); 2408 if (!maxCharWidth) 2409 return left; 2410 2411 LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height(); 2412 LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height(); 2413 2414 // Push in to the nearest character width (truncated so that we pixel snap left). 2415 // FIXME: Should be patched when subpixel layout lands, since this calculation doesn't have to pixel snap 2416 // any more (https://bugs.webkit.org/show_bug.cgi?id=79946). 2417 // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945). 2418 // FIXME: This doesn't work with columns or regions (https://bugs.webkit.org/show_bug.cgi?id=79942). 2419 // FIXME: This doesn't work when the inline position of the object isn't set ahead of time. 2420 // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout. 2421 // (https://bugs.webkit.org/show_bug.cgi?id=79944) 2422 float remainder = fmodf(maxCharWidth - fmodf(left + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth); 2423 left += remainder; 2424 return left; 2425} 2426 2427LayoutUnit RenderBlock::adjustLogicalRightOffsetForLine(LayoutUnit offsetFromFloats, bool applyTextIndent) const 2428{ 2429 LayoutUnit right = offsetFromFloats; 2430 2431 if (applyTextIndent && !style().isLeftToRightDirection()) 2432 right -= textIndentOffset(); 2433 2434 if (style().lineAlign() == LineAlignNone) 2435 return right; 2436 2437 // Push in our right offset so that it is aligned with the character grid. 2438 LayoutState* layoutState = view().layoutState(); 2439 if (!layoutState) 2440 return right; 2441 2442 RenderBlock* lineGrid = layoutState->lineGrid(); 2443 if (!lineGrid || lineGrid->style().writingMode() != style().writingMode()) 2444 return right; 2445 2446 // FIXME: Should letter-spacing apply? This is complicated since it doesn't apply at the edge? 2447 float maxCharWidth = lineGrid->style().font().primaryFont()->maxCharWidth(); 2448 if (!maxCharWidth) 2449 return right; 2450 2451 LayoutUnit lineGridOffset = lineGrid->isHorizontalWritingMode() ? layoutState->lineGridOffset().width(): layoutState->lineGridOffset().height(); 2452 LayoutUnit layoutOffset = lineGrid->isHorizontalWritingMode() ? layoutState->layoutOffset().width() : layoutState->layoutOffset().height(); 2453 2454 // Push in to the nearest character width (truncated so that we pixel snap right). 2455 // FIXME: Should be patched when subpixel layout lands, since this calculation doesn't have to pixel snap 2456 // any more (https://bugs.webkit.org/show_bug.cgi?id=79946). 2457 // FIXME: This is wrong for RTL (https://bugs.webkit.org/show_bug.cgi?id=79945). 2458 // FIXME: This doesn't work with columns or regions (https://bugs.webkit.org/show_bug.cgi?id=79942). 2459 // FIXME: This doesn't work when the inline position of the object isn't set ahead of time. 2460 // FIXME: Dynamic changes to the font or to the inline position need to result in a deep relayout. 2461 // (https://bugs.webkit.org/show_bug.cgi?id=79944) 2462 float remainder = fmodf(fmodf(right + layoutOffset - lineGridOffset, maxCharWidth), maxCharWidth); 2463 right -= ceilf(remainder); 2464 return right; 2465} 2466 2467bool RenderBlock::avoidsFloats() const 2468{ 2469 // Floats can't intrude into our box if we have a non-auto column count or width. 2470 return RenderBox::avoidsFloats() 2471 || !style().hasAutoColumnCount() 2472 || !style().hasAutoColumnWidth() 2473 || style().hasFlowFrom(); 2474} 2475 2476bool RenderBlock::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset) 2477{ 2478 if (!scrollsOverflow()) 2479 return false; 2480 2481 return layer()->hitTestOverflowControls(result, roundedIntPoint(locationInContainer - toLayoutSize(accumulatedOffset))); 2482} 2483 2484Node* RenderBlock::nodeForHitTest() const 2485{ 2486 // If we are in the margins of block elements that are part of a 2487 // continuation we're actually still inside the enclosing element 2488 // that was split. Use the appropriate inner node. 2489 if (isRenderView()) 2490 return &document(); 2491 return isAnonymousBlockContinuation() ? continuation()->element() : element(); 2492} 2493 2494bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) 2495{ 2496 LayoutPoint adjustedLocation(accumulatedOffset + location()); 2497 LayoutSize localOffset = toLayoutSize(adjustedLocation); 2498 2499 RenderFlowThread* flowThread = flowThreadContainingBlock(); 2500 RenderNamedFlowFragment* namedFlowFragment = flowThread ? toRenderNamedFlowFragment(flowThread->currentRegion()) : nullptr; 2501 // If we are now searching inside a region, make sure this element 2502 // is being fragmented into this region. 2503 if (namedFlowFragment && !flowThread->objectShouldFragmentInFlowRegion(this, namedFlowFragment)) 2504 return false; 2505 2506 if (!isRenderView()) { 2507 // Check if we need to do anything at all. 2508 LayoutRect overflowBox = visualOverflowRect(); 2509 flipForWritingMode(overflowBox); 2510 overflowBox.moveBy(adjustedLocation); 2511 if (!locationInContainer.intersects(overflowBox)) 2512 return false; 2513 } 2514 2515 if ((hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) && isPointInOverflowControl(result, locationInContainer.point(), adjustedLocation)) { 2516 updateHitTestResult(result, locationInContainer.point() - localOffset); 2517 // FIXME: isPointInOverflowControl() doesn't handle rect-based tests yet. 2518 if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer)) 2519 return true; 2520 } 2521 2522 if (style().clipPath()) { 2523 switch (style().clipPath()->type()) { 2524 case ClipPathOperation::Shape: { 2525 ShapeClipPathOperation* clipPath = toShapeClipPathOperation(style().clipPath()); 2526 2527 LayoutRect referenceBoxRect; 2528 switch (clipPath->referenceBox()) { 2529 case CSSBoxType::MarginBox: 2530 referenceBoxRect = marginBoxRect(); 2531 break; 2532 case CSSBoxType::BorderBox: 2533 referenceBoxRect = borderBoxRect(); 2534 break; 2535 case CSSBoxType::PaddingBox: 2536 referenceBoxRect = paddingBoxRect(); 2537 break; 2538 case CSSBoxType::ContentBox: 2539 referenceBoxRect = contentBoxRect(); 2540 break; 2541 case CSSBoxType::BoxMissing: 2542 case CSSBoxType::Fill: 2543 case CSSBoxType::Stroke: 2544 case CSSBoxType::ViewBox: 2545 referenceBoxRect = borderBoxRect(); 2546 } 2547 if (!clipPath->pathForReferenceRect(referenceBoxRect).contains(locationInContainer.point() - localOffset, clipPath->windRule())) 2548 return false; 2549 break; 2550 } 2551 // FIXME: handle Reference/Box 2552 case ClipPathOperation::Reference: 2553 case ClipPathOperation::Box: 2554 break; 2555 } 2556 } 2557 2558 // If we have clipping, then we can't have any spillout. 2559 bool useOverflowClip = hasOverflowClip() && !hasSelfPaintingLayer(); 2560 bool useClip = (hasControlClip() || useOverflowClip); 2561 bool checkChildren = !useClip || (hasControlClip() ? locationInContainer.intersects(controlClipRect(adjustedLocation)) : locationInContainer.intersects(overflowClipRect(adjustedLocation, namedFlowFragment, IncludeOverlayScrollbarSize))); 2562 if (checkChildren) { 2563 // Hit test descendants first. 2564 LayoutSize scrolledOffset(localOffset - scrolledContentOffset()); 2565 2566 if (hitTestContents(request, result, locationInContainer, toLayoutPoint(scrolledOffset), hitTestAction)) { 2567 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset)); 2568 return true; 2569 } 2570 if (hitTestAction == HitTestFloat && hitTestFloats(request, result, locationInContainer, toLayoutPoint(scrolledOffset))) 2571 return true; 2572 } 2573 2574 // Check if the point is outside radii. 2575 if (!isRenderView() && style().hasBorderRadius()) { 2576 LayoutRect borderRect = borderBoxRect(); 2577 borderRect.moveBy(adjustedLocation); 2578 RoundedRect border = style().getRoundedBorderFor(borderRect); 2579 if (!locationInContainer.intersects(border)) 2580 return false; 2581 } 2582 2583 // Now hit test our background 2584 if (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground) { 2585 LayoutRect boundsRect(adjustedLocation, size()); 2586 if (visibleToHitTesting() && locationInContainer.intersects(boundsRect)) { 2587 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - localOffset)); 2588 if (!result.addNodeToRectBasedTestResult(nodeForHitTest(), request, locationInContainer, boundsRect)) 2589 return true; 2590 } 2591 } 2592 2593 return false; 2594} 2595 2596bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction) 2597{ 2598 if (childrenInline() && !isTable()) 2599 return hitTestInlineChildren(request, result, locationInContainer, accumulatedOffset, hitTestAction); 2600 2601 // Hit test our children. 2602 HitTestAction childHitTest = hitTestAction; 2603 if (hitTestAction == HitTestChildBlockBackgrounds) 2604 childHitTest = HitTestChildBlockBackground; 2605 for (auto child = lastChildBox(); child; child = child->previousSiblingBox()) { 2606 LayoutPoint childPoint = flipForWritingModeForChild(child, accumulatedOffset); 2607 if (!child->hasSelfPaintingLayer() && !child->isFloating() && child->nodeAtPoint(request, result, locationInContainer, childPoint, childHitTest)) 2608 return true; 2609 } 2610 2611 return false; 2612} 2613 2614static inline bool isEditingBoundary(RenderElement* ancestor, RenderObject& child) 2615{ 2616 ASSERT(!ancestor || ancestor->nonPseudoElement()); 2617 ASSERT(child.nonPseudoNode()); 2618 return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor->parent()->isRenderView()) 2619 || ancestor->nonPseudoElement()->hasEditableStyle() == child.nonPseudoNode()->hasEditableStyle(); 2620} 2621 2622// FIXME: This function should go on RenderObject as an instance method. Then 2623// all cases in which positionForPoint recurs could call this instead to 2624// prevent crossing editable boundaries. This would require many tests. 2625VisiblePosition positionForPointRespectingEditingBoundaries(RenderBlock& parent, RenderBox& child, const LayoutPoint& pointInParentCoordinates) 2626{ 2627 LayoutPoint childLocation = child.location(); 2628 if (child.isInFlowPositioned()) 2629 childLocation += child.offsetForInFlowPosition(); 2630 2631 // FIXME: This is wrong if the child's writing-mode is different from the parent's. 2632 LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates - childLocation)); 2633 2634 // If this is an anonymous renderer, we just recur normally 2635 Element* childElement= child.nonPseudoElement(); 2636 if (!childElement) 2637 return child.positionForPoint(pointInChildCoordinates, nullptr); 2638 2639 // Otherwise, first make sure that the editability of the parent and child agree. 2640 // If they don't agree, then we return a visible position just before or after the child 2641 RenderElement* ancestor = &parent; 2642 while (ancestor && !ancestor->nonPseudoElement()) 2643 ancestor = ancestor->parent(); 2644 2645 // If we can't find an ancestor to check editability on, or editability is unchanged, we recur like normal 2646 if (isEditingBoundary(ancestor, child)) 2647 return child.positionForPoint(pointInChildCoordinates, nullptr); 2648 2649#if PLATFORM(IOS) 2650 // On iOS we want to constrain VisiblePositions to the editable region closest to the input position, so 2651 // we will allow descent from non-editable to editable content. 2652 // FIXME: This constraining must be done at a higher level once we implement contentEditable. For now, if something 2653 // is editable, the whole document will be. 2654 if (childElement->isContentEditable() && !ancestor->element()->isContentEditable()) 2655 return child.positionForPoint(pointInChildCoordinates, nullptr); 2656#endif 2657 2658 // Otherwise return before or after the child, depending on if the click was to the logical left or logical right of the child 2659 LayoutUnit childMiddle = parent.logicalWidthForChild(child) / 2; 2660 LayoutUnit logicalLeft = parent.isHorizontalWritingMode() ? pointInChildCoordinates.x() : pointInChildCoordinates.y(); 2661 if (logicalLeft < childMiddle) 2662 return ancestor->createVisiblePosition(childElement->nodeIndex(), DOWNSTREAM); 2663 return ancestor->createVisiblePosition(childElement->nodeIndex() + 1, UPSTREAM); 2664} 2665 2666VisiblePosition RenderBlock::positionForPointWithInlineChildren(const LayoutPoint&, const RenderRegion*) 2667{ 2668 ASSERT_NOT_REACHED(); 2669 return VisiblePosition(); 2670} 2671 2672static inline bool isChildHitTestCandidate(const RenderBox& box) 2673{ 2674 return box.height() && box.style().visibility() == VISIBLE && !box.isFloatingOrOutOfFlowPositioned() && !box.isInFlowRenderFlowThread(); 2675} 2676 2677// Valid candidates in a FlowThread must be rendered by the region. 2678static inline bool isChildHitTestCandidate(const RenderBox& box, const RenderRegion* region, const LayoutPoint& point) 2679{ 2680 if (!isChildHitTestCandidate(box)) 2681 return false; 2682 if (!region) 2683 return true; 2684 const RenderBlock& block = box.isRenderBlock() ? toRenderBlock(box) : *box.containingBlock(); 2685 return block.regionAtBlockOffset(point.y()) == region; 2686} 2687 2688VisiblePosition RenderBlock::positionForPoint(const LayoutPoint& point, const RenderRegion* region) 2689{ 2690 if (isTable()) 2691 return RenderBox::positionForPoint(point, region); 2692 2693 if (isReplaced()) { 2694 // FIXME: This seems wrong when the object's writing-mode doesn't match the line's writing-mode. 2695 LayoutUnit pointLogicalLeft = isHorizontalWritingMode() ? point.x() : point.y(); 2696 LayoutUnit pointLogicalTop = isHorizontalWritingMode() ? point.y() : point.x(); 2697 2698 if (pointLogicalTop < 0 || (pointLogicalTop < logicalHeight() && pointLogicalLeft < 0)) 2699 return createVisiblePosition(caretMinOffset(), DOWNSTREAM); 2700 if (pointLogicalTop >= logicalHeight() || (pointLogicalTop >= 0 && pointLogicalLeft >= logicalWidth())) 2701 return createVisiblePosition(caretMaxOffset(), DOWNSTREAM); 2702 } 2703 2704 LayoutPoint pointInContents = point; 2705 offsetForContents(pointInContents); 2706 LayoutPoint pointInLogicalContents(pointInContents); 2707 if (!isHorizontalWritingMode()) 2708 pointInLogicalContents = pointInLogicalContents.transposedPoint(); 2709 2710 if (childrenInline()) 2711 return positionForPointWithInlineChildren(pointInLogicalContents, region); 2712 2713 RenderBox* lastCandidateBox = lastChildBox(); 2714 2715 if (!region) 2716 region = regionAtBlockOffset(pointInLogicalContents.y()); 2717 2718 while (lastCandidateBox && !isChildHitTestCandidate(*lastCandidateBox, region, pointInLogicalContents)) 2719 lastCandidateBox = lastCandidateBox->previousSiblingBox(); 2720 2721 bool blocksAreFlipped = style().isFlippedBlocksWritingMode(); 2722 if (lastCandidateBox) { 2723 if (pointInLogicalContents.y() > logicalTopForChild(*lastCandidateBox) 2724 || (!blocksAreFlipped && pointInLogicalContents.y() == logicalTopForChild(*lastCandidateBox))) 2725 return positionForPointRespectingEditingBoundaries(*this, *lastCandidateBox, pointInContents); 2726 2727 for (auto childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { 2728 if (!isChildHitTestCandidate(*childBox, region, pointInLogicalContents)) 2729 continue; 2730 LayoutUnit childLogicalBottom = logicalTopForChild(*childBox) + logicalHeightForChild(*childBox); 2731 // We hit child if our click is above the bottom of its padding box (like IE6/7 and FF3). 2732 if (isChildHitTestCandidate(*childBox, region, pointInLogicalContents) && (pointInLogicalContents.y() < childLogicalBottom 2733 || (blocksAreFlipped && pointInLogicalContents.y() == childLogicalBottom))) 2734 return positionForPointRespectingEditingBoundaries(*this, *childBox, pointInContents); 2735 } 2736 } 2737 2738 // We only get here if there are no hit test candidate children below the click. 2739 return RenderBox::positionForPoint(point, region); 2740} 2741 2742void RenderBlock::offsetForContents(LayoutPoint& offset) const 2743{ 2744 offset = flipForWritingMode(offset); 2745 offset += scrolledContentOffset(); 2746 offset = flipForWritingMode(offset); 2747} 2748 2749void RenderBlock::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const 2750{ 2751 ASSERT(!childrenInline()); 2752 2753 computeBlockPreferredLogicalWidths(minLogicalWidth, maxLogicalWidth); 2754 2755 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); 2756 2757 int scrollbarWidth = instrinsicScrollbarLogicalWidth(); 2758 maxLogicalWidth += scrollbarWidth; 2759 minLogicalWidth += scrollbarWidth; 2760} 2761 2762void RenderBlock::computePreferredLogicalWidths() 2763{ 2764 ASSERT(preferredLogicalWidthsDirty()); 2765 2766 updateFirstLetter(); 2767 2768 m_minPreferredLogicalWidth = 0; 2769 m_maxPreferredLogicalWidth = 0; 2770 2771 const RenderStyle& styleToUse = style(); 2772 if (!isTableCell() && styleToUse.logicalWidth().isFixed() && styleToUse.logicalWidth().value() >= 0 2773 && !(isDeprecatedFlexItem() && !styleToUse.logicalWidth().intValue())) 2774 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalWidth().value()); 2775 else 2776 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 2777 2778 if (styleToUse.logicalMinWidth().isFixed() && styleToUse.logicalMinWidth().value() > 0) { 2779 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value())); 2780 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMinWidth().value())); 2781 } 2782 2783 if (styleToUse.logicalMaxWidth().isFixed()) { 2784 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value())); 2785 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse.logicalMaxWidth().value())); 2786 } 2787 2788 // Table layout uses integers, ceil the preferred widths to ensure that they can contain the contents. 2789 if (isTableCell()) { 2790 m_minPreferredLogicalWidth = m_minPreferredLogicalWidth.ceil(); 2791 m_maxPreferredLogicalWidth = m_maxPreferredLogicalWidth.ceil(); 2792 } 2793 2794 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); 2795 m_minPreferredLogicalWidth += borderAndPadding; 2796 m_maxPreferredLogicalWidth += borderAndPadding; 2797 2798 setPreferredLogicalWidthsDirty(false); 2799} 2800 2801struct InlineMinMaxIterator { 2802/* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to 2803 inline min/max width calculations. Note the following about the way it walks: 2804 (1) Positioned content is skipped (since it does not contribute to min/max width of a block) 2805 (2) We do not drill into the children of floats or replaced elements, since you can't break 2806 in the middle of such an element. 2807 (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have 2808 distinct borders/margin/padding that contribute to the min/max width. 2809*/ 2810 RenderObject* parent; 2811 RenderObject* current; 2812 bool endOfInline; 2813 2814 InlineMinMaxIterator(RenderObject* p, bool end = false) 2815 :parent(p), current(p), endOfInline(end) {} 2816 2817 RenderObject* next(); 2818}; 2819 2820RenderObject* InlineMinMaxIterator::next() 2821{ 2822 RenderObject* result = 0; 2823 bool oldEndOfInline = endOfInline; 2824 endOfInline = false; 2825 while (current || current == parent) { 2826 if (!oldEndOfInline && 2827 (current == parent || 2828 (!current->isFloating() && !current->isReplaced() && !current->isOutOfFlowPositioned()))) 2829 result = current->firstChildSlow(); 2830 if (!result) { 2831 // We hit the end of our inline. (It was empty, e.g., <span></span>.) 2832 if (!oldEndOfInline && current->isRenderInline()) { 2833 result = current; 2834 endOfInline = true; 2835 break; 2836 } 2837 2838 while (current && current != parent) { 2839 result = current->nextSibling(); 2840 if (result) break; 2841 current = current->parent(); 2842 if (current && current != parent && current->isRenderInline()) { 2843 result = current; 2844 endOfInline = true; 2845 break; 2846 } 2847 } 2848 } 2849 2850 if (!result) 2851 break; 2852 2853 if (!result->isOutOfFlowPositioned() && (result->isTextOrLineBreak() || result->isFloating() || result->isReplaced() || result->isRenderInline())) 2854 break; 2855 2856 current = result; 2857 result = 0; 2858 } 2859 2860 // Update our position. 2861 current = result; 2862 return current; 2863} 2864 2865static LayoutUnit getBPMWidth(LayoutUnit childValue, Length cssUnit) 2866{ 2867 if (cssUnit.type() != Auto) 2868 return (cssUnit.isFixed() ? LayoutUnit(cssUnit.value()) : childValue); 2869 return 0; 2870} 2871 2872static LayoutUnit getBorderPaddingMargin(const RenderBoxModelObject* child, bool endOfInline) 2873{ 2874 const RenderStyle& childStyle = child->style(); 2875 if (endOfInline) 2876 return getBPMWidth(child->marginEnd(), childStyle.marginEnd()) + 2877 getBPMWidth(child->paddingEnd(), childStyle.paddingEnd()) + 2878 child->borderEnd(); 2879 return getBPMWidth(child->marginStart(), childStyle.marginStart()) + 2880 getBPMWidth(child->paddingStart(), childStyle.paddingStart()) + 2881 child->borderStart(); 2882} 2883 2884static inline void stripTrailingSpace(float& inlineMax, float& inlineMin, 2885 RenderObject* trailingSpaceChild) 2886{ 2887 if (trailingSpaceChild && trailingSpaceChild->isText()) { 2888 // Collapse away the trailing space at the end of a block. 2889 RenderText* t = toRenderText(trailingSpaceChild); 2890 const UChar space = ' '; 2891 const Font& font = t->style().font(); // FIXME: This ignores first-line. 2892 float spaceWidth = font.width(RenderBlock::constructTextRun(t, font, &space, 1, t->style())); 2893 inlineMax -= spaceWidth + font.wordSpacing(); 2894 if (inlineMin > inlineMax) 2895 inlineMin = inlineMax; 2896 } 2897} 2898 2899static inline void updatePreferredWidth(LayoutUnit& preferredWidth, float& result) 2900{ 2901 LayoutUnit snappedResult = ceiledLayoutUnit(result); 2902 preferredWidth = std::max(snappedResult, preferredWidth); 2903} 2904 2905// With sub-pixel enabled: When converting between floating point and LayoutUnits 2906// we risk losing precision with each conversion. When this occurs while 2907// accumulating our preferred widths, we can wind up with a line width that's 2908// larger than our maxPreferredWidth due to pure float accumulation. 2909// 2910// With sub-pixel disabled: values from Lengths or the render tree aren't subject 2911// to the same loss of precision, as they're always truncated and stored as 2912// integers. We mirror that behavior here to prevent over-allocating our preferred 2913// width. 2914static inline LayoutUnit adjustFloatForSubPixelLayout(float value) 2915{ 2916#if ENABLE(SUBPIXEL_LAYOUT) 2917 return ceiledLayoutUnit(value); 2918#else 2919 return static_cast<int>(value); 2920#endif 2921} 2922 2923 2924void RenderBlock::computeInlinePreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) 2925{ 2926 float inlineMax = 0; 2927 float inlineMin = 0; 2928 2929 const RenderStyle& styleToUse = style(); 2930 RenderBlock* containingBlock = this->containingBlock(); 2931 LayoutUnit cw = containingBlock ? containingBlock->contentLogicalWidth() : LayoutUnit(); 2932 2933 // If we are at the start of a line, we want to ignore all white-space. 2934 // Also strip spaces if we previously had text that ended in a trailing space. 2935 bool stripFrontSpaces = true; 2936 RenderObject* trailingSpaceChild = 0; 2937 2938 // Firefox and Opera will allow a table cell to grow to fit an image inside it under 2939 // very specific cirucumstances (in order to match common WinIE renderings). 2940 // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) 2941 bool allowImagesToBreak = !document().inQuirksMode() || !isTableCell() || !styleToUse.logicalWidth().isIntrinsicOrAuto(); 2942 2943 bool autoWrap, oldAutoWrap; 2944 autoWrap = oldAutoWrap = styleToUse.autoWrap(); 2945 2946 InlineMinMaxIterator childIterator(this); 2947 2948 // Only gets added to the max preffered width once. 2949 bool addedTextIndent = false; 2950 // Signals the text indent was more negative than the min preferred width 2951 bool hasRemainingNegativeTextIndent = false; 2952 2953 LayoutUnit textIndent = minimumValueForLength(styleToUse.textIndent(), cw); 2954 RenderObject* prevFloat = 0; 2955 bool isPrevChildInlineFlow = false; 2956 bool shouldBreakLineAfterText = false; 2957 while (RenderObject* child = childIterator.next()) { 2958 autoWrap = child->isReplaced() ? child->parent()->style().autoWrap() : 2959 child->style().autoWrap(); 2960 2961 if (!child->isBR()) { 2962 // Step One: determine whether or not we need to go ahead and 2963 // terminate our current line. Each discrete chunk can become 2964 // the new min-width, if it is the widest chunk seen so far, and 2965 // it can also become the max-width. 2966 2967 // Children fall into three categories: 2968 // (1) An inline flow object. These objects always have a min/max of 0, 2969 // and are included in the iteration solely so that their margins can 2970 // be added in. 2971 // 2972 // (2) An inline non-text non-flow object, e.g., an inline replaced element. 2973 // These objects can always be on a line by themselves, so in this situation 2974 // we need to go ahead and break the current line, and then add in our own 2975 // margins and min/max width on its own line, and then terminate the line. 2976 // 2977 // (3) A text object. Text runs can have breakable characters at the start, 2978 // the middle or the end. They may also lose whitespace off the front if 2979 // we're already ignoring whitespace. In order to compute accurate min-width 2980 // information, we need three pieces of information. 2981 // (a) the min-width of the first non-breakable run. Should be 0 if the text string 2982 // starts with whitespace. 2983 // (b) the min-width of the last non-breakable run. Should be 0 if the text string 2984 // ends with whitespace. 2985 // (c) the min/max width of the string (trimmed for whitespace). 2986 // 2987 // If the text string starts with whitespace, then we need to go ahead and 2988 // terminate our current line (unless we're already in a whitespace stripping 2989 // mode. 2990 // 2991 // If the text string has a breakable character in the middle, but didn't start 2992 // with whitespace, then we add the width of the first non-breakable run and 2993 // then end the current line. We then need to use the intermediate min/max width 2994 // values (if any of them are larger than our current min/max). We then look at 2995 // the width of the last non-breakable run and use that to start a new line 2996 // (unless we end in whitespace). 2997 const RenderStyle& childStyle = child->style(); 2998 float childMin = 0; 2999 float childMax = 0; 3000 3001 if (!child->isText()) { 3002 if (child->isLineBreakOpportunity()) { 3003 updatePreferredWidth(minLogicalWidth, inlineMin); 3004 inlineMin = 0; 3005 continue; 3006 } 3007 // Case (1) and (2). Inline replaced and inline flow elements. 3008 if (child->isRenderInline()) { 3009 // Add in padding/border/margin from the appropriate side of 3010 // the element. 3011 float bpm = getBorderPaddingMargin(toRenderInline(child), childIterator.endOfInline); 3012 childMin += bpm; 3013 childMax += bpm; 3014 3015 inlineMin += childMin; 3016 inlineMax += childMax; 3017 3018 child->setPreferredLogicalWidthsDirty(false); 3019 } else { 3020 // Inline replaced elts add in their margins to their min/max values. 3021 LayoutUnit margins = 0; 3022 Length startMargin = childStyle.marginStart(); 3023 Length endMargin = childStyle.marginEnd(); 3024 if (startMargin.isFixed()) 3025 margins += adjustFloatForSubPixelLayout(startMargin.value()); 3026 if (endMargin.isFixed()) 3027 margins += adjustFloatForSubPixelLayout(endMargin.value()); 3028 childMin += margins.ceilToFloat(); 3029 childMax += margins.ceilToFloat(); 3030 } 3031 } 3032 3033 if (!child->isRenderInline() && !child->isText()) { 3034 // Case (2). Inline replaced elements and floats. 3035 // Go ahead and terminate the current line as far as 3036 // minwidth is concerned. 3037 childMin += child->minPreferredLogicalWidth().ceilToFloat(); 3038 childMax += child->maxPreferredLogicalWidth().ceilToFloat(); 3039 3040 bool clearPreviousFloat; 3041 if (child->isFloating()) { 3042 clearPreviousFloat = (prevFloat 3043 && ((prevFloat->style().floating() == LeftFloat && (childStyle.clear() & CLEFT)) 3044 || (prevFloat->style().floating() == RightFloat && (childStyle.clear() & CRIGHT)))); 3045 prevFloat = child; 3046 } else 3047 clearPreviousFloat = false; 3048 3049 bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; 3050 if ((canBreakReplacedElement && (autoWrap || oldAutoWrap) && (!isPrevChildInlineFlow || shouldBreakLineAfterText)) || clearPreviousFloat) { 3051 updatePreferredWidth(minLogicalWidth, inlineMin); 3052 inlineMin = 0; 3053 } 3054 3055 // If we're supposed to clear the previous float, then terminate maxwidth as well. 3056 if (clearPreviousFloat) { 3057 updatePreferredWidth(maxLogicalWidth, inlineMax); 3058 inlineMax = 0; 3059 } 3060 3061 // Add in text-indent. This is added in only once. 3062 if (!addedTextIndent && !child->isFloating()) { 3063 LayoutUnit ceiledIndent = textIndent.ceilToFloat(); 3064 childMin += ceiledIndent; 3065 childMax += ceiledIndent; 3066 3067 if (childMin < 0) 3068 textIndent = adjustFloatForSubPixelLayout(childMin); 3069 else 3070 addedTextIndent = true; 3071 } 3072 3073 // Add our width to the max. 3074 inlineMax += std::max<float>(0, childMax); 3075 3076 if (!autoWrap || !canBreakReplacedElement || (isPrevChildInlineFlow && !shouldBreakLineAfterText)) { 3077 if (child->isFloating()) 3078 updatePreferredWidth(minLogicalWidth, childMin); 3079 else 3080 inlineMin += childMin; 3081 } else { 3082 // Now check our line. 3083 updatePreferredWidth(minLogicalWidth, childMin); 3084 3085 // Now start a new line. 3086 inlineMin = 0; 3087 } 3088 3089 if (autoWrap && canBreakReplacedElement && isPrevChildInlineFlow) { 3090 updatePreferredWidth(minLogicalWidth, inlineMin); 3091 inlineMin = 0; 3092 } 3093 3094 // We are no longer stripping whitespace at the start of 3095 // a line. 3096 if (!child->isFloating()) { 3097 stripFrontSpaces = false; 3098 trailingSpaceChild = 0; 3099 } 3100 } else if (child->isText()) { 3101 // Case (3). Text. 3102 RenderText* t = toRenderText(child); 3103 3104 if (t->style().hasTextCombine() && t->isCombineText()) 3105 toRenderCombineText(*t).combineText(); 3106 3107 // Determine if we have a breakable character. Pass in 3108 // whether or not we should ignore any spaces at the front 3109 // of the string. If those are going to be stripped out, 3110 // then they shouldn't be considered in the breakable char 3111 // check. 3112 bool hasBreakableChar, hasBreak; 3113 float beginMin, endMin; 3114 bool beginWS, endWS; 3115 float beginMax, endMax; 3116 t->trimmedPrefWidths(inlineMax, beginMin, beginWS, endMin, endWS, 3117 hasBreakableChar, hasBreak, beginMax, endMax, 3118 childMin, childMax, stripFrontSpaces); 3119 3120 // This text object will not be rendered, but it may still provide a breaking opportunity. 3121 if (!hasBreak && childMax == 0) { 3122 if (autoWrap && (beginWS || endWS)) { 3123 updatePreferredWidth(minLogicalWidth, inlineMin); 3124 inlineMin = 0; 3125 } 3126 continue; 3127 } 3128 3129 if (stripFrontSpaces) 3130 trailingSpaceChild = child; 3131 else 3132 trailingSpaceChild = 0; 3133 3134 // Add in text-indent. This is added in only once. 3135 float ti = 0; 3136 if (!addedTextIndent || hasRemainingNegativeTextIndent) { 3137 ti = textIndent.ceilToFloat(); 3138 childMin += ti; 3139 beginMin += ti; 3140 3141 // It the text indent negative and larger than the child minimum, we re-use the remainder 3142 // in future minimum calculations, but using the negative value again on the maximum 3143 // will lead to under-counting the max pref width. 3144 if (!addedTextIndent) { 3145 childMax += ti; 3146 beginMax += ti; 3147 addedTextIndent = true; 3148 } 3149 3150 if (childMin < 0) { 3151 textIndent = childMin; 3152 hasRemainingNegativeTextIndent = true; 3153 } 3154 } 3155 3156 // If we have no breakable characters at all, 3157 // then this is the easy case. We add ourselves to the current 3158 // min and max and continue. 3159 if (!hasBreakableChar) { 3160 inlineMin += childMin; 3161 } else { 3162 // We have a breakable character. Now we need to know if 3163 // we start and end with whitespace. 3164 if (beginWS) 3165 // Go ahead and end the current line. 3166 updatePreferredWidth(minLogicalWidth, inlineMin); 3167 else { 3168 inlineMin += beginMin; 3169 updatePreferredWidth(minLogicalWidth, inlineMin); 3170 childMin -= ti; 3171 } 3172 3173 inlineMin = childMin; 3174 3175 if (endWS) { 3176 // We end in whitespace, which means we can go ahead 3177 // and end our current line. 3178 updatePreferredWidth(minLogicalWidth, inlineMin); 3179 inlineMin = 0; 3180 shouldBreakLineAfterText = false; 3181 } else { 3182 updatePreferredWidth(minLogicalWidth, inlineMin); 3183 inlineMin = endMin; 3184 shouldBreakLineAfterText = true; 3185 } 3186 } 3187 3188 if (hasBreak) { 3189 inlineMax += beginMax; 3190 updatePreferredWidth(maxLogicalWidth, inlineMax); 3191 updatePreferredWidth(maxLogicalWidth, childMax); 3192 inlineMax = endMax; 3193 addedTextIndent = true; 3194 } else 3195 inlineMax += std::max<float>(0, childMax); 3196 } 3197 3198 // Ignore spaces after a list marker. 3199 if (child->isListMarker()) 3200 stripFrontSpaces = true; 3201 } else { 3202 updatePreferredWidth(minLogicalWidth, inlineMin); 3203 updatePreferredWidth(maxLogicalWidth, inlineMax); 3204 inlineMin = inlineMax = 0; 3205 stripFrontSpaces = true; 3206 trailingSpaceChild = 0; 3207 addedTextIndent = true; 3208 } 3209 3210 if (!child->isText() && child->isRenderInline()) 3211 isPrevChildInlineFlow = true; 3212 else 3213 isPrevChildInlineFlow = false; 3214 3215 oldAutoWrap = autoWrap; 3216 } 3217 3218 if (styleToUse.collapseWhiteSpace()) 3219 stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); 3220 3221 updatePreferredWidth(minLogicalWidth, inlineMin); 3222 updatePreferredWidth(maxLogicalWidth, inlineMax); 3223} 3224 3225void RenderBlock::computeBlockPreferredLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const 3226{ 3227 const RenderStyle& styleToUse = style(); 3228 bool nowrap = styleToUse.whiteSpace() == NOWRAP; 3229 3230 RenderObject* child = firstChild(); 3231 RenderBlock* containingBlock = this->containingBlock(); 3232 LayoutUnit floatLeftWidth = 0, floatRightWidth = 0; 3233 while (child) { 3234 // Positioned children don't affect the min/max width 3235 if (child->isOutOfFlowPositioned()) { 3236 child = child->nextSibling(); 3237 continue; 3238 } 3239 3240 const RenderStyle& childStyle = child->style(); 3241 if (child->isFloating() || (child->isBox() && toRenderBox(child)->avoidsFloats())) { 3242 LayoutUnit floatTotalWidth = floatLeftWidth + floatRightWidth; 3243 if (childStyle.clear() & CLEFT) { 3244 maxLogicalWidth = std::max(floatTotalWidth, maxLogicalWidth); 3245 floatLeftWidth = 0; 3246 } 3247 if (childStyle.clear() & CRIGHT) { 3248 maxLogicalWidth = std::max(floatTotalWidth, maxLogicalWidth); 3249 floatRightWidth = 0; 3250 } 3251 } 3252 3253 // A margin basically has three types: fixed, percentage, and auto (variable). 3254 // Auto and percentage margins simply become 0 when computing min/max width. 3255 // Fixed margins can be added in as is. 3256 Length startMarginLength = childStyle.marginStartUsing(&styleToUse); 3257 Length endMarginLength = childStyle.marginEndUsing(&styleToUse); 3258 LayoutUnit margin = 0; 3259 LayoutUnit marginStart = 0; 3260 LayoutUnit marginEnd = 0; 3261 if (startMarginLength.isFixed()) 3262 marginStart += startMarginLength.value(); 3263 if (endMarginLength.isFixed()) 3264 marginEnd += endMarginLength.value(); 3265 margin = marginStart + marginEnd; 3266 3267 LayoutUnit childMinPreferredLogicalWidth, childMaxPreferredLogicalWidth; 3268 if (child->isBox() && child->isHorizontalWritingMode() != isHorizontalWritingMode()) { 3269 RenderBox* childBox = toRenderBox(child); 3270 LogicalExtentComputedValues computedValues; 3271 childBox->computeLogicalHeight(childBox->borderAndPaddingLogicalHeight(), 0, computedValues); 3272 childMinPreferredLogicalWidth = childMaxPreferredLogicalWidth = computedValues.m_extent; 3273 } else { 3274 childMinPreferredLogicalWidth = child->minPreferredLogicalWidth(); 3275 childMaxPreferredLogicalWidth = child->maxPreferredLogicalWidth(); 3276 } 3277 3278 LayoutUnit w = childMinPreferredLogicalWidth + margin; 3279 minLogicalWidth = std::max(w, minLogicalWidth); 3280 3281 // IE ignores tables for calculation of nowrap. Makes some sense. 3282 if (nowrap && !child->isTable()) 3283 maxLogicalWidth = std::max(w, maxLogicalWidth); 3284 3285 w = childMaxPreferredLogicalWidth + margin; 3286 3287 if (!child->isFloating()) { 3288 if (child->isBox() && toRenderBox(child)->avoidsFloats()) { 3289 // Determine a left and right max value based off whether or not the floats can fit in the 3290 // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin 3291 // is smaller than the float width. 3292 bool ltr = containingBlock ? containingBlock->style().isLeftToRightDirection() : styleToUse.isLeftToRightDirection(); 3293 LayoutUnit marginLogicalLeft = ltr ? marginStart : marginEnd; 3294 LayoutUnit marginLogicalRight = ltr ? marginEnd : marginStart; 3295 LayoutUnit maxLeft = marginLogicalLeft > 0 ? std::max(floatLeftWidth, marginLogicalLeft) : floatLeftWidth + marginLogicalLeft; 3296 LayoutUnit maxRight = marginLogicalRight > 0 ? std::max(floatRightWidth, marginLogicalRight) : floatRightWidth + marginLogicalRight; 3297 w = childMaxPreferredLogicalWidth + maxLeft + maxRight; 3298 w = std::max(w, floatLeftWidth + floatRightWidth); 3299 } 3300 else 3301 maxLogicalWidth = std::max(floatLeftWidth + floatRightWidth, maxLogicalWidth); 3302 floatLeftWidth = floatRightWidth = 0; 3303 } 3304 3305 if (child->isFloating()) { 3306 if (childStyle.floating() == LeftFloat) 3307 floatLeftWidth += w; 3308 else 3309 floatRightWidth += w; 3310 } else 3311 maxLogicalWidth = std::max(w, maxLogicalWidth); 3312 3313 child = child->nextSibling(); 3314 } 3315 3316 // Always make sure these values are non-negative. 3317 minLogicalWidth = std::max<LayoutUnit>(0, minLogicalWidth); 3318 maxLogicalWidth = std::max<LayoutUnit>(0, maxLogicalWidth); 3319 3320 maxLogicalWidth = std::max(floatLeftWidth + floatRightWidth, maxLogicalWidth); 3321} 3322 3323bool RenderBlock::hasLineIfEmpty() const 3324{ 3325 if (!element()) 3326 return false; 3327 3328 if (element()->isRootEditableElement()) 3329 return true; 3330 3331 return false; 3332} 3333 3334LayoutUnit RenderBlock::lineHeight(bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const 3335{ 3336 // Inline blocks are replaced elements. Otherwise, just pass off to 3337 // the base class. If we're being queried as though we're the root line 3338 // box, then the fact that we're an inline-block is irrelevant, and we behave 3339 // just like a block. 3340 if (isReplaced() && linePositionMode == PositionOnContainingLine) 3341 return RenderBox::lineHeight(firstLine, direction, linePositionMode); 3342 3343 if (firstLine && document().styleSheetCollection().usesFirstLineRules()) { 3344 RenderStyle& s = firstLine ? firstLineStyle() : style(); 3345 if (&s != &style()) 3346 return s.computedLineHeight(); 3347 } 3348 3349 if (m_lineHeight == -1) 3350 m_lineHeight = style().computedLineHeight(); 3351 3352 return m_lineHeight; 3353} 3354 3355int RenderBlock::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const 3356{ 3357 // Inline blocks are replaced elements. Otherwise, just pass off to 3358 // the base class. If we're being queried as though we're the root line 3359 // box, then the fact that we're an inline-block is irrelevant, and we behave 3360 // just like a block. 3361 if (isReplaced() && linePositionMode == PositionOnContainingLine) { 3362 // For "leaf" theme objects, let the theme decide what the baseline position is. 3363 // FIXME: Might be better to have a custom CSS property instead, so that if the theme 3364 // is turned off, checkboxes/radios will still have decent baselines. 3365 // FIXME: Need to patch form controls to deal with vertical lines. 3366 if (style().hasAppearance() && !theme().isControlContainer(style().appearance())) 3367 return theme().baselinePosition(*this); 3368 3369 // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in 3370 // the normal flow. We make an exception for marquees, since their baselines are meaningless 3371 // (the content inside them moves). This matches WinIE as well, which just bottom-aligns them. 3372 // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled 3373 // vertically (e.g., an overflow:hidden block that has had scrollTop moved) or if the baseline is outside 3374 // of our content box. 3375 bool ignoreBaseline = (layer() && (layer()->marquee() || (direction == HorizontalLine ? (layer()->verticalScrollbar() || layer()->scrollYOffset() != 0) 3376 : (layer()->horizontalScrollbar() || layer()->scrollXOffset() != 0)))) || (isWritingModeRoot() && !isRubyRun()); 3377 3378 int baselinePos = ignoreBaseline ? -1 : inlineBlockBaseline(direction); 3379 3380 LayoutUnit bottomOfContent = direction == HorizontalLine ? borderTop() + paddingTop() + contentHeight() : borderRight() + paddingRight() + contentWidth(); 3381 if (baselinePos != -1 && baselinePos <= bottomOfContent) 3382 return direction == HorizontalLine ? marginTop() + baselinePos : marginRight() + baselinePos; 3383 3384 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode); 3385 } 3386 3387 const RenderStyle& style = firstLine ? firstLineStyle() : this->style(); 3388 const FontMetrics& fontMetrics = style.fontMetrics(); 3389 return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2; 3390} 3391 3392LayoutUnit RenderBlock::minLineHeightForReplacedRenderer(bool isFirstLine, LayoutUnit replacedHeight) const 3393{ 3394 if (!document().inNoQuirksMode() && replacedHeight) 3395 return replacedHeight; 3396 3397 const RenderStyle& style = isFirstLine ? firstLineStyle() : this->style(); 3398 if (!(style.lineBoxContain() & LineBoxContainBlock)) 3399 return 0; 3400 3401 return std::max<LayoutUnit>(replacedHeight, lineHeight(isFirstLine, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); 3402} 3403 3404int RenderBlock::firstLineBaseline() const 3405{ 3406 if (isWritingModeRoot() && !isRubyRun()) 3407 return -1; 3408 3409 for (RenderBox* curr = firstChildBox(); curr; curr = curr->nextSiblingBox()) { 3410 if (!curr->isFloatingOrOutOfFlowPositioned()) { 3411 int result = curr->firstLineBaseline(); 3412 if (result != -1) 3413 return curr->logicalTop() + result; // Translate to our coordinate space. 3414 } 3415 } 3416 3417 return -1; 3418} 3419 3420int RenderBlock::inlineBlockBaseline(LineDirectionMode lineDirection) const 3421{ 3422 if (isWritingModeRoot() && !isRubyRun()) 3423 return -1; 3424 3425 bool haveNormalFlowChild = false; 3426 for (auto box = lastChildBox(); box; box = box->previousSiblingBox()) { 3427 if (box->isFloatingOrOutOfFlowPositioned()) 3428 continue; 3429 haveNormalFlowChild = true; 3430 int result = box->inlineBlockBaseline(lineDirection); 3431 if (result != -1) 3432 return box->logicalTop() + result; // Translate to our coordinate space. 3433 } 3434 3435 if (!haveNormalFlowChild && hasLineIfEmpty()) { 3436 auto& fontMetrics = firstLineStyle().fontMetrics(); 3437 return fontMetrics.ascent() 3438 + (lineHeight(true, lineDirection, PositionOfInteriorLineBoxes) - fontMetrics.height()) / 2 3439 + (lineDirection == HorizontalLine ? borderTop() + paddingTop() : borderRight() + paddingRight()); 3440 } 3441 3442 return -1; 3443} 3444 3445RenderBlock* RenderBlock::firstLineBlock() const 3446{ 3447 RenderBlock* firstLineBlock = const_cast<RenderBlock*>(this); 3448 bool hasPseudo = false; 3449 while (true) { 3450 hasPseudo = firstLineBlock->style().hasPseudoStyle(FIRST_LINE); 3451 if (hasPseudo) 3452 break; 3453 RenderElement* parentBlock = firstLineBlock->parent(); 3454 // We include isRenderButton in this check because buttons are 3455 // implemented using flex box but should still support first-line. The 3456 // flex box spec requires that flex box does not support first-line, 3457 // though. 3458 // FIXME: Remove when buttons are implemented with align-items instead 3459 // of flexbox. 3460 if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() 3461 || !parentBlock || parentBlock->firstChild() != firstLineBlock || (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton())) 3462 break; 3463 firstLineBlock = toRenderBlock(parentBlock); 3464 } 3465 3466 if (!hasPseudo) 3467 return 0; 3468 3469 return firstLineBlock; 3470} 3471 3472static RenderStyle* styleForFirstLetter(RenderObject* firstLetterBlock, RenderObject* firstLetterContainer) 3473{ 3474 RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(FIRST_LETTER, &firstLetterContainer->firstLineStyle()); 3475 // Force inline display (except for floating first-letters). 3476 pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); 3477 // CSS2 says first-letter can't be positioned. 3478 pseudoStyle->setPosition(StaticPosition); 3479 return pseudoStyle; 3480} 3481 3482// CSS 2.1 http://www.w3.org/TR/CSS21/selector.html#first-letter 3483// "Punctuation (i.e, characters defined in Unicode [UNICODE] in the "open" (Ps), "close" (Pe), 3484// "initial" (Pi). "final" (Pf) and "other" (Po) punctuation classes), that precedes or follows the first letter should be included" 3485static inline bool isPunctuationForFirstLetter(UChar c) 3486{ 3487 return U_GET_GC_MASK(c) & (U_GC_PS_MASK | U_GC_PE_MASK | U_GC_PI_MASK | U_GC_PF_MASK | U_GC_PO_MASK); 3488} 3489 3490static inline bool shouldSkipForFirstLetter(UChar c) 3491{ 3492 return isSpaceOrNewline(c) || c == noBreakSpace || isPunctuationForFirstLetter(c); 3493} 3494 3495static inline RenderBlock* findFirstLetterBlock(RenderBlock* start) 3496{ 3497 RenderBlock* firstLetterBlock = start; 3498 while (true) { 3499 // We include isRenderButton in these two checks because buttons are 3500 // implemented using flex box but should still support first-letter. 3501 // The flex box spec requires that flex box does not support 3502 // first-letter, though. 3503 // FIXME: Remove when buttons are implemented with align-items instead 3504 // of flexbox. 3505 bool canHaveFirstLetterRenderer = firstLetterBlock->style().hasPseudoStyle(FIRST_LETTER) 3506 && firstLetterBlock->canHaveGeneratedChildren() 3507 && (!firstLetterBlock->isFlexibleBox() || firstLetterBlock->isRenderButton()); 3508 if (canHaveFirstLetterRenderer) 3509 return firstLetterBlock; 3510 3511 RenderElement* parentBlock = firstLetterBlock->parent(); 3512 if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock 3513 || (!parentBlock->isRenderBlockFlow() && !parentBlock->isRenderButton())) 3514 return 0; 3515 firstLetterBlock = toRenderBlock(parentBlock); 3516 } 3517 3518 return 0; 3519} 3520 3521void RenderBlock::updateFirstLetterStyle(RenderObject* firstLetterBlock, RenderObject* currentChild) 3522{ 3523 RenderElement* firstLetter = currentChild->parent(); 3524 RenderElement* firstLetterContainer = firstLetter->parent(); 3525 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); 3526 ASSERT(firstLetter->isFloating() || firstLetter->isInline()); 3527 3528 if (Style::determineChange(&firstLetter->style(), pseudoStyle) == Style::Detach) { 3529 // The first-letter renderer needs to be replaced. Create a new renderer of the right type. 3530 RenderBoxModelObject* newFirstLetter; 3531 if (pseudoStyle->display() == INLINE) 3532 newFirstLetter = new RenderInline(document(), *pseudoStyle); 3533 else 3534 newFirstLetter = new RenderBlockFlow(document(), *pseudoStyle); 3535 newFirstLetter->initializeStyle(); 3536 3537 // Move the first letter into the new renderer. 3538 LayoutStateDisabler layoutStateDisabler(&view()); 3539 while (RenderObject* child = firstLetter->firstChild()) { 3540 if (child->isText()) 3541 toRenderText(child)->removeAndDestroyTextBoxes(); 3542 firstLetter->removeChild(*child); 3543 newFirstLetter->addChild(child, 0); 3544 } 3545 3546 RenderObject* nextSibling = firstLetter->nextSibling(); 3547 if (RenderTextFragment* remainingText = toRenderBoxModelObject(firstLetter)->firstLetterRemainingText()) { 3548 ASSERT(remainingText->isAnonymous() || remainingText->textNode()->renderer() == remainingText); 3549 // Replace the old renderer with the new one. 3550 remainingText->setFirstLetter(*newFirstLetter); 3551 newFirstLetter->setFirstLetterRemainingText(remainingText); 3552 } 3553 // To prevent removal of single anonymous block in RenderBlock::removeChild and causing 3554 // |nextSibling| to go stale, we remove the old first letter using removeChildNode first. 3555 firstLetterContainer->removeChildInternal(*firstLetter, NotifyChildren); 3556 firstLetter->destroy(); 3557 firstLetter = newFirstLetter; 3558 firstLetterContainer->addChild(firstLetter, nextSibling); 3559 } else 3560 firstLetter->setStyle(*pseudoStyle); 3561} 3562 3563void RenderBlock::createFirstLetterRenderer(RenderObject* firstLetterBlock, RenderText* currentTextChild) 3564{ 3565 RenderElement* firstLetterContainer = currentTextChild->parent(); 3566 RenderStyle* pseudoStyle = styleForFirstLetter(firstLetterBlock, firstLetterContainer); 3567 RenderBoxModelObject* firstLetter = 0; 3568 if (pseudoStyle->display() == INLINE) 3569 firstLetter = new RenderInline(document(), *pseudoStyle); 3570 else 3571 firstLetter = new RenderBlockFlow(document(), *pseudoStyle); 3572 firstLetter->initializeStyle(); 3573 firstLetterContainer->addChild(firstLetter, currentTextChild); 3574 3575 // The original string is going to be either a generated content string or a DOM node's 3576 // string. We want the original string before it got transformed in case first-letter has 3577 // no text-transform or a different text-transform applied to it. 3578 String oldText = currentTextChild->originalText(); 3579 ASSERT(!oldText.isNull()); 3580 3581 if (!oldText.isEmpty()) { 3582 unsigned length = 0; 3583 3584 // Account for leading spaces and punctuation. 3585 while (length < oldText.length() && shouldSkipForFirstLetter(oldText[length])) 3586 length++; 3587 3588 // Account for first letter. 3589 length++; 3590 3591 // Keep looking for whitespace and allowed punctuation, but avoid 3592 // accumulating just whitespace into the :first-letter. 3593 for (unsigned scanLength = length; scanLength < oldText.length(); ++scanLength) { 3594 UChar c = oldText[scanLength]; 3595 3596 if (!shouldSkipForFirstLetter(c)) 3597 break; 3598 3599 if (isPunctuationForFirstLetter(c)) 3600 length = scanLength + 1; 3601 } 3602 3603 // Construct a text fragment for the text after the first letter. 3604 // This text fragment might be empty. 3605 RenderTextFragment* remainingText; 3606 if (currentTextChild->textNode()) 3607 remainingText = new RenderTextFragment(*currentTextChild->textNode(), oldText, length, oldText.length() - length); 3608 else 3609 remainingText = new RenderTextFragment(document(), oldText, length, oldText.length() - length); 3610 3611 if (remainingText->textNode()) 3612 remainingText->textNode()->setRenderer(remainingText); 3613 3614 firstLetterContainer->addChild(remainingText, currentTextChild); 3615 firstLetterContainer->removeChild(*currentTextChild); 3616 remainingText->setFirstLetter(*firstLetter); 3617 firstLetter->setFirstLetterRemainingText(remainingText); 3618 3619 // construct text fragment for the first letter 3620 RenderTextFragment* letter; 3621 if (remainingText->textNode()) 3622 letter = new RenderTextFragment(*remainingText->textNode(), oldText, 0, length); 3623 else 3624 letter = new RenderTextFragment(document(), oldText, 0, length); 3625 3626 firstLetter->addChild(letter); 3627 3628 currentTextChild->destroy(); 3629 } 3630} 3631 3632void RenderBlock::getFirstLetter(RenderObject*& firstLetter, RenderElement*& firstLetterContainer, RenderObject* skipObject) 3633{ 3634 firstLetter = nullptr; 3635 firstLetterContainer = nullptr; 3636 3637 if (!document().styleSheetCollection().usesFirstLetterRules()) 3638 return; 3639 3640 // Don't recur 3641 if (style().styleType() == FIRST_LETTER) 3642 return; 3643 3644 // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find 3645 // an efficient way to check for that situation though before implementing anything. 3646 firstLetterContainer = findFirstLetterBlock(this); 3647 if (!firstLetterContainer) 3648 return; 3649 3650 // Drill into inlines looking for our first text descendant. 3651 firstLetter = firstLetterContainer->firstChild(); 3652 while (firstLetter) { 3653 if (firstLetter->isText()) { 3654 if (firstLetter == skipObject) { 3655 firstLetter = firstLetter->nextSibling(); 3656 continue; 3657 } 3658 3659 break; 3660 } 3661 3662 RenderElement& current = toRenderElement(*firstLetter); 3663 if (current.isListMarker()) 3664 firstLetter = current.nextSibling(); 3665 else if (current.isFloatingOrOutOfFlowPositioned()) { 3666 if (current.style().styleType() == FIRST_LETTER) { 3667 firstLetter = current.firstChild(); 3668 break; 3669 } 3670 firstLetter = current.nextSibling(); 3671 } else if (current.isReplaced() || current.isRenderButton() || current.isMenuList()) 3672 break; 3673 else if (current.style().hasPseudoStyle(FIRST_LETTER) && current.canHaveGeneratedChildren()) { 3674 // We found a lower-level node with first-letter, which supersedes the higher-level style 3675 firstLetterContainer = ¤t; 3676 firstLetter = current.firstChild(); 3677 } else 3678 firstLetter = current.firstChild(); 3679 } 3680 3681 if (!firstLetter) 3682 firstLetterContainer = nullptr; 3683} 3684 3685void RenderBlock::updateFirstLetter() 3686{ 3687 RenderObject* firstLetterObj; 3688 RenderElement* firstLetterContainer; 3689 getFirstLetter(firstLetterObj, firstLetterContainer); 3690 3691 if (!firstLetterObj) 3692 return; 3693 3694 // If the child already has style, then it has already been created, so we just want 3695 // to update it. 3696 if (firstLetterObj->parent()->style().styleType() == FIRST_LETTER) { 3697 updateFirstLetterStyle(firstLetterContainer, firstLetterObj); 3698 return; 3699 } 3700 3701 if (!firstLetterObj->isText()) 3702 return; 3703 3704 // Our layout state is not valid for the repaints we are going to trigger by 3705 // adding and removing children of firstLetterContainer. 3706 LayoutStateDisabler layoutStateDisabler(&view()); 3707 3708 createFirstLetterRenderer(firstLetterContainer, toRenderText(firstLetterObj)); 3709} 3710 3711LayoutUnit RenderBlock::paginationStrut() const 3712{ 3713 RenderBlockRareData* rareData = getRareData(this); 3714 return rareData ? rareData->m_paginationStrut : LayoutUnit(); 3715} 3716 3717LayoutUnit RenderBlock::pageLogicalOffset() const 3718{ 3719 RenderBlockRareData* rareData = getRareData(this); 3720 return rareData ? rareData->m_pageLogicalOffset : LayoutUnit(); 3721} 3722 3723void RenderBlock::setPaginationStrut(LayoutUnit strut) 3724{ 3725 RenderBlockRareData* rareData = getRareData(this); 3726 if (!rareData) { 3727 if (!strut) 3728 return; 3729 rareData = &ensureRareData(this); 3730 } 3731 rareData->m_paginationStrut = strut; 3732} 3733 3734void RenderBlock::setPageLogicalOffset(LayoutUnit logicalOffset) 3735{ 3736 RenderBlockRareData* rareData = getRareData(this); 3737 if (!rareData) { 3738 if (!logicalOffset) 3739 return; 3740 rareData = &ensureRareData(this); 3741 } 3742 rareData->m_pageLogicalOffset = logicalOffset; 3743} 3744 3745void RenderBlock::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const 3746{ 3747 // For blocks inside inlines, we go ahead and include margins so that we run right up to the 3748 // inline boxes above and below us (thus getting merged with them to form a single irregular 3749 // shape). 3750 if (isAnonymousBlockContinuation()) { 3751 // FIXME: This is wrong for block-flows that are horizontal. 3752 // https://bugs.webkit.org/show_bug.cgi?id=46781 3753 rects.append(pixelSnappedIntRect(accumulatedOffset.x(), accumulatedOffset.y() - collapsedMarginBefore(), 3754 width(), height() + collapsedMarginBefore() + collapsedMarginAfter())); 3755 continuation()->absoluteRects(rects, accumulatedOffset - toLayoutSize(location() + 3756 inlineElementContinuation()->containingBlock()->location())); 3757 } else 3758 rects.append(pixelSnappedIntRect(accumulatedOffset, size())); 3759} 3760 3761void RenderBlock::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const 3762{ 3763 // For blocks inside inlines, we go ahead and include margins so that we run right up to the 3764 // inline boxes above and below us (thus getting merged with them to form a single irregular 3765 // shape). 3766 FloatRect localRect = isAnonymousBlockContinuation() 3767 ? FloatRect(0, -collapsedMarginBefore(), width(), height() + collapsedMarginBefore() + collapsedMarginAfter()) 3768 : FloatRect(0, 0, width(), height()); 3769 3770 // FIXME: This is wrong for block-flows that are horizontal. 3771 // https://bugs.webkit.org/show_bug.cgi?id=46781 3772 RenderFlowThread* flowThread = flowThreadContainingBlock(); 3773 if (!flowThread || !flowThread->absoluteQuadsForBox(quads, wasFixed, this, localRect.y(), localRect.maxY())) 3774 quads.append(localToAbsoluteQuad(localRect, 0 /* mode */, wasFixed)); 3775 3776 if (isAnonymousBlockContinuation()) 3777 continuation()->absoluteQuads(quads, wasFixed); 3778} 3779 3780LayoutRect RenderBlock::rectWithOutlineForRepaint(const RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const 3781{ 3782 LayoutRect r(RenderBox::rectWithOutlineForRepaint(repaintContainer, outlineWidth)); 3783 if (isAnonymousBlockContinuation()) 3784 r.inflateY(collapsedMarginBefore()); // FIXME: This is wrong for block-flows that are horizontal. 3785 return r; 3786} 3787 3788RenderElement* RenderBlock::hoverAncestor() const 3789{ 3790 return isAnonymousBlockContinuation() ? continuation() : RenderBox::hoverAncestor(); 3791} 3792 3793void RenderBlock::updateDragState(bool dragOn) 3794{ 3795 RenderBox::updateDragState(dragOn); 3796 if (RenderBoxModelObject* continuation = this->continuation()) 3797 continuation->updateDragState(dragOn); 3798} 3799 3800const RenderStyle& RenderBlock::outlineStyleForRepaint() const 3801{ 3802 return isAnonymousBlockContinuation() ? continuation()->style() : style(); 3803} 3804 3805void RenderBlock::childBecameNonInline(RenderObject*) 3806{ 3807 makeChildrenNonInline(); 3808 if (isAnonymousBlock() && parent() && parent()->isRenderBlock()) 3809 toRenderBlock(parent())->removeLeftoverAnonymousBlock(this); 3810 // |this| may be dead here 3811} 3812 3813void RenderBlock::updateHitTestResult(HitTestResult& result, const LayoutPoint& point) 3814{ 3815 if (result.innerNode()) 3816 return; 3817 3818 if (Node* n = nodeForHitTest()) { 3819 result.setInnerNode(n); 3820 if (!result.innerNonSharedNode()) 3821 result.setInnerNonSharedNode(n); 3822 result.setLocalPoint(point); 3823 } 3824} 3825 3826LayoutRect RenderBlock::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine) 3827{ 3828 // Do the normal calculation in most cases. 3829 if (firstChild()) 3830 return RenderBox::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine); 3831 3832 LayoutRect caretRect = localCaretRectForEmptyElement(width(), textIndentOffset()); 3833 3834 // FIXME: Does this need to adjust for vertical orientation? 3835 if (extraWidthToEndOfLine) 3836 *extraWidthToEndOfLine = width() - caretRect.maxX(); 3837 3838 return caretRect; 3839} 3840 3841void RenderBlock::addFocusRingRectsForInlineChildren(Vector<IntRect>&, const LayoutPoint&, const RenderLayerModelObject*) 3842{ 3843 ASSERT_NOT_REACHED(); 3844} 3845 3846void RenderBlock::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer) 3847{ 3848 // For blocks inside inlines, we go ahead and include margins so that we run right up to the 3849 // inline boxes above and below us (thus getting merged with them to form a single irregular 3850 // shape). 3851 if (inlineElementContinuation()) { 3852 // FIXME: This check really isn't accurate. 3853 bool nextInlineHasLineBox = inlineElementContinuation()->firstLineBox(); 3854 // FIXME: This is wrong. The principal renderer may not be the continuation preceding this block. 3855 // FIXME: This is wrong for block-flows that are horizontal. 3856 // https://bugs.webkit.org/show_bug.cgi?id=46781 3857 bool prevInlineHasLineBox = toRenderInline(inlineElementContinuation()->element()->renderer())->firstLineBox(); 3858 float topMargin = prevInlineHasLineBox ? collapsedMarginBefore() : LayoutUnit(); 3859 float bottomMargin = nextInlineHasLineBox ? collapsedMarginAfter() : LayoutUnit(); 3860 LayoutRect rect(additionalOffset.x(), additionalOffset.y() - topMargin, width(), height() + topMargin + bottomMargin); 3861 if (!rect.isEmpty()) 3862 rects.append(pixelSnappedIntRect(rect)); 3863 } else if (width() && height()) 3864 rects.append(pixelSnappedIntRect(additionalOffset, size())); 3865 3866 if (!hasOverflowClip() && !hasControlClip()) { 3867 if (childrenInline()) 3868 addFocusRingRectsForInlineChildren(rects, additionalOffset, paintContainer); 3869 3870 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { 3871 if (!curr->isText() && !curr->isListMarker() && curr->isBox()) { 3872 RenderBox* box = toRenderBox(curr); 3873 FloatPoint pos; 3874 // FIXME: This doesn't work correctly with transforms. 3875 if (box->layer()) 3876 pos = curr->localToContainerPoint(FloatPoint(), paintContainer); 3877 else 3878 pos = FloatPoint(additionalOffset.x() + box->x(), additionalOffset.y() + box->y()); 3879 box->addFocusRingRects(rects, flooredLayoutPoint(pos), paintContainer); 3880 } 3881 } 3882 } 3883 3884 if (inlineElementContinuation()) 3885 inlineElementContinuation()->addFocusRingRects(rects, flooredLayoutPoint(LayoutPoint(additionalOffset + inlineElementContinuation()->containingBlock()->location() - location())), paintContainer); 3886} 3887 3888RenderBox* RenderBlock::createAnonymousBoxWithSameTypeAs(const RenderObject* parent) const 3889{ 3890 return createAnonymousWithParentRendererAndDisplay(parent, style().display()); 3891} 3892 3893LayoutUnit RenderBlock::offsetFromLogicalTopOfFirstPage() const 3894{ 3895 LayoutState* layoutState = view().layoutState(); 3896 if (layoutState && !layoutState->isPaginated()) 3897 return 0; 3898 3899 RenderFlowThread* flowThread = flowThreadContainingBlock(); 3900 if (flowThread) 3901 return flowThread->offsetFromLogicalTopOfFirstRegion(this); 3902 3903 if (layoutState) { 3904 ASSERT(layoutState->m_renderer == this); 3905 3906 LayoutSize offsetDelta = layoutState->m_layoutOffset - layoutState->m_pageOffset; 3907 return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width(); 3908 } 3909 3910 ASSERT_NOT_REACHED(); 3911 return 0; 3912} 3913 3914RenderRegion* RenderBlock::regionAtBlockOffset(LayoutUnit blockOffset) const 3915{ 3916 RenderFlowThread* flowThread = flowThreadContainingBlock(); 3917 if (!flowThread || !flowThread->hasValidRegionInfo()) 3918 return 0; 3919 3920 return flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstPage() + blockOffset, true); 3921} 3922 3923static bool canComputeRegionRangeForBox(const RenderBlock* parentBlock, const RenderBox& childBox, const RenderFlowThread* flowThreadContainingBlock) 3924{ 3925 ASSERT(parentBlock); 3926 ASSERT(!childBox.isRenderFlowThread()); 3927 3928 if (!flowThreadContainingBlock) 3929 return false; 3930 3931 if (!flowThreadContainingBlock->hasRegions()) 3932 return false; 3933 3934 if (!childBox.canHaveOutsideRegionRange()) 3935 return false; 3936 3937 return flowThreadContainingBlock->hasCachedRegionRangeForBox(parentBlock); 3938} 3939 3940bool RenderBlock::childBoxIsUnsplittableForFragmentation(const RenderBox& child) const 3941{ 3942 RenderFlowThread* flowThread = flowThreadContainingBlock(); 3943 bool checkColumnBreaks = flowThread && flowThread->shouldCheckColumnBreaks(); 3944 bool checkPageBreaks = !checkColumnBreaks && view().layoutState()->m_pageLogicalHeight; 3945 bool checkRegionBreaks = flowThread && flowThread->isRenderNamedFlowThread(); 3946 return child.isUnsplittableForPagination() || (checkColumnBreaks && child.style().columnBreakInside() == PBAVOID) 3947 || (checkPageBreaks && child.style().pageBreakInside() == PBAVOID) 3948 || (checkRegionBreaks && child.style().regionBreakInside() == PBAVOID); 3949} 3950 3951void RenderBlock::computeRegionRangeForBoxChild(const RenderBox& box) const 3952{ 3953 RenderFlowThread* flowThread = flowThreadContainingBlock(); 3954 ASSERT(canComputeRegionRangeForBox(this, box, flowThread)); 3955 3956 RenderRegion* startRegion; 3957 RenderRegion* endRegion; 3958 LayoutUnit offsetFromLogicalTopOfFirstRegion = box.offsetFromLogicalTopOfFirstPage(); 3959 if (childBoxIsUnsplittableForFragmentation(box)) 3960 startRegion = endRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion, true); 3961 else { 3962 startRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion, true); 3963 endRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion + logicalHeightForChild(box), true); 3964 } 3965 3966 flowThread->setRegionRangeForBox(&box, startRegion, endRegion); 3967} 3968 3969void RenderBlock::estimateRegionRangeForBoxChild(const RenderBox& box) const 3970{ 3971 RenderFlowThread* flowThread = flowThreadContainingBlock(); 3972 if (!canComputeRegionRangeForBox(this, box, flowThread)) 3973 return; 3974 3975 if (childBoxIsUnsplittableForFragmentation(box)) { 3976 computeRegionRangeForBoxChild(box); 3977 return; 3978 } 3979 3980 LogicalExtentComputedValues estimatedValues; 3981 box.computeLogicalHeight(RenderFlowThread::maxLogicalHeight(), logicalTopForChild(box), estimatedValues); 3982 3983 LayoutUnit offsetFromLogicalTopOfFirstRegion = box.offsetFromLogicalTopOfFirstPage(); 3984 RenderRegion* startRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion, true); 3985 RenderRegion* endRegion = flowThread->regionAtBlockOffset(this, offsetFromLogicalTopOfFirstRegion + estimatedValues.m_extent, true); 3986 3987 flowThread->setRegionRangeForBox(&box, startRegion, endRegion); 3988} 3989 3990bool RenderBlock::updateRegionRangeForBoxChild(const RenderBox& box) const 3991{ 3992 RenderFlowThread* flowThread = flowThreadContainingBlock(); 3993 if (!canComputeRegionRangeForBox(this, box, flowThread)) 3994 return false; 3995 3996 RenderRegion* startRegion = nullptr; 3997 RenderRegion* endRegion = nullptr; 3998 flowThread->getRegionRangeForBox(&box, startRegion, endRegion); 3999 4000 computeRegionRangeForBoxChild(box); 4001 4002 RenderRegion* newStartRegion = nullptr; 4003 RenderRegion* newEndRegion = nullptr; 4004 flowThread->getRegionRangeForBox(&box, newStartRegion, newEndRegion); 4005 4006 4007 // Changing the start region means we shift everything and a relayout is needed. 4008 if (newStartRegion != startRegion) 4009 return true; 4010 4011 // The region range of the box has changed. Some boxes (e.g floats) may have been positioned assuming 4012 // a different range. 4013 if (box.needsLayoutAfterRegionRangeChange() && newEndRegion != endRegion) 4014 return true; 4015 4016 return false; 4017} 4018 4019LayoutUnit RenderBlock::collapsedMarginBeforeForChild(const RenderBox& child) const 4020{ 4021 // If the child has the same directionality as we do, then we can just return its 4022 // collapsed margin. 4023 if (!child.isWritingModeRoot()) 4024 return child.collapsedMarginBefore(); 4025 4026 // The child has a different directionality. If the child is parallel, then it's just 4027 // flipped relative to us. We can use the collapsed margin for the opposite edge. 4028 if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) 4029 return child.collapsedMarginAfter(); 4030 4031 // The child is perpendicular to us, which means its margins don't collapse but are on the 4032 // "logical left/right" sides of the child box. We can just return the raw margin in this case. 4033 return marginBeforeForChild(child); 4034} 4035 4036LayoutUnit RenderBlock::collapsedMarginAfterForChild(const RenderBox& child) const 4037{ 4038 // If the child has the same directionality as we do, then we can just return its 4039 // collapsed margin. 4040 if (!child.isWritingModeRoot()) 4041 return child.collapsedMarginAfter(); 4042 4043 // The child has a different directionality. If the child is parallel, then it's just 4044 // flipped relative to us. We can use the collapsed margin for the opposite edge. 4045 if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) 4046 return child.collapsedMarginBefore(); 4047 4048 // The child is perpendicular to us, which means its margins don't collapse but are on the 4049 // "logical left/right" side of the child box. We can just return the raw margin in this case. 4050 return marginAfterForChild(child); 4051} 4052 4053bool RenderBlock::hasMarginBeforeQuirk(const RenderBox& child) const 4054{ 4055 // If the child has the same directionality as we do, then we can just return its 4056 // margin quirk. 4057 if (!child.isWritingModeRoot()) 4058 return child.isRenderBlock() ? toRenderBlock(child).hasMarginBeforeQuirk() : child.style().hasMarginBeforeQuirk(); 4059 4060 // The child has a different directionality. If the child is parallel, then it's just 4061 // flipped relative to us. We can use the opposite edge. 4062 if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) 4063 return child.isRenderBlock() ? toRenderBlock(child).hasMarginAfterQuirk() : child.style().hasMarginAfterQuirk(); 4064 4065 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about 4066 // whether or not authors specified quirky ems, since they're an implementation detail. 4067 return false; 4068} 4069 4070bool RenderBlock::hasMarginAfterQuirk(const RenderBox& child) const 4071{ 4072 // If the child has the same directionality as we do, then we can just return its 4073 // margin quirk. 4074 if (!child.isWritingModeRoot()) 4075 return child.isRenderBlock() ? toRenderBlock(child).hasMarginAfterQuirk() : child.style().hasMarginAfterQuirk(); 4076 4077 // The child has a different directionality. If the child is parallel, then it's just 4078 // flipped relative to us. We can use the opposite edge. 4079 if (child.isHorizontalWritingMode() == isHorizontalWritingMode()) 4080 return child.isRenderBlock() ? toRenderBlock(child).hasMarginBeforeQuirk() : child.style().hasMarginBeforeQuirk(); 4081 4082 // The child is perpendicular to us and box sides are never quirky in html.css, and we don't really care about 4083 // whether or not authors specified quirky ems, since they're an implementation detail. 4084 return false; 4085} 4086 4087const char* RenderBlock::renderName() const 4088{ 4089 if (isBody()) 4090 return "RenderBody"; // FIXME: Temporary hack until we know that the regression tests pass. 4091 4092 if (isFloating()) 4093 return "RenderBlock (floating)"; 4094 if (isOutOfFlowPositioned()) 4095 return "RenderBlock (positioned)"; 4096 if (isAnonymousBlock()) 4097 return "RenderBlock (anonymous)"; 4098 // FIXME: Temporary hack while the new generated content system is being implemented. 4099 if (isPseudoElement()) 4100 return "RenderBlock (generated)"; 4101 if (isAnonymous()) 4102 return "RenderBlock (generated)"; 4103 if (isRelPositioned()) 4104 return "RenderBlock (relative positioned)"; 4105 if (isStickyPositioned()) 4106 return "RenderBlock (sticky positioned)"; 4107 return "RenderBlock"; 4108} 4109 4110template <typename CharacterType> 4111static inline TextRun constructTextRunInternal(RenderObject* context, const Font& font, const CharacterType* characters, int length, const RenderStyle& style, TextRun::ExpansionBehavior expansion) 4112{ 4113 TextDirection textDirection = LTR; 4114 bool directionalOverride = style.rtlOrdering() == VisualOrder; 4115 4116 TextRun run(characters, length, 0, 0, expansion, textDirection, directionalOverride); 4117 if (font.primaryFont()->isSVGFont()) { 4118 ASSERT(context); // FIXME: Thread a RenderObject& to this point so we don't have to dereference anything. 4119 run.setRenderingContext(SVGTextRunRenderingContext::create(*context)); 4120 } 4121 4122 return run; 4123} 4124 4125template <typename CharacterType> 4126static inline TextRun constructTextRunInternal(RenderObject* context, const Font& font, const CharacterType* characters, int length, const RenderStyle& style, TextRun::ExpansionBehavior expansion, TextRunFlags flags) 4127{ 4128 TextDirection textDirection = LTR; 4129 bool directionalOverride = style.rtlOrdering() == VisualOrder; 4130 if (flags != DefaultTextRunFlags) { 4131 if (flags & RespectDirection) 4132 textDirection = style.direction(); 4133 if (flags & RespectDirectionOverride) 4134 directionalOverride |= isOverride(style.unicodeBidi()); 4135 } 4136 TextRun run(characters, length, 0, 0, expansion, textDirection, directionalOverride); 4137 if (font.primaryFont()->isSVGFont()) { 4138 ASSERT(context); // FIXME: Thread a RenderObject& to this point so we don't have to dereference anything. 4139 run.setRenderingContext(SVGTextRunRenderingContext::create(*context)); 4140 } 4141 4142 return run; 4143} 4144 4145TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const LChar* characters, int length, const RenderStyle& style, TextRun::ExpansionBehavior expansion) 4146{ 4147 return constructTextRunInternal(context, font, characters, length, style, expansion); 4148} 4149 4150TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const UChar* characters, int length, const RenderStyle& style, TextRun::ExpansionBehavior expansion) 4151{ 4152 return constructTextRunInternal(context, font, characters, length, style, expansion); 4153} 4154 4155TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const RenderText* text, const RenderStyle& style, TextRun::ExpansionBehavior expansion) 4156{ 4157 if (text->is8Bit()) 4158 return constructTextRunInternal(context, font, text->characters8(), text->textLength(), style, expansion); 4159 return constructTextRunInternal(context, font, text->characters16(), text->textLength(), style, expansion); 4160} 4161 4162TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const RenderText* text, unsigned offset, unsigned length, const RenderStyle& style, TextRun::ExpansionBehavior expansion) 4163{ 4164 ASSERT(offset + length <= text->textLength()); 4165 if (text->is8Bit()) 4166 return constructTextRunInternal(context, font, text->characters8() + offset, length, style, expansion); 4167 return constructTextRunInternal(context, font, text->characters16() + offset, length, style, expansion); 4168} 4169 4170TextRun RenderBlock::constructTextRun(RenderObject* context, const Font& font, const String& string, const RenderStyle& style, TextRun::ExpansionBehavior expansion, TextRunFlags flags) 4171{ 4172 unsigned length = string.length(); 4173 4174 if (!length || string.is8Bit()) 4175 return constructTextRunInternal(context, font, string.characters8(), length, style, expansion, flags); 4176 return constructTextRunInternal(context, font, string.characters16(), length, style, expansion, flags); 4177} 4178 4179RenderBlock* RenderBlock::createAnonymousWithParentRendererAndDisplay(const RenderObject* parent, EDisplay display) 4180{ 4181 // FIXME: Do we need to convert all our inline displays to block-type in the anonymous logic ? 4182 RenderBlock* newBox; 4183 if (display == FLEX || display == INLINE_FLEX) 4184 newBox = new RenderFlexibleBox(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), FLEX)); 4185 else 4186 newBox = new RenderBlockFlow(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), BLOCK)); 4187 4188 newBox->initializeStyle(); 4189 return newBox; 4190} 4191 4192#ifndef NDEBUG 4193void RenderBlock::checkPositionedObjectsNeedLayout() 4194{ 4195 if (!gPositionedDescendantsMap) 4196 return; 4197 4198 TrackedRendererListHashSet* positionedDescendantSet = positionedObjects(); 4199 if (!positionedDescendantSet) 4200 return; 4201 4202 for (auto it = positionedDescendantSet->begin(), end = positionedDescendantSet->end(); it != end; ++it) { 4203 RenderBox* currBox = *it; 4204 ASSERT(!currBox->needsLayout()); 4205 } 4206} 4207 4208void RenderBlock::showLineTreeAndMark(const InlineBox*, const char*, const InlineBox*, const char*, const RenderObject*) const 4209{ 4210 showRenderObject(); 4211} 4212 4213#endif 4214 4215} // namespace WebCore 4216