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