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