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
39namespace blink {
40
41RenderRubyRun::RenderRubyRun()
42    : RenderBlockFlow(0)
43{
44    setReplaced(true);
45    setInline(true);
46}
47
48RenderRubyRun::~RenderRubyRun()
49{
50}
51
52bool 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
59bool 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
66RenderRubyText* 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
75RenderRubyBase* RenderRubyRun::rubyBase() const
76{
77    RenderObject* child = lastChild();
78    return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
79}
80
81RenderRubyBase* RenderRubyRun::rubyBaseSafe()
82{
83    RenderRubyBase* base = rubyBase();
84    if (!base) {
85        base = createRubyBase();
86        RenderBlockFlow::addChild(base);
87    }
88    return base;
89}
90
91bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const
92{
93    return child->isRubyText() || child->isInline();
94}
95
96void 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
140void 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
181RenderRubyBase* 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
190RenderRubyRun* 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
200RenderObject* 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
212void 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
258void 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