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
39using namespace std;
40
41namespace WebCore {
42
43RenderRubyRun::RenderRubyRun(Node* node)
44    : RenderBlock(node)
45{
46    setReplaced(true);
47    setInline(true);
48}
49
50RenderRubyRun::~RenderRubyRun()
51{
52}
53
54bool 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
61bool 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
68bool RenderRubyRun::isEmpty() const
69{
70    return !hasRubyText() && !hasRubyBase();
71}
72
73RenderRubyText* RenderRubyRun::rubyText() const
74{
75    RenderObject* child = firstChild();
76    return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0;
77}
78
79RenderRubyBase* RenderRubyRun::rubyBase() const
80{
81    RenderObject* child = lastChild();
82    return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
83}
84
85RenderRubyBase* RenderRubyRun::rubyBaseSafe()
86{
87    RenderRubyBase* base = rubyBase();
88    if (!base) {
89        base = createRubyBase();
90        RenderBlock::addChild(base);
91    }
92    return base;
93}
94
95RenderBlock* RenderRubyRun::firstLineBlock() const
96{
97    return 0;
98}
99
100void RenderRubyRun::updateFirstLetter()
101{
102}
103
104bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const
105{
106    return child->isRubyText() || child->isInline();
107}
108
109void 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
153void 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
194RenderRubyBase* 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
204RenderRubyRun* 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
214RenderObject* 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
226void 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
271void 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