[RenderTreeBuilder] Introduce RenderTreebuilder::takeChild
[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) : nullptr;
82 }
83
84 RenderRubyBase* RenderRubyRun::rubyBase() const
85 {
86     RenderObject* child = lastChild();
87     return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : nullptr;
88 }
89
90 RenderBlock* RenderRubyRun::firstLineBlock() const
91 {
92     return nullptr;
93 }
94
95 bool RenderRubyRun::isChildAllowed(const RenderObject& child, const RenderStyle&) const
96 {
97     return child.isInline() || child.isRubyText();
98 }
99
100 RenderPtr<RenderRubyBase> RenderRubyRun::createRubyBase() const
101 {
102     auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK);
103     newStyle.setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER?
104     auto renderer = createRenderer<RenderRubyBase>(document(), WTFMove(newStyle));
105     renderer->initializeStyle();
106     return renderer;
107 }
108
109 RenderPtr<RenderRubyRun> RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby)
110 {
111     ASSERT(isRuby(parentRuby));
112     auto renderer = createRenderer<RenderRubyRun>(parentRuby->document(), RenderStyle::createAnonymousStyleWithDisplay(parentRuby->style(), INLINE_BLOCK));
113     renderer->initializeStyle();
114     return renderer;
115 }
116
117 void RenderRubyRun::layoutExcludedChildren(bool relayoutChildren)
118 {
119     RenderBlockFlow::layoutExcludedChildren(relayoutChildren);
120
121     StackStats::LayoutCheckPoint layoutCheckPoint;
122     // Don't bother positioning the RenderRubyRun yet.
123     RenderRubyText* rt = rubyText();
124     if (!rt)
125         return;
126     rt->setIsExcludedFromNormalLayout(true);
127     if (relayoutChildren)
128         rt->setChildNeedsLayout(MarkOnlyThis);
129     rt->layoutIfNeeded();
130 }
131
132 void RenderRubyRun::layout()
133 {
134     if (RenderRubyBase* base = rubyBase())
135         base->reset();
136     RenderBlockFlow::layout();
137 }
138
139 void RenderRubyRun::layoutBlock(bool relayoutChildren, LayoutUnit pageHeight)
140 {
141     if (!relayoutChildren) {
142         // Since the extra relayout in RenderBlockFlow::updateRubyForJustifiedText() causes the size of the RenderRubyText/RenderRubyBase
143         // dependent on the line's current expansion, whenever we relayout the RenderRubyRun, we need to relayout the RenderRubyBase/RenderRubyText as well.
144         // FIXME: We should take the expansion opportunities into account if possible.
145         relayoutChildren = style().textAlign() == JUSTIFY;
146     }
147
148     RenderBlockFlow::layoutBlock(relayoutChildren, pageHeight);
149
150     RenderRubyText* rt = rubyText();
151     if (!rt)
152         return;
153
154     rt->setLogicalLeft(0);
155
156     // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase.
157     LayoutUnit lastLineRubyTextBottom = rt->logicalHeight();
158     LayoutUnit firstLineRubyTextTop = 0;
159     RootInlineBox* rootBox = rt->lastRootBox();
160     if (rootBox) {
161         // In order to align, we have to ignore negative leading.
162         firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow();
163         lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow();
164     }
165     
166     if (isHorizontalWritingMode() && rt->style().rubyPosition() == RubyPositionInterCharacter) {
167         // Bopomofo. We need to move the RenderRubyText over to the right side and center it
168         // vertically relative to the base.
169         const FontCascade& font = style().fontCascade();
170         float distanceBetweenBase = max(font.letterSpacing(), 2.0f * rt->style().fontCascade().fontMetrics().height());
171         setWidth(width() + distanceBetweenBase - font.letterSpacing());
172         if (RenderRubyBase* rb = rubyBase()) {
173             LayoutUnit firstLineTop = 0;
174             LayoutUnit lastLineBottom = logicalHeight();
175             RootInlineBox* rootBox = rb->firstRootBox();
176             if (rootBox)
177                 firstLineTop = rootBox->logicalTopLayoutOverflow();
178             firstLineTop += rb->logicalTop();
179             if (rootBox)
180                 lastLineBottom = rootBox->logicalBottomLayoutOverflow();
181             lastLineBottom += rb->logicalTop();
182             rt->setX(rb->x() + rb->width() - font.letterSpacing());
183             LayoutUnit extent = lastLineBottom - firstLineTop;
184             rt->setY(firstLineTop + (extent - rt->height()) / 2);
185         }
186     } else if (style().isFlippedLinesWritingMode() == (style().rubyPosition() == RubyPositionAfter)) {
187         LayoutUnit firstLineTop = 0;
188         if (RenderRubyBase* rb = rubyBase()) {
189             RootInlineBox* rootBox = rb->firstRootBox();
190             if (rootBox)
191                 firstLineTop = rootBox->logicalTopLayoutOverflow();
192             firstLineTop += rb->logicalTop();
193         }
194         
195         rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop);
196     } else {
197         LayoutUnit lastLineBottom = logicalHeight();
198         if (RenderRubyBase* rb = rubyBase()) {
199             RootInlineBox* rootBox = rb->lastRootBox();
200             if (rootBox)
201                 lastLineBottom = rootBox->logicalBottomLayoutOverflow();
202             lastLineBottom += rb->logicalTop();
203         }
204
205         rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom);
206     }
207
208     // Update our overflow to account for the new RenderRubyText position.
209     computeOverflow(clientLogicalBottom());
210 }
211
212 static bool shouldOverhang(bool firstLine, const RenderObject* renderer, const RenderRubyBase& rubyBase)
213 {
214     if (!renderer || !renderer->isText())
215         return false;
216     const RenderStyle& rubyBaseStyle = firstLine ? rubyBase.firstLineStyle() : rubyBase.style();
217     const RenderStyle& style = firstLine ? renderer->firstLineStyle() : renderer->style();
218     return style.computedFontPixelSize() <= rubyBaseStyle.computedFontPixelSize();
219 }
220
221 void RenderRubyRun::getOverhang(bool firstLine, RenderObject* startRenderer, RenderObject* endRenderer, float& startOverhang, float& endOverhang) const
222 {
223     ASSERT(!needsLayout());
224
225     startOverhang = 0;
226     endOverhang = 0;
227
228     RenderRubyBase* rubyBase = this->rubyBase();
229     RenderRubyText* rubyText = this->rubyText();
230
231     if (!rubyBase || !rubyText)
232         return;
233
234     if (!rubyBase->firstRootBox())
235         return;
236
237     LayoutUnit logicalWidth = this->logicalWidth();
238     float logicalLeftOverhang = std::numeric_limits<float>::max();
239     float logicalRightOverhang = std::numeric_limits<float>::max();
240     for (RootInlineBox* rootInlineBox = rubyBase->firstRootBox(); rootInlineBox; rootInlineBox = rootInlineBox->nextRootBox()) {
241         logicalLeftOverhang = std::min<float>(logicalLeftOverhang, rootInlineBox->logicalLeft());
242         logicalRightOverhang = std::min<float>(logicalRightOverhang, logicalWidth - rootInlineBox->logicalRight());
243     }
244
245     startOverhang = style().isLeftToRightDirection() ? logicalLeftOverhang : logicalRightOverhang;
246     endOverhang = style().isLeftToRightDirection() ? logicalRightOverhang : logicalLeftOverhang;
247
248     if (!shouldOverhang(firstLine, startRenderer, *rubyBase))
249         startOverhang = 0;
250     if (!shouldOverhang(firstLine, endRenderer, *rubyBase))
251         endOverhang = 0;
252
253     // We overhang a ruby only if the neighboring render object is a text.
254     // We can overhang the ruby by no more than half the width of the neighboring text
255     // and no more than half the font size.
256     const RenderStyle& rubyTextStyle = firstLine ? rubyText->firstLineStyle() : rubyText->style();
257     float halfWidthOfFontSize = rubyTextStyle.computedFontPixelSize() / 2.;
258     if (startOverhang)
259         startOverhang = std::min(startOverhang, std::min(downcast<RenderText>(*startRenderer).minLogicalWidth(), halfWidthOfFontSize));
260     if (endOverhang)
261         endOverhang = std::min(endOverhang, std::min(downcast<RenderText>(*endRenderer).minLogicalWidth(), halfWidthOfFontSize));
262 }
263
264 void RenderRubyRun::updatePriorContextFromCachedBreakIterator(LazyLineBreakIterator& iterator) const
265 {
266     iterator.setPriorContext(m_lastCharacter, m_secondToLastCharacter);
267 }
268
269 bool RenderRubyRun::canBreakBefore(const LazyLineBreakIterator& iterator) const
270 {
271     RenderRubyText* rubyText = this->rubyText();
272     if (!rubyText)
273         return true;
274     return rubyText->canBreakBefore(iterator);
275 }
276
277 } // namespace WebCore