Changeset 60494 in webkit


Ignore:
Timestamp:
Jun 1, 2010 11:58:30 AM (14 years ago)
Author:
hyatt@apple.com
Message:

https://bugs.webkit.org/show_bug.cgi?id=15550, complete implementation of column-span. Add support for nested column
spans. When a column span is nested inside multiple enclosing blocks, the blocks have to be split around the column-span.
We do this using block element continuations, the same kind of solution we employed for blocks inside inlines.

Reviewed by Beth Dakin.

The code for block continuations is very similar to the code for inline continuations. It may be possible to refactor the
code into RenderBoxModelObject so that more of it can be shared, but this first pass avoids that so as not to risk
causing any regressions in core rendering.

Note also that - just as with inline continuations - you can't unsplit block continuations yet. There is no technical limitation
here... the functions just need to be written to handle it.

Added new tests in fast/multicol/span.

  • rendering/RenderBlock.cpp:

(WebCore::RenderBlock::styleDidChange):
(WebCore::RenderBlock::continuationBefore):
(WebCore::RenderBlock::addChildToContinuation):
(WebCore::RenderBlock::containingColumnsBlock):
(WebCore::RenderBlock::clone):
(WebCore::RenderBlock::splitBlocks):
(WebCore::RenderBlock::splitFlow):
(WebCore::RenderBlock::splitAnonymousBlocksAroundChild):
(WebCore::RenderBlock::makeChildrenAnonymousColumnBlocks):
(WebCore::RenderBlock::columnsBlockForSpanningElement):
(WebCore::RenderBlock::addChildIgnoringAnonymousColumnBlocks):
(WebCore::RenderBlock::addChild):
(WebCore::RenderBlock::addChildIgnoringContinuation):
(WebCore::RenderBlock::blockElementContinuation):
(WebCore::RenderBlock::layoutColumns):

  • rendering/RenderBlock.h:
Location:
trunk
Files:
8 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r60493 r60494  
     12010-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
    1362010-06-01  Alexey Proskuryakov  <ap@apple.com>
    237
  • trunk/WebCore/rendering/RenderBlock.cpp

    r60408 r60494  
    231231    RenderBox::styleDidChange(diff, oldStyle);
    232232
     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
    233243    // FIXME: We could save this call when the change only affected non-inherited properties
    234244    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
     
    265275}
    266276
     277RenderBlock* 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
     302void 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
    267339void RenderBlock::addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild)
    268340{
     
    311383}
    312384
     385RenderBlock* 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
     402RenderBlock* RenderBlock::clone() const
     403{
     404    RenderBlock* o = new (renderArena()) RenderBlock(node());
     405    o->setStyle(style());
     406    o->setChildrenInline(childrenInline());
     407    return o;
     408}
     409
     410void 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
     479void 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
    313533RenderObject* RenderBlock::splitAnonymousBlocksAroundChild(RenderObject* beforeChild)
    314534{
    315535    while (beforeChild->parent() != this) {
    316536        RenderBlock* blockToSplit = toRenderBlock(beforeChild->parent());
    317         ASSERT(blockToSplit->isAnonymousBlock() && !blockToSplit->continuation());
    318        
    319537        if (blockToSplit->firstChild() != beforeChild) {
    320538            // We have to split the parentBlock into two blocks.
     
    330548            beforeChild = blockToSplit;
    331549    }
    332 
    333550    return beforeChild;
    334551}
     
    343560    // Delete the block's line boxes before we do the split.
    344561    block->deleteLineBoxTree();
    345    
     562
    346563    if (beforeChild && beforeChild->parent() != this)
    347564        beforeChild = splitAnonymousBlocksAroundChild(beforeChild);
    348        
     565
    349566    if (beforeChild != firstChild()) {
    350567        pre = block->createAnonymousColumnsBlock();
     
    388605}
    389606
    390 static RenderBlock* columnsBlockForSpanningElement(RenderBlock* block, RenderObject* newChild)
    391 {
    392     // FIXME: The style of this function may seem weird.
    393     // This function is the gateway for the addition of column-span support.  Support will be added in three stages:
     607RenderBlock* 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:
    394611    // (1) Immediate children of a multi-column block can span.
    395612    // (2) Nested block-level children with only block-level ancestors between them and the multi-column block can span.
    396613    // (3) Nested children with block or inline ancestors between them and the multi-column block can span (this is when we
    397614    // 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;
    403625}
    404626
     
    415637
    416638    // Check for a spanning element in columns.
    417     RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(this, newChild);
     639    RenderBlock* columnsBlockAncestor = columnsBlockForSpanningElement(newChild);
    418640    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
    423665        // the column-spanning |newChild|.  We take all of the children from before |newChild| and put them into
    424666        // 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 :after
    429         // 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 insertion
    435                              // point to be 0.  It's just a straight append now.
    436 
    437667        makeChildrenAnonymousColumnBlocks(beforeChild, newBox, newChild);
    438668        return;
     
    518748void RenderBlock::addChild(RenderObject* newChild, RenderObject* beforeChild)
    519749{
    520     if (!isAnonymous() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock()))
     750    if (continuation() && !isAnonymousBlock())
     751        return addChildToContinuation(newChild, beforeChild);
     752    return addChildIgnoringContinuation(newChild, beforeChild);
     753}
     754
     755void RenderBlock::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild)
     756{
     757    if (!isAnonymousBlock() && firstChild() && (firstChild()->isAnonymousColumnsBlock() || firstChild()->isAnonymousColumnSpanBlock()))
    521758        return addChildToAnonymousColumnBlocks(newChild, beforeChild);
    522759    return addChildIgnoringAnonymousColumnBlocks(newChild, beforeChild);
     
    20602297    if (nextContinuation->isAnonymousBlock())
    20612298        return nextContinuation->blockElementContinuation();
    2062     return 0;
     2299    return nextContinuation;
    20632300}
    20642301   
     
    39394176        colHeight = requestedColumnHeight;
    39404177    else if (computeIntrinsicHeight)
    3941         colHeight = availableHeight / desiredColumnCount + columnSlop;
     4178        colHeight = min(availableHeight, availableHeight / desiredColumnCount + columnSlop);
    39424179    else
    39434180        colHeight = availableHeight;
  • trunk/WebCore/rendering/RenderBlock.h

    r60344 r60494  
    244244    virtual void dirtyLinesFromChangedChild(RenderObject* child) { m_lineBoxes.dirtyLinesFromChangedChild(this, child); }
    245245
     246    void addChildToContinuation(RenderObject* newChild, RenderObject* beforeChild);
     247    void addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild);
    246248    void addChildToAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild);
    247249    virtual void addChildIgnoringAnonymousColumnBlocks(RenderObject* newChild, RenderObject* beforeChild = 0);
     
    411413
    412414    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);
    413423   
    414424    struct FloatingObject : Noncopyable {
Note: See TracChangeset for help on using the changeset viewer.