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