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 | |
38 | namespace WebCore { |
39 | |
40 | static inline RenderRubyRun& findRubyRunParent(RenderObject& child) |
41 | { |
42 | return *lineageOfType<RenderRubyRun>(child).first(); |
43 | } |
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() == DisplayType::InlineBlock)); |
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() == PseudoId::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() == PseudoId::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.isRenderMultiColumnFlow() |
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 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 | |
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 | RenderTreeBuilder::Ruby::Ruby(RenderTreeBuilder& builder) |
119 | : m_builder(builder) |
120 | { |
121 | } |
122 | |
123 | void 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 | |
151 | void 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 | |
176 | void RenderTreeBuilder::Ruby::moveChildren(RenderRubyBase& from, RenderRubyBase& to) |
177 | { |
178 | moveChildrenInternal(from, to); |
179 | from.addFloatsToNewParent(to); |
180 | } |
181 | |
182 | void 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 | |
198 | void 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 | |
246 | RenderElement& 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 | |
308 | RenderElement& 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 | |
370 | RenderRubyBase& 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 | |
381 | RenderPtr<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 | |
405 | RenderPtr<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 | |
429 | RenderPtr<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 | |