2010-02-08 Maciej Stachowiak <mjs@apple.com>
[WebKit-https.git] / WebCore / rendering / RenderRubyRun.cpp
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
40 using namespace std;
41
42 namespace WebCore {
43
44 RenderRubyRun::RenderRubyRun(Node* node)
45     : RenderBlock(node)
46     , m_beingDestroyed(false)
47 {
48     setReplaced(true);
49     setInline(true);
50 }
51
52 RenderRubyRun::~RenderRubyRun()
53 {
54 }
55
56 void 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
63 bool 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
70 bool 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
77 bool RenderRubyRun::isEmpty() const
78 {
79     return !hasRubyText() && !hasRubyBase();
80 }
81
82 RenderRubyText* RenderRubyRun::rubyText() const
83 {
84     RenderObject* child = firstChild();
85     return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0;
86 }
87
88 RenderRubyBase* RenderRubyRun::rubyBase() const
89 {
90     RenderObject* child = lastChild();
91     return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
92 }
93
94 RenderRubyBase* RenderRubyRun::rubyBaseSafe()
95 {
96     RenderRubyBase* base = rubyBase();
97     if (!base) {
98         base = createRubyBase();
99         RenderBlock::addChild(base);
100     }
101     return base;
102 }
103
104 RenderBlock* RenderRubyRun::firstLineBlock() const
105 {
106     return 0;
107 }
108
109 void RenderRubyRun::updateFirstLetter()
110 {
111 }
112
113 bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const
114 {
115     return child->isRubyText() || child->isInline();
116 }
117
118 void 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
164 void 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
204 RenderRubyBase* 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
215 RenderRubyRun* 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