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 "RenderRubyRun.h"
     34 
     35 #include "RenderRubyBase.h"
     36 #include "RenderRubyText.h"
     37 #include "RenderView.h"
     38 
     39 using namespace std;
     40 
     41 namespace WebCore {
     42 
     43 RenderRubyRun::RenderRubyRun(Node* node)
     44     : RenderBlock(node)
     45 {
     46     setReplaced(true);
     47     setInline(true);
     48 }
     49 
     50 RenderRubyRun::~RenderRubyRun()
     51 {
     52 }
     53 
     54 bool RenderRubyRun::hasRubyText() const
     55 {
     56     // The only place where a ruby text can be is in the first position
     57     // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
     58     return firstChild() && firstChild()->isRubyText();
     59 }
     60 
     61 bool RenderRubyRun::hasRubyBase() const
     62 {
     63     // The only place where a ruby base can be is in the last position
     64     // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
     65     return lastChild() && lastChild()->isRubyBase();
     66 }
     67 
     68 bool RenderRubyRun::isEmpty() const
     69 {
     70     return !hasRubyText() && !hasRubyBase();
     71 }
     72 
     73 RenderRubyText* RenderRubyRun::rubyText() const
     74 {
     75     RenderObject* child = firstChild();
     76     return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0;
     77 }
     78 
     79 RenderRubyBase* RenderRubyRun::rubyBase() const
     80 {
     81     RenderObject* child = lastChild();
     82     return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
     83 }
     84 
     85 RenderRubyBase* RenderRubyRun::rubyBaseSafe()
     86 {
     87     RenderRubyBase* base = rubyBase();
     88     if (!base) {
     89         base = createRubyBase();
     90         RenderBlock::addChild(base);
     91     }
     92     return base;
     93 }
     94 
     95 RenderBlock* RenderRubyRun::firstLineBlock() const
     96 {
     97     return 0;
     98 }
     99 
    100 void RenderRubyRun::updateFirstLetter()
    101 {
    102 }
    103 
    104 bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const
    105 {
    106     return child->isRubyText() || child->isInline();
    107 }
    108 
    109 void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild)
    110 {
    111     ASSERT(child);
    112 
    113     if (child->isRubyText()) {
    114         if (!beforeChild) {
    115             // RenderRuby has already ascertained that we can add the child here.
    116             ASSERT(!hasRubyText());
    117             // prepend ruby texts as first child
    118             RenderBlock::addChild(child, firstChild());
    119         }  else if (beforeChild->isRubyText()) {
    120             // New text is inserted just before another.
    121             // In this case the new text takes the place of the old one, and
    122             // the old text goes into a new run that is inserted as next sibling.
    123             ASSERT(beforeChild->parent() == this);
    124             RenderObject* ruby = parent();
    125             ASSERT(ruby->isRuby());
    126             RenderBlock* newRun = staticCreateRubyRun(ruby);
    127             ruby->addChild(newRun, nextSibling());
    128             // Add the new ruby text and move the old one to the new run
    129             // Note: Doing it in this order and not using RenderRubyRun's methods,
    130             // in order to avoid automatic removal of the ruby run in case there is no
    131             // other child besides the old ruby text.
    132             RenderBlock::addChild(child, beforeChild);
    133             RenderBlock::removeChild(beforeChild);
    134             newRun->addChild(beforeChild);
    135         } else if (hasRubyBase()) {
    136             // Insertion before a ruby base object.
    137             // In this case we need insert a new run before the current one and split the base.
    138             RenderObject* ruby = parent();
    139             RenderRubyRun* newRun = staticCreateRubyRun(ruby);
    140             ruby->addChild(newRun, this);
    141             newRun->addChild(child);
    142             rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild);
    143         }
    144     } else {
    145         // child is not a text -> insert it into the base
    146         // (append it instead if beforeChild is the ruby text)
    147         if (beforeChild && beforeChild->isRubyText())
    148             beforeChild = 0;
    149         rubyBaseSafe()->addChild(child, beforeChild);
    150     }
    151 }
    152 
    153 void RenderRubyRun::removeChild(RenderObject* child)
    154 {
    155     // If the child is a ruby text, then merge the ruby base with the base of
    156     // the right sibling run, if possible.
    157     if (!beingDestroyed() && !documentBeingDestroyed() && child->isRubyText()) {
    158         RenderRubyBase* base = rubyBase();
    159         RenderObject* rightNeighbour = nextSibling();
    160         if (base && rightNeighbour && rightNeighbour->isRubyRun()) {
    161             // Ruby run without a base can happen only at the first run.
    162             RenderRubyRun* rightRun = static_cast<RenderRubyRun*>(rightNeighbour);
    163             if (rightRun->hasRubyBase()) {
    164                 RenderRubyBase* rightBase = rightRun->rubyBaseSafe();
    165                 // Collect all children in a single base, then swap the bases.
    166                 rightBase->moveChildren(base);
    167                 moveChildTo(rightRun, base);
    168                 rightRun->moveChildTo(this, rightBase);
    169                 // The now empty ruby base will be removed below.
    170             }
    171         }
    172     }
    173 
    174     RenderBlock::removeChild(child);
    175 
    176     if (!beingDestroyed() && !documentBeingDestroyed()) {
    177         // Check if our base (if any) is now empty. If so, destroy it.
    178         RenderBlock* base = rubyBase();
    179         if (base && !base->firstChild()) {
    180             RenderBlock::removeChild(base);
    181             base->deleteLineBoxTree();
    182             base->destroy();
    183         }
    184 
    185         // If any of the above leaves the run empty, destroy it as well.
    186         if (isEmpty()) {
    187             parent()->removeChild(this);
    188             deleteLineBoxTree();
    189             destroy();
    190         }
    191     }
    192 }
    193 
    194 RenderRubyBase* RenderRubyRun::createRubyBase() const
    195 {
    196     RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */);
    197     RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(style());
    198     newStyle->setDisplay(BLOCK);
    199     newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER?
    200     rb->setStyle(newStyle.release());
    201     return rb;
    202 }
    203 
    204 RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby)
    205 {
    206     ASSERT(parentRuby && parentRuby->isRuby());
    207     RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */);
    208     RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(parentRuby->style());
    209     newStyle->setDisplay(INLINE_BLOCK);
    210     rr->setStyle(newStyle.release());
    211     return rr;
    212 }
    213 
    214 RenderObject* RenderRubyRun::layoutSpecialExcludedChild(bool relayoutChildren)
    215 {
    216     // Don't bother positioning the RenderRubyRun yet.
    217     RenderRubyText* rt = rubyText();
    218     if (!rt)
    219         return 0;
    220     if (relayoutChildren)
    221         rt->setChildNeedsLayout(true, false);
    222     rt->layoutIfNeeded();
    223     return rt;
    224 }
    225 
    226 void RenderRubyRun::layout()
    227 {
    228     RenderBlock::layout();
    229 
    230     // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase.
    231     RenderRubyText* rt = rubyText();
    232     if (!rt)
    233         return;
    234 
    235     int lastLineRubyTextBottom = rt->logicalHeight();
    236     int firstLineRubyTextTop = 0;
    237     RootInlineBox* rootBox = rt->lastRootBox();
    238     if (rootBox) {
    239         // In order to align, we have to ignore negative leading.
    240         firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow();
    241         lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow();
    242     }
    243 
    244     if (!style()->isFlippedLinesWritingMode()) {
    245         int firstLineTop = 0;
    246         if (RenderRubyBase* rb = rubyBase()) {
    247             RootInlineBox* rootBox = rb->firstRootBox();
    248             if (rootBox)
    249                 firstLineTop = rootBox->logicalTopLayoutOverflow();
    250             firstLineTop += rb->logicalTop();
    251         }
    252 
    253         rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop);
    254     } else {
    255         int lastLineBottom = logicalHeight();
    256         if (RenderRubyBase* rb = rubyBase()) {
    257             RootInlineBox* rootBox = rb->lastRootBox();
    258             if (rootBox)
    259                 lastLineBottom = rootBox->logicalBottomLayoutOverflow();
    260             lastLineBottom += rb->logicalTop();
    261         }
    262 
    263         rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom);
    264     }
    265 
    266     // Update our overflow to account for the new RenderRubyText position.
    267     m_overflow.clear();
    268     computeOverflow(clientLogicalBottom());
    269 }
    270 
    271 void RenderRubyRun::getOverhang(bool firstLine, RenderObject* startRenderer, RenderObject* endRenderer, int& startOverhang, int& endOverhang) const
    272 {
    273     ASSERT(!needsLayout());
    274 
    275     startOverhang = 0;
    276     endOverhang = 0;
    277 
    278     RenderRubyBase* rubyBase = this->rubyBase();
    279     RenderRubyText* rubyText = this->rubyText();
    280 
    281     if (!rubyBase || !rubyText)
    282         return;
    283 
    284     if (!rubyBase->firstRootBox())
    285         return;
    286 
    287     int logicalWidth = this->logicalWidth();
    288 
    289     // No more than half a ruby is allowed to overhang.
    290     int logicalLeftOverhang = rubyText->style(firstLine)->fontSize() / 2;
    291     int logicalRightOverhang = logicalLeftOverhang;
    292 
    293     for (RootInlineBox* rootInlineBox = rubyBase->firstRootBox(); rootInlineBox; rootInlineBox = rootInlineBox->nextRootBox()) {
    294         logicalLeftOverhang = min<int>(logicalLeftOverhang, rootInlineBox->logicalLeft());
    295         logicalRightOverhang = min<int>(logicalRightOverhang, logicalWidth - rootInlineBox->logicalRight());
    296     }
    297 
    298     startOverhang = style()->isLeftToRightDirection() ? logicalLeftOverhang : logicalRightOverhang;
    299     endOverhang = style()->isLeftToRightDirection() ? logicalRightOverhang : logicalLeftOverhang;
    300 
    301     if (!startRenderer || !startRenderer->isText() || startRenderer->style(firstLine)->fontSize() > rubyBase->style(firstLine)->fontSize())
    302         startOverhang = 0;
    303 
    304     if (!endRenderer || !endRenderer->isText() || endRenderer->style(firstLine)->fontSize() > rubyBase->style(firstLine)->fontSize())
    305         endOverhang = 0;
    306 }
    307 
    308 } // namespace WebCore
    309