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