• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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