1/*
2 * Copyright (C) 2017 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "RenderTreeBuilderRuby.h"
28
29#include "RenderAncestorIterator.h"
30#include "RenderRuby.h"
31#include "RenderRubyBase.h"
32#include "RenderRubyRun.h"
33#include "RenderTreeBuilder.h"
34#include "RenderTreeBuilderBlock.h"
35#include "RenderTreeBuilderBlockFlow.h"
36#include "RenderTreeBuilderInline.h"
37
38namespace WebCore {
39
40static inline RenderRubyRun& findRubyRunParent(RenderObject& child)
41{
42 return *lineageOfType<RenderRubyRun>(child).first();
43}
44
45static 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() == DisplayType::InlineBlock));
52
53 return object
54 && isRuby(object->parent())
55 && is<RenderBlock>(*object)
56 && !is<RenderRubyRun>(*object);
57}
58
59static 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() == PseudoId::Before;
65}
66
67static 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() == PseudoId::After;
73}
74
75#ifndef ASSERT_DISABLED
76static inline bool isRubyChildForNormalRemoval(const RenderObject& object)
77{
78 return object.isRubyRun()
79 || object.isBeforeContent()
80 || object.isAfterContent()
81 || object.isRenderMultiColumnFlow()
82 || object.isRenderMultiColumnSet()
83 || isAnonymousRubyInlineBlock(&object);
84}
85#endif
86
87static inline RenderBlock* rubyBeforeBlock(const RenderElement* ruby)
88{
89 RenderObject* child = ruby->firstChild();
90 return isRubyBeforeBlock(child) ? downcast<RenderBlock>(child) : nullptr;
91}
92
93static inline RenderBlock* rubyAfterBlock(const RenderElement* ruby)
94{
95 RenderObject* child = ruby->lastChild();
96 return isRubyAfterBlock(child) ? downcast<RenderBlock>(child) : nullptr;
97}
98
99static auto createAnonymousRubyInlineBlock(RenderObject& ruby)
100{
101 auto newBlock = createRenderer<RenderBlockFlow>(ruby.document(), RenderStyle::createAnonymousStyleWithDisplay(ruby.style(), DisplayType::InlineBlock));
102 newBlock->initializeStyle();
103 return newBlock;
104}
105
106static 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
118RenderTreeBuilder::Ruby::Ruby(RenderTreeBuilder& builder)
119 : m_builder(builder)
120{
121}
122
123void RenderTreeBuilder::Ruby::moveInlineChildren(RenderRubyBase& from, RenderRubyBase& to, RenderObject* beforeChild)
124{
125 ASSERT(from.childrenInline());
126
127 if (!from.firstChild())
128 return;
129
130 RenderBlock* toBlock = nullptr;
131 if (to.childrenInline()) {
132 // The standard and easy case: move the children into the target base
133 toBlock = &to;
134 } else {
135 // We need to wrap the inline objects into an anonymous block.
136 // If toBase has a suitable block, we re-use it, otherwise create a new one.
137 auto* lastChild = to.lastChild();
138 if (lastChild && lastChild->isAnonymousBlock() && lastChild->childrenInline())
139 toBlock = downcast<RenderBlock>(lastChild);
140 else {
141 auto newToBlock = to.createAnonymousBlock();
142 toBlock = newToBlock.get();
143 m_builder.attachToRenderElementInternal(to, WTFMove(newToBlock));
144 }
145 }
146 ASSERT(toBlock);
147 // Move our inline children into the target block we determined above.
148 m_builder.moveChildren(from, *toBlock, from.firstChild(), beforeChild, RenderTreeBuilder::NormalizeAfterInsertion::No);
149}
150
151void RenderTreeBuilder::Ruby::moveBlockChildren(RenderRubyBase& from, RenderRubyBase& to, RenderObject* beforeChild)
152{
153 ASSERT(!from.childrenInline());
154
155 if (!from.firstChild())
156 return;
157
158 if (to.childrenInline())
159 m_builder.makeChildrenNonInline(to);
160
161 // If an anonymous block would be put next to another such block, then merge those.
162 auto* firstChildHere = from.firstChild();
163 auto* lastChildThere = to.lastChild();
164 if (firstChildHere->isAnonymousBlock() && firstChildHere->childrenInline()
165 && lastChildThere && lastChildThere->isAnonymousBlock() && lastChildThere->childrenInline()) {
166 auto* anonBlockHere = downcast<RenderBlock>(firstChildHere);
167 auto* anonBlockThere = downcast<RenderBlock>(lastChildThere);
168 m_builder.moveAllChildren(*anonBlockHere, *anonBlockThere, RenderTreeBuilder::NormalizeAfterInsertion::Yes);
169 anonBlockHere->deleteLines();
170 m_builder.destroy(*anonBlockHere);
171 }
172 // Move all remaining children normally.
173 m_builder.moveChildren(from, to, from.firstChild(), beforeChild, RenderTreeBuilder::NormalizeAfterInsertion::No);
174}
175
176void RenderTreeBuilder::Ruby::moveChildren(RenderRubyBase& from, RenderRubyBase& to)
177{
178 moveChildrenInternal(from, to);
179 from.addFloatsToNewParent(to);
180}
181
182void RenderTreeBuilder::Ruby::moveChildrenInternal(RenderRubyBase& from, RenderRubyBase& to, RenderObject* beforeChild)
183{
184 // This function removes all children that are before (!) beforeChild
185 // and appends them to toBase.
186 if (beforeChild && beforeChild->parent() != &from)
187 beforeChild = m_builder.splitAnonymousBoxesAroundChild(from, *beforeChild);
188
189 if (from.childrenInline())
190 moveInlineChildren(from, to, beforeChild);
191 else
192 moveBlockChildren(from, to, beforeChild);
193
194 from.setNeedsLayoutAndPrefWidthsRecalc();
195 to.setNeedsLayoutAndPrefWidthsRecalc();
196}
197
198void RenderTreeBuilder::Ruby::attach(RenderRubyRun& parent, RenderPtr<RenderObject> child, RenderObject* beforeChild)
199{
200 if (child->isRubyText()) {
201 if (!beforeChild) {
202 // RenderRuby has already ascertained that we can add the child here.
203 ASSERT(!parent.hasRubyText());
204 // prepend ruby texts as first child
205 m_builder.blockFlowBuilder().attach(parent, WTFMove(child), parent.firstChild());
206 return;
207 }
208 if (beforeChild->isRubyText()) {
209 // New text is inserted just before another.
210 // In this case the new text takes the place of the old one, and
211 // the old text goes into a new run that is inserted as next sibling.
212 ASSERT(beforeChild->parent() == &parent);
213 RenderElement* ruby = parent.parent();
214 ASSERT(isRuby(ruby));
215 auto newRun = RenderRubyRun::staticCreateRubyRun(ruby);
216 m_builder.attach(*ruby, WTFMove(newRun), parent.nextSibling());
217 // Add the new ruby text and move the old one to the new run
218 // Note: Doing it in this order and not using RenderRubyRun's methods,
219 // in order to avoid automatic removal of the ruby run in case there is no
220 // other child besides the old ruby text.
221 m_builder.blockFlowBuilder().attach(parent, WTFMove(child), beforeChild);
222 auto takenBeforeChild = m_builder.blockBuilder().detach(parent, *beforeChild);
223
224 m_builder.attach(*newRun, WTFMove(takenBeforeChild));
225 return;
226 }
227 if (parent.hasRubyBase()) {
228 // Insertion before a ruby base object.
229 // In this case we need insert a new run before the current one and split the base.
230 RenderElement* ruby = parent.parent();
231 auto newRun = RenderRubyRun::staticCreateRubyRun(ruby);
232 auto& run = *newRun;
233 m_builder.attach(*ruby, WTFMove(newRun), &parent);
234 m_builder.attach(run, WTFMove(child));
235 moveChildrenInternal(rubyBaseSafe(parent), rubyBaseSafe(run), beforeChild);
236 }
237 return;
238 }
239 // child is not a text -> insert it into the base
240 // (append it instead if beforeChild is the ruby text)
241 if (beforeChild && beforeChild->isRubyText())
242 beforeChild = nullptr;
243 m_builder.attach(rubyBaseSafe(parent), WTFMove(child), beforeChild);
244}
245
246RenderElement& RenderTreeBuilder::Ruby::findOrCreateParentForChild(RenderRubyAsBlock& parent, const RenderObject& child, RenderObject*& beforeChild)
247{
248 // Insert :before and :after content before/after the RenderRubyRun(s)
249 if (child.isBeforeContent()) {
250 // Add generated inline content normally
251 if (child.isInline())
252 return parent;
253 // Wrap non-inline content in an anonymous inline-block.
254 auto* beforeBlock = rubyBeforeBlock(&parent);
255 if (!beforeBlock) {
256 auto newBlock = createAnonymousRubyInlineBlock(parent);
257 beforeBlock = newBlock.get();
258 m_builder.blockFlowBuilder().attach(parent, WTFMove(newBlock), parent.firstChild());
259 }
260 beforeChild = nullptr;
261 return *beforeBlock;
262 }
263
264 if (child.isAfterContent()) {
265 // Add generated inline content normally
266 if (child.isInline())
267 return parent;
268 // Wrap non-inline content with an anonymous inline-block.
269 auto* afterBlock = rubyAfterBlock(&parent);
270 if (!afterBlock) {
271 auto newBlock = createAnonymousRubyInlineBlock(parent);
272 afterBlock = newBlock.get();
273 m_builder.blockFlowBuilder().attach(parent, WTFMove(newBlock), nullptr);
274 }
275 beforeChild = nullptr;
276 return *afterBlock;
277 }
278
279 // If the child is a ruby run, just add it normally.
280 if (child.isRubyRun())
281 return parent;
282
283 if (beforeChild && !parent.isAfterContent(beforeChild)) {
284 // insert child into run
285 ASSERT(!beforeChild->isRubyRun());
286 auto* run = beforeChild->parent();
287 while (run && !run->isRubyRun())
288 run = run->parent();
289 if (run)
290 return *run;
291 ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent!
292 // Emergency fallback: fall through and just append.
293 }
294
295 // If the new child would be appended, try to add the child to the previous run
296 // if possible, or create a new run otherwise.
297 // (The RenderRubyRun object will handle the details)
298 auto* lastRun = lastRubyRun(&parent);
299 if (!lastRun || lastRun->hasRubyText()) {
300 auto newRun = RenderRubyRun::staticCreateRubyRun(&parent);
301 lastRun = newRun.get();
302 m_builder.blockFlowBuilder().attach(parent, WTFMove(newRun), beforeChild);
303 }
304 beforeChild = nullptr;
305 return *lastRun;
306}
307
308RenderElement& RenderTreeBuilder::Ruby::findOrCreateParentForChild(RenderRubyAsInline& parent, const RenderObject& child, RenderObject*& beforeChild)
309{
310 // Insert :before and :after content before/after the RenderRubyRun(s)
311 if (child.isBeforeContent()) {
312 // Add generated inline content normally
313 if (child.isInline())
314 return parent;
315 // Wrap non-inline content with an anonymous inline-block.
316 auto* beforeBlock = rubyBeforeBlock(&parent);
317 if (!beforeBlock) {
318 auto newBlock = createAnonymousRubyInlineBlock(parent);
319 beforeBlock = newBlock.get();
320 m_builder.inlineBuilder().attach(parent, WTFMove(newBlock), parent.firstChild());
321 }
322 beforeChild = nullptr;
323 return *beforeBlock;
324 }
325
326 if (child.isAfterContent()) {
327 // Add generated inline content normally
328 if (child.isInline())
329 return parent;
330 // Wrap non-inline content with an anonymous inline-block.
331 auto* afterBlock = rubyAfterBlock(&parent);
332 if (!afterBlock) {
333 auto newBlock = createAnonymousRubyInlineBlock(parent);
334 afterBlock = newBlock.get();
335 m_builder.inlineBuilder().attach(parent, WTFMove(newBlock), nullptr);
336 }
337 beforeChild = nullptr;
338 return *afterBlock;
339 }
340
341 // If the child is a ruby run, just add it normally.
342 if (child.isRubyRun())
343 return parent;
344
345 if (beforeChild && !parent.isAfterContent(beforeChild)) {
346 // insert child into run
347 ASSERT(!beforeChild->isRubyRun());
348 auto* run = beforeChild->parent();
349 while (run && !run->isRubyRun())
350 run = run->parent();
351 if (run)
352 return *run;
353 ASSERT_NOT_REACHED(); // beforeChild should always have a run as parent!
354 // Emergency fallback: fall through and just append.
355 }
356
357 // If the new child would be appended, try to add the child to the previous run
358 // if possible, or create a new run otherwise.
359 // (The RenderRubyRun object will handle the details)
360 auto* lastRun = lastRubyRun(&parent);
361 if (!lastRun || lastRun->hasRubyText()) {
362 auto newRun = RenderRubyRun::staticCreateRubyRun(&parent);
363 lastRun = newRun.get();
364 m_builder.inlineBuilder().attach(parent, WTFMove(newRun), beforeChild);
365 }
366 beforeChild = nullptr;
367 return *lastRun;
368}
369
370RenderRubyBase& RenderTreeBuilder::Ruby::rubyBaseSafe(RenderRubyRun& rubyRun)
371{
372 auto* base = rubyRun.rubyBase();
373 if (!base) {
374 auto newBase = rubyRun.createRubyBase();
375 base = newBase.get();
376 m_builder.blockFlowBuilder().attach(rubyRun, WTFMove(newBase), nullptr);
377 }
378 return *base;
379}
380
381RenderPtr<RenderObject> RenderTreeBuilder::Ruby::detach(RenderRubyAsInline& parent, RenderObject& child)
382{
383 // If the child's parent is *this (must be a ruby run or generated content or anonymous block),
384 // just use the normal remove method.
385 if (child.parent() == &parent) {
386#ifndef ASSERT_DISABLED
387 ASSERT(isRubyChildForNormalRemoval(child));
388#endif
389 return m_builder.detachFromRenderElement(parent, child);
390 }
391 // If the child's parent is an anoymous block (must be generated :before/:after content)
392 // just use the block's remove method.
393 if (isAnonymousRubyInlineBlock(child.parent())) {
394 ASSERT(child.isBeforeContent() || child.isAfterContent());
395 auto& parent = *child.parent();
396 auto takenChild = m_builder.detach(parent, child);
397 m_builder.destroy(parent);
398 return takenChild;
399 }
400
401 // Otherwise find the containing run and remove it from there.
402 return m_builder.detach(findRubyRunParent(child), child);
403}
404
405RenderPtr<RenderObject> RenderTreeBuilder::Ruby::detach(RenderRubyAsBlock& parent, RenderObject& child)
406{
407 // If the child's parent is *this (must be a ruby run or generated content or anonymous block),
408 // just use the normal remove method.
409 if (child.parent() == &parent) {
410#ifndef ASSERT_DISABLED
411 ASSERT(isRubyChildForNormalRemoval(child));
412#endif
413 return m_builder.blockBuilder().detach(parent, child);
414 }
415 // If the child's parent is an anoymous block (must be generated :before/:after content)
416 // just use the block's remove method.
417 if (isAnonymousRubyInlineBlock(child.parent())) {
418 ASSERT(child.isBeforeContent() || child.isAfterContent());
419 auto& parent = *child.parent();
420 auto takenChild = m_builder.detach(parent, child);
421 m_builder.destroy(parent);
422 return takenChild;
423 }
424
425 // Otherwise find the containing run and remove it from there.
426 return m_builder.detach(findRubyRunParent(child), child);
427}
428
429RenderPtr<RenderObject> RenderTreeBuilder::Ruby::detach(RenderRubyRun& parent, RenderObject& child)
430{
431 // If the child is a ruby text, then merge the ruby base with the base of
432 // the right sibling run, if possible.
433 if (!parent.beingDestroyed() && !parent.renderTreeBeingDestroyed() && child.isRubyText()) {
434 RenderRubyBase* base = parent.rubyBase();
435 RenderObject* rightNeighbour = parent.nextSibling();
436 if (base && is<RenderRubyRun>(rightNeighbour)) {
437 // Ruby run without a base can happen only at the first run.
438 RenderRubyRun& rightRun = downcast<RenderRubyRun>(*rightNeighbour);
439 if (rightRun.hasRubyBase()) {
440 RenderRubyBase* rightBase = rightRun.rubyBase();
441 // Collect all children in a single base, then swap the bases.
442 moveChildren(*rightBase, *base);
443 m_builder.move(parent, rightRun, *base, RenderTreeBuilder::NormalizeAfterInsertion::No);
444 m_builder.move(rightRun, parent, *rightBase, RenderTreeBuilder::NormalizeAfterInsertion::No);
445 // The now empty ruby base will be removed below.
446 ASSERT(!parent.rubyBase()->firstChild());
447 }
448 }
449 }
450
451 auto takenChild = m_builder.blockBuilder().detach(parent, child);
452
453 if (!parent.beingDestroyed() && !parent.renderTreeBeingDestroyed()) {
454 // Check if our base (if any) is now empty. If so, destroy it.
455 RenderBlock* base = parent.rubyBase();
456 if (base && !base->firstChild()) {
457 auto takenBase = m_builder.blockBuilder().detach(parent, *base);
458 base->deleteLines();
459 }
460 }
461 return takenChild;
462}
463
464}
465