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