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