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