|
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, base); |
|
179 rightRun->moveChildTo(this, 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 // ENABLE(RUBY) |