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 "core/rendering/RenderRubyRun.h"
34
35 #include "core/rendering/LayoutRectRecorder.h"
36 #include "core/rendering/RenderRubyBase.h"
37 #include "core/rendering/RenderRubyText.h"
38 #include "core/rendering/RenderText.h"
39
40 using namespace std;
41
42 namespace WebCore {
43
RenderRubyRun()44 RenderRubyRun::RenderRubyRun()
45 : RenderBlockFlow(0)
46 {
47 setReplaced(true);
48 setInline(true);
49 }
50
~RenderRubyRun()51 RenderRubyRun::~RenderRubyRun()
52 {
53 }
54
hasRubyText() const55 bool RenderRubyRun::hasRubyText() const
56 {
57 // The only place where a ruby text can be is in the first position
58 // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
59 return firstChild() && firstChild()->isRubyText();
60 }
61
hasRubyBase() const62 bool RenderRubyRun::hasRubyBase() const
63 {
64 // The only place where a ruby base can be is in the last position
65 // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves.
66 return lastChild() && lastChild()->isRubyBase();
67 }
68
isEmpty() const69 bool RenderRubyRun::isEmpty() const
70 {
71 return !hasRubyText() && !hasRubyBase();
72 }
73
rubyText() const74 RenderRubyText* RenderRubyRun::rubyText() const
75 {
76 RenderObject* child = firstChild();
77 // If in future it becomes necessary to support floating or positioned ruby text,
78 // layout will have to be changed to handle them properly.
79 ASSERT(!child || !child->isRubyText() || !child->isFloatingOrOutOfFlowPositioned());
80 return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0;
81 }
82
rubyBase() const83 RenderRubyBase* RenderRubyRun::rubyBase() const
84 {
85 RenderObject* child = lastChild();
86 return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
87 }
88
rubyBaseSafe()89 RenderRubyBase* RenderRubyRun::rubyBaseSafe()
90 {
91 RenderRubyBase* base = rubyBase();
92 if (!base) {
93 base = createRubyBase();
94 RenderBlock::addChild(base);
95 }
96 return base;
97 }
98
firstLineBlock() const99 RenderBlock* RenderRubyRun::firstLineBlock() const
100 {
101 return 0;
102 }
103
updateFirstLetter()104 void RenderRubyRun::updateFirstLetter()
105 {
106 }
107
isChildAllowed(RenderObject * child,RenderStyle *) const108 bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const
109 {
110 return child->isRubyText() || child->isInline();
111 }
112
addChild(RenderObject * child,RenderObject * beforeChild)113 void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild)
114 {
115 ASSERT(child);
116
117 if (child->isRubyText()) {
118 if (!beforeChild) {
119 // RenderRuby has already ascertained that we can add the child here.
120 ASSERT(!hasRubyText());
121 // prepend ruby texts as first child
122 RenderBlock::addChild(child, firstChild());
123 } else if (beforeChild->isRubyText()) {
124 // New text is inserted just before another.
125 // In this case the new text takes the place of the old one, and
126 // the old text goes into a new run that is inserted as next sibling.
127 ASSERT(beforeChild->parent() == this);
128 RenderObject* ruby = parent();
129 ASSERT(ruby->isRuby());
130 RenderBlock* newRun = staticCreateRubyRun(ruby);
131 ruby->addChild(newRun, nextSibling());
132 // Add the new ruby text and move the old one to the new run
133 // Note: Doing it in this order and not using RenderRubyRun's methods,
134 // in order to avoid automatic removal of the ruby run in case there is no
135 // other child besides the old ruby text.
136 RenderBlock::addChild(child, beforeChild);
137 RenderBlock::removeChild(beforeChild);
138 newRun->addChild(beforeChild);
139 } else if (hasRubyBase()) {
140 // Insertion before a ruby base object.
141 // In this case we need insert a new run before the current one and split the base.
142 RenderObject* ruby = parent();
143 RenderRubyRun* newRun = staticCreateRubyRun(ruby);
144 ruby->addChild(newRun, this);
145 newRun->addChild(child);
146 rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild);
147 }
148 } else {
149 // child is not a text -> insert it into the base
150 // (append it instead if beforeChild is the ruby text)
151 if (beforeChild && beforeChild->isRubyText())
152 beforeChild = 0;
153 rubyBaseSafe()->addChild(child, beforeChild);
154 }
155 }
156
removeChild(RenderObject * child)157 void RenderRubyRun::removeChild(RenderObject* child)
158 {
159 // If the child is a ruby text, then merge the ruby base with the base of
160 // the right sibling run, if possible.
161 if (!beingDestroyed() && !documentBeingDestroyed() && child->isRubyText()) {
162 RenderRubyBase* base = rubyBase();
163 RenderObject* rightNeighbour = nextSibling();
164 if (base && rightNeighbour && rightNeighbour->isRubyRun()) {
165 // Ruby run without a base can happen only at the first run.
166 RenderRubyRun* rightRun = toRenderRubyRun(rightNeighbour);
167 if (rightRun->hasRubyBase()) {
168 RenderRubyBase* rightBase = rightRun->rubyBaseSafe();
169 // Collect all children in a single base, then swap the bases.
170 rightBase->moveChildren(base);
171 moveChildTo(rightRun, base);
172 rightRun->moveChildTo(this, rightBase);
173 // The now empty ruby base will be removed below.
174 ASSERT(!rubyBase()->firstChild());
175 }
176 }
177 }
178
179 RenderBlock::removeChild(child);
180
181 if (!beingDestroyed() && !documentBeingDestroyed()) {
182 // Check if our base (if any) is now empty. If so, destroy it.
183 RenderBlock* base = rubyBase();
184 if (base && !base->firstChild()) {
185 RenderBlock::removeChild(base);
186 base->deleteLineBoxTree();
187 base->destroy();
188 }
189
190 // If any of the above leaves the run empty, destroy it as well.
191 if (isEmpty()) {
192 parent()->removeChild(this);
193 deleteLineBoxTree();
194 destroy();
195 }
196 }
197 }
198
createRubyBase() const199 RenderRubyBase* RenderRubyRun::createRubyBase() const
200 {
201 RenderRubyBase* renderer = RenderRubyBase::createAnonymous(&document());
202 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK);
203 newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER?
204 renderer->setStyle(newStyle.release());
205 return renderer;
206 }
207
staticCreateRubyRun(const RenderObject * parentRuby)208 RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby)
209 {
210 ASSERT(parentRuby && parentRuby->isRuby());
211 RenderRubyRun* rr = new RenderRubyRun();
212 rr->setDocumentForAnonymous(&parentRuby->document());
213 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parentRuby->style(), INLINE_BLOCK);
214 rr->setStyle(newStyle.release());
215 return rr;
216 }
217
layoutSpecialExcludedChild(bool relayoutChildren,SubtreeLayoutScope & layoutScope)218 RenderObject* RenderRubyRun::layoutSpecialExcludedChild(bool relayoutChildren, SubtreeLayoutScope& layoutScope)
219 {
220 // Don't bother positioning the RenderRubyRun yet.
221 RenderRubyText* rt = rubyText();
222 if (!rt)
223 return 0;
224 if (relayoutChildren)
225 layoutScope.setChildNeedsLayout(rt);
226 rt->layoutIfNeeded();
227 return rt;
228 }
229
layout()230 void RenderRubyRun::layout()
231 {
232 LayoutRectRecorder recorder(*this);
233 RenderBlock::layout();
234
235 RenderRubyText* rt = rubyText();
236 if (!rt)
237 return;
238
239 rt->setLogicalLeft(0);
240
241 // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase.
242 LayoutUnit lastLineRubyTextBottom = rt->logicalHeight();
243 LayoutUnit firstLineRubyTextTop = 0;
244 RootInlineBox* rootBox = rt->lastRootBox();
245 if (rootBox) {
246 // In order to align, we have to ignore negative leading.
247 firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow();
248 lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow();
249 }
250
251 if (style()->isFlippedLinesWritingMode() == (style()->rubyPosition() == RubyPositionAfter)) {
252 LayoutUnit firstLineTop = 0;
253 if (RenderRubyBase* rb = rubyBase()) {
254 RootInlineBox* rootBox = rb->firstRootBox();
255 if (rootBox)
256 firstLineTop = rootBox->logicalTopLayoutOverflow();
257 firstLineTop += rb->logicalTop();
258 }
259
260 rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop);
261 } else {
262 LayoutUnit lastLineBottom = logicalHeight();
263 if (RenderRubyBase* rb = rubyBase()) {
264 RootInlineBox* rootBox = rb->lastRootBox();
265 if (rootBox)
266 lastLineBottom = rootBox->logicalBottomLayoutOverflow();
267 lastLineBottom += rb->logicalTop();
268 }
269
270 rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom);
271 }
272
273 // Update our overflow to account for the new RenderRubyText position.
274 computeOverflow(clientLogicalBottom());
275 }
276
getOverhang(bool firstLine,RenderObject * startRenderer,RenderObject * endRenderer,int & startOverhang,int & endOverhang) const277 void RenderRubyRun::getOverhang(bool firstLine, RenderObject* startRenderer, RenderObject* endRenderer, int& startOverhang, int& endOverhang) const
278 {
279 ASSERT(!needsLayout());
280
281 startOverhang = 0;
282 endOverhang = 0;
283
284 RenderRubyBase* rubyBase = this->rubyBase();
285 RenderRubyText* rubyText = this->rubyText();
286
287 if (!rubyBase || !rubyText)
288 return;
289
290 if (!rubyBase->firstRootBox())
291 return;
292
293 int logicalWidth = this->logicalWidth();
294 int logicalLeftOverhang = numeric_limits<int>::max();
295 int logicalRightOverhang = numeric_limits<int>::max();
296 for (RootInlineBox* rootInlineBox = rubyBase->firstRootBox(); rootInlineBox; rootInlineBox = rootInlineBox->nextRootBox()) {
297 logicalLeftOverhang = min<int>(logicalLeftOverhang, rootInlineBox->logicalLeft());
298 logicalRightOverhang = min<int>(logicalRightOverhang, logicalWidth - rootInlineBox->logicalRight());
299 }
300
301 startOverhang = style()->isLeftToRightDirection() ? logicalLeftOverhang : logicalRightOverhang;
302 endOverhang = style()->isLeftToRightDirection() ? logicalRightOverhang : logicalLeftOverhang;
303
304 if (!startRenderer || !startRenderer->isText() || startRenderer->style(firstLine)->fontSize() > rubyBase->style(firstLine)->fontSize())
305 startOverhang = 0;
306
307 if (!endRenderer || !endRenderer->isText() || endRenderer->style(firstLine)->fontSize() > rubyBase->style(firstLine)->fontSize())
308 endOverhang = 0;
309
310 // We overhang a ruby only if the neighboring render object is a text.
311 // We can overhang the ruby by no more than half the width of the neighboring text
312 // and no more than half the font size.
313 int halfWidthOfFontSize = rubyText->style(firstLine)->fontSize() / 2;
314 if (startOverhang)
315 startOverhang = min<int>(startOverhang, min<int>(toRenderText(startRenderer)->minLogicalWidth(), halfWidthOfFontSize));
316 if (endOverhang)
317 endOverhang = min<int>(endOverhang, min<int>(toRenderText(endRenderer)->minLogicalWidth(), halfWidthOfFontSize));
318 }
319
320 } // namespace WebCore
321