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