2 * Copyright (C) 2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
33 #include "RenderRubyRun.h"
35 #include "RenderRubyBase.h"
36 #include "RenderRubyText.h"
37 #include "RenderView.h"
43 RenderRubyRun::RenderRubyRun(Node* node)
50 RenderRubyRun::~RenderRubyRun()
54 bool RenderRubyRun::hasRubyText() const
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();
61 bool RenderRubyRun::hasRubyBase() const
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();
68 bool RenderRubyRun::isEmpty() const
70 return !hasRubyText() && !hasRubyBase();
73 RenderRubyText* RenderRubyRun::rubyText() const
75 RenderObject* child = firstChild();
76 return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0;
79 RenderRubyBase* RenderRubyRun::rubyBase() const
81 RenderObject* child = lastChild();
82 return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0;
85 RenderRubyBase* RenderRubyRun::rubyBaseSafe()
87 RenderRubyBase* base = rubyBase();
89 base = createRubyBase();
90 RenderBlock::addChild(base);
95 RenderBlock* RenderRubyRun::firstLineBlock() const
100 void RenderRubyRun::updateFirstLetter()
104 bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const
106 return child->isRubyText() || child->isInline();
109 void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild)
113 // If child is a ruby text
114 if (child->isRubyText()) {
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);
138 // Insertion before a ruby base object.
139 // In this case we need insert a new run before the current one and split the base.
140 RenderObject* ruby = parent();
141 RenderRubyRun* newRun = staticCreateRubyRun(ruby);
142 ruby->addChild(newRun, this);
143 newRun->addChild(child);
144 rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild);
148 // child is not a text -> insert it into the base
149 // (append it instead if beforeChild is the ruby text)
150 if (beforeChild && beforeChild->isRubyText())
152 rubyBaseSafe()->addChild(child, beforeChild);
156 void RenderRubyRun::removeChild(RenderObject* child)
158 // If the child is a ruby text, then merge the ruby base with the base of
159 // the right sibling run, if possible.
160 if (!beingDestroyed() && !documentBeingDestroyed() && child->isRubyText()) {
161 RenderRubyBase* base = rubyBase();
162 RenderObject* rightNeighbour = nextSibling();
163 if (base && rightNeighbour && rightNeighbour->isRubyRun()) {
164 // Ruby run without a base can happen only at the first run.
165 RenderRubyRun* rightRun = static_cast<RenderRubyRun*>(rightNeighbour);
166 if (rightRun->hasRubyBase()) {
167 RenderRubyBase* rightBase = rightRun->rubyBaseSafe();
168 // Collect all children in a single base, then swap the bases.
169 rightBase->moveChildren(base);
170 moveChildTo(rightRun, base);
171 rightRun->moveChildTo(this, rightBase);
172 // The now empty ruby base will be removed below.
177 RenderBlock::removeChild(child);
179 if (!beingDestroyed() && !documentBeingDestroyed()) {
180 // Check if our base (if any) is now empty. If so, destroy it.
181 RenderBlock* base = rubyBase();
182 if (base && !base->firstChild()) {
183 RenderBlock::removeChild(base);
184 base->deleteLineBoxTree();
188 // If any of the above leaves the run empty, destroy it as well.
190 parent()->removeChild(this);
197 RenderRubyBase* RenderRubyRun::createRubyBase() const
199 RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */);
200 RefPtr<RenderStyle> newStyle = RenderStyle::create();
201 newStyle->inheritFrom(style());
202 newStyle->setDisplay(BLOCK);
203 newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER?
204 rb->setStyle(newStyle.release());
208 RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby)
210 ASSERT(parentRuby && parentRuby->isRuby());
211 RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */);
212 RefPtr<RenderStyle> newStyle = RenderStyle::create();
213 newStyle->inheritFrom(parentRuby->style());
214 newStyle->setDisplay(INLINE_BLOCK);
215 rr->setStyle(newStyle.release());
219 RenderObject* RenderRubyRun::layoutSpecialExcludedChild(bool relayoutChildren)
221 // Don't bother positioning the RenderRubyRun yet.
222 RenderRubyText* rt = rubyText();
225 if (relayoutChildren)
226 rt->setChildNeedsLayout(true, false);
227 rt->layoutIfNeeded();
231 void RenderRubyRun::layout()
233 RenderBlock::layout();
235 // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase.
236 RenderRubyText* rt = rubyText();
240 int lastLineRubyTextBottom = rt->logicalHeight();
241 int firstLineRubyTextTop = 0;
242 RootInlineBox* rootBox = rt->lastRootBox();
244 // In order to align, we have to ignore negative leading.
245 firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow();
246 lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow();
249 if (!style()->isFlippedLinesWritingMode()) {
250 int firstLineTop = 0;
251 if (RenderRubyBase* rb = rubyBase()) {
252 RootInlineBox* rootBox = rb->firstRootBox();
254 firstLineTop = rootBox->logicalTopLayoutOverflow();
255 firstLineTop += rb->logicalTop();
258 rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop);
260 int lastLineBottom = logicalHeight();
261 if (RenderRubyBase* rb = rubyBase()) {
262 RootInlineBox* rootBox = rb->lastRootBox();
264 lastLineBottom = rootBox->logicalBottomLayoutOverflow();
265 lastLineBottom += rb->logicalTop();
268 rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom);
271 // Update our overflow to account for the new RenderRubyText position.
273 computeOverflow(clientLogicalBottom());
276 } // namespace WebCore