• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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