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#if ENABLE(RUBY) 34#include "RenderRubyRun.h" 35 36#include "RenderRubyBase.h" 37#include "RenderRubyText.h" 38#include "RenderView.h" 39 40using namespace std; 41 42namespace WebCore { 43 44RenderRubyRun::RenderRubyRun(Node* node) 45 : RenderBlock(node) 46 , m_beingDestroyed(false) 47{ 48 setReplaced(true); 49 setInline(true); 50} 51 52RenderRubyRun::~RenderRubyRun() 53{ 54} 55 56void RenderRubyRun::destroy() 57{ 58 // Mark if the run is being destroyed to avoid trouble in removeChild(). 59 m_beingDestroyed = true; 60 RenderBlock::destroy(); 61} 62 63bool RenderRubyRun::hasRubyText() const 64{ 65 // The only place where a ruby text can be is in the first position 66 // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves. 67 return firstChild() && firstChild()->isRubyText(); 68} 69 70bool RenderRubyRun::hasRubyBase() const 71{ 72 // The only place where a ruby base can be is in the last position 73 // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves. 74 return lastChild() && lastChild()->isRubyBase(); 75} 76 77bool RenderRubyRun::isEmpty() const 78{ 79 return !hasRubyText() && !hasRubyBase(); 80} 81 82RenderRubyText* RenderRubyRun::rubyText() const 83{ 84 RenderObject* child = firstChild(); 85 return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0; 86} 87 88RenderRubyBase* RenderRubyRun::rubyBase() const 89{ 90 RenderObject* child = lastChild(); 91 return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0; 92} 93 94RenderRubyBase* RenderRubyRun::rubyBaseSafe() 95{ 96 RenderRubyBase* base = rubyBase(); 97 if (!base) { 98 base = createRubyBase(); 99 RenderBlock::addChild(base); 100 } 101 return base; 102} 103 104RenderBlock* RenderRubyRun::firstLineBlock() const 105{ 106 return 0; 107} 108 109void RenderRubyRun::updateFirstLetter() 110{ 111} 112 113bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const 114{ 115 return child->isRubyText() || child->isInline(); 116} 117 118void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild) 119{ 120 ASSERT(child); 121 122 // If child is a ruby text 123 if (child->isRubyText()) { 124 if (!beforeChild) { 125 // RenderRuby has already ascertained that we can add the child here. 126 ASSERT(!hasRubyText()); 127 // prepend ruby texts as first child 128 RenderBlock::addChild(child, firstChild()); 129 } else if (beforeChild->isRubyText()) { 130 // New text is inserted just before another. 131 // In this case the new text takes the place of the old one, and 132 // the old text goes into a new run that is inserted as next sibling. 133 ASSERT(beforeChild->parent() == this); 134 RenderObject* ruby = parent(); 135 ASSERT(ruby->isRuby()); 136 RenderBlock* newRun = staticCreateRubyRun(ruby); 137 ruby->addChild(newRun, nextSibling()); 138 // Add the new ruby text and move the old one to the new run 139 // Note: Doing it in this order and not using RenderRubyRun's methods, 140 // in order to avoid automatic removal of the ruby run in case there is no 141 // other child besides the old ruby text. 142 RenderBlock::addChild(child, beforeChild); 143 RenderBlock::removeChild(beforeChild); 144 newRun->addChild(beforeChild); 145 } else { 146 ASSERT(hasRubyBase()); // Otherwise beforeChild would be borked. 147 // Insertion before a ruby base object. 148 // In this case we need insert a new run before the current one and split the base. 149 RenderObject* ruby = parent(); 150 RenderRubyRun* newRun = staticCreateRubyRun(ruby); 151 ruby->addChild(newRun, this); 152 newRun->addChild(child); 153 rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild); 154 } 155 } else { 156 // child is not a text -> insert it into the base 157 // (append it instead if beforeChild is the ruby text) 158 if (beforeChild && beforeChild->isRubyText()) 159 beforeChild = 0; 160 rubyBaseSafe()->addChild(child, beforeChild); 161 } 162} 163 164void RenderRubyRun::removeChild(RenderObject* child) 165{ 166 // If the child is a ruby text, then merge the ruby base with the base of 167 // the right sibling run, if possible. 168 if (!m_beingDestroyed && !documentBeingDestroyed() && child->isRubyText()) { 169 RenderRubyBase* base = rubyBase(); 170 RenderObject* rightNeighbour = nextSibling(); 171 if (base && rightNeighbour && rightNeighbour->isRubyRun()) { 172 // Ruby run without a base can happen only at the first run. 173 RenderRubyRun* rightRun = static_cast<RenderRubyRun*>(rightNeighbour); 174 ASSERT(rightRun->hasRubyBase()); 175 RenderRubyBase* rightBase = rightRun->rubyBaseSafe(); 176 // Collect all children in a single base, then swap the bases. 177 rightBase->moveChildren(base); 178 moveChildTo(rightRun, rightRun->children(), base); 179 rightRun->moveChildTo(this, children(), rightBase); 180 // The now empty ruby base will be removed below. 181 } 182 } 183 184 RenderBlock::removeChild(child); 185 186 if (!m_beingDestroyed && !documentBeingDestroyed()) { 187 // Check if our base (if any) is now empty. If so, destroy it. 188 RenderBlock* base = rubyBase(); 189 if (base && !base->firstChild()) { 190 RenderBlock::removeChild(base); 191 base->deleteLineBoxTree(); 192 base->destroy(); 193 } 194 195 // If any of the above leaves the run empty, destroy it as well. 196 if (isEmpty()) { 197 parent()->removeChild(this); 198 deleteLineBoxTree(); 199 destroy(); 200 } 201 } 202} 203 204RenderRubyBase* RenderRubyRun::createRubyBase() const 205{ 206 RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */); 207 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 208 newStyle->inheritFrom(style()); 209 newStyle->setDisplay(BLOCK); 210 newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER? 211 rb->setStyle(newStyle.release()); 212 return rb; 213} 214 215RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby) 216{ 217 ASSERT(parentRuby && parentRuby->isRuby()); 218 RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */); 219 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 220 newStyle->inheritFrom(parentRuby->style()); 221 newStyle->setDisplay(INLINE_BLOCK); 222 rr->setStyle(newStyle.release()); 223 return rr; 224} 225 226} // namespace WebCore 227 228#endif 229