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