2 * Copyright (C) 2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "core/rendering/RenderRuby.h"
35 #include "core/frame/UseCounter.h"
36 #include "core/rendering/RenderRubyRun.h"
37 #include "core/rendering/style/RenderStyle.h"
38 #include "wtf/RefPtr.h"
42 //=== generic helper functions to avoid excessive code duplication ===
44 static inline bool isAnonymousRubyInlineBlock(const RenderObject* object)
47 || !object->parent()->isRuby()
48 || object->isRubyRun()
49 || (object->isInline() && (object->isBeforeContent() || object->isAfterContent()))
50 || (object->isAnonymous() && object->isRenderBlock() && object->style()->display() == INLINE_BLOCK));
53 && object->parent()->isRuby()
54 && object->isRenderBlock()
55 && !object->isRubyRun();
58 static inline bool isRubyBeforeBlock(const RenderObject* object)
60 return isAnonymousRubyInlineBlock(object)
61 && !object->previousSibling()
62 && toRenderBlock(object)->firstChild()
63 && toRenderBlock(object)->firstChild()->style()->styleType() == BEFORE;
66 static inline bool isRubyAfterBlock(const RenderObject* object)
68 return isAnonymousRubyInlineBlock(object)
69 && !object->nextSibling()
70 && toRenderBlock(object)->firstChild()
71 && toRenderBlock(object)->firstChild()->style()->styleType() == AFTER;
74 static inline RenderBlock* rubyBeforeBlock(const RenderObject* ruby)
76 RenderObject* child = ruby->slowFirstChild();
77 return isRubyBeforeBlock(child) ? toRenderBlock(child) : 0;
80 static inline RenderBlock* rubyAfterBlock(const RenderObject* ruby)
82 RenderObject* child = ruby->slowLastChild();
83 return isRubyAfterBlock(child) ? toRenderBlock(child) : 0;
86 static RenderBlockFlow* createAnonymousRubyInlineBlock(RenderObject* ruby)
88 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(ruby->style(), INLINE_BLOCK);
89 RenderBlockFlow* newBlock = RenderBlockFlow::createAnonymous(&ruby->document());
90 newBlock->setStyle(newStyle.release());
94 static RenderRubyRun* lastRubyRun(const RenderObject* ruby)
96 RenderObject* child = ruby->slowLastChild();
97 if (child && !child->isRubyRun())
98 child = child->previousSibling();
99 ASSERT(!child || child->isRubyRun() || child->isBeforeContent() || child == rubyBeforeBlock(ruby));
100 return child && child->isRubyRun() ? toRenderRubyRun(child) : 0;
103 static inline RenderRubyRun* findRubyRunParent(RenderObject* child)
105 while (child && !child->isRubyRun())
106 child = child->parent();
107 return toRenderRubyRun(child);
110 //=== ruby as inline object ===
112 RenderRubyAsInline::RenderRubyAsInline(Element* element)
113 : RenderInline(element)
115 UseCounter::count(document(), UseCounter::RenderRuby);
118 RenderRubyAsInline::~RenderRubyAsInline()
122 void RenderRubyAsInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
124 RenderInline::styleDidChange(diff, oldStyle);
125 propagateStyleToAnonymousChildren();
128 void RenderRubyAsInline::addChild(RenderObject* child, RenderObject* beforeChild)
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());
136 // Wrap non-inline content with an anonymous inline-block.
137 RenderBlock* beforeBlock = rubyBeforeBlock(this);
139 beforeBlock = createAnonymousRubyInlineBlock(this);
140 RenderInline::addChild(beforeBlock, firstChild());
142 beforeBlock->addChild(child);
146 if (child->isAfterContent()) {
147 if (child->isInline()) {
148 // Add generated inline content normally
149 RenderInline::addChild(child);
151 // Wrap non-inline content with an anonymous inline-block.
152 RenderBlock* afterBlock = rubyAfterBlock(this);
154 afterBlock = createAnonymousRubyInlineBlock(this);
155 RenderInline::addChild(afterBlock);
157 afterBlock->addChild(child);
162 // If the child is a ruby run, just add it normally.
163 if (child->isRubyRun()) {
164 RenderInline::addChild(child, beforeChild);
168 if (beforeChild && !isAfterContent(beforeChild)) {
169 // insert child into run
170 RenderObject* run = beforeChild;
171 while (run && !run->isRubyRun())
174 if (beforeChild == run)
175 beforeChild = toRenderRubyRun(beforeChild)->firstChild();
176 ASSERT(!beforeChild || beforeChild->isDescendantOf(run));
177 run->addChild(child, beforeChild);
180 ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent!
181 // Emergency fallback: fall through and just append.
184 // If the new child would be appended, try to add the child to the previous run
185 // if possible, or create a new run otherwise.
186 // (The RenderRubyRun object will handle the details)
187 RenderRubyRun* lastRun = lastRubyRun(this);
188 if (!lastRun || lastRun->hasRubyText()) {
189 lastRun = RenderRubyRun::staticCreateRubyRun(this);
190 RenderInline::addChild(lastRun, beforeChild);
192 lastRun->addChild(child);
195 void RenderRubyAsInline::removeChild(RenderObject* child)
197 // If the child's parent is *this (must be a ruby run or generated content or anonymous block),
198 // just use the normal remove method.
199 if (child->parent() == this) {
200 ASSERT(child->isRubyRun() || child->isBeforeContent() || child->isAfterContent() || isAnonymousRubyInlineBlock(child));
201 RenderInline::removeChild(child);
204 // If the child's parent is an anoymous block (must be generated :before/:after content)
205 // just use the block's remove method.
206 if (isAnonymousRubyInlineBlock(child->parent())) {
207 ASSERT(child->isBeforeContent() || child->isAfterContent());
208 child->parent()->removeChild(child);
209 removeChild(child->parent());
213 // Otherwise find the containing run and remove it from there.
214 RenderRubyRun* run = findRubyRunParent(child);
216 run->removeChild(child);
219 //=== ruby as block object ===
221 RenderRubyAsBlock::RenderRubyAsBlock(Element* element)
222 : RenderBlockFlow(element)
224 UseCounter::count(document(), UseCounter::RenderRuby);
227 RenderRubyAsBlock::~RenderRubyAsBlock()
231 void RenderRubyAsBlock::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
233 RenderBlockFlow::styleDidChange(diff, oldStyle);
234 propagateStyleToAnonymousChildren();
237 void RenderRubyAsBlock::addChild(RenderObject* child, RenderObject* beforeChild)
239 // Insert :before and :after content before/after the RenderRubyRun(s)
240 if (child->isBeforeContent()) {
241 if (child->isInline()) {
242 // Add generated inline content normally
243 RenderBlockFlow::addChild(child, firstChild());
245 // Wrap non-inline content with an anonymous inline-block.
246 RenderBlock* beforeBlock = rubyBeforeBlock(this);
248 beforeBlock = createAnonymousRubyInlineBlock(this);
249 RenderBlockFlow::addChild(beforeBlock, firstChild());
251 beforeBlock->addChild(child);
255 if (child->isAfterContent()) {
256 if (child->isInline()) {
257 // Add generated inline content normally
258 RenderBlockFlow::addChild(child);
260 // Wrap non-inline content with an anonymous inline-block.
261 RenderBlock* afterBlock = rubyAfterBlock(this);
263 afterBlock = createAnonymousRubyInlineBlock(this);
264 RenderBlockFlow::addChild(afterBlock);
266 afterBlock->addChild(child);
271 // If the child is a ruby run, just add it normally.
272 if (child->isRubyRun()) {
273 RenderBlockFlow::addChild(child, beforeChild);
277 if (beforeChild && !isAfterContent(beforeChild)) {
278 // insert child into run
279 RenderObject* run = beforeChild;
280 while (run && !run->isRubyRun())
283 if (beforeChild == run)
284 beforeChild = toRenderRubyRun(beforeChild)->firstChild();
285 ASSERT(!beforeChild || beforeChild->isDescendantOf(run));
286 run->addChild(child, beforeChild);
289 ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent!
290 // Emergency fallback: fall through and just append.
293 // If the new child would be appended, try to add the child to the previous run
294 // if possible, or create a new run otherwise.
295 // (The RenderRubyRun object will handle the details)
296 RenderRubyRun* lastRun = lastRubyRun(this);
297 if (!lastRun || lastRun->hasRubyText()) {
298 lastRun = RenderRubyRun::staticCreateRubyRun(this);
299 RenderBlockFlow::addChild(lastRun, beforeChild);
301 lastRun->addChild(child);
304 void RenderRubyAsBlock::removeChild(RenderObject* child)
306 // If the child's parent is *this (must be a ruby run or generated content or anonymous block),
307 // just use the normal remove method.
308 if (child->parent() == this) {
309 ASSERT(child->isRubyRun() || child->isBeforeContent() || child->isAfterContent() || isAnonymousRubyInlineBlock(child));
310 RenderBlockFlow::removeChild(child);
313 // If the child's parent is an anoymous block (must be generated :before/:after content)
314 // just use the block's remove method.
315 if (isAnonymousRubyInlineBlock(child->parent())) {
316 ASSERT(child->isBeforeContent() || child->isAfterContent());
317 child->parent()->removeChild(child);
318 removeChild(child->parent());
322 // Otherwise find the containing run and remove it from there.
323 RenderRubyRun* run = findRubyRunParent(child);
325 run->removeChild(child);