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