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 "RenderRuby.h" 34 35 #include "RenderRubyRun.h" 36 #include "RenderStyle.h" 37 #include <wtf/RefPtr.h> 38 39 namespace WebCore { 40 41 //=== generic helper functions to avoid excessive code duplication === 42 43 static inline bool isAnonymousRubyInlineBlock(const RenderObject* object) 44 { 45 ASSERT(!object 46 || !object->parent()->isRuby() 47 || object->isRubyRun() 48 || (object->isInline() && (object->isBeforeContent() || object->isAfterContent())) 49 || (object->isAnonymous() && object->isRenderBlock() && object->style()->display() == INLINE_BLOCK)); 50 51 return object 52 && object->parent()->isRuby() 53 && object->isRenderBlock() 54 && !object->isRubyRun(); 55 } 56 57 static inline bool isRubyBeforeBlock(const RenderObject* object) 58 { 59 return isAnonymousRubyInlineBlock(object) 60 && !object->previousSibling() 61 && object->firstChild() 62 && object->firstChild()->style()->styleType() == BEFORE; 63 } 64 65 static inline bool isRubyAfterBlock(const RenderObject* object) 66 { 67 return isAnonymousRubyInlineBlock(object) 68 && !object->nextSibling() 69 && object->firstChild() 70 && object->firstChild()->style()->styleType() == AFTER; 71 } 72 73 static inline RenderBlock* rubyBeforeBlock(const RenderObject* ruby) 74 { 75 RenderObject* child = ruby->firstChild(); 76 return isRubyBeforeBlock(child) ? static_cast<RenderBlock*>(child) : 0; 77 } 78 79 static inline RenderBlock* rubyAfterBlock(const RenderObject* ruby) 80 { 81 RenderObject* child = ruby->lastChild(); 82 return isRubyAfterBlock(child) ? static_cast<RenderBlock*>(child) : 0; 83 } 84 85 static RenderBlock* createAnonymousRubyInlineBlock(RenderObject* ruby) 86 { 87 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(ruby->style()); 88 newStyle->setDisplay(INLINE_BLOCK); 89 90 RenderBlock* newBlock = new (ruby->renderArena()) RenderBlock(ruby->document() /* anonymous box */); 91 newBlock->setStyle(newStyle.release()); 92 return newBlock; 93 } 94 95 static RenderRubyRun* lastRubyRun(const RenderObject* ruby) 96 { 97 RenderObject* child = ruby->lastChild(); 98 if (child && !child->isRubyRun()) 99 child = child->previousSibling(); 100 ASSERT(!child || child->isRubyRun() || child->isBeforeContent() || child == rubyBeforeBlock(ruby)); 101 return child && child->isRubyRun() ? static_cast<RenderRubyRun*>(child) : 0; 102 } 103 104 static inline RenderRubyRun* findRubyRunParent(RenderObject* child) 105 { 106 while (child && !child->isRubyRun()) 107 child = child->parent(); 108 return static_cast<RenderRubyRun*>(child); 109 } 110 111 //=== ruby as inline object === 112 113 RenderRubyAsInline::RenderRubyAsInline(Node* node) 114 : RenderInline(node) 115 { 116 } 117 118 RenderRubyAsInline::~RenderRubyAsInline() 119 { 120 } 121 122 void RenderRubyAsInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 123 { 124 RenderInline::styleDidChange(diff, oldStyle); 125 propagateStyleToAnonymousChildren(); 126 } 127 128 void RenderRubyAsInline::addChild(RenderObject* child, RenderObject* beforeChild) 129 { 130 // Insert :before and :after content before/after the RenderRubyRun(s) 131 if (child->isBeforeContent()) { 132 if (child->isInline()) { 133 // Add generated inline content normally 134 RenderInline::addChild(child, firstChild()); 135 } else { 136 // Wrap non-inline content with an anonymous inline-block. 137 RenderBlock* beforeBlock = rubyBeforeBlock(this); 138 if (!beforeBlock) { 139 beforeBlock = createAnonymousRubyInlineBlock(this); 140 RenderInline::addChild(beforeBlock, firstChild()); 141 } 142 beforeBlock->addChild(child); 143 } 144 return; 145 } 146 if (child->isAfterContent()) { 147 if (child->isInline()) { 148 // Add generated inline content normally 149 RenderInline::addChild(child); 150 } else { 151 // Wrap non-inline content with an anonymous inline-block. 152 RenderBlock* afterBlock = rubyAfterBlock(this); 153 if (!afterBlock) { 154 afterBlock = createAnonymousRubyInlineBlock(this); 155 RenderInline::addChild(afterBlock); 156 } 157 afterBlock->addChild(child); 158 } 159 return; 160 } 161 162 // If the child is a ruby run, just add it normally. 163 if (child->isRubyRun()) { 164 RenderInline::addChild(child, beforeChild); 165 return; 166 } 167 168 if (beforeChild && !isAfterContent(beforeChild)) { 169 // insert child into run 170 ASSERT(!beforeChild->isRubyRun()); 171 RenderObject* run = beforeChild; 172 while (run && !run->isRubyRun()) 173 run = run->parent(); 174 if (run) { 175 run->addChild(child, beforeChild); 176 return; 177 } 178 ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent! 179 // Emergency fallback: fall through and just append. 180 } 181 182 // If the new child would be appended, try to add the child to the previous run 183 // if possible, or create a new run otherwise. 184 // (The RenderRubyRun object will handle the details) 185 RenderRubyRun* lastRun = lastRubyRun(this); 186 if (!lastRun || lastRun->hasRubyText()) { 187 lastRun = RenderRubyRun::staticCreateRubyRun(this); 188 RenderInline::addChild(lastRun); 189 } 190 lastRun->addChild(child); 191 } 192 193 void RenderRubyAsInline::removeChild(RenderObject* child) 194 { 195 // If the child's parent is *this (must be a ruby run or generated content or anonymous block), 196 // just use the normal remove method. 197 if (child->parent() == this) { 198 ASSERT(child->isRubyRun() || child->isBeforeContent() || child->isAfterContent() || isAnonymousRubyInlineBlock(child)); 199 RenderInline::removeChild(child); 200 return; 201 } 202 // If the child's parent is an anoymous block (must be generated :before/:after content) 203 // just use the block's remove method. 204 if (isAnonymousRubyInlineBlock(child->parent())) { 205 ASSERT(child->isBeforeContent() || child->isAfterContent()); 206 child->parent()->removeChild(child); 207 removeChild(child->parent()); 208 return; 209 } 210 211 // Otherwise find the containing run and remove it from there. 212 RenderRubyRun* run = findRubyRunParent(child); 213 ASSERT(run); 214 run->removeChild(child); 215 } 216 217 218 //=== ruby as block object === 219 220 RenderRubyAsBlock::RenderRubyAsBlock(Node* node) 221 : RenderBlock(node) 222 { 223 } 224 225 RenderRubyAsBlock::~RenderRubyAsBlock() 226 { 227 } 228 229 void RenderRubyAsBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 230 { 231 RenderBlock::styleDidChange(diff, oldStyle); 232 propagateStyleToAnonymousChildren(); 233 } 234 235 void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild) 236 { 237 // Insert :before and :after content before/after the RenderRubyRun(s) 238 if (child->isBeforeContent()) { 239 if (child->isInline()) { 240 // Add generated inline content normally 241 RenderBlock::addChild(child, firstChild()); 242 } else { 243 // Wrap non-inline content with an anonymous inline-block. 244 RenderBlock* beforeBlock = rubyBeforeBlock(this); 245 if (!beforeBlock) { 246 beforeBlock = createAnonymousRubyInlineBlock(this); 247 RenderBlock::addChild(beforeBlock, firstChild()); 248 } 249 beforeBlock->addChild(child); 250 } 251 return; 252 } 253 if (child->isAfterContent()) { 254 if (child->isInline()) { 255 // Add generated inline content normally 256 RenderBlock::addChild(child); 257 } else { 258 // Wrap non-inline content with an anonymous inline-block. 259 RenderBlock* afterBlock = rubyAfterBlock(this); 260 if (!afterBlock) { 261 afterBlock = createAnonymousRubyInlineBlock(this); 262 RenderBlock::addChild(afterBlock); 263 } 264 afterBlock->addChild(child); 265 } 266 return; 267 } 268 269 // If the child is a ruby run, just add it normally. 270 if (child->isRubyRun()) { 271 RenderBlock::addChild(child, beforeChild); 272 return; 273 } 274 275 if (beforeChild && !isAfterContent(beforeChild)) { 276 // insert child into run 277 ASSERT(!beforeChild->isRubyRun()); 278 RenderObject* run = beforeChild; 279 while (run && !run->isRubyRun()) 280 run = run->parent(); 281 if (run) { 282 run->addChild(child, beforeChild); 283 return; 284 } 285 ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent! 286 // Emergency fallback: fall through and just append. 287 } 288 289 // If the new child would be appended, try to add the child to the previous run 290 // if possible, or create a new run otherwise. 291 // (The RenderRubyRun object will handle the details) 292 RenderRubyRun* lastRun = lastRubyRun(this); 293 if (!lastRun || lastRun->hasRubyText()) { 294 lastRun = RenderRubyRun::staticCreateRubyRun(this); 295 RenderBlock::addChild(lastRun); 296 } 297 lastRun->addChild(child); 298 } 299 300 void RenderRubyAsBlock::removeChild(RenderObject* child) 301 { 302 // If the child's parent is *this (must be a ruby run or generated content or anonymous block), 303 // just use the normal remove method. 304 if (child->parent() == this) { 305 ASSERT(child->isRubyRun() || child->isBeforeContent() || child->isAfterContent() || isAnonymousRubyInlineBlock(child)); 306 RenderBlock::removeChild(child); 307 return; 308 } 309 // If the child's parent is an anoymous block (must be generated :before/:after content) 310 // just use the block's remove method. 311 if (isAnonymousRubyInlineBlock(child->parent())) { 312 ASSERT(child->isBeforeContent() || child->isAfterContent()); 313 child->parent()->removeChild(child); 314 removeChild(child->parent()); 315 return; 316 } 317 318 // Otherwise find the containing run and remove it from there. 319 RenderRubyRun* run = findRubyRunParent(child); 320 ASSERT(run); 321 run->removeChild(child); 322 } 323 324 } // namespace WebCore 325