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 "RenderRuby.h"
34
35 #include "RenderRubyRun.h"
36 #include "RenderStyle.h"
37 #include <wtf/RefPtr.h>
38
39 namespace WebCore {
40
41 //=== generic helper functions to avoid excessive code duplication ===
42
isAnonymousRubyInlineBlock(const RenderObject * object)43 static inline bool isAnonymousRubyInlineBlock(const RenderObject* object)
44 {
45 ASSERT(!object
46 || !object->parent()->isRuby()
47 || object->isRubyRun()
48 || (object->isInline() && (object->isBeforeContent() || object->isAfterContent()))
49 || (object->isAnonymous() && object->isRenderBlock() && object->style()->display() == INLINE_BLOCK));
50
51 return object
52 && object->parent()->isRuby()
53 && object->isRenderBlock()
54 && !object->isRubyRun();
55 }
56
isRubyBeforeBlock(const RenderObject * object)57 static inline bool isRubyBeforeBlock(const RenderObject* object)
58 {
59 return isAnonymousRubyInlineBlock(object)
60 && !object->previousSibling()
61 && object->firstChild()
62 && object->firstChild()->style()->styleType() == BEFORE;
63 }
64
isRubyAfterBlock(const RenderObject * object)65 static inline bool isRubyAfterBlock(const RenderObject* object)
66 {
67 return isAnonymousRubyInlineBlock(object)
68 && !object->nextSibling()
69 && object->firstChild()
70 && object->firstChild()->style()->styleType() == AFTER;
71 }
72
rubyBeforeBlock(const RenderObject * ruby)73 static inline RenderBlock* rubyBeforeBlock(const RenderObject* ruby)
74 {
75 RenderObject* child = ruby->firstChild();
76 return isRubyBeforeBlock(child) ? static_cast<RenderBlock*>(child) : 0;
77 }
78
rubyAfterBlock(const RenderObject * ruby)79 static inline RenderBlock* rubyAfterBlock(const RenderObject* ruby)
80 {
81 RenderObject* child = ruby->lastChild();
82 return isRubyAfterBlock(child) ? static_cast<RenderBlock*>(child) : 0;
83 }
84
createAnonymousRubyInlineBlock(RenderObject * ruby)85 static RenderBlock* createAnonymousRubyInlineBlock(RenderObject* ruby)
86 {
87 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyle(ruby->style());
88 newStyle->setDisplay(INLINE_BLOCK);
89
90 RenderBlock* newBlock = new (ruby->renderArena()) RenderBlock(ruby->document() /* anonymous box */);
91 newBlock->setStyle(newStyle.release());
92 return newBlock;
93 }
94
lastRubyRun(const RenderObject * ruby)95 static RenderRubyRun* lastRubyRun(const RenderObject* ruby)
96 {
97 RenderObject* child = ruby->lastChild();
98 if (child && !child->isRubyRun())
99 child = child->previousSibling();
100 ASSERT(!child || child->isRubyRun() || child->isBeforeContent() || child == rubyBeforeBlock(ruby));
101 return child && child->isRubyRun() ? static_cast<RenderRubyRun*>(child) : 0;
102 }
103
findRubyRunParent(RenderObject * child)104 static inline RenderRubyRun* findRubyRunParent(RenderObject* child)
105 {
106 while (child && !child->isRubyRun())
107 child = child->parent();
108 return static_cast<RenderRubyRun*>(child);
109 }
110
111 //=== ruby as inline object ===
112
RenderRubyAsInline(Node * node)113 RenderRubyAsInline::RenderRubyAsInline(Node* node)
114 : RenderInline(node)
115 {
116 }
117
~RenderRubyAsInline()118 RenderRubyAsInline::~RenderRubyAsInline()
119 {
120 }
121
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)122 void RenderRubyAsInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
123 {
124 RenderInline::styleDidChange(diff, oldStyle);
125 propagateStyleToAnonymousChildren();
126 }
127
addChild(RenderObject * child,RenderObject * beforeChild)128 void RenderRubyAsInline::addChild(RenderObject* child, RenderObject* beforeChild)
129 {
130 // Insert :before and :after content before/after the RenderRubyRun(s)
131 if (child->isBeforeContent()) {
132 if (child->isInline()) {
133 // Add generated inline content normally
134 RenderInline::addChild(child, firstChild());
135 } else {
136 // Wrap non-inline content with an anonymous inline-block.
137 RenderBlock* beforeBlock = rubyBeforeBlock(this);
138 if (!beforeBlock) {
139 beforeBlock = createAnonymousRubyInlineBlock(this);
140 RenderInline::addChild(beforeBlock, firstChild());
141 }
142 beforeBlock->addChild(child);
143 }
144 return;
145 }
146 if (child->isAfterContent()) {
147 if (child->isInline()) {
148 // Add generated inline content normally
149 RenderInline::addChild(child);
150 } else {
151 // Wrap non-inline content with an anonymous inline-block.
152 RenderBlock* afterBlock = rubyAfterBlock(this);
153 if (!afterBlock) {
154 afterBlock = createAnonymousRubyInlineBlock(this);
155 RenderInline::addChild(afterBlock);
156 }
157 afterBlock->addChild(child);
158 }
159 return;
160 }
161
162 // If the child is a ruby run, just add it normally.
163 if (child->isRubyRun()) {
164 RenderInline::addChild(child, beforeChild);
165 return;
166 }
167
168 if (beforeChild && !isAfterContent(beforeChild)) {
169 // insert child into run
170 ASSERT(!beforeChild->isRubyRun());
171 RenderObject* run = beforeChild;
172 while (run && !run->isRubyRun())
173 run = run->parent();
174 if (run) {
175 run->addChild(child, beforeChild);
176 return;
177 }
178 ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent!
179 // Emergency fallback: fall through and just append.
180 }
181
182 // If the new child would be appended, try to add the child to the previous run
183 // if possible, or create a new run otherwise.
184 // (The RenderRubyRun object will handle the details)
185 RenderRubyRun* lastRun = lastRubyRun(this);
186 if (!lastRun || lastRun->hasRubyText()) {
187 lastRun = RenderRubyRun::staticCreateRubyRun(this);
188 RenderInline::addChild(lastRun);
189 }
190 lastRun->addChild(child);
191 }
192
removeChild(RenderObject * child)193 void RenderRubyAsInline::removeChild(RenderObject* child)
194 {
195 // If the child's parent is *this (must be a ruby run or generated content or anonymous block),
196 // just use the normal remove method.
197 if (child->parent() == this) {
198 ASSERT(child->isRubyRun() || child->isBeforeContent() || child->isAfterContent() || isAnonymousRubyInlineBlock(child));
199 RenderInline::removeChild(child);
200 return;
201 }
202 // If the child's parent is an anoymous block (must be generated :before/:after content)
203 // just use the block's remove method.
204 if (isAnonymousRubyInlineBlock(child->parent())) {
205 ASSERT(child->isBeforeContent() || child->isAfterContent());
206 child->parent()->removeChild(child);
207 removeChild(child->parent());
208 return;
209 }
210
211 // Otherwise find the containing run and remove it from there.
212 RenderRubyRun* run = findRubyRunParent(child);
213 ASSERT(run);
214 run->removeChild(child);
215 }
216
217
218 //=== ruby as block object ===
219
RenderRubyAsBlock(Node * node)220 RenderRubyAsBlock::RenderRubyAsBlock(Node* node)
221 : RenderBlock(node)
222 {
223 }
224
~RenderRubyAsBlock()225 RenderRubyAsBlock::~RenderRubyAsBlock()
226 {
227 }
228
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)229 void RenderRubyAsBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
230 {
231 RenderBlock::styleDidChange(diff, oldStyle);
232 propagateStyleToAnonymousChildren();
233 }
234
addChild(RenderObject * child,RenderObject * beforeChild)235 void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild)
236 {
237 // Insert :before and :after content before/after the RenderRubyRun(s)
238 if (child->isBeforeContent()) {
239 if (child->isInline()) {
240 // Add generated inline content normally
241 RenderBlock::addChild(child, firstChild());
242 } else {
243 // Wrap non-inline content with an anonymous inline-block.
244 RenderBlock* beforeBlock = rubyBeforeBlock(this);
245 if (!beforeBlock) {
246 beforeBlock = createAnonymousRubyInlineBlock(this);
247 RenderBlock::addChild(beforeBlock, firstChild());
248 }
249 beforeBlock->addChild(child);
250 }
251 return;
252 }
253 if (child->isAfterContent()) {
254 if (child->isInline()) {
255 // Add generated inline content normally
256 RenderBlock::addChild(child);
257 } else {
258 // Wrap non-inline content with an anonymous inline-block.
259 RenderBlock* afterBlock = rubyAfterBlock(this);
260 if (!afterBlock) {
261 afterBlock = createAnonymousRubyInlineBlock(this);
262 RenderBlock::addChild(afterBlock);
263 }
264 afterBlock->addChild(child);
265 }
266 return;
267 }
268
269 // If the child is a ruby run, just add it normally.
270 if (child->isRubyRun()) {
271 RenderBlock::addChild(child, beforeChild);
272 return;
273 }
274
275 if (beforeChild && !isAfterContent(beforeChild)) {
276 // insert child into run
277 ASSERT(!beforeChild->isRubyRun());
278 RenderObject* run = beforeChild;
279 while (run && !run->isRubyRun())
280 run = run->parent();
281 if (run) {
282 run->addChild(child, beforeChild);
283 return;
284 }
285 ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent!
286 // Emergency fallback: fall through and just append.
287 }
288
289 // If the new child would be appended, try to add the child to the previous run
290 // if possible, or create a new run otherwise.
291 // (The RenderRubyRun object will handle the details)
292 RenderRubyRun* lastRun = lastRubyRun(this);
293 if (!lastRun || lastRun->hasRubyText()) {
294 lastRun = RenderRubyRun::staticCreateRubyRun(this);
295 RenderBlock::addChild(lastRun);
296 }
297 lastRun->addChild(child);
298 }
299
removeChild(RenderObject * child)300 void RenderRubyAsBlock::removeChild(RenderObject* child)
301 {
302 // If the child's parent is *this (must be a ruby run or generated content or anonymous block),
303 // just use the normal remove method.
304 if (child->parent() == this) {
305 ASSERT(child->isRubyRun() || child->isBeforeContent() || child->isAfterContent() || isAnonymousRubyInlineBlock(child));
306 RenderBlock::removeChild(child);
307 return;
308 }
309 // If the child's parent is an anoymous block (must be generated :before/:after content)
310 // just use the block's remove method.
311 if (isAnonymousRubyInlineBlock(child->parent())) {
312 ASSERT(child->isBeforeContent() || child->isAfterContent());
313 child->parent()->removeChild(child);
314 removeChild(child->parent());
315 return;
316 }
317
318 // Otherwise find the containing run and remove it from there.
319 RenderRubyRun* run = findRubyRunParent(child);
320 ASSERT(run);
321 run->removeChild(child);
322 }
323
324 } // namespace WebCore
325