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 #include "RenderRubyBase.h" 34 #include "RenderRubyRun.h" 35 #include "RenderRubyText.h" 36 37 using namespace std; 38 39 namespace WebCore { 40 41 RenderRubyBase::RenderRubyBase(Node* node) 42 : RenderBlock(node) 43 { 44 setInline(false); 45 } 46 47 RenderRubyBase::~RenderRubyBase() 48 { 49 } 50 51 bool RenderRubyBase::isChildAllowed(RenderObject* child, RenderStyle*) const 52 { 53 return child->isInline(); 54 } 55 56 bool RenderRubyBase::hasOnlyWrappedInlineChildren(RenderObject* beforeChild) const 57 { 58 // Tests whether all children in the base before beforeChild are either floated/positioned, 59 // or inline objects wrapped in anonymous blocks. 60 // Note that beforeChild may be 0, in which case all children are looked at. 61 for (RenderObject* child = firstChild(); child != beforeChild; child = child->nextSibling()) { 62 if (!child->isFloatingOrPositioned() && !(child->isAnonymousBlock() && child->childrenInline())) 63 return false; 64 } 65 return true; 66 } 67 68 void RenderRubyBase::moveChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) 69 { 70 // This function removes all children that are before (!) beforeChild 71 // and appends them to toBase. 72 ASSERT(toBase); 73 74 // First make sure that beforeChild (if set) is indeed a direct child of this. 75 // Inline children might be wrapped in an anonymous block if there's a continuation. 76 // Theoretically, in ruby bases, this can happen with only the first such a child, 77 // so it should be OK to just climb the tree. 78 while (fromBeforeChild && fromBeforeChild->parent() != this) 79 fromBeforeChild = fromBeforeChild->parent(); 80 81 if (childrenInline()) 82 moveInlineChildren(toBase, fromBeforeChild); 83 else 84 moveBlockChildren(toBase, fromBeforeChild); 85 86 setNeedsLayoutAndPrefWidthsRecalc(); 87 toBase->setNeedsLayoutAndPrefWidthsRecalc(); 88 } 89 90 void RenderRubyBase::moveInlineChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) 91 { 92 RenderBlock* toBlock; 93 94 if (toBase->childrenInline()) { 95 // The standard and easy case: move the children into the target base 96 toBlock = toBase; 97 } else { 98 // We need to wrap the inline objects into an anonymous block. 99 // If toBase has a suitable block, we re-use it, otherwise create a new one. 100 RenderObject* lastChild = toBase->lastChild(); 101 if (lastChild && lastChild->isAnonymousBlock() && lastChild->childrenInline()) 102 toBlock = toRenderBlock(lastChild); 103 else { 104 toBlock = toBase->createAnonymousBlock(); 105 toBase->children()->appendChildNode(toBase, toBlock); 106 } 107 } 108 // Move our inline children into the target block we determined above. 109 moveChildrenTo(toBlock, firstChild(), fromBeforeChild); 110 } 111 112 void RenderRubyBase::moveBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) 113 { 114 if (toBase->childrenInline()) { 115 // First check whether we move only wrapped inline objects. 116 if (hasOnlyWrappedInlineChildren(fromBeforeChild)) { 117 // The reason why the base is in block flow must be after beforeChild. 118 // We therefore can extract the inline objects and move them to toBase. 119 for (RenderObject* child = firstChild(); child != fromBeforeChild; child = firstChild()) { 120 if (child->isAnonymousBlock()) { 121 RenderBlock* anonBlock = toRenderBlock(child); 122 ASSERT(anonBlock->childrenInline()); 123 ASSERT(!anonBlock->inlineElementContinuation()); 124 anonBlock->moveAllChildrenTo(toBase, toBase->children()); 125 anonBlock->deleteLineBoxTree(); 126 anonBlock->destroy(); 127 } else { 128 ASSERT(child->isFloatingOrPositioned()); 129 moveChildTo(toBase, child); 130 } 131 } 132 } else { 133 // Moving block children -> have to set toBase as block flow 134 toBase->makeChildrenNonInline(); 135 // Move children, potentially collapsing anonymous block wrappers. 136 mergeBlockChildren(toBase, fromBeforeChild); 137 138 // Now we need to check if the leftover children are all inline. 139 // If so, make this base inline again. 140 if (hasOnlyWrappedInlineChildren()) { 141 RenderObject* next = 0; 142 for (RenderObject* child = firstChild(); child; child = next) { 143 next = child->nextSibling(); 144 if (child->isFloatingOrPositioned()) 145 continue; 146 ASSERT(child->isAnonymousBlock()); 147 148 RenderBlock* anonBlock = toRenderBlock(child); 149 ASSERT(anonBlock->childrenInline()); 150 ASSERT(!anonBlock->inlineElementContinuation()); 151 // Move inline children out of anonymous block. 152 anonBlock->moveAllChildrenTo(this, anonBlock); 153 anonBlock->deleteLineBoxTree(); 154 anonBlock->destroy(); 155 } 156 setChildrenInline(true); 157 } 158 } 159 } else 160 mergeBlockChildren(toBase, fromBeforeChild); 161 } 162 163 void RenderRubyBase::mergeBlockChildren(RenderRubyBase* toBase, RenderObject* fromBeforeChild) 164 { 165 // This function removes all children that are before fromBeforeChild and appends them to toBase. 166 ASSERT(!childrenInline()); 167 ASSERT(toBase); 168 ASSERT(!toBase->childrenInline()); 169 170 // Quick check whether we have anything to do, to simplify the following code. 171 if (fromBeforeChild != firstChild()) 172 return; 173 174 // If an anonymous block would be put next to another such block, then merge those. 175 RenderObject* firstChildHere = firstChild(); 176 RenderObject* lastChildThere = toBase->lastChild(); 177 if (firstChildHere && firstChildHere->isAnonymousBlock() && firstChildHere->childrenInline() 178 && lastChildThere && lastChildThere->isAnonymousBlock() && lastChildThere->childrenInline()) { 179 RenderBlock* anonBlockHere = toRenderBlock(firstChildHere); 180 RenderBlock* anonBlockThere = toRenderBlock(lastChildThere); 181 anonBlockHere->moveAllChildrenTo(anonBlockThere, anonBlockThere->children()); 182 anonBlockHere->deleteLineBoxTree(); 183 anonBlockHere->destroy(); 184 } 185 // Move all remaining children normally. 186 moveChildrenTo(toBase, firstChild(), fromBeforeChild); 187 } 188 189 RenderRubyRun* RenderRubyBase::rubyRun() const 190 { 191 ASSERT(parent()); 192 ASSERT(parent()->isRubyRun()); 193 194 return static_cast<RenderRubyRun*>(parent()); 195 } 196 197 ETextAlign RenderRubyBase::textAlignmentForLine(bool /* endsWithSoftBreak */) const 198 { 199 return JUSTIFY; 200 } 201 202 void RenderRubyBase::adjustInlineDirectionLineBounds(int expansionOpportunityCount, float& logicalLeft, float& logicalWidth) const 203 { 204 int maxPreferredLogicalWidth = this->maxPreferredLogicalWidth(); 205 if (maxPreferredLogicalWidth >= logicalWidth) 206 return; 207 208 // Inset the ruby base by half the inter-ideograph expansion amount. 209 float inset = (logicalWidth - maxPreferredLogicalWidth) / (expansionOpportunityCount + 1); 210 211 logicalLeft += inset / 2; 212 logicalWidth -= inset; 213 } 214 215 } // namespace WebCore 216