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 "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