📄 renderinline.cpp
字号:
/* * This file is part of the render object implementation for KHTML. * * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */#include "config.h"#include "RenderInline.h"#include "FloatQuad.h"#include "GraphicsContext.h"#include "HitTestResult.h"#include "Page.h"#include "RenderArena.h"#include "RenderBlock.h"#include "RenderView.h"#include "VisiblePosition.h"#if ENABLE(DASHBOARD_SUPPORT)#include "Frame.h"#endifusing namespace std;namespace WebCore {RenderInline::RenderInline(Node* node) : RenderBoxModelObject(node) , m_continuation(0) , m_lineHeight(-1) , m_verticalPosition(PositionUndefined){ setChildrenInline(true);}RenderInline::~RenderInline(){}void RenderInline::destroy(){ // Detach our continuation first. if (m_continuation) m_continuation->destroy(); m_continuation = 0; // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. children()->destroyLeftoverChildren(); if (!documentBeingDestroyed()) { if (firstLineBox()) { // We can't wait for RenderBoxModelObject::destroy to clear the selection, // because by then we will have nuked the line boxes. // FIXME: The SelectionController should be responsible for this when it // is notified of DOM mutations. if (isSelectionBorder()) view()->clearSelection(); // If line boxes are contained inside a root, that means we're an inline. // In that case, we need to remove all the line boxes so that the parent // lines aren't pointing to deleted children. If the first line box does // not have a parent that means they are either already disconnected or // root lines that can just be destroyed without disconnecting. if (firstLineBox()->parent()) { for (InlineRunBox* box = firstLineBox(); box; box = box->nextLineBox()) box->remove(); } } else if (isInline() && parent()) parent()->dirtyLinesFromChangedChild(this); } m_lineBoxes.deleteLineBoxes(renderArena()); RenderBoxModelObject::destroy();}RenderInline* RenderInline::inlineContinuation() const{ if (!m_continuation || m_continuation->isInline()) return toRenderInline(m_continuation); return toRenderBlock(m_continuation)->inlineContinuation();}void RenderInline::updateBoxModelInfoFromStyle(){ RenderBoxModelObject::updateBoxModelInfoFromStyle(); setInline(true); // Needed for run-ins, since run-in is considered a block display type. // FIXME: Support transforms and reflections on inline flows someday. setHasTransform(false); setHasReflection(false); }void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle){ RenderBoxModelObject::styleDidChange(diff, oldStyle); // Ensure that all of the split inlines pick up the new style. We // only do this if we're an inline, since we don't want to propagate // a block's style to the other inlines. // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before // and after the block share the same style, but the block doesn't // need to pass its style on to anyone else. for (RenderInline* currCont = inlineContinuation(); currCont; currCont = currCont->inlineContinuation()) { RenderBoxModelObject* nextCont = currCont->continuation(); currCont->setContinuation(0); currCont->setStyle(style()); currCont->setContinuation(nextCont); } m_lineHeight = -1; // Update pseudos for :before and :after now. if (!isAnonymous() && document()->usesBeforeAfterRules()) { children()->updateBeforeAfterContent(this, BEFORE); children()->updateBeforeAfterContent(this, AFTER); }}static inline bool isAfterContent(RenderObject* child){ if (!child) return false; if (child->style()->styleType() != AFTER) return false; // Text nodes don't have their own styles, so ignore the style on a text node. if (child->isText() && !child->isBR()) return false; return true;}void RenderInline::addChild(RenderObject* newChild, RenderObject* beforeChild){ if (continuation()) return addChildToContinuation(newChild, beforeChild); return addChildIgnoringContinuation(newChild, beforeChild);}static RenderBoxModelObject* nextContinuation(RenderObject* renderer){ if (renderer->isInline() && !renderer->isReplaced()) return toRenderInline(renderer)->continuation(); return toRenderBlock(renderer)->inlineContinuation();}RenderBoxModelObject* RenderInline::continuationBefore(RenderObject* beforeChild){ if (beforeChild && beforeChild->parent() == this) return this; RenderBoxModelObject* curr = nextContinuation(this); RenderBoxModelObject* nextToLast = this; RenderBoxModelObject* last = this; while (curr) { if (beforeChild && beforeChild->parent() == curr) { if (curr->firstChild() == beforeChild) return last; return curr; } nextToLast = last; last = curr; curr = nextContinuation(curr); } if (!beforeChild && !last->firstChild()) return nextToLast; return last;}void RenderInline::addChildIgnoringContinuation(RenderObject* newChild, RenderObject* beforeChild){ // Make sure we don't append things after :after-generated content if we have it. if (!beforeChild && isAfterContent(lastChild())) beforeChild = lastChild(); if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) { // We are placing a block inside an inline. We have to perform a split of this // inline into continuations. This involves creating an anonymous block box to hold // |newChild|. We then make that block box a continuation of this inline. We take all of // the children after |beforeChild| and put them in a clone of this object. RefPtr<RenderStyle> newStyle = RenderStyle::create(); newStyle->inheritFrom(style()); newStyle->setDisplay(BLOCK); RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); newBox->setStyle(newStyle.release()); RenderBoxModelObject* oldContinuation = continuation(); setContinuation(newBox); // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after // content gets properly destroyed. bool isLastChild = (beforeChild == lastChild()); if (document()->usesBeforeAfterRules()) children()->updateBeforeAfterContent(this, AFTER); if (isLastChild && beforeChild != lastChild()) beforeChild = 0; // We destroyed the last child, so now we need to update our insertion // point to be 0. It's just a straight append now. splitFlow(beforeChild, newBox, newChild, oldContinuation); return; } RenderBoxModelObject::addChild(newChild, beforeChild); newChild->setNeedsLayoutAndPrefWidthsRecalc();}RenderInline* RenderInline::cloneInline(RenderInline* src){ RenderInline* o = new (src->renderArena()) RenderInline(src->node()); o->setStyle(src->style()); return o;}void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock, RenderObject* beforeChild, RenderBoxModelObject* oldCont){ // Create a clone of this inline. RenderInline* clone = cloneInline(this); clone->setContinuation(oldCont); // Now take all of the children from beforeChild to the end and remove // them from |this| and place them in the clone. RenderObject* o = beforeChild; while (o) { RenderObject* tmp = o; o = tmp->nextSibling(); clone->addChildIgnoringContinuation(children()->removeChildNode(this, tmp), 0); tmp->setNeedsLayoutAndPrefWidthsRecalc(); } // Hook |clone| up as the continuation of the middle block. middleBlock->setInlineContinuation(clone); // We have been reparented and are now under the fromBlock. We need // to walk up our inline parent chain until we hit the containing block. // Once we hit the containing block we're done. RenderBoxModelObject* curr = static_cast<RenderBoxModelObject*>(parent()); RenderBoxModelObject* currChild = this; // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone. // There will eventually be a better approach to this problem that will let us nest to a much // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in // incorrect rendering, but the alternative is to hang forever. unsigned splitDepth = 1; const unsigned cMaxSplitDepth = 200; while (curr && curr != fromBlock) { ASSERT(curr->isRenderInline()); if (splitDepth < cMaxSplitDepth) { // Create a new clone. RenderInline* cloneChild = clone; clone = cloneInline(toRenderInline(curr)); // Insert our child clone as the first child. clone->addChildIgnoringContinuation(cloneChild, 0); // Hook the clone up as a continuation of |curr|. RenderInline* inlineCurr = toRenderInline(curr); oldCont = inlineCurr->continuation(); inlineCurr->setContinuation(clone); clone->setContinuation(oldCont); // Someone may have indirectly caused a <q> to split. When this happens, the :after content // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after // content gets properly destroyed. if (document()->usesBeforeAfterRules()) inlineCurr->children()->updateBeforeAfterContent(this, AFTER); // Now we need to take all of the children starting from the first child // *after* currChild and append them all to the clone. o = currChild->nextSibling(); while (o) { RenderObject* tmp = o; o = tmp->nextSibling(); clone->addChildIgnoringContinuation(inlineCurr->children()->removeChildNode(curr, tmp), 0); tmp->setNeedsLayoutAndPrefWidthsRecalc(); } } // Keep walking up the chain. currChild = curr; curr = static_cast<RenderBoxModelObject*>(curr->parent()); splitDepth++; } // Now we are at the block level. We need to put the clone into the toBlock. toBlock->children()->appendChildNode(toBlock, clone); // Now take all the children after currChild and remove them from the fromBlock // and put them in the toBlock. o = currChild->nextSibling(); while (o) { RenderObject* tmp = o; o = tmp->nextSibling(); toBlock->children()->appendChildNode(toBlock, fromBlock->children()->removeChildNode(fromBlock, tmp)); }}void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, RenderObject* newChild, RenderBoxModelObject* oldCont){ RenderBlock* pre = 0; RenderBlock* block = containingBlock(); // Delete our line boxes before we do the inline split into continuations. block->deleteLineBoxTree(); bool madeNewBeforeBlock = false; if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) { // We can reuse this block and make it the preBlock of the next continuation. pre = block; block = block->containingBlock(); } else { // No anonymous block available for use. Make one. pre = block->createAnonymousBlock(); madeNewBeforeBlock = true; } RenderBlock* post = block->createAnonymousBlock();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -