Changeset 60494 in webkit
- Timestamp:
- Jun 1, 2010 11:58:30 AM (14 years ago)
- Location:
- trunk
- Files:
-
- 8 added
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r60493 r60494 1 2010-05-28 David Hyatt <hyatt@apple.com> 2 3 Reviewed by Beth Dakin. 4 5 https://bugs.webkit.org/show_bug.cgi?id=15550, complete implementation of column-span. Add support for nested column 6 spans. When a column span is nested inside multiple enclosing blocks, the blocks have to be split around the column-span. 7 We do this using block element continuations, the same kind of solution we employed for blocks inside inlines. 8 9 The code for block continuations is very similar to the code for inline continuations. It may be possible to refactor the 10 code into RenderBoxModelObject so that more of it can be shared, but this first pass avoids that so as not to risk 11 causing any regressions in core rendering. 12 13 Note also that - just as with inline continuations - you can't unsplit block continuations yet. There is no technical limitation 14 here... the functions just need to be written to handle it. 15 16 Added new tests in fast/multicol/span. 17 18 * rendering/RenderBlock.cpp: 19 (WebCore::RenderBlock::styleDidChange): 20 (WebCore::RenderBlock::continuationBefore): 21 (WebCore::RenderBlock::addChildToContinuation): 22 (WebCore::RenderBlock::containingColumnsBlock): 23 (WebCore::RenderBlock::clone): 24 (WebCore::RenderBlock::splitBlocks): 25 (WebCore::RenderBlock::splitFlow): 26 (WebCore::RenderBlock::splitAnonymousBlocksAroundChild): 27 (WebCore::RenderBlock::makeChildrenAnonymousColumnBlocks): 28 (WebCore::RenderBlock::columnsBlockForSpanningElement): 29 (WebCore::RenderBlock::addChildIgnoringAnonymousColumnBlocks): 30 (WebCore::RenderBlock::addChild): 31 (WebCore::RenderBlock::addChildIgnoringContinuation): 32 (WebCore::RenderBlock::blockElementContinuation): 33 (WebCore::RenderBlock::layoutColumns): 34 * rendering/RenderBlock.h: 35 1 36 2010-06-01 Alexey Proskuryakov <ap@apple.com> 2 37 -
trunk/WebCore/rendering/RenderBlock.cpp
r60408 r60494 231 231 RenderBox::styleDidChange(diff, oldStyle); 232 232 233 if (!isAnonymousBlock()) { 234 // Ensure that all of our continuation blocks pick up the new style. 235 for (RenderBlock* currCont = blockElementContinuation(); currCont; currCont = currCont->blockElementContinuation()) { 236 RenderBoxModelObject* nextCont = currCont->continuation(); 237 currCont->setContinuation(0); 238 currCont->setStyle(style()); 239 currCont->setContinuation(nextCont); 240 } 241 } 242 233 243 // FIXME: We could save this call when the change only affected non-inherited properties 234 244 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { … … 265 275 } 266 276 277 RenderBlock* RenderBlock::continuationBefore(RenderObject* beforeChild) 278 { 279 if (beforeChild && beforeChild->parent() == this) 280 return this; 281 282 RenderBlock* curr = toRenderBlock(continuation()); 283 RenderBlock* nextToLast = this; 284 RenderBlock* last = this; 285 while (curr) { 286 if (beforeChild && beforeChild->parent() == curr) { 287 if (curr->firstChild() == beforeChild) 288 return last; 289 return curr; 290 } 291 292 nextToLast = last; 293 last = curr; 294 curr = toRenderBlock(curr->continuation()); 295 } 296 297 if (!beforeChild && !last->firstChild()) 298 return nextToLast; 299 return last; 300 } 301 302 void RenderBlock::addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild) 303 { 304 RenderBlock* flow = continuationBefore(beforeChild); 305 ASSERT(!beforeChild || beforeChild->parent()->isAnonymousColumnSpanBlock() || beforeChild->parent()->isRenderBlock()); 306 RenderBoxModelObject* beforeChildParent = 0; 307 if (beforeChild) 308 beforeChildParent = toRenderBoxModelObject(beforeChild->parent()); 309 else { 310 RenderBoxModelObject* cont = flow->continuation(); 311 if (cont) 312 beforeChildParent = cont; 313 else 314 beforeChildParent = flow; 315 } 316 317 if (newChild->isFloatingOrPositioned()) 318 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 319 320 // A continuation always consists of two potential candidates: a block or an anonymous 321 // column span box holding column span children. 322 bool childIsNormal = newChild->isInline() || !newChild->style()->columnSpan(); 323 bool bcpIsNormal = beforeChildParent->isInline() || !beforeChildParent->style()->columnSpan(); 324 bool flowIsNormal = flow->isInline() || !flow->style()->columnSpan(); 325 326 if (flow == beforeChildParent) 327 return flow->addChildIgnoringContinuation(newChild, beforeChild); 328 329 // The goal here is to match up if we can, so that we can coalesce and create the 330 // minimal # of continuations needed for the inline. 331 if (childIsNormal == bcpIsNormal) 332 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 333 if (flowIsNormal == childIsNormal) 334 return flow->addChildIgnoringContinuation(newChild, 0); // Just treat like an append. 335 return beforeChildParent->addChildIgnoringContinuation(newChild, beforeChild); 336 } 337 338 267 339 void RenderBlock::addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild) 268 340 { … … 311 383 } 312 384 385 RenderBlock* RenderBlock::containingColumnsBlock(bool allowAnonymousColumnBlock) 386 { 387 for (RenderObject* curr = this; curr; curr = curr->parent()) { 388 if (!curr->isRenderBlock() || curr->isFloatingOrPositioned() || curr->isTableCell() || curr->isRoot() || curr->isRenderView() || curr->hasOverflowClip() 389 || curr->isInlineBlockOrInlineTable()) 390 return 0; 391 392 RenderBlock* currBlock = toRenderBlock(curr); 393 if (currBlock->style()->specifiesColumns() && (allowAnonymousColumnBlock || !currBlock->isAnonymousColumnsBlock())) 394 return currBlock; 395 396 if (currBlock->isAnonymousColumnSpanBlock()) 397 return 0; 398 } 399 return 0; 400 } 401 402 RenderBlock* RenderBlock::clone() const 403 { 404 RenderBlock* o = new (renderArena()) RenderBlock(node()); 405 o->setStyle(style()); 406 o->setChildrenInline(childrenInline()); 407 return o; 408 } 409 410 void RenderBlock::splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, 411 RenderBlock* middleBlock, 412 RenderObject* beforeChild, RenderBoxModelObject* oldCont) 413 { 414 // Create a clone of this inline. 415 RenderBlock* cloneBlock = clone(); 416 cloneBlock->setContinuation(oldCont); 417 418 // Now take all of the children from beforeChild to the end and remove 419 // them from |this| and place them in the clone. 420 if (!beforeChild && isAfterContent(lastChild())) 421 beforeChild = lastChild(); 422 moveChildrenTo(cloneBlock, beforeChild, 0); 423 424 // Hook |clone| up as the continuation of the middle block. 425 middleBlock->setContinuation(cloneBlock); 426 427 // We have been reparented and are now under the fromBlock. We need 428 // to walk up our block parent chain until we hit the containing anonymous columns block. 429 // Once we hit the anonymous columns block we're done. 430 RenderBoxModelObject* curr = toRenderBoxModelObject(parent()); 431 RenderBoxModelObject* currChild = this; 432 433 while (curr && curr != fromBlock) { 434 ASSERT(curr->isRenderBlock() && !curr->isAnonymousBlock()); 435 436 RenderBlock* blockCurr = toRenderBlock(curr); 437 438 // Create a new clone. 439 RenderBlock* cloneChild = cloneBlock; 440 cloneBlock = blockCurr->clone(); 441 442 // Insert our child clone as the first child. 443 cloneBlock->children()->appendChildNode(cloneBlock, cloneChild); 444 445 // Hook the clone up as a continuation of |curr|. Note we do encounter 446 // anonymous blocks possibly as we walk up the block chain. When we split an 447 // anonymous block, there's no need to do any continuation hookup, since we haven't 448 // actually split a real element. 449 if (!blockCurr->isAnonymousBlock()) { 450 oldCont = blockCurr->continuation(); 451 blockCurr->setContinuation(cloneBlock); 452 cloneBlock->setContinuation(oldCont); 453 } 454 455 // Someone may have indirectly caused a <q> to split. When this happens, the :after content 456 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after 457 // content gets properly destroyed. 458 if (document()->usesBeforeAfterRules()) 459 blockCurr->children()->updateBeforeAfterContent(blockCurr, AFTER); 460 461 // Now we need to take all of the children starting from the first child 462 // *after* currChild and append them all to the clone. 463 RenderObject* afterContent = isAfterContent(cloneBlock->lastChild()) ? cloneBlock->lastChild() : 0; 464 blockCurr->moveChildrenTo(cloneBlock, currChild->nextSibling(), 0, afterContent); 465 466 // Keep walking up the chain. 467 currChild = curr; 468 curr = toRenderBoxModelObject(curr->parent()); 469 } 470 471 // Now we are at the columns block level. We need to put the clone into the toBlock. 472 toBlock->children()->appendChildNode(toBlock, cloneBlock); 473 474 // Now take all the children after currChild and remove them from the fromBlock 475 // and put them in the toBlock. 476 fromBlock->moveChildrenTo(toBlock, currChild->nextSibling(), 0); 477 } 478 479 void RenderBlock::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, 480 RenderObject* newChild, RenderBoxModelObject* oldCont) 481 { 482 RenderBlock* pre = 0; 483 RenderBlock* block = containingColumnsBlock(); 484 485 // Delete our line boxes before we do the inline split into continuations. 486 block->deleteLineBoxTree(); 487 488 bool madeNewBeforeBlock = false; 489 if (block->isAnonymousColumnsBlock()) { 490 // We can reuse this block and make it the preBlock of the next continuation. 491 pre = block; 492 pre->removePositionedObjects(0); 493 block = toRenderBlock(block->parent()); 494 } else { 495 // No anonymous block available for use. Make one. 496 pre = block->createAnonymousColumnsBlock(); 497 pre->setChildrenInline(false); 498 madeNewBeforeBlock = true; 499 } 500 501 RenderBlock* post = block->createAnonymousColumnsBlock(); 502 post->setChildrenInline(false); 503 504 RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); 505 if (madeNewBeforeBlock) 506 block->children()->insertChildNode(block, pre, boxFirst); 507 block->children()->insertChildNode(block, newBlockBox, boxFirst); 508 block->children()->insertChildNode(block, post, boxFirst); 509 block->setChildrenInline(false); 510 511 if (madeNewBeforeBlock) 512 block->moveChildrenTo(pre, boxFirst, 0); 513 514 splitBlocks(pre, post, newBlockBox, beforeChild, oldCont); 515 516 // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting 517 // time in makeChildrenNonInline by just setting this explicitly up front. 518 newBlockBox->setChildrenInline(false); 519 520 // We delayed adding the newChild until now so that the |newBlockBox| would be fully 521 // connected, thus allowing newChild access to a renderArena should it need 522 // to wrap itself in additional boxes (e.g., table construction). 523 newBlockBox->addChild(newChild); 524 525 // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) 526 // get deleted properly. Because objects moves from the pre block into the post block, we want to 527 // make new line boxes instead of leaving the old line boxes around. 528 pre->setNeedsLayoutAndPrefWidthsRecalc(); 529 block->setNeedsLayoutAndPrefWidthsRecalc(); 530 post->setNeedsLayoutAndPrefWidthsRecalc(); 531 } 532 313 533 RenderObject* RenderBlock::splitAnonymousBlocksAroundChild(RenderObject* beforeChild) 314 534 { 315 535 while (beforeChild->parent() != this) { 316 536 RenderBlock* blockToSplit = toRenderBlock(beforeChild->parent()); 317 ASSERT(blockToSplit->isAnonymousBlock() && !blockToSplit->continuation());318 319 537 if (blockToSplit->firstChild() != beforeChild) { 320 538 // We have to split the parentBlock into two blocks. … … 330 548 beforeChild = blockToSplit; 331 549 } 332 333 550 return beforeChild; 334 551 } … … 343 560 // Delete the block's line boxes before we do the split. 344 561 block->deleteLineBoxTree(); 345 562 346 563 if (beforeChild && beforeChild->parent() != this) 347 564 beforeChild = splitAnonymousBlocksAroundChild(beforeChild); 348 565 349 566 if (beforeChild != firstChild()) { 350 567 pre = block->createAnonymousColumnsBlock(); … … 388 605 } 389 606 390 static RenderBlock* columnsBlockForSpanningElement(RenderBlock* block,RenderObject* newChild)391 { 392 // FIXME: Th e style of this function may seem weird.393 // This function is the gateway for the addition of column-span support. Support will be addedin three stages:607 RenderBlock* RenderBlock::columnsBlockForSpanningElement(RenderObject* newChild) 608 { 609 // FIXME: This function is the gateway for the addition of column-span support. It will 610 // be added to in three stages: 394 611 // (1) Immediate children of a multi-column block can span. 395 612 // (2) Nested block-level children with only block-level ancestors between them and the multi-column block can span. 396 613 // (3) Nested children with block or inline ancestors between them and the multi-column block can span (this is when we 397 614 // cross the streams and have to cope with both types of continuations mixed together). 398 // This function currently only supports (1). 399 if (!newChild->isText() && newChild->style()->columnSpan() && !newChild->isFloatingOrPositioned() 400 && !newChild->isInline() && !block->isAnonymousColumnSpanBlock() && block->style()->specifiesColumns()) 401 return block; 402 return 0; 615 // This function currently supports (1) and (2). 616 RenderBlock* columnsBlockAncestor = 0; 617 if (!newChild->isText() && newChild->style()->columnSpan() && !newChild->isFloatingOrPositioned() 618 && !newChild->isInline() && !isAnonymousColumnSpanBlock()) { 619 if (style()->specifiesColumns()) 620 columnsBlockAncestor = this; 621 else 622 columnsBlockAncestor = toRenderBlock(parent())->containingColumnsBlock(false); 623 } 624 return columnsBlockAncestor; 403 625 } 404 626 … … 415 637 416 638 // Check for a spanning element in columns. 417 RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement( this,newChild);639 RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild); 418 640 if (columnsBlockAncestor) { 419 ASSERT(columnsBlockAncestor == this); 420 421 // We are placing a column-span element inside a block. We have to perform a split of this 422 // block's children. This involves creating an anonymous block box to hold 641 // We are placing a column-span element inside a block. 642 RenderBlock* newBox = createAnonymousColumnSpanBlock(); 643 644 if (columnsBlockAncestor != this) { 645 // We are nested inside a multi-column element and are being split by the span. We have to break up 646 // our block into continuations. 647 RenderBoxModelObject* oldContinuation = continuation(); 648 setContinuation(newBox); 649 650 // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content 651 // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after 652 // content gets properly destroyed. 653 bool isLastChild = (beforeChild == lastChild()); 654 if (document()->usesBeforeAfterRules()) 655 children()->updateBeforeAfterContent(this, AFTER); 656 if (isLastChild && beforeChild != lastChild()) 657 beforeChild = 0; // We destroyed the last child, so now we need to update our insertion 658 // point to be 0. It's just a straight append now. 659 660 splitFlow(beforeChild, newBox, newChild, oldContinuation); 661 return; 662 } 663 664 // We have to perform a split of this block's children. This involves creating an anonymous block box to hold 423 665 // the column-spanning |newChild|. We take all of the children from before |newChild| and put them into 424 666 // one anonymous columns block, and all of the children after |newChild| go into another anonymous block. 425 RenderBlock* newBox = createAnonymousColumnSpanBlock();426 427 // Someone may have put the spanning element inside an element with generated content, causing a split. When this happens,428 // the :after content has to move into the last anonymous block. Call updateBeforeAfterContent to ensure that our :after429 // content gets properly destroyed.430 bool isLastChild = (beforeChild == lastChild());431 if (document()->usesBeforeAfterRules())432 children()->updateBeforeAfterContent(this, AFTER);433 if (isLastChild && beforeChild != lastChild())434 beforeChild = 0; // We destroyed the last child, so now we need to update our insertion435 // point to be 0. It's just a straight append now.436 437 667 makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild); 438 668 return; … … 518 748 void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild) 519 749 { 520 if (!isAnonymous() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock())) 750 if (continuation() && !isAnonymousBlock()) 751 return addChildToContinuation(newChild, beforeChild); 752 return addChildIgnoringContinuation(newChild, beforeChild); 753 } 754 755 void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild) 756 { 757 if (!isAnonymousBlock() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock())) 521 758 return addChildToAnonymousColumnBlocks(newChild, beforeChild); 522 759 return addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild); … … 2060 2297 if (nextContinuation->isAnonymousBlock()) 2061 2298 return nextContinuation->blockElementContinuation(); 2062 return 0;2299 return nextContinuation; 2063 2300 } 2064 2301 … … 3939 4176 colHeight = requestedColumnHeight; 3940 4177 else if (computeIntrinsicHeight) 3941 colHeight = availableHeight / desiredColumnCount + columnSlop;4178 colHeight = min(availableHeight, availableHeight / desiredColumnCount + columnSlop); 3942 4179 else 3943 4180 colHeight = availableHeight; -
trunk/WebCore/rendering/RenderBlock.h
r60344 r60494 244 244 virtual void dirtyLinesFromChangedChild(RenderObject* child) { m_lineBoxes.dirtyLinesFromChangedChild(this, child); } 245 245 246 void addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild); 247 void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild); 246 248 void addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild); 247 249 virtual void addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild = 0); … … 411 413 412 414 RenderObject* splitAnonymousBlocksAroundChild(RenderObject* beforeChild); 415 void splitBlocks(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock, 416 RenderObject* beforeChild, RenderBoxModelObject* oldCont); 417 void splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, 418 RenderObject* newChild, RenderBoxModelObject* oldCont); 419 RenderBlock* clone() const; 420 RenderBlock* continuationBefore(RenderObject* beforeChild); 421 RenderBlock* containingColumnsBlock(bool allowAnonymousColumnBlock = true); 422 RenderBlock* columnsBlockForSpanningElement(RenderObject* newChild); 413 423 414 424 struct FloatingObject : Noncopyable {
Note: See TracChangeset
for help on using the changeset viewer.