📄 render_block.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 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
//#define DEBUG
//#define DEBUG_LAYOUT
//#define BOX_DEBUG
//#define FLOAT_DEBUG
#include <kdebug.h>
#include "rendering/render_text.h"
#include "rendering/render_table.h"
#include "rendering/render_canvas.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_docimpl.h"
#include "xml/dom_position.h"
#include "html/html_formimpl.h"
#include "render_block.h"
#include "editing/selection.h"
#include "khtmlview.h"
#include "khtml_part.h"
#include "htmltags.h"
using namespace DOM;
namespace khtml {
// -------------------------------------------------------------------------------------------------------
// Our MarginInfo state used when laying out block children.
RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int top, int bottom)
{
// Whether or not we can collapse our own margins with our children. We don't do this
// if we had any border/padding (obviously), if we're the root or HTML elements, or if
// we're positioned, floating, a table cell.
m_canCollapseWithChildren = !block->isCanvas() && !block->isRoot() && !block->isPositioned() &&
!block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable();
m_canCollapseTopWithChildren = m_canCollapseWithChildren && (top == 0) && block->style()->marginTopCollapse() != MSEPARATE;
// If any height other than auto is specified in CSS, then we don't collapse our bottom
// margins with our children's margins. To do otherwise would be to risk odd visual
// effects when the children overflow out of the parent block and yet still collapse
// with it. We also don't collapse if we have any bottom border/padding.
m_canCollapseBottomWithChildren = m_canCollapseWithChildren && (bottom == 0) &&
(block->style()->height().isVariable() && block->style()->height().value == 0) && block->style()->marginBottomCollapse() != MSEPARATE;
m_quirkContainer = block->isTableCell() || block->isBody() || block->style()->marginTopCollapse() == MDISCARD ||
block->style()->marginBottomCollapse() == MDISCARD;
m_atTopOfBlock = true;
m_atBottomOfBlock = false;
m_posMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(true) : 0;
m_negMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(false) : 0;
m_selfCollapsingBlockClearedFloat = false;
m_topQuirk = m_bottomQuirk = m_determinedTopQuirk = false;
}
// -------------------------------------------------------------------------------------------------------
RenderBlock::RenderBlock(DOM::NodeImpl* node)
:RenderFlow(node)
{
m_childrenInline = true;
m_floatingObjects = 0;
m_positionedObjects = 0;
m_pre = false;
m_firstLine = false;
m_hasMarkupTruncation = false;
m_selectionState = SelectionNone;
m_clearStatus = CNONE;
m_maxTopPosMargin = m_maxTopNegMargin = m_maxBottomPosMargin = m_maxBottomNegMargin = 0;
m_topMarginQuirk = m_bottomMarginQuirk = false;
m_overflowHeight = m_overflowWidth = 0;
m_overflowLeft = m_overflowTop = 0;
}
RenderBlock::~RenderBlock()
{
delete m_floatingObjects;
delete m_positionedObjects;
}
void RenderBlock::setStyle(RenderStyle* _style)
{
setReplaced(_style->isDisplayReplacedType());
RenderFlow::setStyle(_style);
m_pre = false;
if (_style->whiteSpace() == PRE)
m_pre = true;
// ### we could save this call when the change only affected
// non inherited properties
RenderObject *child = firstChild();
while (child != 0)
{
if (child->isAnonymousBlock())
{
RenderStyle* newStyle = new (renderArena()) RenderStyle();
newStyle->inheritFrom(style());
newStyle->setDisplay(BLOCK);
child->setStyle(newStyle);
}
child = child->nextSibling();
}
m_lineHeight = -1;
// Update pseudos for :before and :after now.
updatePseudoChild(RenderStyle::BEFORE, firstChild());
updatePseudoChild(RenderStyle::AFTER, lastChild());
}
void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild)
{
// Make sure we don't append things after :after-generated content if we have it.
if (!beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER)
beforeChild = lastChild();
bool madeBoxesNonInline = FALSE;
// If the requested beforeChild is not one of our children, then this is most likely because
// there is an anonymous block box within this object that contains the beforeChild. So
// just insert the child into the anonymous block box instead of here.
if (beforeChild && beforeChild->parent() != this) {
KHTMLAssert(beforeChild->parent());
KHTMLAssert(beforeChild->parent()->isAnonymousBlock());
if (newChild->isInline()) {
beforeChild->parent()->addChild(newChild,beforeChild);
return;
}
else if (beforeChild->parent()->firstChild() != beforeChild)
return beforeChild->parent()->addChild(newChild, beforeChild);
else
return addChildToFlow(newChild, beforeChild->parent());
}
// A block has to either have all of its children inline, or all of its children as blocks.
// So, if our children are currently inline and a block child has to be inserted, we move all our
// inline children into anonymous block boxes
if ( m_childrenInline && !newChild->isInline() && !newChild->isFloatingOrPositioned() )
{
// This is a block with inline content. Wrap the inline content in anonymous blocks.
makeChildrenNonInline(beforeChild);
madeBoxesNonInline = true;
if (beforeChild && beforeChild->parent() != this) {
beforeChild = beforeChild->parent();
KHTMLAssert(beforeChild->isAnonymousBlock());
KHTMLAssert(beforeChild->parent() == this);
}
}
else if (!m_childrenInline && !newChild->isFloatingOrPositioned())
{
// If we're inserting an inline child but all of our children are blocks, then we have to make sure
// it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise
// a new one is created and inserted into our list of children in the appropriate position.
if (newChild->isInline()) {
if (beforeChild) {
if (beforeChild->previousSibling() && beforeChild->previousSibling()->isAnonymousBlock()) {
beforeChild->previousSibling()->addChild(newChild);
return;
}
}
else {
if (m_last && m_last->isAnonymousBlock()) {
m_last->addChild(newChild);
return;
}
}
// no suitable existing anonymous box - create a new one
RenderBlock* newBox = createAnonymousBlock();
RenderContainer::addChild(newBox,beforeChild);
newBox->addChild(newChild);
return;
}
}
RenderContainer::addChild(newChild,beforeChild);
// ### care about aligned stuff
if ( madeBoxesNonInline )
removeLeftoverAnonymousBoxes();
}
static void getInlineRun(RenderObject* start, RenderObject* boundary,
RenderObject*& inlineRunStart,
RenderObject*& inlineRunEnd)
{
// Beginning at |start| we find the largest contiguous run of inlines that
// we can. We denote the run with start and end points, |inlineRunStart|
// and |inlineRunEnd|. Note that these two values may be the same if
// we encounter only one inline.
//
// We skip any non-inlines we encounter as long as we haven't found any
// inlines yet.
//
// |boundary| indicates a non-inclusive boundary point. Regardless of whether |boundary|
// is inline or not, we will not include it in a run with inlines before it. It's as though we encountered
// a non-inline.
// Start by skipping as many non-inlines as we can.
RenderObject * curr = start;
bool sawInline;
do {
while (curr && !(curr->isInline() || curr->isFloatingOrPositioned()))
curr = curr->nextSibling();
inlineRunStart = inlineRunEnd = curr;
if (!curr)
return; // No more inline children to be found.
sawInline = curr->isInline();
curr = curr->nextSibling();
while (curr && (curr->isInline() || curr->isFloatingOrPositioned()) && (curr != boundary)) {
inlineRunEnd = curr;
if (curr->isInline())
sawInline = true;
curr = curr->nextSibling();
}
} while (!sawInline);
}
void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint)
{
// makeChildrenNonInline takes a block whose children are *all* inline and it
// makes sure that inline children are coalesced under anonymous
// blocks. If |insertionPoint| is defined, then it represents the insertion point for
// the new block child that is causing us to have to wrap all the inlines. This
// means that we cannot coalesce inlines before |insertionPoint| with inlines following
// |insertionPoint|, because the new child is going to be inserted in between the inlines,
// splitting them.
KHTMLAssert(isInlineBlockOrInlineTable() || !isInline());
KHTMLAssert(!insertionPoint || insertionPoint->parent() == this);
m_childrenInline = false;
RenderObject *child = firstChild();
while (child) {
RenderObject *inlineRunStart, *inlineRunEnd;
getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd);
if (!inlineRunStart)
break;
child = inlineRunEnd->nextSibling();
RenderBlock* box = createAnonymousBlock();
insertChildNode(box, inlineRunStart);
RenderObject* o = inlineRunStart;
while(o != inlineRunEnd)
{
RenderObject* no = o;
o = no->nextSibling();
box->appendChildNode(removeChildNode(no));
}
box->appendChildNode(removeChildNode(inlineRunEnd));
}
#ifndef NDEBUG
for (RenderObject *c = firstChild(); c; c = c->nextSibling())
KHTMLAssert(!c->isInline());
#endif
}
void RenderBlock::removeChild(RenderObject *oldChild)
{
// If this child is a block, and if our previous and next siblings are
// both anonymous blocks with inline content, then we can go ahead and
// fold the inline content back together.
RenderObject* prev = oldChild->previousSibling();
RenderObject* next = oldChild->nextSibling();
bool canDeleteAnonymousBlocks = !documentBeingDestroyed() && !isInline() && !oldChild->isInline() &&
!oldChild->continuation() &&
(!prev || (prev->isAnonymousBlock() && prev->childrenInline())) &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -