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