7d5cd35a5165ba9826f09b6cc9e4200a4e8104ae
[WebKit-https.git] / Source / WebCore / rendering / RenderRubyRun.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 "RenderRubyRun.h"
34
35 #include "RenderRuby.h"
36 #include "RenderRubyBase.h"
37 #include "RenderRubyText.h"
38 #include "RenderText.h"
39 #include "RenderView.h"
40 #include "StyleInheritedData.h"
41 #include <wtf/IsoMallocInlines.h>
42 #include <wtf/StackStats.h>
43
44 namespace WebCore {
45
46 using namespace std;
47
48 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderRubyRun);
49
50 RenderRubyRun::RenderRubyRun(Document& document, RenderStyle&& style)
51     : RenderBlockFlow(document, WTFMove(style))
52     , m_lastCharacter(0)
53     , m_secondToLastCharacter(0)
54 {
55     setReplaced(true);
56     setInline(true);
57 }
58
59 RenderRubyRun::~RenderRubyRun() = default;
60
61 bool RenderRubyRun::hasRubyText() const
62 {
63     // The only place where a ruby text can be is in the first position
64     // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
65     return firstChild() && firstChild()->isRubyText();
66 }
67
68 bool RenderRubyRun::hasRubyBase() const
69 {
70     // The only place where a ruby base can be is in the last position
71     // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
72     return lastChild() && lastChild()->isRubyBase();
73 }
74
75 RenderRubyText* RenderRubyRun::rubyText() const
76 {
77     RenderObject* child = firstChild();
78     // If in future it becomes necessary to support floating or positioned ruby text,
79     // layout will have to be changed to handle them properly.
80     ASSERT(!child || !child->isRubyText() || !child->isFloatingOrOutOfFlowPositioned());
81     return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0;
82 }
83
84 RenderRubyBase* RenderRubyRun::rubyBase() const
85 {
86     RenderObject* child = lastChild();
87     return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
88 }
89
90 RenderRubyBase* RenderRubyRun::rubyBaseSafe()
91 {
92     RenderRubyBase* base = rubyBase();
93     if (!base) {
94         auto newBase = createRubyBase();
95         base = newBase.get();
96         RenderBlockFlow::addChild(WTFMove(newBase));
97     }
98     return base;
99 }
100
101 RenderBlock* RenderRubyRun::firstLineBlock() const
102 {
103     return 0;
104 }
105
106 bool RenderRubyRun::isChildAllowed(const RenderObject& child, const RenderStyle&) const
107 {
108     return child.isInline() || child.isRubyText();
109 }
110
111 void RenderRubyRun::addChild(RenderPtr<RenderObject> child, RenderObject* beforeChild)
112 {
113     ASSERT(child);
114
115     if (child->isRubyText()) {
116         if (!beforeChild) {
117             // RenderRuby has already ascertained that we can add the child here.
118             ASSERT(!hasRubyText());
119             // prepend ruby texts as first child
120             RenderBlockFlow::addChild(WTFMove(child), firstChild());
121         }  else if (beforeChild->isRubyText()) {
122             // New text is inserted just before another.
123             // In this case the new text takes the place of the old one, and
124             // the old text goes into a new run that is inserted as next sibling.
125             ASSERT(beforeChild->parent() == this);
126             RenderElement* ruby = parent();
127             ASSERT(isRuby(ruby));
128             auto newRun = staticCreateRubyRun(ruby);
129             ruby->addChild(WTFMove(newRun), nextSibling());
130             // Add the new ruby text and move the old one to the new run
131             // Note: Doing it in this order and not using RenderRubyRun's methods,
132             // in order to avoid automatic removal of the ruby run in case there is no
133             // other child besides the old ruby text.
134             RenderBlockFlow::addChild(WTFMove(child), beforeChild);
135             auto takenBeforeChild = RenderBlockFlow::takeChild(*beforeChild);
136             newRun->addChild(WTFMove(takenBeforeChild));
137         } else if (hasRubyBase()) {
138             // Insertion before a ruby base object.
139             // In this case we need insert a new run before the current one and split the base.
140             RenderElement* ruby = parent();
141             auto newRun = staticCreateRubyRun(ruby);
142             auto& run = *newRun;
143             ruby->addChild(WTFMove(newRun), this);
144             run.addChild(WTFMove(child));
145             rubyBaseSafe()->moveChildren(run.rubyBaseSafe(), beforeChild);
146         }
147     } else {
148         // child is not a text -> insert it into the base
149         // (append it instead if beforeChild is the ruby text)
150         if (beforeChild && beforeChild->isRubyText())
151             beforeChild = 0;
152         rubyBaseSafe()->addChild(WTFMove(child), beforeChild);
153     }
154 }
155
156 RenderPtr<RenderObject> RenderRubyRun::takeChild(RenderObject& child)
157 {
158     // If the child is a ruby text, then merge the ruby base with the base of
159     // the right sibling run, if possible.
160     if (!beingDestroyed() && !renderTreeBeingDestroyed() && child.isRubyText()) {
161         RenderRubyBase* base = rubyBase();
162         RenderObject* rightNeighbour = nextSibling();
163         if (base && is<RenderRubyRun>(rightNeighbour)) {
164             // Ruby run without a base can happen only at the first run.
165             RenderRubyRun& rightRun = downcast<RenderRubyRun>(*rightNeighbour);
166             if (rightRun.hasRubyBase()) {
167                 RenderRubyBase* rightBase = rightRun.rubyBaseSafe();
168                 // Collect all children in a single base, then swap the bases.
169                 rightBase->mergeChildrenWithBase(*base);
170                 moveChildTo(&rightRun, base, RenderBoxModelObject::NormalizeAfterInsertion::No);
171                 rightRun.moveChildTo(this, rightBase, RenderBoxModelObject::NormalizeAfterInsertion::No);
172                 // The now empty ruby base will be removed below.
173                 ASSERT(!rubyBase()->firstChild());
174             }
175         }
176     }
177
178     auto takenChild = RenderBlockFlow::takeChild(child);
179
180     if (!beingDestroyed() && !renderTreeBeingDestroyed()) {
181         // Check if our base (if any) is now empty. If so, destroy it.
182         RenderBlock* base = rubyBase();
183         if (base && !base->firstChild()) {
184             auto takenBase = RenderBlockFlow::takeChild(*base);
185             base->deleteLines();
186         }
187     }
188
189     return takenChild;
190 }
191
192 RenderPtr<RenderRubyBase> RenderRubyRun::createRubyBase() const
193 {
194     auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK);
195     newStyle.setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER?
196     auto renderer = createRenderer<RenderRubyBase>(document(), WTFMove(newStyle));
197     renderer->initializeStyle();
198     return renderer;
199 }
200
201 RenderPtr<RenderRubyRun> RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby)
202 {
203     ASSERT(isRuby(parentRuby));
204     auto renderer = createRenderer<RenderRubyRun>(parentRuby->document(), RenderStyle::createAnonymousStyleWithDisplay(parentRuby->style(), INLINE_BLOCK));
205     renderer->initializeStyle();
206     return renderer;
207 }
208
209 void RenderRubyRun::layoutExcludedChildren(bool relayoutChildren)
210 {
211     RenderBlockFlow::layoutExcludedChildren(relayoutChildren);
212
213     StackStats::LayoutCheckPoint layoutCheckPoint;
214     // Don't bother positioning the RenderRubyRun yet.
215     RenderRubyText* rt = rubyText();
216     if (!rt)
217         return;
218     rt->setIsExcludedFromNormalLayout(true);
219     if (relayoutChildren)
220         rt->setChildNeedsLayout(MarkOnlyThis);
221     rt->layoutIfNeeded();
222 }
223
224 void RenderRubyRun::layout()
225 {
226     if (RenderRubyBase* base = rubyBase())
227         base->reset();
228     RenderBlockFlow::layout();
229 }
230
231 void RenderRubyRun::layoutBlock(bool relayoutChildren, LayoutUnit pageHeight)
232 {
233     if (!relayoutChildren) {
234         // Since the extra relayout in RenderBlockFlow::updateRubyForJustifiedText() causes the size of the RenderRubyText/RenderRubyBase
235         // dependent on the line's current expansion, whenever we relayout the RenderRubyRun, we need to relayout the RenderRubyBase/RenderRubyText as well.
236         // FIXME: We should take the expansion opportunities into account if possible.
237         relayoutChildren = style().textAlign() == JUSTIFY;
238     }
239
240     RenderBlockFlow::layoutBlock(relayoutChildren, pageHeight);
241
242     RenderRubyText* rt = rubyText();
243     if (!rt)
244         return;
245
246     rt->setLogicalLeft(0);
247
248     // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase.
249     LayoutUnit lastLineRubyTextBottom = rt->logicalHeight();
250     LayoutUnit firstLineRubyTextTop = 0;
251     RootInlineBox* rootBox = rt->lastRootBox();
252     if (rootBox) {
253         // In order to align, we have to ignore negative leading.
254         firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow();
255         lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow();
256     }
257     
258     if (isHorizontalWritingMode() && rt->style().rubyPosition() == RubyPositionInterCharacter) {
259         // Bopomofo. We need to move the RenderRubyText over to the right side and center it
260         // vertically relative to the base.
261         const FontCascade& font = style().fontCascade();
262         float distanceBetweenBase = max(font.letterSpacing(), 2.0f * rt->style().fontCascade().fontMetrics().height());
263         setWidth(width() + distanceBetweenBase - font.letterSpacing());
264         if (RenderRubyBase* rb = rubyBase()) {
265             LayoutUnit firstLineTop = 0;
266             LayoutUnit lastLineBottom = logicalHeight();
267             RootInlineBox* rootBox = rb->firstRootBox();
268             if (rootBox)
269                 firstLineTop = rootBox->logicalTopLayoutOverflow();
270             firstLineTop += rb->logicalTop();
271             if (rootBox)
272                 lastLineBottom = rootBox->logicalBottomLayoutOverflow();
273             lastLineBottom += rb->logicalTop();
274             rt->setX(rb->x() + rb->width() - font.letterSpacing());
275             LayoutUnit extent = lastLineBottom - firstLineTop;
276             rt->setY(firstLineTop + (extent - rt->height()) / 2);
277         }
278     } else if (style().isFlippedLinesWritingMode() == (style().rubyPosition() == RubyPositionAfter)) {
279         LayoutUnit firstLineTop = 0;
280         if (RenderRubyBase* rb = rubyBase()) {
281             RootInlineBox* rootBox = rb->firstRootBox();
282             if (rootBox)
283                 firstLineTop = rootBox->logicalTopLayoutOverflow();
284             firstLineTop += rb->logicalTop();
285         }
286         
287         rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop);
288     } else {
289         LayoutUnit lastLineBottom = logicalHeight();
290         if (RenderRubyBase* rb = rubyBase()) {
291             RootInlineBox* rootBox = rb->lastRootBox();
292             if (rootBox)
293                 lastLineBottom = rootBox->logicalBottomLayoutOverflow();
294             lastLineBottom += rb->logicalTop();
295         }
296
297         rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom);
298     }
299
300     // Update our overflow to account for the new RenderRubyText position.
301     computeOverflow(clientLogicalBottom());
302 }
303
304 static bool shouldOverhang(bool firstLine, const RenderObject* renderer, const RenderRubyBase& rubyBase)
305 {
306     if (!renderer || !renderer->isText())
307         return false;
308     const RenderStyle& rubyBaseStyle = firstLine ? rubyBase.firstLineStyle() : rubyBase.style();
309     const RenderStyle& style = firstLine ? renderer->firstLineStyle() : renderer->style();
310     return style.computedFontPixelSize() <= rubyBaseStyle.computedFontPixelSize();
311 }
312
313 void RenderRubyRun::getOverhang(bool firstLine, RenderObject* startRenderer, RenderObject* endRenderer, float& startOverhang, float& endOverhang) const
314 {
315     ASSERT(!needsLayout());
316
317     startOverhang = 0;
318     endOverhang = 0;
319
320     RenderRubyBase* rubyBase = this->rubyBase();
321     RenderRubyText* rubyText = this->rubyText();
322
323     if (!rubyBase || !rubyText)
324         return;
325
326     if (!rubyBase->firstRootBox())
327         return;
328
329     LayoutUnit logicalWidth = this->logicalWidth();
330     float logicalLeftOverhang = std::numeric_limits<float>::max();
331     float logicalRightOverhang = std::numeric_limits<float>::max();
332     for (RootInlineBox* rootInlineBox = rubyBase->firstRootBox(); rootInlineBox; rootInlineBox = rootInlineBox->nextRootBox()) {
333         logicalLeftOverhang = std::min<float>(logicalLeftOverhang, rootInlineBox->logicalLeft());
334         logicalRightOverhang = std::min<float>(logicalRightOverhang, logicalWidth - rootInlineBox->logicalRight());
335     }
336
337     startOverhang = style().isLeftToRightDirection() ? logicalLeftOverhang : logicalRightOverhang;
338     endOverhang = style().isLeftToRightDirection() ? logicalRightOverhang : logicalLeftOverhang;
339
340     if (!shouldOverhang(firstLine, startRenderer, *rubyBase))
341         startOverhang = 0;
342     if (!shouldOverhang(firstLine, endRenderer, *rubyBase))
343         endOverhang = 0;
344
345     // We overhang a ruby only if the neighboring render object is a text.
346     // We can overhang the ruby by no more than half the width of the neighboring text
347     // and no more than half the font size.
348     const RenderStyle& rubyTextStyle = firstLine ? rubyText->firstLineStyle() : rubyText->style();
349     float halfWidthOfFontSize = rubyTextStyle.computedFontPixelSize() / 2.;
350     if (startOverhang)
351         startOverhang = std::min(startOverhang, std::min(downcast<RenderText>(*startRenderer).minLogicalWidth(), halfWidthOfFontSize));
352     if (endOverhang)
353         endOverhang = std::min(endOverhang, std::min(downcast<RenderText>(*endRenderer).minLogicalWidth(), halfWidthOfFontSize));
354 }
355
356 void RenderRubyRun::updatePriorContextFromCachedBreakIterator(LazyLineBreakIterator& iterator) const
357 {
358     iterator.setPriorContext(m_lastCharacter, m_secondToLastCharacter);
359 }
360
361 bool RenderRubyRun::canBreakBefore(const LazyLineBreakIterator& iterator) const
362 {
363     RenderRubyText* rubyText = this->rubyText();
364     if (!rubyText)
365         return true;
366     return rubyText->canBreakBefore(iterator);
367 }
368
369 } // namespace WebCore