2010-02-08 Maciej Stachowiak <mjs@apple.com>
[WebKit-https.git] / WebCore / rendering / RenderRubyBase.cpp
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