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