Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 
     33 #if ENABLE(RUBY)
     34 #include "RenderRubyBase.h"
     35 
     36 namespace WebCore {
     37 
     38 RenderRubyBase::RenderRubyBase(Node* node)
     39     : RenderBlock(node)
     40 {
     41     setInline(false);
     42 }
     43 
     44 RenderRubyBase::~RenderRubyBase()
     45 {
     46 }
     47 
     48 bool RenderRubyBase::isChildAllowed(RenderObject* child, RenderStyle*) const
     49 {
     50     return child->isInline();
     51 }
     52 
     53 bool RenderRubyBase::hasOnlyWrappedInlineChildren(RenderObject* beforeChild) const
     54 {
     55     // Tests whether all children in the base before beforeChild are either floated/positioned,
     56     // or inline objects wrapped in anonymous blocks.
     57     // Note that beforeChild may be 0, in which case all children are looked at.
     58     for (RenderObject* child = firstChild(); child != beforeChild; child = child->nextSibling()) {
     59         if (!child->isFloatingOrPositioned() && !(child->isAnonymousBlock() && child->childrenInline()))
     60             return false;
     61     }
     62     return true;
     63 }
     64 
     65 void RenderRubyBase::moveChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild)
     66 {
     67     // This function removes all children that are before (!) beforeChild
     68     // and appends them to toBase.
     69     ASSERT(toBase);
     70 
     71     // First make sure that beforeChild (if set) is indeed a direct child of this.
     72     // Inline children might be wrapped in an anonymous block if there's a continuation.
     73     // Theoretically, in ruby bases, this can happen with only the first such a child,
     74     // so it should be OK to just climb the tree.
     75     while (fromBeforeChild && fromBeforeChild->parent() != this)
     76         fromBeforeChild = fromBeforeChild->parent();
     77 
     78     if (childrenInline())
     79         moveInlineChildren(toBase, fromBeforeChild);
     80     else
     81         moveBlockChildren(toBase, fromBeforeChild);
     82 
     83     setNeedsLayoutAndPrefWidthsRecalc();
     84     toBase->setNeedsLayoutAndPrefWidthsRecalc();
     85 }
     86 
     87 void RenderRubyBase::moveInlineChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild)
     88 {
     89     RenderBlock* toBlock;
     90 
     91     if (toBase->childrenInline()) {
     92         // The standard and easy case: move the children into the target base
     93         toBlock = toBase;
     94     } else {
     95         // We need to wrap the inline objects into an anonymous block.
     96         // If toBase has a suitable block, we re-use it, otherwise create a new one.
     97         RenderObject* lastChild = toBase->lastChild();
     98         if (lastChild && lastChild->isAnonymousBlock() && lastChild->childrenInline())
     99             toBlock = toRenderBlock(lastChild);
    100         else {
    101             toBlock = toBase->createAnonymousBlock();
    102             toBase->children()->appendChildNode(toBase, toBlock);
    103         }
    104     }
    105     // Move our inline children into the target block we determined above.
    106     for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild())
    107         moveChildTo(toBlock, toBlock->children(), child);
    108 }
    109 
    110 void RenderRubyBase::moveBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild)
    111 {
    112     if (toBase->childrenInline()) {
    113         // First check whether we move only wrapped inline objects.
    114         if (hasOnlyWrappedInlineChildren(fromBeforeChild)) {
    115             // The reason why the base is in block flow must be after beforeChild.
    116             // We therefore can extract the inline objects and move them to toBase.
    117             for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild()) {
    118                 if (child->isAnonymousBlock()) {
    119                     RenderBlock* anonBlock = toRenderBlock(child);
    120                     ASSERT(anonBlock->childrenInline());
    121                     ASSERT(!anonBlock->inlineContinuation());
    122                     anonBlock->moveAllChildrenTo(toBase, toBase->children());
    123                     anonBlock->deleteLineBoxTree();
    124                     anonBlock->destroy();
    125                 } else {
    126                     ASSERT(child->isFloatingOrPositioned());
    127                     moveChildTo(toBase, toBase->children(), child);
    128                 }
    129             }
    130         } else {
    131             // Moving block children -> have to set toBase as block flow
    132             toBase->makeChildrenNonInline();
    133             // Move children, potentially collapsing anonymous block wrappers.
    134             mergeBlockChildren(toBase, fromBeforeChild);
    135 
    136             // Now we need to check if the leftover children are all inline.
    137             // If so, make this base inline again.
    138             if (hasOnlyWrappedInlineChildren()) {
    139                 RenderObject* next = 0;
    140                 for (RenderObject* child = firstChild(); child; child = next) {
    141                     next = child->nextSibling();
    142                     if (child->isFloatingOrPositioned())
    143                         continue;
    144                     ASSERT(child->isAnonymousBlock());
    145 
    146                     RenderBlock* anonBlock = toRenderBlock(child);
    147                     ASSERT(anonBlock->childrenInline());
    148                     ASSERT(!anonBlock->inlineContinuation());
    149                     // Move inline children out of anonymous block.
    150                     anonBlock->moveAllChildrenTo(this, children(), anonBlock);
    151                     anonBlock->deleteLineBoxTree();
    152                     anonBlock->destroy();
    153                 }
    154                 setChildrenInline(true);
    155             }
    156         }
    157     } else
    158         mergeBlockChildren(toBase, fromBeforeChild);
    159 }
    160 
    161 void RenderRubyBase::mergeBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild)
    162 {
    163     // This function removes all children that are before fromBeforeChild and appends them to toBase.
    164     ASSERT(!childrenInline());
    165     ASSERT(toBase);
    166     ASSERT(!toBase->childrenInline());
    167 
    168     // Quick check whether we have anything to do, to simplify the following code.
    169     if (fromBeforeChild != firstChild())
    170         return;
    171 
    172     // If an anonymous block would be put next to another such block, then merge those.
    173     RenderObject* firstChildHere = firstChild();
    174     RenderObject* lastChildThere = toBase->lastChild();
    175     if (firstChildHere && firstChildHere->isAnonymousBlock() && firstChildHere->childrenInline()
    176             && lastChildThere && lastChildThere->isAnonymousBlock() && lastChildThere->childrenInline()) {
    177         RenderBlock* anonBlockHere = toRenderBlock(firstChildHere);
    178         RenderBlock* anonBlockThere = toRenderBlock(lastChildThere);
    179         anonBlockHere->moveAllChildrenTo(anonBlockThere, anonBlockThere->children());
    180         anonBlockHere->deleteLineBoxTree();
    181         anonBlockHere->destroy();
    182     }
    183     // Move all remaining children normally.
    184     for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild())
    185         moveChildTo(toBase, toBase->children(), child);
    186 }
    187 
    188 } // namespace WebCore
    189 
    190 #endif
    191