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 40 using namespace std; 41 42 namespace WebCore { 43 44 RenderRubyRun::RenderRubyRun(Node* node) 45 : RenderBlock(node) 46 , m_beingDestroyed(false) 47 { 48 setReplaced(true); 49 setInline(true); 50 } 51 52 RenderRubyRun::~RenderRubyRun() 53 { 54 } 55 56 void 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 63 bool 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 70 bool 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 77 bool RenderRubyRun::isEmpty() const 78 { 79 return !hasRubyText() && !hasRubyBase(); 80 } 81 82 RenderRubyText* RenderRubyRun::rubyText() const 83 { 84 RenderObject* child = firstChild(); 85 return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0; 86 } 87 88 RenderRubyBase* RenderRubyRun::rubyBase() const 89 { 90 RenderObject* child = lastChild(); 91 return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0; 92 } 93 94 RenderRubyBase* RenderRubyRun::rubyBaseSafe() 95 { 96 RenderRubyBase* base = rubyBase(); 97 if (!base) { 98 base = createRubyBase(); 99 RenderBlock::addChild(base); 100 } 101 return base; 102 } 103 104 RenderBlock* RenderRubyRun::firstLineBlock() const 105 { 106 return 0; 107 } 108 109 void RenderRubyRun::updateFirstLetter() 110 { 111 } 112 113 bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const 114 { 115 return child->isRubyText() || child->isInline(); 116 } 117 118 void 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 if (hasRubyBase()) { 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 } 156 } else { 157 // child is not a text -> insert it into the base 158 // (append it instead if beforeChild is the ruby text) 159 if (beforeChild && beforeChild->isRubyText()) 160 beforeChild = 0; 161 rubyBaseSafe()->addChild(child, beforeChild); 162 } 163 } 164 165 void RenderRubyRun::removeChild(RenderObject* child) 166 { 167 // If the child is a ruby text, then merge the ruby base with the base of 168 // the right sibling run, if possible. 169 if (!m_beingDestroyed && !documentBeingDestroyed() && child->isRubyText()) { 170 RenderRubyBase* base = rubyBase(); 171 RenderObject* rightNeighbour = nextSibling(); 172 if (base && rightNeighbour && rightNeighbour->isRubyRun()) { 173 // Ruby run without a base can happen only at the first run. 174 RenderRubyRun* rightRun = static_cast<RenderRubyRun*>(rightNeighbour); 175 if (rightRun->hasRubyBase()) { 176 RenderRubyBase* rightBase = rightRun->rubyBaseSafe(); 177 // Collect all children in a single base, then swap the bases. 178 rightBase->moveChildren(base); 179 moveChildTo(rightRun, rightRun->children(), base); 180 rightRun->moveChildTo(this, children(), rightBase); 181 // The now empty ruby base will be removed below. 182 } 183 } 184 } 185 186 RenderBlock::removeChild(child); 187 188 if (!m_beingDestroyed && !documentBeingDestroyed()) { 189 // Check if our base (if any) is now empty. If so, destroy it. 190 RenderBlock* base = rubyBase(); 191 if (base && !base->firstChild()) { 192 RenderBlock::removeChild(base); 193 base->deleteLineBoxTree(); 194 base->destroy(); 195 } 196 197 // If any of the above leaves the run empty, destroy it as well. 198 if (isEmpty()) { 199 parent()->removeChild(this); 200 deleteLineBoxTree(); 201 destroy(); 202 } 203 } 204 } 205 206 RenderRubyBase* RenderRubyRun::createRubyBase() const 207 { 208 RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */); 209 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 210 newStyle->inheritFrom(style()); 211 newStyle->setDisplay(BLOCK); 212 newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER? 213 rb->setStyle(newStyle.release()); 214 return rb; 215 } 216 217 RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby) 218 { 219 ASSERT(parentRuby && parentRuby->isRuby()); 220 RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */); 221 RefPtr<RenderStyle> newStyle = RenderStyle::create(); 222 newStyle->inheritFrom(parentRuby->style()); 223 newStyle->setDisplay(INLINE_BLOCK); 224 rr->setStyle(newStyle.release()); 225 return rr; 226 } 227 228 } // namespace WebCore 229 230 #endif 231