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