diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderRubyRun.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderRubyRun.cpp | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/RenderRubyRun.cpp b/Source/WebCore/rendering/RenderRubyRun.cpp new file mode 100644 index 0000000..c12e543 --- /dev/null +++ b/Source/WebCore/rendering/RenderRubyRun.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "RenderRubyRun.h" + +#include "RenderRubyBase.h" +#include "RenderRubyText.h" +#include "RenderView.h" + +using namespace std; + +namespace WebCore { + +RenderRubyRun::RenderRubyRun(Node* node) + : RenderBlock(node) + , m_beingDestroyed(false) +{ + setReplaced(true); + setInline(true); +} + +RenderRubyRun::~RenderRubyRun() +{ +} + +void RenderRubyRun::destroy() +{ + // Mark if the run is being destroyed to avoid trouble in removeChild(). + m_beingDestroyed = true; + RenderBlock::destroy(); +} + +bool RenderRubyRun::hasRubyText() const +{ + // The only place where a ruby text can be is in the first position + // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves. + return firstChild() && firstChild()->isRubyText(); +} + +bool RenderRubyRun::hasRubyBase() const +{ + // The only place where a ruby base can be is in the last position + // Note: As anonymous blocks, ruby runs do not have ':before' or ':after' content themselves. + return lastChild() && lastChild()->isRubyBase(); +} + +bool RenderRubyRun::isEmpty() const +{ + return !hasRubyText() && !hasRubyBase(); +} + +RenderRubyText* RenderRubyRun::rubyText() const +{ + RenderObject* child = firstChild(); + return child && child->isRubyText() ? static_cast<RenderRubyText*>(child) : 0; +} + +RenderRubyBase* RenderRubyRun::rubyBase() const +{ + RenderObject* child = lastChild(); + return child && child->isRubyBase() ? static_cast<RenderRubyBase*>(child) : 0; +} + +RenderRubyBase* RenderRubyRun::rubyBaseSafe() +{ + RenderRubyBase* base = rubyBase(); + if (!base) { + base = createRubyBase(); + RenderBlock::addChild(base); + } + return base; +} + +RenderBlock* RenderRubyRun::firstLineBlock() const +{ + return 0; +} + +void RenderRubyRun::updateFirstLetter() +{ +} + +bool RenderRubyRun::isChildAllowed(RenderObject* child, RenderStyle*) const +{ + return child->isRubyText() || child->isInline(); +} + +void RenderRubyRun::addChild(RenderObject* child, RenderObject* beforeChild) +{ + ASSERT(child); + + // If child is a ruby text + if (child->isRubyText()) { + if (!beforeChild) { + // RenderRuby has already ascertained that we can add the child here. + ASSERT(!hasRubyText()); + // prepend ruby texts as first child + RenderBlock::addChild(child, firstChild()); + } else if (beforeChild->isRubyText()) { + // New text is inserted just before another. + // In this case the new text takes the place of the old one, and + // the old text goes into a new run that is inserted as next sibling. + ASSERT(beforeChild->parent() == this); + RenderObject* ruby = parent(); + ASSERT(ruby->isRuby()); + RenderBlock* newRun = staticCreateRubyRun(ruby); + ruby->addChild(newRun, nextSibling()); + // Add the new ruby text and move the old one to the new run + // Note: Doing it in this order and not using RenderRubyRun's methods, + // in order to avoid automatic removal of the ruby run in case there is no + // other child besides the old ruby text. + RenderBlock::addChild(child, beforeChild); + RenderBlock::removeChild(beforeChild); + newRun->addChild(beforeChild); + } else { + if (hasRubyBase()) { + // Insertion before a ruby base object. + // In this case we need insert a new run before the current one and split the base. + RenderObject* ruby = parent(); + RenderRubyRun* newRun = staticCreateRubyRun(ruby); + ruby->addChild(newRun, this); + newRun->addChild(child); + rubyBaseSafe()->moveChildren(newRun->rubyBaseSafe(), beforeChild); + } + } + } else { + // child is not a text -> insert it into the base + // (append it instead if beforeChild is the ruby text) + if (beforeChild && beforeChild->isRubyText()) + beforeChild = 0; + rubyBaseSafe()->addChild(child, beforeChild); + } +} + +void RenderRubyRun::removeChild(RenderObject* child) +{ + // If the child is a ruby text, then merge the ruby base with the base of + // the right sibling run, if possible. + if (!m_beingDestroyed && !documentBeingDestroyed() && child->isRubyText()) { + RenderRubyBase* base = rubyBase(); + RenderObject* rightNeighbour = nextSibling(); + if (base && rightNeighbour && rightNeighbour->isRubyRun()) { + // Ruby run without a base can happen only at the first run. + RenderRubyRun* rightRun = static_cast<RenderRubyRun*>(rightNeighbour); + if (rightRun->hasRubyBase()) { + RenderRubyBase* rightBase = rightRun->rubyBaseSafe(); + // Collect all children in a single base, then swap the bases. + rightBase->moveChildren(base); + moveChildTo(rightRun, base); + rightRun->moveChildTo(this, rightBase); + // The now empty ruby base will be removed below. + } + } + } + + RenderBlock::removeChild(child); + + if (!m_beingDestroyed && !documentBeingDestroyed()) { + // Check if our base (if any) is now empty. If so, destroy it. + RenderBlock* base = rubyBase(); + if (base && !base->firstChild()) { + RenderBlock::removeChild(base); + base->deleteLineBoxTree(); + base->destroy(); + } + + // If any of the above leaves the run empty, destroy it as well. + if (isEmpty()) { + parent()->removeChild(this); + deleteLineBoxTree(); + destroy(); + } + } +} + +RenderRubyBase* RenderRubyRun::createRubyBase() const +{ + RenderRubyBase* rb = new (renderArena()) RenderRubyBase(document() /* anonymous */); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(BLOCK); + newStyle->setTextAlign(CENTER); // FIXME: use WEBKIT_CENTER? + rb->setStyle(newStyle.release()); + return rb; +} + +RenderRubyRun* RenderRubyRun::staticCreateRubyRun(const RenderObject* parentRuby) +{ + ASSERT(parentRuby && parentRuby->isRuby()); + RenderRubyRun* rr = new (parentRuby->renderArena()) RenderRubyRun(parentRuby->document() /* anonymous */); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(parentRuby->style()); + newStyle->setDisplay(INLINE_BLOCK); + rr->setStyle(newStyle.release()); + return rr; +} + +RenderObject* RenderRubyRun::layoutSpecialExcludedChild(bool relayoutChildren) +{ + // Don't bother positioning the RenderRubyRun yet. + RenderRubyText* rt = rubyText(); + if (!rt) + return 0; + if (relayoutChildren) + rt->setChildNeedsLayout(true, false); + rt->layoutIfNeeded(); + return rt; +} + +void RenderRubyRun::layout() +{ + RenderBlock::layout(); + + // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase. + RenderRubyText* rt = rubyText(); + if (!rt) + return; + + int lastLineRubyTextBottom = rt->logicalHeight(); + int firstLineRubyTextTop = 0; + RootInlineBox* rootBox = rt->lastRootBox(); + if (rootBox) { + // In order to align, we have to ignore negative leading. + firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow(); + lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow(); + } + + if (!style()->isFlippedLinesWritingMode()) { + int firstLineTop = 0; + if (RenderRubyBase* rb = rubyBase()) { + RootInlineBox* rootBox = rb->firstRootBox(); + if (rootBox) + firstLineTop = rootBox->logicalTopLayoutOverflow(); + firstLineTop += rb->logicalTop(); + } + + rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop); + } else { + int lastLineBottom = logicalHeight(); + if (RenderRubyBase* rb = rubyBase()) { + RootInlineBox* rootBox = rb->lastRootBox(); + if (rootBox) + lastLineBottom = rootBox->logicalBottomLayoutOverflow(); + lastLineBottom += rb->logicalTop(); + } + + rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom); + } + + // Update our overflow to account for the new RenderRubyText position. + m_overflow.clear(); + computeOverflow(clientLogicalBottom()); +} + +} // namespace WebCore |