📄 render_block.cpp
字号:
// See if we have a compact element. If we do, then try to tuck the compact element into the margin space of the next block.
next = handleCompactChild(child, compactInfo, handled);
if (handled) return next;
// Finally, see if we have a run-in element.
return handleRunInChild(child, handled);
}
RenderObject* RenderBlock::handlePositionedChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled)
{
if (child->isPositioned()) {
handled = true;
child->containingBlock()->insertPositionedObject(child);
adjustPositionedBlock(child, marginInfo);
return child->nextSibling();
}
return 0;
}
RenderObject* RenderBlock::handleFloatingChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled)
{
if (child->isFloating()) {
handled = true;
insertFloatingObject(child);
adjustFloatingBlock(marginInfo);
return child->nextSibling();
}
return 0;
}
RenderObject* RenderBlock::handleCompactChild(RenderObject* child, CompactInfo& compactInfo, bool& handled)
{
// FIXME: We only deal with one compact at a time. It is unclear what should be
// done if multiple contiguous compacts are encountered. For now we assume that
// compact A followed by another compact B should simply be treated as block A.
if (child->isCompact() && !compactInfo.compact() && (child->childrenInline() || child->isReplaced())) {
// Get the next non-positioned/non-floating RenderBlock.
RenderObject* next = child->nextSibling();
RenderObject* curr = next;
while (curr && curr->isFloatingOrPositioned())
curr = curr->nextSibling();
if (curr && curr->isRenderBlock() && !curr->isCompact() && !curr->isRunIn()) {
curr->calcWidth(); // So that horizontal margins are correct.
child->setInline(true); // Need to compute the margins/width for the child as though it is an inline, so that it won't try to puff up the margins to
// fill the containing block width.
child->calcWidth();
int childMargins = child->marginLeft() + child->marginRight();
int margin = style()->direction() == LTR ? curr->marginLeft() : curr->marginRight();
if (margin >= (childMargins + child->maxWidth())) {
// The compact will fit in the margin.
handled = true;
compactInfo.set(child, curr);
child->setPos(0,0); // This position will be updated to reflect the compact's
// desired position and the line box for the compact will
// pick that position up.
// Remove the child.
RenderObject* next = child->nextSibling();
removeChildNode(child);
// Now insert the child under |curr|.
curr->insertChildNode(child, curr->firstChild());
return next;
}
else
child->setInline(false); // We didn't fit, so we remain a block-level element.
}
}
return 0;
}
void RenderBlock::insertCompactIfNeeded(RenderObject* child, CompactInfo& compactInfo)
{
if (compactInfo.matches(child)) {
// We have a compact child to squeeze in.
RenderObject* compactChild = compactInfo.compact();
int compactXPos = borderLeft() + paddingLeft() + compactChild->marginLeft();
if (style()->direction() == RTL) {
compactChild->calcWidth(); // have to do this because of the capped maxwidth
compactXPos = width() - borderRight() - paddingRight() - marginRight() -
compactChild->width() - compactChild->marginRight();
}
compactXPos -= child->xPos(); // Put compactXPos into the child's coordinate space.
compactChild->setPos(compactXPos, compactChild->yPos()); // Set the x position.
compactInfo.clear();
}
}
RenderObject* RenderBlock::handleRunInChild(RenderObject* child, bool& handled)
{
// See if we have a run-in element with inline children. If the
// children aren't inline, then just treat the run-in as a normal
// block.
if (child->isRunIn() && (child->childrenInline() || child->isReplaced())) {
// Get the next non-positioned/non-floating RenderBlock.
RenderObject* curr = child->nextSibling();
while (curr && curr->isFloatingOrPositioned())
curr = curr->nextSibling();
if (curr && (curr->isRenderBlock() && curr->childrenInline() && !curr->isCompact() && !curr->isRunIn())) {
// The block acts like an inline, so just null out its
// position.
handled = true;
child->setInline(true);
child->setPos(0,0);
// Remove the child.
RenderObject* next = child->nextSibling();
removeChildNode(child);
// Now insert the child under |curr|.
curr->insertChildNode(child, curr->firstChild());
return next;
}
}
return 0;
}
void RenderBlock::collapseMargins(RenderObject* child, MarginInfo& marginInfo, int yPosEstimate)
{
// Get our max pos and neg top margins.
int posTop = child->maxTopMargin(true);
int negTop = child->maxTopMargin(false);
// For self-collapsing blocks, collapse our bottom margins into our
// top to get new posTop and negTop values.
if (child->isSelfCollapsingBlock()) {
posTop = kMax(posTop, child->maxBottomMargin(true));
negTop = kMax(negTop, child->maxBottomMargin(false));
}
// See if the top margin is quirky. We only care if this child has
// margins that will collapse with us.
bool topQuirk = child->isTopMarginQuirk() || style()->marginTopCollapse() == MDISCARD;
if (marginInfo.canCollapseWithTop()) {
// This child is collapsing with the top of the
// block. If it has larger margin values, then we need to update
// our own maximal values.
if (!style()->htmlHacks() || !marginInfo.quirkContainer() || !topQuirk) {
m_maxTopPosMargin = kMax(posTop, m_maxTopPosMargin);
m_maxTopNegMargin = kMax(negTop, m_maxTopNegMargin);
}
// The minute any of the margins involved isn't a quirk, don't
// collapse it away, even if the margin is smaller (www.webreference.com
// has an example of this, a <dt> with 0.8em author-specified inside
// a <dl> inside a <td>.
if (!marginInfo.determinedTopQuirk() && !topQuirk && (posTop-negTop)) {
m_topMarginQuirk = false;
marginInfo.setDeterminedTopQuirk(true);
}
if (!marginInfo.determinedTopQuirk() && topQuirk && marginTop() == 0)
// We have no top margin and our top child has a quirky margin.
// We will pick up this quirky margin and pass it through.
// This deals with the <td><div><p> case.
// Don't do this for a block that split two inlines though. You do
// still apply margins in this case.
m_topMarginQuirk = true;
}
if (marginInfo.quirkContainer() && marginInfo.atTopOfBlock() && (posTop - negTop))
marginInfo.setTopQuirk(topQuirk);
int ypos = m_height;
if (child->isSelfCollapsingBlock()) {
// This child has no height. We need to compute our
// position before we collapse the child's margins together,
// so that we can get an accurate position for the zero-height block.
int collapsedTopPos = kMax(marginInfo.posMargin(), child->maxTopMargin(true));
int collapsedTopNeg = kMax(marginInfo.negMargin(), child->maxTopMargin(false));
marginInfo.setMargin(collapsedTopPos, collapsedTopNeg);
// Now collapse the child's margins together, which means examining our
// bottom margin values as well.
marginInfo.setPosMarginIfLarger(child->maxBottomMargin(true));
marginInfo.setNegMarginIfLarger(child->maxBottomMargin(false));
if (!marginInfo.canCollapseWithTop())
// We need to make sure that the position of the self-collapsing block
// is correct, since it could have overflowing content
// that needs to be positioned correctly (e.g., a block that
// had a specified height of 0 but that actually had subcontent).
ypos = m_height + collapsedTopPos - collapsedTopNeg;
}
else {
if (child->style()->marginTopCollapse() == MSEPARATE) {
m_height += marginInfo.margin() + child->marginTop();
ypos = m_height;
}
else if (!marginInfo.atTopOfBlock() ||
(!marginInfo.canCollapseTopWithChildren()
&& (!style()->htmlHacks() || !marginInfo.quirkContainer() || !marginInfo.topQuirk()))) {
// We're collapsing with a previous sibling's margins and not
// with the top of the block.
m_height += kMax(marginInfo.posMargin(), posTop) - kMax(marginInfo.negMargin(), negTop);
ypos = m_height;
}
marginInfo.setPosMargin(child->maxBottomMargin(true));
marginInfo.setNegMargin(child->maxBottomMargin(false));
if (marginInfo.margin())
marginInfo.setBottomQuirk(child->isBottomMarginQuirk() || style()->marginBottomCollapse() == MDISCARD);
marginInfo.setSelfCollapsingBlockClearedFloat(false);
}
child->setPos(child->xPos(), ypos);
if (ypos != yPosEstimate) {
if (child->style()->width().isPercent() && child->usesLineWidth())
// The child's width is a percentage of the line width.
// When the child shifts to clear an item, its width can
// change (because it has more available line width).
// So go ahead and mark the item as dirty.
child->setChildNeedsLayout(true);
if (!child->avoidsFloats() && child->containsFloats())
child->markAllDescendantsWithFloatsForLayout();
// Our guess was wrong. Make the child lay itself out again.
child->layoutIfNeeded();
}
}
void RenderBlock::clearFloatsIfNeeded(RenderObject* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin)
{
int heightIncrease = getClearDelta(child);
if (heightIncrease) {
// The child needs to be lowered. Move the child so that it just clears the float.
child->setPos(child->xPos(), child->yPos() + heightIncrease);
// Increase our height by the amount we had to clear.
if (!child->isSelfCollapsingBlock())
m_height += heightIncrease;
else {
// For self-collapsing blocks that clear, they may end up collapsing
// into the bottom of the parent block. We simulate this behavior by
// setting our positive margin value to compensate for the clear.
marginInfo.setPosMargin(kMax(0, child->yPos() - m_height));
marginInfo.setNegMargin(0);
marginInfo.setSelfCollapsingBlockClearedFloat(true);
}
if (marginInfo.canCollapseWithTop()) {
// We can no longer collapse with the top of the block since a clear
// occurred. The empty blocks collapse into the cleared block.
// FIXME: This isn't quite correct. Need clarification for what to do
// if the height the cleared block is offset by is smaller than the
// margins involved.
m_maxTopPosMargin = oldTopPosMargin;
m_maxTopNegMargin = oldTopNegMargin;
marginInfo.setAtTopOfBlock(false);
}
// If our value of clear caused us to be repositioned vertically to be
// underneath a float, we might have to do another layout to take into account
// the extra space we now have available.
if (child->style()->width().isPercent() && child->usesLineWidth())
// The child's width is a percentage of the line width.
// When the child shifts to clear an item, its width can
// change (because it has more available line width).
// So go ahead and mark the item as dirty.
child->setChildNeedsLayout(true);
if (!child->avoidsFloats() && child->containsFloats())
child->markAllDescendantsWithFloatsForLayout();
child->layoutIfNeeded();
}
}
int RenderBlock::estimateVerticalPosition(RenderObject* child, const MarginInfo& marginInfo)
{
// FIXME: We need to eliminate the estimation of vertical position, because when it's wrong we sometimes trigger a pathological
// relayout if there are intruding floats.
int yPosEstimate = m_height;
if (!marginInfo.canCollapseWithTop()) {
int childMarginTop = child->selfNeedsLayout() ? child->marginTop() : child->collapsedMarginTop();
yPosEstimate += kMax(marginInfo.margin(), childMarginTop);
}
return yPosEstimate;
}
void RenderBlock::determineHorizontalPosition(RenderObject* child)
{
if (style()->direction() == LTR) {
int xPos = borderLeft() + paddingLeft();
// Add in our left margin.
int chPos = xPos + child->marginLeft();
// Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need
// to shift over as necessary to dodge any floats that might get in the way.
if (child->avoidsFloats()) {
int leftOff = leftOffset(m_height);
if (style()->textAlign() != KHTML_CENTER && child->style()->marginLeft().type != Variable) {
if (child->marginLeft() < 0)
leftOff += child->marginLeft();
chPos = kMax(chPos, leftOff); // Let the float sit in the child's margin if it can fit.
}
else if (leftOff != xPos) {
// The object is shifting right. The object might be centered, so we need to
// recalculate our horizontal margins. Note that the containing block content
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -