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