Home | History | Annotate | Download | only in rendering
      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