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