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#if ENABLE(RUBY)
34#include "RenderRubyRun.h"
35
36#include "RenderRubyBase.h"
37#include "RenderRubyText.h"
38#include "RenderView.h"
39
40using namespace std;
41
42namespace WebCore {
43
44RenderRubyRun::RenderRubyRun(Node* node)
45    : RenderBlock(node)
46    , m_beingDestroyed(false)
47{
48    setReplaced(true);
49    setInline(true);
50}
51
52RenderRubyRun::~RenderRubyRun()
53{
54}
55
56void RenderRubyRun::destroy()
57{
58    // Mark if the run is being destroyed to avoid trouble in removeChild().
59    m_beingDestroyed = true;
60    RenderBlock::destroy();
61}
62
63bool RenderRubyRun::hasRubyText() const
64{
65    // The only place where a ruby text can be is in the first position
66    // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
67    return firstChild() && firstChild()->isRubyText();
68}
69
70bool RenderRubyRun::hasRubyBase() const
71{
72    // The only place where a ruby base can be is in the last position
73    // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
74    return lastChild() && lastChild()->isRubyBase();
75}
76
77bool RenderRubyRun::isEmpty() const
78{
79    return !hasRubyText() && !hasRubyBase();
80}
81
82RenderRubyText* RenderRubyRun::rubyText() const
83{
84    RenderObject* child = firstChild();
85    return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0;
86}
87
88RenderRubyBase* RenderRubyRun::rubyBase() const
89{
90    RenderObject* child = lastChild();
91    return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
92}
93
94RenderRubyBase* RenderRubyRun::rubyBaseSafe()
95{
96    RenderRubyBase* base = rubyBase();
97    if (!base) {
98        base = createRubyBase();
99        RenderBlock::addChild(base);
100    }
101    return base;
102}
103
104RenderBlock* RenderRubyRun::firstLineBlock() const
105{
106    return 0;
107}
108
109void RenderRubyRun::updateFirstLetter()
110{
111}
112
113bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const
114{
115    return child->isRubyText() || child->isInline();
116}
117
118void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild)
119{
120    ASSERT(child);
121
122    // If child is a ruby text
123    if (child->isRubyText()) {
124        if (!beforeChild) {
125            // RenderRuby has already ascertained that we can add the child here.
126            ASSERT(!hasRubyText());
127            // prepend ruby texts as first child
128            RenderBlock::addChild(child, firstChild());
129        }  else if (beforeChild->isRubyText()) {
130            // New text is inserted just before another.
131            // In this case the new text takes the place of the old one, and
132            // the old text goes into a new run that is inserted as next sibling.
133            ASSERT(beforeChild->parent() == this);
134            RenderObject* ruby = parent();
135            ASSERT(ruby->isRuby());
136            RenderBlock* newRun = staticCreateRubyRun(ruby);
137            ruby->addChild(newRun, nextSibling());
138            // Add the new ruby text and move the old one to the new run
139            // Note: Doing it in this order and not using RenderRubyRun's methods,
140            // in order to avoid automatic removal of the ruby run in case there is no
141            // other child besides the old ruby text.
142            RenderBlock::addChild(child, beforeChild);
143            RenderBlock::removeChild(beforeChild);
144            newRun->addChild(beforeChild);
145        } else {
146            ASSERT(hasRubyBase()); // Otherwise beforeChild would be borked.
147            // Insertion before a ruby base object.
148            // In this case we need insert a new run before the current one and split the base.
149            RenderObject* ruby = parent();
150            RenderRubyRun* newRun = staticCreateRubyRun(ruby);
151            ruby->addChild(newRun, this);
152            newRun->addChild(child);
153            rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild);
154        }
155    } else {
156        // child is not a text -> insert it into the base
157        // (append it instead if beforeChild is the ruby text)
158        if (beforeChild && beforeChild->isRubyText())
159            beforeChild = 0;
160        rubyBaseSafe()->addChild(child, beforeChild);
161    }
162}
163
164void RenderRubyRun::removeChild(RenderObject* child)
165{
166    // If the child is a ruby text, then merge the ruby base with the base of
167    // the right sibling run, if possible.
168    if (!m_beingDestroyed && !documentBeingDestroyed() && child->isRubyText()) {
169        RenderRubyBase* base = rubyBase();
170        RenderObject* rightNeighbour = nextSibling();
171        if (base && rightNeighbour && rightNeighbour->isRubyRun()) {
172            // Ruby run without a base can happen only at the first run.
173            RenderRubyRun* rightRun = static_cast<RenderRubyRun*>(rightNeighbour);
174            ASSERT(rightRun->hasRubyBase());
175            RenderRubyBase* rightBase = rightRun->rubyBaseSafe();
176            // Collect all children in a single base, then swap the bases.
177            rightBase->moveChildren(base);
178            moveChildTo(rightRun, rightRun->children(), base);
179            rightRun->moveChildTo(this, children(), rightBase);
180            // The now empty ruby base will be removed below.
181        }
182    }
183
184    RenderBlock::removeChild(child);
185
186    if (!m_beingDestroyed && !documentBeingDestroyed()) {
187        // Check if our base (if any) is now empty. If so, destroy it.
188        RenderBlock* base = rubyBase();
189        if (base && !base->firstChild()) {
190            RenderBlock::removeChild(base);
191            base->deleteLineBoxTree();
192            base->destroy();
193        }
194
195        // If any of the above leaves the run empty, destroy it as well.
196        if (isEmpty()) {
197            parent()->removeChild(this);
198            deleteLineBoxTree();
199            destroy();
200        }
201    }
202}
203
204RenderRubyBase* RenderRubyRun::createRubyBase() const
205{
206    RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */);
207    RefPtr<RenderStyle> newStyle = RenderStyle::create();
208    newStyle->inheritFrom(style());
209    newStyle->setDisplay(BLOCK);
210    newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER?
211    rb->setStyle(newStyle.release());
212    return rb;
213}
214
215RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby)
216{
217    ASSERT(parentRuby && parentRuby->isRuby());
218    RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */);
219    RefPtr<RenderStyle> newStyle = RenderStyle::create();
220    newStyle->inheritFrom(parentRuby->style());
221    newStyle->setDisplay(INLINE_BLOCK);
222    rr->setStyle(newStyle.release());
223    return rr;
224}
225
226} // namespace WebCore
227
228#endif
229