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