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 "core/rendering/RenderRubyRun.h" 34 35 #include "core/rendering/RenderRubyBase.h" 36 #include "core/rendering/RenderRubyText.h" 37 #include "core/rendering/RenderText.h" 38 39 namespace blink { 40 41 RenderRubyRun::RenderRubyRun() 42 : RenderBlockFlow(0) 43 { 44 setReplaced(true); 45 setInline(true); 46 } 47 48 RenderRubyRun::~RenderRubyRun() 49 { 50 } 51 52 bool RenderRubyRun::hasRubyText() const 53 { 54 // The only place where a ruby text can be is in the first position 55 // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves. 56 return firstChild() && firstChild()->isRubyText(); 57 } 58 59 bool RenderRubyRun::hasRubyBase() const 60 { 61 // The only place where a ruby base can be is in the last position 62 // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves. 63 return lastChild() && lastChild()->isRubyBase(); 64 } 65 66 RenderRubyText* RenderRubyRun::rubyText() const 67 { 68 RenderObject* child = firstChild(); 69 // If in future it becomes necessary to support floating or positioned ruby text, 70 // layout will have to be changed to handle them properly. 71 ASSERT(!child || !child->isRubyText() || !child->isFloatingOrOutOfFlowPositioned()); 72 return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0; 73 } 74 75 RenderRubyBase* RenderRubyRun::rubyBase() const 76 { 77 RenderObject* child = lastChild(); 78 return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0; 79 } 80 81 RenderRubyBase* RenderRubyRun::rubyBaseSafe() 82 { 83 RenderRubyBase* base = rubyBase(); 84 if (!base) { 85 base = createRubyBase(); 86 RenderBlockFlow::addChild(base); 87 } 88 return base; 89 } 90 91 bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const 92 { 93 return child->isRubyText() || child->isInline(); 94 } 95 96 void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild) 97 { 98 ASSERT(child); 99 100 if (child->isRubyText()) { 101 if (!beforeChild) { 102 // RenderRuby has already ascertained that we can add the child here. 103 ASSERT(!hasRubyText()); 104 // prepend ruby texts as first child 105 RenderBlockFlow::addChild(child, firstChild()); 106 } else if (beforeChild->isRubyText()) { 107 // New text is inserted just before another. 108 // In this case the new text takes the place of the old one, and 109 // the old text goes into a new run that is inserted as next sibling. 110 ASSERT(beforeChild->parent() == this); 111 RenderObject* ruby = parent(); 112 ASSERT(ruby->isRuby()); 113 RenderBlock* newRun = staticCreateRubyRun(ruby); 114 ruby->addChild(newRun, nextSibling()); 115 // Add the new ruby text and move the old one to the new run 116 // Note: Doing it in this order and not using RenderRubyRun's methods, 117 // in order to avoid automatic removal of the ruby run in case there is no 118 // other child besides the old ruby text. 119 RenderBlockFlow::addChild(child, beforeChild); 120 RenderBlockFlow::removeChild(beforeChild); 121 newRun->addChild(beforeChild); 122 } else if (hasRubyBase()) { 123 // Insertion before a ruby base object. 124 // In this case we need insert a new run before the current one and split the base. 125 RenderObject* ruby = parent(); 126 RenderRubyRun* newRun = staticCreateRubyRun(ruby); 127 ruby->addChild(newRun, this); 128 newRun->addChild(child); 129 rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild); 130 } 131 } else { 132 // child is not a text -> insert it into the base 133 // (append it instead if beforeChild is the ruby text) 134 if (beforeChild && beforeChild->isRubyText()) 135 beforeChild = 0; 136 rubyBaseSafe()->addChild(child, beforeChild); 137 } 138 } 139 140 void RenderRubyRun::removeChild(RenderObject* child) 141 { 142 // If the child is a ruby text, then merge the ruby base with the base of 143 // the right sibling run, if possible. 144 if (!beingDestroyed() && !documentBeingDestroyed() && child->isRubyText()) { 145 RenderRubyBase* base = rubyBase(); 146 RenderObject* rightNeighbour = nextSibling(); 147 if (base && rightNeighbour && rightNeighbour->isRubyRun()) { 148 // Ruby run without a base can happen only at the first run. 149 RenderRubyRun* rightRun = toRenderRubyRun(rightNeighbour); 150 if (rightRun->hasRubyBase()) { 151 RenderRubyBase* rightBase = rightRun->rubyBaseSafe(); 152 // Collect all children in a single base, then swap the bases. 153 rightBase->moveChildren(base); 154 moveChildTo(rightRun, base); 155 rightRun->moveChildTo(this, rightBase); 156 // The now empty ruby base will be removed below. 157 ASSERT(!rubyBase()->firstChild()); 158 } 159 } 160 } 161 162 RenderBlockFlow::removeChild(child); 163 164 if (!beingDestroyed() && !documentBeingDestroyed()) { 165 // Check if our base (if any) is now empty. If so, destroy it. 166 RenderBlock* base = rubyBase(); 167 if (base && !base->firstChild()) { 168 RenderBlockFlow::removeChild(base); 169 base->deleteLineBoxTree(); 170 base->destroy(); 171 } 172 173 // If any of the above leaves the run empty, destroy it as well. 174 if (!hasRubyText() && !hasRubyBase()) { 175 deleteLineBoxTree(); 176 destroy(); 177 } 178 } 179 } 180 181 RenderRubyBase* RenderRubyRun::createRubyBase() const 182 { 183 RenderRubyBase* renderer = RenderRubyBase::createAnonymous(&document()); 184 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK); 185 newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER? 186 renderer->setStyle(newStyle.release()); 187 return renderer; 188 } 189 190 RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby) 191 { 192 ASSERT(parentRuby && parentRuby->isRuby()); 193 RenderRubyRun* rr = new RenderRubyRun(); 194 rr->setDocumentForAnonymous(&parentRuby->document()); 195 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parentRuby->style(), INLINE_BLOCK); 196 rr->setStyle(newStyle.release()); 197 return rr; 198 } 199 200 RenderObject* RenderRubyRun::layoutSpecialExcludedChild(bool relayoutChildren, SubtreeLayoutScope& layoutScope) 201 { 202 // Don't bother positioning the RenderRubyRun yet. 203 RenderRubyText* rt = rubyText(); 204 if (!rt) 205 return 0; 206 if (relayoutChildren) 207 layoutScope.setChildNeedsLayout(rt); 208 rt->layoutIfNeeded(); 209 return rt; 210 } 211 212 void RenderRubyRun::layout() 213 { 214 RenderBlockFlow::layout(); 215 216 RenderRubyText* rt = rubyText(); 217 if (!rt) 218 return; 219 220 rt->setLogicalLeft(0); 221 222 // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase. 223 LayoutUnit lastLineRubyTextBottom = rt->logicalHeight(); 224 LayoutUnit firstLineRubyTextTop = 0; 225 RootInlineBox* rootBox = rt->lastRootBox(); 226 if (rootBox) { 227 // In order to align, we have to ignore negative leading. 228 firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow(); 229 lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow(); 230 } 231 232 if (style()->isFlippedLinesWritingMode() == (style()->rubyPosition() == RubyPositionAfter)) { 233 LayoutUnit firstLineTop = 0; 234 if (RenderRubyBase* rb = rubyBase()) { 235 RootInlineBox* rootBox = rb->firstRootBox(); 236 if (rootBox) 237 firstLineTop = rootBox->logicalTopLayoutOverflow(); 238 firstLineTop += rb->logicalTop(); 239 } 240 241 rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop); 242 } else { 243 LayoutUnit lastLineBottom = logicalHeight(); 244 if (RenderRubyBase* rb = rubyBase()) { 245 RootInlineBox* rootBox = rb->lastRootBox(); 246 if (rootBox) 247 lastLineBottom = rootBox->logicalBottomLayoutOverflow(); 248 lastLineBottom += rb->logicalTop(); 249 } 250 251 rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom); 252 } 253 254 // Update our overflow to account for the new RenderRubyText position. 255 computeOverflow(clientLogicalBottom()); 256 } 257 258 void RenderRubyRun::getOverhang(bool firstLine, RenderObject* startRenderer, RenderObject* endRenderer, int& startOverhang, int& endOverhang) const 259 { 260 ASSERT(!needsLayout()); 261 262 startOverhang = 0; 263 endOverhang = 0; 264 265 RenderRubyBase* rubyBase = this->rubyBase(); 266 RenderRubyText* rubyText = this->rubyText(); 267 268 if (!rubyBase || !rubyText) 269 return; 270 271 if (!rubyBase->firstRootBox()) 272 return; 273 274 int logicalWidth = this->logicalWidth(); 275 int logicalLeftOverhang = std::numeric_limits<int>::max(); 276 int logicalRightOverhang = std::numeric_limits<int>::max(); 277 for (RootInlineBox* rootInlineBox = rubyBase->firstRootBox(); rootInlineBox; rootInlineBox = rootInlineBox->nextRootBox()) { 278 logicalLeftOverhang = std::min<int>(logicalLeftOverhang, rootInlineBox->logicalLeft()); 279 logicalRightOverhang = std::min<int>(logicalRightOverhang, logicalWidth - rootInlineBox->logicalRight()); 280 } 281 282 startOverhang = style()->isLeftToRightDirection() ? logicalLeftOverhang : logicalRightOverhang; 283 endOverhang = style()->isLeftToRightDirection() ? logicalRightOverhang : logicalLeftOverhang; 284 285 if (!startRenderer || !startRenderer->isText() || startRenderer->style(firstLine)->fontSize() > rubyBase->style(firstLine)->fontSize()) 286 startOverhang = 0; 287 288 if (!endRenderer || !endRenderer->isText() || endRenderer->style(firstLine)->fontSize() > rubyBase->style(firstLine)->fontSize()) 289 endOverhang = 0; 290 291 // We overhang a ruby only if the neighboring render object is a text. 292 // We can overhang the ruby by no more than half the width of the neighboring text 293 // and no more than half the font size. 294 int halfWidthOfFontSize = rubyText->style(firstLine)->fontSize() / 2; 295 if (startOverhang) 296 startOverhang = std::min<int>(startOverhang, std::min<int>(toRenderText(startRenderer)->minLogicalWidth(), halfWidthOfFontSize)); 297 if (endOverhang) 298 endOverhang = std::min<int>(endOverhang, std::min<int>(toRenderText(endRenderer)->minLogicalWidth(), halfWidthOfFontSize)); 299 } 300 301 } // namespace blink 302