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