File indexing completed on 2024-04-28 15:23:55
0001 /* 0002 * This file is part of the render object implementation for KHTML. 0003 * 0004 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) 0005 * (C) 1999-2003 Antti Koivisto (koivisto@kde.org) 0006 * (C) 2002-2003 Dirk Mueller (mueller@kde.org) 0007 * (C) 2003-2009 Apple Computer, Inc. 0008 * (C) 2004-2009 Germain Garand (germain@ebooksfrance.org) 0009 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) 0010 * (C) 2006 Charles Samuels (charles@kde.org) 0011 * 0012 * This library is free software; you can redistribute it and/or 0013 * modify it under the terms of the GNU Library General Public 0014 * License as published by the Free Software Foundation; either 0015 * version 2 of the License, or (at your option) any later version. 0016 * 0017 * This library is distributed in the hope that it will be useful, 0018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0020 * Library General Public License for more details. 0021 * 0022 * You should have received a copy of the GNU Library General Public License 0023 * along with this library; see the file COPYING.LIB. If not, write to 0024 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0025 * Boston, MA 02110-1301, USA. 0026 * 0027 */ 0028 0029 //#define DEBUG 0030 //#define DEBUG_LAYOUT 0031 //#define BOX_DEBUG 0032 //#define FLOAT_DEBUG 0033 //#define PAGE_DEBUG 0034 0035 #include "render_block.h" 0036 0037 #include "khtml_debug.h" 0038 #include <limits.h> 0039 #include "render_text.h" 0040 #include "render_table.h" 0041 #include "render_canvas.h" 0042 #include "render_layer.h" 0043 #include "rendering/render_position.h" 0044 0045 #include <xml/dom_nodeimpl.h> 0046 #include <xml/dom_docimpl.h> 0047 #include <xml/dom_selection.h> 0048 #include <html/html_formimpl.h> 0049 0050 #include <khtmlview.h> 0051 #include <khtml_part.h> 0052 0053 using namespace DOM; 0054 0055 namespace khtml 0056 { 0057 0058 // ------------------------------------------------------------------------------------------------------- 0059 0060 // Our MarginInfo state used when laying out block children. 0061 RenderBlock::MarginInfo::MarginInfo(RenderBlock *block, int top, int bottom) 0062 { 0063 // Whether or not we can collapse our own margins with our children. We don't do this 0064 // if we had any border/padding (obviously), if we're the root or HTML elements, or if 0065 // we're positioned, floating, a table cell. 0066 m_canCollapseWithChildren = !block->isCanvas() && !block->isRoot() && !block->isPositioned() && 0067 !block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable(); 0068 0069 m_canCollapseTopWithChildren = m_canCollapseWithChildren && (top == 0) /*&& block->style()->marginTopCollapse() != MSEPARATE */; 0070 0071 // If any height other than auto is specified in CSS, then we don't collapse our bottom 0072 // margins with our children's margins. To do otherwise would be to risk odd visual 0073 // effects when the children overflow out of the parent block and yet still collapse 0074 // with it. We also don't collapse if we have any bottom border/padding. 0075 m_canCollapseBottomWithChildren = m_canCollapseWithChildren && (bottom == 0) && 0076 (block->style()->height().isAuto() && block->style()->height().isZero()) /*&& block->style()->marginBottomCollapse() != MSEPARATE*/; 0077 0078 m_quirkContainer = block->isTableCell() || block->isBody() /*|| block->style()->marginTopCollapse() == MDISCARD || 0079 block->style()->marginBottomCollapse() == MDISCARD*/; 0080 0081 m_atTopOfBlock = true; 0082 m_atBottomOfBlock = false; 0083 0084 m_posMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(true) : 0; 0085 m_negMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(false) : 0; 0086 0087 m_selfCollapsingBlockClearedFloat = false; 0088 0089 m_topQuirk = m_bottomQuirk = m_determinedTopQuirk = false; 0090 } 0091 0092 // ------------------------------------------------------------------------------------------------------- 0093 0094 RenderBlock::RenderBlock(DOM::NodeImpl *node) 0095 : RenderFlow(node) 0096 { 0097 m_childrenInline = true; 0098 m_floatingObjects = nullptr; 0099 m_positionedObjects = nullptr; 0100 m_firstLine = false; 0101 m_avoidPageBreak = false; 0102 m_clearStatus = CNONE; 0103 m_maxTopPosMargin = m_maxTopNegMargin = m_maxBottomPosMargin = m_maxBottomNegMargin = 0; 0104 m_topMarginQuirk = m_bottomMarginQuirk = false; 0105 m_overflowHeight = m_overflowWidth = 0; 0106 m_overflowLeft = m_overflowTop = 0; 0107 } 0108 0109 RenderBlock::~RenderBlock() 0110 { 0111 if (m_floatingObjects) { 0112 QListIterator<FloatingObject *> it(*m_floatingObjects); 0113 while (it.hasNext()) { 0114 delete it.next(); 0115 } 0116 } 0117 delete m_floatingObjects; 0118 delete m_positionedObjects; 0119 } 0120 0121 void RenderBlock::setStyle(RenderStyle *_style) 0122 { 0123 setReplaced(_style->isDisplayReplacedType()); 0124 0125 RenderFlow::setStyle(_style); 0126 0127 // ### we could save this call when the change only affected 0128 // non inherited properties 0129 RenderObject *child = firstChild(); 0130 while (child != nullptr) { 0131 if (child->isAnonymousBlock()) { 0132 RenderStyle *newStyle = new RenderStyle(); 0133 newStyle->inheritFrom(style()); 0134 newStyle->setDisplay(BLOCK); 0135 child->setStyle(newStyle); 0136 } 0137 child = child->nextSibling(); 0138 } 0139 0140 if (attached()) { 0141 // Update generated content and ::inside 0142 updateReplacedContent(); 0143 // Update pseudos for :before and :after 0144 updatePseudoChildren(); 0145 } 0146 0147 // handled by close() during parsing 0148 // ### remove close move upto updatePseudo 0149 if (!document()->parsing()) { 0150 updateFirstLetter(); 0151 } 0152 } 0153 0154 // Attach handles initial setStyle that requires parent nodes 0155 void RenderBlock::attach() 0156 { 0157 RenderFlow::attach(); 0158 0159 updateReplacedContent(); 0160 updatePseudoChildren(); 0161 } 0162 0163 static inline bool isFirstLetterPunct(const QChar *c) 0164 { 0165 // CSS2.1/3 definition for ::first-letter doesn't include Pc or Pd. 0166 if (c->isPunct()) { 0167 QChar::Category cat = c->category(); 0168 return cat != QChar::Punctuation_Connector && 0169 cat != QChar::Punctuation_Dash; 0170 } 0171 return false; 0172 } 0173 0174 void RenderBlock::updateFirstLetter() 0175 { 0176 // Only blocks with inline-children can generate a first-letter 0177 if (!childrenInline() || !firstChild()) { 0178 return; 0179 } 0180 0181 // Don't recurse 0182 if (style()->styleType() == RenderStyle::FIRST_LETTER) { 0183 return; 0184 } 0185 0186 // The first-letter style is inheritable. 0187 RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LETTER); 0188 RenderObject *o = this; 0189 while (o && !pseudoStyle) { 0190 // ### We should ignore empty preceding siblings 0191 if (o->parent() && o->parent()->firstChild() == this) { 0192 o = o->parent(); 0193 } else { 0194 break; 0195 } 0196 pseudoStyle = o->style()->getPseudoStyle(RenderStyle::FIRST_LETTER); 0197 }; 0198 0199 // FIXME: Currently we don't delete first-letters, this is 0200 // handled instead in NodeImpl::diff by issuing Detach on first-letter changes. 0201 if (!pseudoStyle) { 0202 return; 0203 } 0204 0205 // Drill into inlines looking for our first text child. 0206 RenderObject *firstText = firstChild(); 0207 while (firstText && firstText->needsLayout() && !firstText->isFloating() && !firstText->isRenderBlock() && !firstText->isReplaced() && !firstText->isText()) 0208 // ### We should skip first children with only white-space and punctuation 0209 { 0210 firstText = firstText->firstChild(); 0211 } 0212 0213 if (firstText && firstText->isText() && !firstText->isBR()) { 0214 RenderObject *firstLetterObject = nullptr; 0215 // Find the old first-letter 0216 if (firstText->parent()->style()->styleType() == RenderStyle::FIRST_LETTER) { 0217 firstLetterObject = firstText->parent(); 0218 } 0219 0220 // Force inline display (except for floating first-letters) 0221 pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE); 0222 pseudoStyle->setPosition(PSTATIC); // CSS2 says first-letter can't be positioned. 0223 0224 if (firstLetterObject != nullptr) { 0225 firstLetterObject->setStyle(pseudoStyle); 0226 RenderStyle *newStyle = new RenderStyle(); 0227 newStyle->inheritFrom(pseudoStyle); 0228 firstText->setStyle(newStyle); 0229 return; 0230 } 0231 0232 RenderText *textObj = static_cast<RenderText *>(firstText); 0233 RenderObject *firstLetterContainer = firstText->parent(); 0234 0235 firstLetterObject = RenderFlow::createFlow(node(), pseudoStyle, renderArena()); 0236 firstLetterObject->setIsAnonymous(true); 0237 firstLetterContainer->addChild(firstLetterObject, firstLetterContainer->firstChild()); 0238 0239 // if this object is the result of a :begin, then the text may have not been 0240 // generated yet if it is a counter 0241 if (textObj->recalcMinMax()) { 0242 textObj->recalcMinMaxWidths(); 0243 } 0244 0245 // The original string is going to be either a generated content string or a DOM node's 0246 // string. We want the original string before it got transformed in case first-letter has 0247 // no text-transform or a different text-transform applied to it. 0248 DOMStringImpl *oldText = textObj->originalString(); 0249 if (!oldText) { 0250 oldText = textObj->string(); 0251 } 0252 // ### In theory a first-letter can stretch across multiple text objects, if they only contain 0253 // punctuation and white-space 0254 if (oldText->l >= 1) { 0255 oldText->ref(); 0256 // begin: we need skip leading whitespace so that RenderBlock::findNextLineBreak 0257 // won't think we're continuing from a previous run 0258 unsigned int begin = 0; // the position that first-letter begins 0259 unsigned int length = 0; // the position that "the rest" begins 0260 while (length < oldText->l && (oldText->s + length)->isSpace()) { 0261 length++; 0262 } 0263 begin = length; 0264 while (length < oldText->l && 0265 (isFirstLetterPunct(oldText->s + length) || (oldText->s + length)->isSpace())) { 0266 length++; 0267 } 0268 if (length < oldText->l && 0269 !((oldText->s + length)->isSpace() || isFirstLetterPunct(oldText->s + length))) { 0270 length++; 0271 } 0272 while (length < oldText->l && isFirstLetterPunct(oldText->s + length)) { 0273 length++; 0274 } 0275 0276 // we need to generated a remainingText object even if no text is left 0277 // because it holds the place and style for the old textObj 0278 RenderTextFragment *remainingText = 0279 new(renderArena()) RenderTextFragment(textObj->node(), oldText, length, oldText->l - length); 0280 remainingText->setIsAnonymous(textObj->isAnonymous()); 0281 remainingText->setStyle(textObj->style()); 0282 if (remainingText->element()) { 0283 remainingText->element()->setRenderer(remainingText); 0284 } 0285 0286 RenderObject *nextObj = textObj->nextSibling(); 0287 textObj->detach(); 0288 firstLetterContainer->addChild(remainingText, nextObj); 0289 0290 RenderTextFragment *letter = 0291 new(renderArena()) RenderTextFragment(remainingText->node(), oldText, begin, length - begin); 0292 letter->setIsAnonymous(remainingText->isAnonymous()); 0293 RenderStyle *newStyle = new RenderStyle(); 0294 newStyle->inheritFrom(pseudoStyle); 0295 letter->setStyle(newStyle); 0296 firstLetterObject->addChild(letter); 0297 oldText->deref(); 0298 0299 remainingText->setFirstLetter(letter); 0300 } 0301 firstLetterObject->close(); 0302 } 0303 } 0304 0305 void RenderBlock::addChildToFlow(RenderObject *newChild, RenderObject *beforeChild) 0306 { 0307 // Make sure we don't append things after :after-generated content if we have it. 0308 if (!beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER) { 0309 beforeChild = lastChild(); 0310 } 0311 0312 bool madeBoxesNonInline = false; 0313 0314 // If the requested beforeChild is not one of our children, then this is most likely because 0315 // there is an anonymous block box within this object that contains the beforeChild. So 0316 // just insert the child into the anonymous block box instead of here. This may also be 0317 // needed in cases of things like anonymous tables. 0318 if (beforeChild && beforeChild->parent() != this) { 0319 0320 KHTMLAssert(beforeChild->parent()); 0321 0322 // In the special case where we are prepending a block-level element before 0323 // something contained inside an anonymous block, we can just prepend it before 0324 // the anonymous block. 0325 if (!newChild->isInline() && beforeChild->parent()->isAnonymousBlock() && 0326 beforeChild->parent()->parent() == this && 0327 beforeChild->parent()->firstChild() == beforeChild) { 0328 return addChildToFlow(newChild, beforeChild->parent()); 0329 } 0330 0331 // Otherwise find our kid inside which the beforeChild is, and delegate to it. 0332 // This may be many levels deep due to anonymous tables, table sections, etc. 0333 RenderObject *responsible = beforeChild->parent(); 0334 while (responsible->parent() != this) { 0335 responsible = responsible->parent(); 0336 } 0337 0338 return responsible->addChild(newChild, beforeChild); 0339 } 0340 0341 // prevent elements that haven't received a layout yet from getting painted by pushing 0342 // them far above the top of the page 0343 if (!newChild->isInline()) { 0344 newChild->setPos(newChild->xPos(), -500000); 0345 } 0346 0347 // A block has to either have all of its children inline, or all of its children as blocks. 0348 // So, if our children are currently inline and a block child has to be inserted, we move all our 0349 // inline children into anonymous block boxes 0350 if (m_childrenInline && !newChild->isInline() && !newChild->isFloatingOrPositioned()) { 0351 // This is a block with inline content. Wrap the inline content in anonymous blocks. 0352 makeChildrenNonInline(beforeChild); 0353 madeBoxesNonInline = true; 0354 0355 if (beforeChild && beforeChild->parent() != this) { 0356 beforeChild = beforeChild->parent(); 0357 KHTMLAssert(beforeChild->isAnonymousBlock()); 0358 KHTMLAssert(beforeChild->parent() == this); 0359 } 0360 } else if (!m_childrenInline && !newChild->isFloatingOrPositioned()) { 0361 // If we're inserting an inline child but all of our children are blocks, then we have to make sure 0362 // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise 0363 // a new one is created and inserted into our list of children in the appropriate position. 0364 if (newChild->isInline()) { 0365 if (beforeChild) { 0366 if (beforeChild->previousSibling() && beforeChild->previousSibling()->isAnonymousBlock()) { 0367 beforeChild->previousSibling()->addChild(newChild); 0368 return; 0369 } 0370 } else { 0371 if (m_last && m_last->isAnonymousBlock()) { 0372 m_last->addChild(newChild); 0373 return; 0374 } 0375 } 0376 0377 // no suitable existing anonymous box - create a new one 0378 RenderBlock *newBox = createAnonymousBlock(); 0379 RenderBox::addChild(newBox, beforeChild); 0380 newBox->addChild(newChild); 0381 0382 //the above may actually destroy newBox in case an anonymous 0383 //table got created, and made the anonymous block redundant. 0384 //so look up what to hide indirectly. 0385 RenderObject *toHide = newChild; 0386 while (toHide->parent() != this) { 0387 toHide = toHide->parent(); 0388 } 0389 0390 toHide->setPos(toHide->xPos(), -500000); 0391 return; 0392 } else { 0393 // We are adding another block child... if the current last child is an anonymous box 0394 // then it needs to be closed. 0395 // ### get rid of the closing thing altogether this will only work during initial parsing 0396 if (lastChild() && lastChild()->isAnonymous()) { 0397 lastChild()->close(); 0398 } 0399 } 0400 } 0401 0402 RenderBox::addChild(newChild, beforeChild); 0403 // ### care about aligned stuff 0404 0405 if (madeBoxesNonInline && isAnonymousBlock()) { 0406 parent()->removeSuperfluousAnonymousBlockChild(this); 0407 } 0408 // we might be deleted now 0409 } 0410 0411 static void getInlineRun(RenderObject *start, RenderObject *stop, 0412 RenderObject *&inlineRunStart, 0413 RenderObject *&inlineRunEnd) 0414 { 0415 // Beginning at |start| we find the largest contiguous run of inlines that 0416 // we can. We denote the run with start and end points, |inlineRunStart| 0417 // and |inlineRunEnd|. Note that these two values may be the same if 0418 // we encounter only one inline. 0419 // 0420 // We skip any non-inlines we encounter as long as we haven't found any 0421 // inlines yet. 0422 // 0423 // 0424 // |stop| indicates a non-inclusive stop point. Regardless of whether |stop| 0425 // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered 0426 // a non-inline. 0427 0428 RenderObject *curr = start; 0429 bool sawInline; 0430 do { 0431 while (curr && !(curr->isInline() || curr->isFloatingOrPositioned())) { 0432 curr = curr->nextSibling(); 0433 } 0434 0435 inlineRunStart = inlineRunEnd = curr; 0436 0437 if (!curr) { 0438 return; // No more inline children to be found. 0439 } 0440 0441 sawInline = curr->isInline(); 0442 0443 curr = curr->nextSibling(); 0444 while (curr && (curr->isInline() || curr->isFloatingOrPositioned()) && (curr != stop)) { 0445 inlineRunEnd = curr; 0446 if (curr->isInline()) { 0447 sawInline = true; 0448 } 0449 curr = curr->nextSibling(); 0450 } 0451 } while (!sawInline); 0452 0453 } 0454 0455 void RenderBlock::deleteLineBoxTree() 0456 { 0457 InlineFlowBox *line = m_firstLineBox; 0458 InlineFlowBox *nextLine; 0459 while (line) { 0460 nextLine = line->nextFlowBox(); 0461 line->deleteLine(renderArena()); 0462 line = nextLine; 0463 } 0464 m_firstLineBox = m_lastLineBox = nullptr; 0465 } 0466 0467 short RenderBlock::baselinePosition(bool firstLine) const 0468 { 0469 // CSS2.1-10.8.1 "The baseline of an 'inline-block' is the baseline of its last line box 0470 // in the normal flow, unless it has either no in-flow line boxes or if its 'overflow' 0471 // property has a computed value other than 'visible', in which case the baseline is the bottom margin edge." 0472 0473 if (isReplaced() && !hasOverflowClip() && !needsLayout()) { 0474 int res = getBaselineOfLastLineBox(); 0475 if (res != -1) { 0476 return res + marginTop(); 0477 } 0478 } 0479 return RenderBox::baselinePosition(firstLine); 0480 } 0481 0482 int RenderBlock::getBaselineOfLastLineBox() const 0483 { 0484 if (!isBlockFlow()) { 0485 return -1; 0486 } 0487 0488 if (childrenInline()) { 0489 // if (!firstLineBox() && hasLineIfEmpty()) 0490 // return RenderFlow::baselinePosition(true) + borderTop() + paddingTop(); 0491 if (lastLineBox()) { 0492 return lastLineBox()->yPos() + lastLineBox()->baseline(); 0493 } 0494 return -1; 0495 } else { 0496 // bool haveNormalFlowChild = false; 0497 for (RenderObject *curr = lastChild(); curr; curr = curr->previousSibling()) { 0498 if (!curr->isFloatingOrPositioned() && curr->isBlockFlow()) { 0499 // haveNormalFlowChild = true; 0500 int result = static_cast<RenderBlock *>(curr)->getBaselineOfLastLineBox(); 0501 if (result != -1) { 0502 return curr->yPos() + result; // Translate to our coordinate space. 0503 } 0504 } 0505 } 0506 // if (!haveNormalFlowChild && isRenderButton()) // hasLineIfEmpty() 0507 // return RenderFlow::baselinePosition(true) + borderTop() + paddingTop(); 0508 } 0509 0510 return -1; 0511 } 0512 0513 void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) 0514 { 0515 // makeChildrenNonInline takes a block whose children are *all* inline and it 0516 // makes sure that inline children are coalesced under anonymous 0517 // blocks. If |insertionPoint| is defined, then it represents the insertion point for 0518 // the new block child that is causing us to have to wrap all the inlines. This 0519 // means that we cannot coalesce inlines before |insertionPoint| with inlines following 0520 // |insertionPoint|, because the new child is going to be inserted in between the inlines, 0521 // splitting them. 0522 KHTMLAssert(isReplacedBlock() || !isInline()); 0523 KHTMLAssert(!insertionPoint || insertionPoint->parent() == this); 0524 0525 deleteLineBoxTree(); 0526 0527 m_childrenInline = false; 0528 0529 RenderObject *child = firstChild(); 0530 0531 while (child) { 0532 RenderObject *inlineRunStart, *inlineRunEnd; 0533 getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd); 0534 0535 if (!inlineRunStart) { 0536 break; 0537 } 0538 0539 child = inlineRunEnd->nextSibling(); 0540 0541 RenderBlock *box = createAnonymousBlock(); 0542 insertChildNode(box, inlineRunStart); 0543 RenderObject *o = inlineRunStart; 0544 while (o != inlineRunEnd) { 0545 RenderObject *no = o; 0546 o = no->nextSibling(); 0547 box->appendChildNode(removeChildNode(no)); 0548 } 0549 box->appendChildNode(removeChildNode(inlineRunEnd)); 0550 box->close(); 0551 box->setPos(box->xPos(), -500000); 0552 } 0553 } 0554 0555 void RenderBlock::makePageBreakAvoidBlocks() 0556 { 0557 KHTMLAssert(!childrenInline()); 0558 KHTMLAssert(canvas()->pagedMode()); 0559 0560 RenderObject *breakAfter = firstChild(); 0561 RenderObject *breakBefore = breakAfter ? breakAfter->nextSibling() : nullptr; 0562 0563 RenderBlock *pageRun = nullptr; 0564 0565 // ### Should follow margin-collapsing rules, skipping self-collapsing blocks 0566 // and exporting page-breaks from first/last child when collapsing with parent margin. 0567 while (breakAfter) { 0568 if (breakAfter->isRenderBlock() && !breakAfter->childrenInline()) { 0569 static_cast<RenderBlock *>(breakAfter)->makePageBreakAvoidBlocks(); 0570 } 0571 EPageBreak pbafter = breakAfter->style()->pageBreakAfter(); 0572 EPageBreak pbbefore = breakBefore ? breakBefore->style()->pageBreakBefore() : PBALWAYS; 0573 if ((pbafter == PBAVOID && pbbefore == PBAVOID) || 0574 (pbafter == PBAVOID && pbbefore == PBAUTO) || 0575 (pbafter == PBAUTO && pbbefore == PBAVOID)) { 0576 if (!pageRun) { 0577 pageRun = createAnonymousBlock(); 0578 pageRun->m_avoidPageBreak = true; 0579 pageRun->setChildrenInline(false); 0580 } 0581 pageRun->appendChildNode(removeChildNode(breakAfter)); 0582 } else { 0583 if (pageRun) { 0584 pageRun->appendChildNode(removeChildNode(breakAfter)); 0585 pageRun->close(); 0586 insertChildNode(pageRun, breakBefore); 0587 pageRun = nullptr; 0588 } 0589 } 0590 breakAfter = breakBefore; 0591 breakBefore = breakBefore ? breakBefore->nextSibling() : nullptr; 0592 } 0593 0594 // recurse into positioned block children as well. 0595 if (m_positionedObjects) { 0596 RenderObject *obj; 0597 QListIterator<RenderObject *> it(*m_positionedObjects); 0598 while (it.hasNext()) { 0599 obj = it.next(); 0600 if (obj->isRenderBlock() && !obj->childrenInline()) { 0601 static_cast<RenderBlock *>(obj)->makePageBreakAvoidBlocks(); 0602 } 0603 } 0604 } 0605 0606 // recurse into floating block children. 0607 if (m_floatingObjects) { 0608 FloatingObject *obj; 0609 QListIterator<FloatingObject *> it(*m_floatingObjects); 0610 while (it.hasNext()) { 0611 obj = it.next(); 0612 if (obj->node->isRenderBlock() && !obj->node->childrenInline()) { 0613 static_cast<RenderBlock *>(obj->node)->makePageBreakAvoidBlocks(); 0614 } 0615 } 0616 } 0617 } 0618 0619 void RenderBlock::removeChild(RenderObject *oldChild) 0620 { 0621 // If this child is a block, and if our previous and next siblings are 0622 // both anonymous blocks with inline content, then we can go ahead and 0623 // fold the inline content back together. 0624 RenderObject *prev = oldChild->previousSibling(); 0625 RenderObject *next = oldChild->nextSibling(); 0626 RenderObject *lc = nullptr; 0627 bool mergedBlocks = false; 0628 bool checkContinuationMerge = false; 0629 if (!documentBeingDestroyed() && !isInline() && !oldChild->isInline() && !oldChild->continuation()) { 0630 if (prev && prev->isAnonymousBlock() && prev->childrenInline() && 0631 next && next->isAnonymousBlock() && next->childrenInline()) { 0632 // Take all the children out of the |next| block and put them in 0633 // the |prev| block. 0634 RenderObject *o = next->firstChild(); 0635 while (o) { 0636 RenderObject *no = o; 0637 o = no->nextSibling(); 0638 prev->appendChildNode(next->removeChildNode(no)); 0639 } 0640 0641 // Detach the now-empty block. 0642 static_cast<RenderBlock *>(next)->deleteLineBoxTree(); 0643 next->detach(); 0644 0645 mergedBlocks = true; 0646 } 0647 0648 // Check if there are continuations we could merge 0649 checkContinuationMerge = (mergedBlocks || (!prev && !next)) && continuation() && isAnonymousBlock() && continuation()->isRenderInline() && 0650 previousSibling() && previousSibling()->isAnonymousBlock() && (lc = previousSibling()->lastChild()); 0651 0652 if (checkContinuationMerge) { 0653 while (lc->lastChild() && lc->continuation()) { 0654 lc = lc->lastChild(); 0655 } 0656 checkContinuationMerge = lc->isRenderInline() && lc->continuation() && (lc->continuation() == this); 0657 } 0658 if (checkContinuationMerge) { 0659 RenderObject *prev = lc->parent(); 0660 RenderObject *cont = continuation()->parent(); 0661 while (prev && cont) { 0662 if (prev == previousSibling() && cont == nextSibling() && cont->isAnonymousBlock()) { 0663 break; 0664 } 0665 if (!prev->continuation() || prev->continuation() != cont) { 0666 checkContinuationMerge = false; 0667 break; 0668 } 0669 prev = prev->parent(); 0670 cont = cont->parent(); 0671 } 0672 } 0673 } 0674 0675 RenderFlow::removeChild(oldChild); 0676 0677 if (mergedBlocks && prev && !prev->previousSibling() && !prev->nextSibling()) { 0678 // The remerge has knocked us down to containing only a single anonymous 0679 // box. We can go ahead and pull the content right back up into our 0680 // box. 0681 RenderBlock *anonBlock = static_cast<RenderBlock *>(prev); 0682 m_childrenInline = true; 0683 RenderObject *o = anonBlock->firstChild(); 0684 while (o) { 0685 RenderObject *no = o; 0686 o = no->nextSibling(); 0687 appendChildNode(anonBlock->removeChildNode(no)); 0688 } 0689 0690 // Detach the now-empty block. 0691 anonBlock->deleteLineBoxTree(); 0692 anonBlock->detach(); 0693 } 0694 if (checkContinuationMerge && ((!prev && !next) || m_childrenInline)) { 0695 // |oldChild| was a block that split an inline into continuations. 0696 // Now that we only have inline content left, we may merge back those continuations 0697 // into a single inline. 0698 assert(lc->isRenderInline()); 0699 RenderFlow *prev = static_cast<RenderFlow *>(lc); 0700 while (RenderFlow *next = prev->continuation()) { 0701 RenderObject *o = next->firstChild(); 0702 while (o) { 0703 RenderObject *no = o; 0704 o = no->nextSibling(); 0705 prev->appendChildNode(next->removeChildNode(no)); 0706 } 0707 prev->setContinuation(next->continuation()); 0708 next->setContinuation(nullptr); 0709 if (next != this) { 0710 next->detach(); 0711 prev = static_cast<RenderFlow *>(prev->parent()); 0712 assert(!prev || prev->isRenderInline() || prev->isRenderBlock()); 0713 } 0714 } 0715 assert(nextSibling() && nextSibling()->isAnonymousBlock()); 0716 if (!nextSibling()->firstChild()) { 0717 static_cast<RenderBlock *>(nextSibling())->deleteLineBoxTree(); 0718 nextSibling()->detach(); 0719 } 0720 deleteLineBoxTree(); 0721 detach(); 0722 } 0723 } 0724 0725 bool RenderBlock::isSelfCollapsingBlock() const 0726 { 0727 // We are not self-collapsing if we 0728 // (a) have a non-zero height according to layout (an optimization to avoid wasting time) 0729 // (b) are a table, 0730 // (c) have border/padding, 0731 // (d) have a min-height 0732 if (m_height > 0 || 0733 isTable() || (borderBottom() + paddingBottom() + borderTop() + paddingTop()) != 0 || 0734 style()->minHeight().isPositive()) { 0735 return false; 0736 } 0737 0738 bool hasAutoHeight = style()->height().isAuto(); 0739 if (style()->height().isPercent() && !style()->htmlHacks()) { 0740 hasAutoHeight = true; 0741 for (RenderBlock *cb = containingBlock(); !cb->isCanvas(); cb = cb->containingBlock()) { 0742 if (cb->style()->height().isFixed() || cb->isTableCell()) { 0743 hasAutoHeight = false; 0744 } 0745 } 0746 } 0747 0748 // If the height is 0 or auto, then whether or not we are a self-collapsing block depends 0749 // on whether we have content that is all self-collapsing or not. 0750 if (hasAutoHeight || ((style()->height().isFixed() || style()->height().isPercent()) && style()->height().isZero())) { 0751 // If the block has inline children, see if we generated any line boxes. If we have any 0752 // line boxes, then we can't be self-collapsing, since we have content. 0753 if (childrenInline()) { 0754 return !firstLineBox(); 0755 } 0756 0757 // Whether or not we collapse is dependent on whether all our normal flow children 0758 // are also self-collapsing. 0759 for (RenderObject *child = firstChild(); child; child = child->nextSibling()) { 0760 if (child->isFloatingOrPositioned()) { 0761 continue; 0762 } 0763 if (!child->isSelfCollapsingBlock()) { 0764 return false; 0765 } 0766 } 0767 return true; 0768 } 0769 return false; 0770 } 0771 0772 void RenderBlock::layout() 0773 { 0774 // Table cells call layoutBlock directly, so don't add any logic here. Put code into 0775 // layoutBlock(). 0776 layoutBlock(false); 0777 } 0778 0779 void RenderBlock::layoutBlock(bool relayoutChildren) 0780 { 0781 if (isInline() && !isReplacedBlock()) { 0782 setNeedsLayout(false); 0783 return; 0784 } 0785 // qCDebug(KHTML_LOG) << renderName() << " " << this << "::layoutBlock() start"; 0786 // QTime t; 0787 // t.start(); 0788 KHTMLAssert(needsLayout()); 0789 KHTMLAssert(minMaxKnown()); 0790 0791 if (canvas()->pagedMode()) { 0792 relayoutChildren = true; 0793 } 0794 0795 if (markedForRepaint()) { 0796 repaintDuringLayout(); 0797 setMarkedForRepaint(false); 0798 } 0799 0800 if (!relayoutChildren && posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) { 0801 // All we have to is lay out our positioned objects. 0802 layoutPositionedObjects(relayoutChildren); 0803 if (hasOverflowClip()) { 0804 m_layer->checkScrollbarsAfterLayout(); 0805 } 0806 setNeedsLayout(false); 0807 return; 0808 } 0809 0810 int oldWidth = m_width; 0811 0812 calcWidth(); 0813 m_overflowWidth = m_width; 0814 m_overflowLeft = 0; 0815 if (style()->direction() == LTR) { 0816 int cw = 0; 0817 if (style()->textIndent().isPercent()) { 0818 cw = containingBlock()->contentWidth(); 0819 } 0820 m_overflowLeft = qMin(0, style()->textIndent().minWidth(cw)); 0821 } 0822 0823 if (oldWidth != m_width) { 0824 relayoutChildren = true; 0825 } 0826 0827 // qCDebug(KHTML_LOG) << floatingObjects << "," << oldWidth << "," 0828 // << m_width << ","<< needsLayout() << "," << isAnonymousBox() << "," 0829 // << isPositioned(); 0830 0831 #ifdef DEBUG_LAYOUT 0832 qCDebug(KHTML_LOG) << renderName() << "(RenderBlock) " << this << " ::layout() width=" << m_width << ", needsLayout=" << needsLayout(); 0833 if (containingBlock() == static_cast<RenderObject *>(this)) { 0834 qCDebug(KHTML_LOG) << renderName() << ": containingBlock == this"; 0835 } 0836 #endif 0837 0838 clearFloats(); 0839 0840 int previousHeight = m_height; 0841 m_height = 0; 0842 m_overflowHeight = 0; 0843 m_clearStatus = CNONE; 0844 0845 // We use four values, maxTopPos, maxPosNeg, maxBottomPos, and maxBottomNeg, to track 0846 // our current maximal positive and negative margins. These values are used when we 0847 // are collapsed with adjacent blocks, so for example, if you have block A and B 0848 // collapsing together, then you'd take the maximal positive margin from both A and B 0849 // and subtract it from the maximal negative margin from both A and B to get the 0850 // true collapsed margin. This algorithm is recursive, so when we finish layout() 0851 // our block knows its current maximal positive/negative values. 0852 // 0853 // Start out by setting our margin values to our current margins. Table cells have 0854 // no margins, so we don't fill in the values for table cells. 0855 if (!isTableCell()) { 0856 initMaxMarginValues(); 0857 0858 m_topMarginQuirk = style()->marginTop().isQuirk(); 0859 m_bottomMarginQuirk = style()->marginBottom().isQuirk(); 0860 0861 if (element() && element()->id() == ID_FORM && static_cast<HTMLFormElementImpl *>(element())->isMalformed()) 0862 // See if this form is malformed (i.e., unclosed). If so, don't give the form 0863 // a bottom margin. 0864 { 0865 m_maxBottomPosMargin = m_maxBottomNegMargin = 0; 0866 } 0867 } 0868 0869 if (scrollsOverflow() && m_layer) { 0870 // For overflow:scroll blocks, ensure we have both scrollbars in place always. 0871 if (style()->overflowX() == OSCROLL) { 0872 m_layer->showScrollbar(Qt::Horizontal, true); 0873 } 0874 if (style()->overflowY() == OSCROLL) { 0875 m_layer->showScrollbar(Qt::Vertical, true); 0876 } 0877 } 0878 0879 setContainsPageBreak(false); 0880 0881 if (childrenInline()) { 0882 layoutInlineChildren(relayoutChildren); 0883 } else { 0884 layoutBlockChildren(relayoutChildren); 0885 } 0886 0887 // Expand our intrinsic height to encompass floats. 0888 int toAdd = borderBottom() + paddingBottom(); 0889 if (m_layer && scrollsOverflowX() && style()->height().isAuto()) { 0890 toAdd += m_layer->horizontalScrollbarHeight(); 0891 } 0892 if (floatBottom() + toAdd > m_height && (isFloatingOrPositioned() || flowAroundFloats())) { 0893 m_overflowHeight = m_height = floatBottom() + toAdd; 0894 } 0895 0896 int oldHeight = m_height; 0897 calcHeight(); 0898 if (oldHeight != m_height) { 0899 m_overflowHeight -= toAdd; 0900 if (m_layer && scrollsOverflowY()) { 0901 // overflow-height only includes padding-bottom when it scrolls 0902 m_overflowHeight += paddingBottom(); 0903 } 0904 // If the block got expanded in size, then increase our overflowheight to match. 0905 if (m_overflowHeight < m_height) { 0906 m_overflowHeight = m_height; 0907 } 0908 } 0909 if (previousHeight != m_height) { 0910 relayoutChildren = true; 0911 } 0912 0913 if (isTableCell()) { 0914 // Table cells need to grow to accommodate both overhanging floats and 0915 // blocks that have overflowed content. 0916 // Check for an overhanging float first. 0917 // FIXME: This needs to look at the last flow, not the last child. 0918 if (lastChild() && lastChild()->hasOverhangingFloats() && !lastChild()->hasOverflowClip()) { 0919 KHTMLAssert(lastChild()->isRenderBlock()); 0920 m_height = lastChild()->yPos() + static_cast<RenderBlock *>(lastChild())->floatBottom(); 0921 m_height += borderBottom() + paddingBottom(); 0922 } 0923 0924 if (m_overflowHeight > m_height && !hasOverflowClip()) { 0925 m_height = m_overflowHeight + borderBottom() + paddingBottom(); 0926 } 0927 } 0928 0929 if (hasOverhangingFloats() && ((isFloating() && style()->height().isAuto()) || isTableCell())) { 0930 m_height = floatBottom(); 0931 m_height += borderBottom() + paddingBottom(); 0932 } 0933 0934 if (canvas()->pagedMode()) { 0935 #ifdef PAGE_DEBUG 0936 qCDebug(KHTML_LOG) << renderName() << " Page Bottom: " << pageTopAfter(0); 0937 qCDebug(KHTML_LOG) << renderName() << " Bottom: " << m_height; 0938 #endif 0939 bool needsPageBreak = false; 0940 int xpage = crossesPageBreak(0, m_height); 0941 if (xpage) { 0942 needsPageBreak = true; 0943 #ifdef PAGE_DEBUG 0944 qCDebug(KHTML_LOG) << renderName() << " crosses to page " << xpage; 0945 #endif 0946 } 0947 if (needsPageBreak && !containsPageBreak()) { 0948 setNeedsPageClear(true); 0949 #ifdef PAGE_DEBUG 0950 qCDebug(KHTML_LOG) << renderName() << " marked for page-clear"; 0951 #endif 0952 } 0953 } 0954 0955 layoutPositionedObjects(relayoutChildren); 0956 0957 // Always ensure our overflow width/height are at least as large as our width/height. 0958 m_overflowWidth = qMax(m_overflowWidth, (int)m_width); 0959 m_overflowHeight = qMax(m_overflowHeight, m_height); 0960 0961 // Update our scrollbars if we're overflow:auto/scroll now that we know if 0962 // we overflow or not. 0963 if (hasOverflowClip() && m_layer) { 0964 m_layer->checkScrollbarsAfterLayout(); 0965 } 0966 0967 setNeedsLayout(false); 0968 } 0969 0970 void RenderBlock::adjustPositionedBlock(RenderObject *child, const MarginInfo &marginInfo) 0971 { 0972 if (child->isBox() && child->hasStaticX()) { 0973 if (style()->direction() == LTR) { 0974 static_cast<RenderBox *>(child)->setStaticX(borderLeft() + paddingLeft()); 0975 } else { 0976 static_cast<RenderBox *>(child)->setStaticX(borderRight() + paddingRight()); 0977 } 0978 } 0979 0980 if (child->isBox() && child->hasStaticY()) { 0981 int y = m_height; 0982 if (!marginInfo.canCollapseWithTop()) { 0983 child->calcVerticalMargins(); 0984 int marginTop = child->marginTop(); 0985 int collapsedTopPos = marginInfo.posMargin(); 0986 int collapsedTopNeg = marginInfo.negMargin(); 0987 if (marginTop > 0) { 0988 if (marginTop > collapsedTopPos) { 0989 collapsedTopPos = marginTop; 0990 } 0991 } else { 0992 if (-marginTop > collapsedTopNeg) { 0993 collapsedTopNeg = -marginTop; 0994 } 0995 } 0996 y += (collapsedTopPos - collapsedTopNeg) - marginTop; 0997 } 0998 static_cast<RenderBox *>(child)->setStaticY(y); 0999 } 1000 } 1001 1002 void RenderBlock::adjustFloatingBlock(const MarginInfo &marginInfo) 1003 { 1004 // The float should be positioned taking into account the bottom margin 1005 // of the previous flow. We add that margin into the height, get the 1006 // float positioned properly, and then subtract the margin out of the 1007 // height again. In the case of self-collapsing blocks, we always just 1008 // use the top margins, since the self-collapsing block collapsed its 1009 // own bottom margin into its top margin. 1010 // 1011 // Note also that the previous flow may collapse its margin into the top of 1012 // our block. If this is the case, then we do not add the margin in to our 1013 // height when computing the position of the float. This condition can be tested 1014 // for by simply calling canCollapseWithTop. See 1015 // https://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/046.html for 1016 // an example of this scenario. 1017 int marginOffset = marginInfo.canCollapseWithTop() ? 0 : marginInfo.margin(); 1018 m_height += marginOffset; 1019 positionNewFloats(); 1020 m_height -= marginOffset; 1021 } 1022 1023 RenderObject *RenderBlock::handleSpecialChild(RenderObject *child, const MarginInfo &marginInfo, CompactInfo &compactInfo, bool &handled) 1024 { 1025 // Handle positioned children first. 1026 RenderObject *next = handlePositionedChild(child, marginInfo, handled); 1027 if (handled) { 1028 return next; 1029 } 1030 1031 // Handle floating children next. 1032 next = handleFloatingChild(child, marginInfo, handled); 1033 if (handled) { 1034 return next; 1035 } 1036 1037 // 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. 1038 next = handleCompactChild(child, compactInfo, marginInfo, handled); 1039 if (handled) { 1040 return next; 1041 } 1042 1043 // Finally, see if we have a run-in element. 1044 return handleRunInChild(child, handled); 1045 } 1046 1047 RenderObject *RenderBlock::handlePositionedChild(RenderObject *child, const MarginInfo &marginInfo, bool &handled) 1048 { 1049 if (child->isPositioned()) { 1050 handled = true; 1051 if (!child->inPosObjectList()) { 1052 child->containingBlock()->insertPositionedObject(child); 1053 } 1054 adjustPositionedBlock(child, marginInfo); 1055 return child->nextSibling(); 1056 } 1057 return nullptr; 1058 } 1059 1060 RenderObject *RenderBlock::handleFloatingChild(RenderObject *child, const MarginInfo &marginInfo, bool &handled) 1061 { 1062 if (child->isFloating()) { 1063 handled = true; 1064 insertFloatingObject(child); 1065 adjustFloatingBlock(marginInfo); 1066 return child->nextSibling(); 1067 } 1068 return nullptr; 1069 } 1070 1071 static inline bool isAnonymousWhitespace(RenderObject *o) 1072 { 1073 if (!o->isAnonymous()) { 1074 return false; 1075 } 1076 RenderObject *fc = o->firstChild(); 1077 return fc && fc == o->lastChild() && fc->isText() && static_cast<RenderText *>(fc)->stringLength() == 1 && 1078 static_cast<RenderText *>(fc)->text()[0].unicode() == ' '; 1079 } 1080 1081 RenderObject *RenderBlock::handleCompactChild(RenderObject *child, CompactInfo &compactInfo, const MarginInfo &marginInfo, bool &handled) 1082 { 1083 if (!child->isCompact()) { 1084 return nullptr; 1085 } 1086 // FIXME: We only deal with one compact at a time. It is unclear what should be 1087 // done if multiple contiguous compacts are encountered. For now we assume that 1088 // compact A followed by another compact B should simply be treated as block A. 1089 if (!compactInfo.compact() && (child->childrenInline() || child->isReplaced())) { 1090 // Get the next non-positioned/non-floating RenderBlock. 1091 RenderObject *next = child->nextSibling(); 1092 RenderObject *curr = next; 1093 while (curr && (curr->isFloatingOrPositioned() || isAnonymousWhitespace(curr) || curr->isAnonymousBlock())) { 1094 curr = curr->nextSibling(); 1095 } 1096 if (curr && curr->isRenderBlock() && !curr->isCompact() && !curr->isRunIn()) { 1097 curr->calcWidth(); // So that horizontal margins are correct. 1098 // Need to compute margins for the child as though it is a block. 1099 child->style()->setDisplay(BLOCK); 1100 child->calcWidth(); 1101 child->style()->setDisplay(COMPACT); 1102 1103 int childMargins = child->marginLeft() + child->marginRight(); 1104 int margin = style()->direction() == LTR ? curr->marginLeft() : curr->marginRight(); 1105 if (margin >= (childMargins + child->maxWidth())) { 1106 // The compact will fit in the margin. 1107 handled = true; 1108 compactInfo.set(child, curr); 1109 child->layoutIfNeeded(); 1110 int off = marginInfo.margin(); 1111 m_height += off + curr->marginTop() < child->marginTop() ? 1112 child->marginTop() - curr->marginTop() - off : 0; 1113 1114 child->setPos(0, 0); // This position will be updated to reflect the compact's 1115 // desired position and the line box for the compact will 1116 // pick that position up. 1117 return next; 1118 } 1119 } 1120 } 1121 child->style()->setDisplay(BLOCK); 1122 child->layoutIfNeeded(); 1123 child->style()->setDisplay(COMPACT); 1124 return nullptr; 1125 } 1126 1127 void RenderBlock::adjustSizeForCompactIfNeeded(RenderObject *child, CompactInfo &compactInfo) 1128 { 1129 // if the compact is bigger than the block it was run into 1130 // then "this" block should take the height of the compact 1131 if (compactInfo.matches(child)) { 1132 // We have a compact child to squeeze in. 1133 RenderObject *compactChild = compactInfo.compact(); 1134 if (compactChild->height() > child->height()) { 1135 m_height += compactChild->height() - child->height(); 1136 } 1137 } 1138 } 1139 1140 void RenderBlock::insertCompactIfNeeded(RenderObject *child, CompactInfo &compactInfo) 1141 { 1142 if (compactInfo.matches(child)) { 1143 // We have a compact child to squeeze in. 1144 RenderObject *compactChild = compactInfo.compact(); 1145 int compactXPos = borderLeft() + paddingLeft() + compactChild->marginLeft(); 1146 if (style()->direction() == RTL) { 1147 compactChild->calcWidth(); // have to do this because of the capped maxwidth 1148 compactXPos = width() - borderRight() - paddingRight() - 1149 compactChild->width() - compactChild->marginRight(); 1150 } 1151 1152 int compactYPos = child->yPos() + child->borderTop() + child->paddingTop() 1153 - compactChild->paddingTop() - compactChild->borderTop(); 1154 int adj = 0; 1155 KHTMLAssert(child->isRenderBlock()); 1156 InlineRunBox *b = static_cast<RenderBlock *>(child)->firstLineBox(); 1157 InlineRunBox *c = static_cast<RenderBlock *>(compactChild)->firstLineBox(); 1158 if (b && c) { 1159 // adjust our vertical position 1160 int vpos = compactChild->getVerticalPosition(true, child); 1161 if (vpos == PositionBottom) { 1162 adj = b->height() > c->height() ? (b->height() + b->yPos() - c->height() - c->yPos()) : 0; 1163 } else if (vpos == PositionTop) { 1164 adj = b->yPos() - c->yPos(); 1165 } else { 1166 adj = vpos; 1167 } 1168 compactYPos += adj; 1169 } 1170 Length newLineHeight(qMax(compactChild->lineHeight(true) + adj, (int)child->lineHeight(true)), khtml::Fixed); 1171 child->style()->setLineHeight(newLineHeight); 1172 child->setNeedsLayout(true, false); 1173 child->layout(); 1174 1175 compactChild->setPos(compactXPos, compactYPos); // Set the x position. 1176 compactInfo.clear(); 1177 } 1178 } 1179 1180 RenderObject *RenderBlock::handleRunInChild(RenderObject *child, bool &handled) 1181 { 1182 if (!child->isRunIn()) { 1183 return nullptr; 1184 } 1185 // See if we have a run-in element with inline children. If the 1186 // children aren't inline, then just treat the run-in as a normal 1187 // block. 1188 if (child->childrenInline() || child->isReplaced()) { 1189 // Get the next non-positioned/non-floating RenderBlock. 1190 RenderObject *curr = child->nextSibling(); 1191 while (curr && (curr->isFloatingOrPositioned() || isAnonymousWhitespace(curr) || curr->isAnonymousBlock())) { 1192 curr = curr->nextSibling(); 1193 } 1194 if (curr && (curr->isRenderBlock() && curr->childrenInline() && !curr->isCompact() && !curr->isRunIn())) { 1195 // The block acts like an inline, so just null out its 1196 // position. 1197 handled = true; 1198 child->setInline(true); 1199 child->setPos(0, 0); 1200 1201 // Remove the child. 1202 RenderObject *next = child->nextSibling(); 1203 removeChildNode(child); 1204 1205 // Now insert the child under |curr|. 1206 curr->insertChildNode(child, curr->firstChild()); 1207 return next; 1208 } 1209 } 1210 return nullptr; 1211 } 1212 1213 int RenderBlock::collapseMargins(RenderObject *child, MarginInfo &marginInfo, int yPosEstimate) 1214 { 1215 Q_UNUSED(yPosEstimate); 1216 // Get our max pos and neg top margins. 1217 int posTop = child->maxTopMargin(true); 1218 int negTop = child->maxTopMargin(false); 1219 1220 // For self-collapsing blocks, collapse our bottom margins into our 1221 // top to get new posTop and negTop values. 1222 if (child->isSelfCollapsingBlock()) { 1223 posTop = qMax(posTop, (int)child->maxBottomMargin(true)); 1224 negTop = qMax(negTop, (int)child->maxBottomMargin(false)); 1225 } 1226 1227 // See if the top margin is quirky. We only care if this child has 1228 // margins that will collapse with us. 1229 bool topQuirk = child->isTopMarginQuirk() /*|| style()->marginTopCollapse() == MDISCARD*/; 1230 1231 if (marginInfo.canCollapseWithTop()) { 1232 // This child is collapsing with the top of the 1233 // block. If it has larger margin values, then we need to update 1234 // our own maximal values. 1235 if (!style()->htmlHacks() || !marginInfo.quirkContainer() || !topQuirk) { 1236 m_maxTopPosMargin = qMax(posTop, (int)m_maxTopPosMargin); 1237 m_maxTopNegMargin = qMax(negTop, (int)m_maxTopNegMargin); 1238 } 1239 1240 // The minute any of the margins involved isn't a quirk, don't 1241 // collapse it away, even if the margin is smaller (www.webreference.com 1242 // has an example of this, a <dt> with 0.8em author-specified inside 1243 // a <dl> inside a <td>. 1244 if (!marginInfo.determinedTopQuirk() && !topQuirk && (posTop - negTop)) { 1245 m_topMarginQuirk = false; 1246 marginInfo.setDeterminedTopQuirk(true); 1247 } 1248 1249 if (!marginInfo.determinedTopQuirk() && topQuirk && marginTop() == 0) 1250 // We have no top margin and our top child has a quirky margin. 1251 // We will pick up this quirky margin and pass it through. 1252 // This deals with the <td><div><p> case. 1253 // Don't do this for a block that split two inlines though. You do 1254 // still apply margins in this case. 1255 { 1256 m_topMarginQuirk = true; 1257 } 1258 } 1259 1260 if (marginInfo.quirkContainer() && marginInfo.atTopOfBlock() && (posTop - negTop)) { 1261 marginInfo.setTopQuirk(topQuirk); 1262 } 1263 1264 int ypos = m_height; 1265 if (child->isSelfCollapsingBlock()) { 1266 // This child has no height. We need to compute our 1267 // position before we collapse the child's margins together, 1268 // so that we can get an accurate position for the zero-height block. 1269 int collapsedTopPos = qMax(marginInfo.posMargin(), (int)child->maxTopMargin(true)); 1270 int collapsedTopNeg = qMax(marginInfo.negMargin(), (int)child->maxTopMargin(false)); 1271 marginInfo.setMargin(collapsedTopPos, collapsedTopNeg); 1272 1273 // Now collapse the child's margins together, which means examining our 1274 // bottom margin values as well. 1275 marginInfo.setPosMarginIfLarger(child->maxBottomMargin(true)); 1276 marginInfo.setNegMarginIfLarger(child->maxBottomMargin(false)); 1277 1278 if (!marginInfo.canCollapseWithTop()) 1279 // We need to make sure that the position of the self-collapsing block 1280 // is correct, since it could have overflowing content 1281 // that needs to be positioned correctly (e.g., a block that 1282 // had a specified height of 0 but that actually had subcontent). 1283 { 1284 ypos = m_height + collapsedTopPos - collapsedTopNeg; 1285 } 1286 } else { 1287 #ifdef APPLE_CHANGES 1288 if (child->style()->marginTopCollapse() == MSEPARATE) { 1289 m_height += marginInfo.margin() + child->marginTop(); 1290 ypos = m_height; 1291 } else 1292 #endif 1293 if (!marginInfo.atTopOfBlock() || 1294 (!marginInfo.canCollapseTopWithChildren() 1295 && (!style()->htmlHacks() || !marginInfo.quirkContainer() || !marginInfo.topQuirk()))) { 1296 // We're collapsing with a previous sibling's margins and not 1297 // with the top of the block. 1298 m_height += qMax(marginInfo.posMargin(), posTop) - qMax(marginInfo.negMargin(), negTop); 1299 ypos = m_height; 1300 } 1301 1302 marginInfo.setPosMargin(child->maxBottomMargin(true)); 1303 marginInfo.setNegMargin(child->maxBottomMargin(false)); 1304 1305 if (marginInfo.margin()) { 1306 marginInfo.setBottomQuirk(child->isBottomMarginQuirk() /*|| style()->marginBottomCollapse() == MDISCARD*/); 1307 } 1308 1309 marginInfo.setSelfCollapsingBlockClearedFloat(false); 1310 } 1311 return ypos; 1312 } 1313 1314 int RenderBlock::clearFloatsIfNeeded(RenderObject *child, MarginInfo &marginInfo, int oldTopPosMargin, int oldTopNegMargin, int yPos) 1315 { 1316 int heightIncrease = getClearDelta(child, yPos); 1317 if (heightIncrease) { 1318 1319 // Increase our height by the amount we had to clear. 1320 bool selfCollapsing = child->isSelfCollapsingBlock(); 1321 if (!selfCollapsing) { 1322 m_height += heightIncrease; 1323 } else { 1324 // For self-collapsing blocks that clear, they may end up collapsing 1325 // into the bottom of the parent block. We simulate this behavior by 1326 // setting our positive margin value to compensate for the clear. 1327 marginInfo.setPosMargin(qMax(0, child->yPos() - m_height)); 1328 marginInfo.setNegMargin(0); 1329 marginInfo.setSelfCollapsingBlockClearedFloat(true); 1330 } 1331 1332 if (marginInfo.canCollapseWithTop()) { 1333 // We can no longer collapse with the top of the block since a clear 1334 // occurred. The empty blocks collapse into the cleared block. 1335 // FIXME: This isn't quite correct. Need clarification for what to do 1336 // if the height the cleared block is offset by is smaller than the 1337 // margins involved. 1338 m_maxTopPosMargin = oldTopPosMargin; 1339 m_maxTopNegMargin = oldTopNegMargin; 1340 marginInfo.setAtTopOfBlock(false); 1341 } 1342 /* 1343 // If our value of clear caused us to be repositioned vertically to be 1344 // underneath a float, we might have to do another layout to take into account 1345 // the extra space we now have available. 1346 if (!selfCollapsing && !child->style()->width().isFixed() && child->usesLineWidth()) 1347 // The child's width is a percentage of the line width. 1348 // When the child shifts to clear an item, its width can 1349 // change (because it has more available line width). 1350 // So go ahead and mark the item as dirty. 1351 child->setChildNeedsLayout(true); 1352 if (!child->flowAroundFloats() && child->hasFloats()) 1353 child->markAllDescendantsWithFloatsForLayout(); 1354 child->layoutIfNeeded(); 1355 */ 1356 return yPos + heightIncrease; 1357 } 1358 return yPos; 1359 } 1360 1361 bool RenderBlock::canClear(RenderObject *child, PageBreakLevel level) 1362 { 1363 KHTMLAssert(child->parent() && child->parent() == this); 1364 KHTMLAssert(canvas()->pagedMode()); 1365 1366 // Positioned elements cannot be moved. Only normal flow and floating. 1367 if (child->isPositioned() || child->isRelPositioned()) { 1368 return false; 1369 } 1370 1371 switch (level) { 1372 case PageBreakNormal: 1373 // check page-break-inside: avoid 1374 if (!style()->pageBreakInside()) 1375 // we cannot, but can our parent? 1376 if (!parent()->canClear(this, level)) { 1377 return false; 1378 } 1379 case PageBreakHarder: 1380 // check page-break-after/before: avoid 1381 if (m_avoidPageBreak) 1382 // we cannot, but can our parent? 1383 if (!parent()->canClear(this, level)) { 1384 return false; 1385 } 1386 case PageBreakForced: 1387 // child is larger than page-height and is forced to break 1388 if (child->height() > canvas()->pageHeight()) { 1389 return false; 1390 } 1391 return true; 1392 } 1393 assert(false); 1394 return false; 1395 } 1396 1397 void RenderBlock::clearPageBreak(RenderObject *child, int pageBottom) 1398 { 1399 KHTMLAssert(child->parent() && child->parent() == this); 1400 KHTMLAssert(canvas()->pagedMode()); 1401 1402 if (child->yPos() >= pageBottom) { 1403 return; 1404 } 1405 1406 int heightIncrease = 0; 1407 1408 heightIncrease = pageBottom - child->yPos(); 1409 1410 // ### should never happen, canClear should have been called to detect it. 1411 if (child->height() > canvas()->pageHeight()) { 1412 // qCDebug(KHTML_LOG) << "### child is too large to clear: " << child->height() << " > " << canvas()->pageHeight(); 1413 return; 1414 } 1415 1416 // The child needs to be lowered. Move the child so that it just clears the break. 1417 child->setPos(child->xPos(), pageBottom); 1418 1419 #ifdef PAGE_DEBUG 1420 qCDebug(KHTML_LOG) << "Cleared block " << heightIncrease << "px"; 1421 #endif 1422 1423 // Increase our height by the amount we had to clear. 1424 m_height += heightIncrease; 1425 1426 // We might have to do another layout to take into account 1427 // the extra space we now have available. 1428 if (!child->style()->width().isFixed() && child->usesLineWidth()) 1429 // The child's width is a percentage of the line width. 1430 // When the child shifts to clear a page-break, its width can 1431 // change (because it has more available line width). 1432 // So go ahead and mark the item as dirty. 1433 { 1434 child->setChildNeedsLayout(true); 1435 } 1436 if (!child->flowAroundFloats() && child->hasFloats()) { 1437 child->markAllDescendantsWithFloatsForLayout(); 1438 } 1439 if (child->containsPageBreak()) { 1440 child->setNeedsLayout(true); 1441 } 1442 child->layoutIfNeeded(); 1443 1444 child->setAfterPageBreak(true); 1445 } 1446 1447 int RenderBlock::estimateVerticalPosition(RenderObject *child, const MarginInfo &marginInfo) 1448 { 1449 // FIXME: We need to eliminate the estimation of vertical position, because 1450 // when it's wrong we sometimes trigger a pathological relayout if there are 1451 // intruding floats. 1452 int yPosEstimate = m_height; 1453 if (!marginInfo.canCollapseWithTop()) { 1454 int childMarginTop = child->selfNeedsLayout() ? child->marginTop() : child->collapsedMarginTop(); 1455 yPosEstimate += qMax(marginInfo.margin(), childMarginTop); 1456 } 1457 yPosEstimate += getClearDelta(child, yPosEstimate); 1458 return yPosEstimate; 1459 } 1460 1461 void RenderBlock::determineHorizontalPosition(RenderObject *child) 1462 { 1463 if (style()->direction() == LTR) { 1464 int xPos = borderLeft() + paddingLeft(); 1465 1466 if (m_layer && scrollsOverflowY() && m_layer->hasReversedScrollbar()) { 1467 xPos += m_layer->verticalScrollbarWidth(); 1468 } 1469 1470 // Add in our left margin. 1471 int chPos = xPos + child->marginLeft(); 1472 1473 // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need 1474 // to shift over as necessary to dodge any floats that might get in the way. 1475 if (child->flowAroundFloats()) { 1476 int leftOff = leftOffset(m_height); 1477 if (style()->textAlign() != KHTML_CENTER && !child->style()->marginLeft().isAuto()) { 1478 if (child->marginLeft() < 0) { 1479 leftOff += child->marginLeft(); 1480 } 1481 chPos = qMax(chPos, leftOff); // Let the float sit in the child's margin if it can fit. 1482 } else if (leftOff != xPos) { 1483 // The object is shifting right. The object might be centered, so we need to 1484 // recalculate our horizontal margins. Note that the containing block content 1485 // width computation will take into account the delta between |leftOff| and |xPos| 1486 // so that we can just pass the content width in directly to the |calcHorizontalMargins| 1487 // function. 1488 static_cast<RenderBox *>(child)->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->yPos())); 1489 chPos = leftOff + child->marginLeft(); 1490 } 1491 } 1492 1493 child->setPos(chPos, child->yPos()); 1494 } else { 1495 int xPos = m_width - borderRight() - paddingRight(); 1496 if (m_layer && scrollsOverflowY() && !m_layer->hasReversedScrollbar()) { 1497 xPos -= m_layer->verticalScrollbarWidth(); 1498 } 1499 int chPos = xPos - (child->width() + child->marginRight()); 1500 if (child->flowAroundFloats()) { 1501 int rightOff = rightOffset(m_height); 1502 if (style()->textAlign() != KHTML_CENTER && !child->style()->marginRight().isAuto()) { 1503 if (child->marginRight() < 0) { 1504 rightOff -= child->marginRight(); 1505 } 1506 chPos = qMin(chPos, rightOff - child->width()); // Let the float sit in the child's margin if it can fit. 1507 } else if (rightOff != xPos) { 1508 // The object is shifting left. The object might be centered, so we need to 1509 // recalculate our horizontal margins. Note that the containing block content 1510 // width computation will take into account the delta between |rightOff| and |xPos| 1511 // so that we can just pass the content width in directly to the |calcHorizontalMargins| 1512 // function. 1513 static_cast<RenderBox *>(child)->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->yPos())); 1514 chPos = rightOff - child->marginRight() - child->width(); 1515 } 1516 } 1517 child->setPos(chPos, child->yPos()); 1518 } 1519 } 1520 1521 void RenderBlock::setCollapsedBottomMargin(const MarginInfo &marginInfo) 1522 { 1523 if (marginInfo.canCollapseWithBottom() && !marginInfo.canCollapseWithTop()) { 1524 // Update our max pos/neg bottom margins, since we collapsed our bottom margins 1525 // with our children. 1526 m_maxBottomPosMargin = qMax((int)m_maxBottomPosMargin, marginInfo.posMargin()); 1527 m_maxBottomNegMargin = qMax((int)m_maxBottomNegMargin, marginInfo.negMargin()); 1528 1529 if (!marginInfo.bottomQuirk()) { 1530 m_bottomMarginQuirk = false; 1531 } 1532 1533 if (marginInfo.bottomQuirk() && marginBottom() == 0) 1534 // We have no bottom margin and our last child has a quirky margin. 1535 // We will pick up this quirky margin and pass it through. 1536 // This deals with the <td><div><p> case. 1537 { 1538 m_bottomMarginQuirk = true; 1539 } 1540 } 1541 } 1542 1543 void RenderBlock::handleBottomOfBlock(int top, int bottom, MarginInfo &marginInfo) 1544 { 1545 // If our last flow was a self-collapsing block that cleared a float, then we don't 1546 // collapse it with the bottom of the block. 1547 if (!marginInfo.selfCollapsingBlockClearedFloat()) { 1548 marginInfo.setAtBottomOfBlock(true); 1549 } 1550 1551 // If we can't collapse with children then go ahead and add in the bottom margin. 1552 if (!marginInfo.canCollapseWithBottom() && !marginInfo.canCollapseWithTop() 1553 && (!style()->htmlHacks() || !marginInfo.quirkContainer() || !marginInfo.bottomQuirk())) { 1554 m_height += marginInfo.margin(); 1555 } 1556 1557 // Now add in our bottom border/padding. 1558 m_height += bottom; 1559 1560 // Negative margins can cause our height to shrink below our minimal height (border/padding). 1561 // If this happens, ensure that the computed height is increased to the minimal height. 1562 m_height = qMax(m_height, top + bottom); 1563 1564 // Always make sure our overflow height is at least our height. 1565 m_overflowHeight = qMax(m_height, m_overflowHeight); 1566 1567 // Update our bottom collapsed margin info. 1568 setCollapsedBottomMargin(marginInfo); 1569 } 1570 1571 void RenderBlock::layoutBlockChildren(bool relayoutChildren) 1572 { 1573 #ifdef DEBUG_LAYOUT 1574 qCDebug(KHTML_LOG) << renderName() << " layoutBlockChildren( " << this << " ), relayoutChildren=" << relayoutChildren; 1575 #endif 1576 1577 int top = borderTop() + paddingTop(); 1578 int bottom = borderBottom() + paddingBottom(); 1579 if (m_layer && scrollsOverflowX() && style()->height().isAuto()) { 1580 bottom += m_layer->horizontalScrollbarHeight(); 1581 } 1582 1583 m_height = m_overflowHeight = top; 1584 1585 // The margin struct caches all our current margin collapsing state. 1586 // The compact struct caches state when we encounter compacts. 1587 MarginInfo marginInfo(this, top, bottom); 1588 CompactInfo compactInfo; 1589 1590 // Fieldsets need to find their legend and position it inside the border of the object. 1591 // The legend then gets skipped during normal layout. 1592 RenderObject *legend = layoutLegend(relayoutChildren); 1593 1594 PageBreakInfo pageBreakInfo(pageTopAfter(0)); 1595 1596 int previousFloatBottom = 0; 1597 RenderObject *child = firstChild(); 1598 while (child != nullptr) { 1599 if (legend == child) { 1600 child = child->nextSibling(); 1601 continue; // Skip the legend, since it has already been positioned up in the fieldset's border. 1602 } 1603 1604 int oldTopPosMargin = m_maxTopPosMargin; 1605 int oldTopNegMargin = m_maxTopNegMargin; 1606 1607 // make sure we relayout children if we need it. 1608 if ((!child->isPositioned() || child->isPosWithStaticDim()) && (relayoutChildren || 1609 (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) || 1610 (child->isRenderBlock() && child->style()->height().isPercent()) || 1611 (child->isBody() && child->style()->htmlHacks()))) { 1612 child->setChildNeedsLayout(true); 1613 } 1614 1615 // Handle the four types of special elements first. These include positioned content, floating content, compacts and 1616 // run-ins. When we encounter these four types of objects, we don't actually lay them out as normal flow blocks. 1617 bool handled = false; 1618 RenderObject *next = handleSpecialChild(child, marginInfo, compactInfo, handled); 1619 if (handled) { 1620 child = next; 1621 continue; 1622 } 1623 1624 // The child is a normal flow object. Compute its vertical margins now. 1625 child->calcVerticalMargins(); 1626 1627 #ifdef APPLE_CHANGES /* margin-*-collapse not merged yet */ 1628 // Do not allow a collapse if the margin top collapse style is set to SEPARATE. 1629 if (child->style()->marginTopCollapse() == MSEPARATE) { 1630 marginInfo.setAtTopOfBlock(false); 1631 marginInfo.clearMargin(); 1632 } 1633 #endif 1634 1635 // Try to guess our correct y position. In most cases this guess will 1636 // be correct. Only if we're wrong (when we compute the real y position) 1637 // will we have to potentially relayout. 1638 int yPosEstimate = estimateVerticalPosition(child, marginInfo); 1639 bool markDescendantsWithFloats = false; 1640 if (yPosEstimate != child->yPos() && !child->flowAroundFloats() && child->hasFloats()) { 1641 markDescendantsWithFloats = true; 1642 } else if (!child->flowAroundFloats() || child->usesLineWidth()) { 1643 // If an element might be affected by the presence of floats, then always mark it for 1644 // layout. 1645 int fb = qMax(previousFloatBottom, floatBottom()); 1646 if (fb > yPosEstimate) { 1647 markDescendantsWithFloats = true; 1648 } 1649 } 1650 1651 if (child->isRenderBlock()) { 1652 if (markDescendantsWithFloats) { 1653 child->markAllDescendantsWithFloatsForLayout(); 1654 } 1655 previousFloatBottom = qMax(previousFloatBottom, child->yPos() + static_cast<RenderBlock *>(child)->floatBottom()); 1656 } 1657 1658 // Go ahead and position the child as though it didn't collapse with the top. 1659 child->setPos(child->xPos(), yPosEstimate); 1660 child->layoutIfNeeded(); 1661 1662 // Now determine the correct ypos based on examination of collapsing margin 1663 // values. 1664 int yBeforeClear = collapseMargins(child, marginInfo, yPosEstimate); 1665 1666 // Now check for clear. 1667 int yAfterClear = clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin, yBeforeClear); 1668 1669 child->setPos(child->xPos(), yAfterClear); 1670 1671 // Now we have a final y position. See if it really does end up being different from our estimate. 1672 if (yAfterClear != yPosEstimate) { 1673 if (child->usesLineWidth()) { 1674 // The child's width depends on the line width. 1675 // When the child shifts to clear an item, its width can 1676 // change (because it has more available line width). 1677 // So go ahead and mark the item as dirty. 1678 child->setChildNeedsLayout(true, false); 1679 } 1680 1681 if (!child->flowAroundFloats() && child->hasFloats()) { 1682 child->markAllDescendantsWithFloatsForLayout(); 1683 } 1684 1685 // Our guess was wrong. Make the child lay itself out again. 1686 child->layoutIfNeeded(); 1687 } 1688 1689 // We are no longer at the top of the block if we encounter a non-empty child. 1690 // This has to be done after checking for clear, so that margins can be reset if a clear occurred. 1691 if (marginInfo.atTopOfBlock() && !child->isSelfCollapsingBlock()) { 1692 marginInfo.setAtTopOfBlock(false); 1693 } 1694 1695 // Now place the child in the correct horizontal position 1696 determineHorizontalPosition(child); 1697 1698 adjustSizeForCompactIfNeeded(child, compactInfo); 1699 // Update our height now that the child has been placed in the correct position. 1700 m_height += child->height(); 1701 1702 #ifdef APPLE_CHANGES 1703 if (child->style()->marginBottomCollapse() == MSEPARATE) { 1704 m_height += child->marginBottom(); 1705 marginInfo.clearMargin(); 1706 } 1707 #endif 1708 1709 // Check for page-breaks 1710 if (canvas()->pagedMode()) { 1711 clearChildOfPageBreaks(child, pageBreakInfo, marginInfo); 1712 } 1713 1714 if (child->hasOverhangingFloats() && !child->flowAroundFloats()) { 1715 // need to add the child's floats to our floating objects list, but not in the case where 1716 // overflow is auto/scroll 1717 addOverHangingFloats(static_cast<RenderBlock *>(child), -child->xPos(), -child->yPos(), true); 1718 } 1719 1720 // See if this child has made our overflow need to grow. 1721 int effX = child->effectiveXPos(); 1722 int effY = child->effectiveYPos(); 1723 m_overflowWidth = qMax(effX + child->effectiveWidth(), m_overflowWidth); 1724 m_overflowLeft = qMin(effX, m_overflowLeft); 1725 m_overflowHeight = qMax(effY + child->effectiveHeight(), m_overflowHeight); 1726 m_overflowTop = qMin(effY, m_overflowTop); 1727 1728 // Insert our compact into the block margin if we have one. 1729 insertCompactIfNeeded(child, compactInfo); 1730 1731 child = child->nextSibling(); 1732 } 1733 1734 // The last child had forced page-break-after 1735 if (pageBreakInfo.forcePageBreak()) { 1736 m_height = pageBreakInfo.pageBottom(); 1737 } 1738 1739 // Now do the handling of the bottom of the block, adding in our bottom border/padding and 1740 // determining the correct collapsed bottom margin information. 1741 handleBottomOfBlock(top, bottom, marginInfo); 1742 1743 setNeedsLayout(false); 1744 } 1745 1746 void RenderBlock::clearChildOfPageBreaks(RenderObject *child, PageBreakInfo &pageBreakInfo, MarginInfo &marginInfo) 1747 { 1748 (void)marginInfo; 1749 int childTop = child->yPos(); 1750 int childBottom = child->yPos() + child->height(); 1751 #ifdef PAGE_DEBUG 1752 qCDebug(KHTML_LOG) << renderName() << " ChildTop: " << childTop << " ChildBottom: " << childBottom; 1753 #endif 1754 1755 bool forcePageBreak = pageBreakInfo.forcePageBreak() || child->style()->pageBreakBefore() == PBALWAYS; 1756 #ifdef PAGE_DEBUG 1757 if (forcePageBreak) { 1758 qCDebug(KHTML_LOG) << renderName() << "Forced break required"; 1759 } 1760 #endif 1761 1762 int xpage = crossesPageBreak(childTop, childBottom); 1763 if (xpage || forcePageBreak) { 1764 if (!forcePageBreak && child->containsPageBreak() && !child->needsPageClear()) { 1765 #ifdef PAGE_DEBUG 1766 qCDebug(KHTML_LOG) << renderName() << " Child contains page-break to page " << xpage; 1767 #endif 1768 // ### Actually this assumes floating children are breaking/clearing 1769 // nicely as well. 1770 setContainsPageBreak(true); 1771 } else { 1772 bool doBreak = true; 1773 // don't break before the first child or when page-break-inside is avoid 1774 if (!forcePageBreak && (!style()->pageBreakInside() || m_avoidPageBreak || child == firstChild())) { 1775 if (parent() && parent()->canClear(this, (m_avoidPageBreak) ? PageBreakHarder : PageBreakNormal)) { 1776 #ifdef PAGE_DEBUG 1777 qCDebug(KHTML_LOG) << renderName() << "Avoid page-break inside"; 1778 #endif 1779 child->setNeedsPageClear(false); 1780 setNeedsPageClear(true); 1781 doBreak = false; 1782 } 1783 #ifdef PAGE_DEBUG 1784 else { 1785 qCDebug(KHTML_LOG) << renderName() << "Ignoring page-break avoid"; 1786 } 1787 #endif 1788 } 1789 if (doBreak) { 1790 #ifdef PAGE_DEBUG 1791 qCDebug(KHTML_LOG) << renderName() << " Clearing child of page-break"; 1792 qCDebug(KHTML_LOG) << renderName() << " child top of page " << xpage; 1793 #endif 1794 clearPageBreak(child, pageBreakInfo.pageBottom()); 1795 child->setNeedsPageClear(false); 1796 setContainsPageBreak(true); 1797 } 1798 } 1799 pageBreakInfo.setPageBottom(pageBreakInfo.pageBottom() + canvas()->pageHeight()); 1800 } else if (child->yPos() >= pageBreakInfo.pageBottom()) { 1801 bool doBreak = true; 1802 #ifdef PAGE_DEBUG 1803 qCDebug(KHTML_LOG) << "Page-break between children"; 1804 #endif 1805 if (!style()->pageBreakInside() || m_avoidPageBreak) { 1806 if (parent() && parent()->canClear(this, (m_avoidPageBreak) ? PageBreakHarder : PageBreakNormal)) { 1807 #ifdef PAGE_DEBUG 1808 qCDebug(KHTML_LOG) << "Avoid page-break inside"; 1809 #endif 1810 child->setNeedsPageClear(false); 1811 setNeedsPageClear(true); 1812 doBreak = false; 1813 } 1814 #ifdef PAGE_DEBUG 1815 else { 1816 qCDebug(KHTML_LOG) << "Ignoring page-break avoid"; 1817 } 1818 #endif 1819 } 1820 if (doBreak) { 1821 // Break between children 1822 setContainsPageBreak(true); 1823 // ### Should collapse top-margin with page-margin 1824 } 1825 pageBreakInfo.setPageBottom(pageBreakInfo.pageBottom() + canvas()->pageHeight()); 1826 } 1827 1828 // Do we need a forced page-break before next child? 1829 pageBreakInfo.setForcePageBreak(false); 1830 if (child->style()->pageBreakAfter() == PBALWAYS) { 1831 pageBreakInfo.setForcePageBreak(true); 1832 } 1833 } 1834 1835 void RenderBlock::layoutPositionedObjects(bool relayoutChildren) 1836 { 1837 if (m_positionedObjects) { 1838 for (int i = 0; i < m_positionedObjects->size(); i++) { // size() can grow during loop 1839 RenderObject *const r = m_positionedObjects->at(i); 1840 if (r->markedForRepaint()) { 1841 r->repaintDuringLayout(); 1842 r->setMarkedForRepaint(false); 1843 } 1844 if (relayoutChildren || (r->isPosWithStaticDim() && r->parent() != this && r->parent()->isBlockFlow())) { 1845 r->setChildNeedsLayout(true, false); 1846 r->dirtyFormattingContext(false); 1847 } 1848 r->layoutIfNeeded(); 1849 } 1850 } 1851 } 1852 1853 void RenderBlock::paint(PaintInfo &pI, int _tx, int _ty) 1854 { 1855 _tx += m_x; 1856 _ty += m_y; 1857 1858 // check if we need to do anything at all... 1859 if (!isRoot() && !isInlineFlow() && !isRelPositioned() && !isPositioned()) { 1860 int h = m_overflowHeight; 1861 int yPos = _ty; 1862 if (m_floatingObjects && floatBottom() > h) { 1863 h = floatBottom(); 1864 } 1865 1866 yPos += overflowTop(); 1867 1868 int os = maximalOutlineSize(pI.phase); 1869 if ((yPos > pI.r.bottom() + os) || (_ty + h <= pI.r.y() - os)) { 1870 return; 1871 } 1872 } 1873 1874 paintObject(pI, _tx, _ty); 1875 } 1876 1877 void RenderBlock::paintObject(PaintInfo &pI, int _tx, int _ty, bool shouldPaintOutline) 1878 { 1879 #ifdef DEBUG_LAYOUT 1880 qCDebug(KHTML_LOG) << renderName() << "(RenderBlock) " << this << " ::paintObject() w/h = (" << width() << "/" << height() << ")"; 1881 #endif 1882 1883 // If we're a repositioned run-in, don't paint background/borders. 1884 bool inlineFlow = isInlineFlow(); 1885 1886 // 1. paint background, borders etc 1887 if (!inlineFlow && 1888 (pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground) && 1889 shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE) { 1890 paintBoxDecorations(pI, _tx, _ty); 1891 } 1892 1893 if (pI.phase == PaintActionElementBackground) { 1894 return; 1895 } 1896 if (pI.phase == PaintActionChildBackgrounds) { 1897 pI.phase = PaintActionChildBackground; 1898 } 1899 1900 // 2. paint contents 1901 int scrolledX = _tx; 1902 int scrolledY = _ty; 1903 if (hasOverflowClip() && m_layer) { 1904 m_layer->subtractScrollOffset(scrolledX, scrolledY); 1905 } 1906 1907 if (childrenInline()) { 1908 paintLines(pI, scrolledX, scrolledY); 1909 } else { 1910 for (RenderObject *child = firstChild(); child; child = child->nextSibling()) 1911 if (!child->layer() && !child->isFloating()) { 1912 child->paint(pI, scrolledX, scrolledY); 1913 } 1914 } 1915 1916 // 3. paint floats. 1917 if (!inlineFlow && (pI.phase == PaintActionFloat || pI.phase == PaintActionSelection)) { 1918 paintFloats(pI, scrolledX, scrolledY, pI.phase == PaintActionSelection); 1919 } 1920 1921 // 4. paint outline. 1922 if (shouldPaintOutline && !inlineFlow && pI.phase == PaintActionOutline && 1923 style()->outlineWidth() && style()->visibility() == VISIBLE) { 1924 paintOutline(pI.p, _tx, _ty, width(), height(), style()); 1925 } 1926 1927 // 5. paint caret. 1928 /* 1929 If the caret's node's render object's containing block is this block, 1930 and the paint action is PaintActionForeground, 1931 then paint the caret. 1932 */ 1933 if ((!canvas()->hasSelection() && pI.phase == PaintActionForeground) 1934 || pI.phase == PaintActionSelection) { 1935 KHTMLPart *part = document()->part(); 1936 const Selection &s = part->caret(); 1937 NodeImpl *baseNode = s.extent().node(); 1938 RenderObject *renderer = baseNode ? baseNode->renderer() : nullptr; 1939 if (renderer && renderer->containingBlock() == this && (part->isCaretMode() || baseNode->isContentEditable())) { 1940 part->paintCaret(pI.p, pI.r); 1941 part->paintDragCaret(pI.p, pI.r); 1942 } 1943 } 1944 1945 #ifdef BOX_DEBUG 1946 if (style() && style()->visibility() == VISIBLE) { 1947 if (isAnonymous()) { 1948 outlineBox(pI.p, _tx, _ty, "green"); 1949 } 1950 if (isFloating()) { 1951 outlineBox(pI.p, _tx, _ty, "yellow"); 1952 } else { 1953 outlineBox(pI.p, _tx, _ty); 1954 } 1955 } 1956 #endif 1957 } 1958 1959 QRegion RenderBlock::visibleFloatingRegion(int x, int y) const 1960 { 1961 if (!m_floatingObjects) { 1962 return QRegion(); 1963 } 1964 FloatingObject *fo; 1965 QRegion r; 1966 QListIterator<FloatingObject *> it(*m_floatingObjects); 1967 while (it.hasNext()) { 1968 fo = it.next(); 1969 if (!fo->noPaint && !fo->node->layer() && fo->node->style()->visibility() == VISIBLE) { 1970 const RenderStyle *s = fo->node->style(); 1971 int ow = s->outlineSize(); 1972 if (s->backgroundImage() || s->backgroundColor().isValid() || s->hasBorder() || fo->node->isReplaced() || ow) { 1973 r += QRect(x - ow + fo->left + fo->node->marginLeft(), 1974 y - ow + fo->startY + fo->node->marginTop(), 1975 fo->width + ow * 2 - fo->node->marginLeft() - fo->node->marginRight(), 1976 fo->endY - fo->startY + ow * 2 - fo->node->marginTop() - fo->node->marginBottom()); 1977 } else { 1978 r += fo->node->visibleFlowRegion(x + fo->left + fo->node->marginLeft(), y + fo->startY + fo->node->marginTop()); 1979 } 1980 } 1981 } 1982 return r; 1983 } 1984 1985 void RenderBlock::paintFloats(PaintInfo &pI, int _tx, int _ty, bool paintSelection) 1986 { 1987 if (!m_floatingObjects) { 1988 return; 1989 } 1990 1991 FloatingObject *r; 1992 QListIterator<FloatingObject *> it(*m_floatingObjects); 1993 while (it.hasNext()) { 1994 r = it.next(); 1995 // Only paint the object if our noPaint flag isn't set. 1996 if (r->node->isFloating() && !r->noPaint && !r->node->layer()) { 1997 PaintAction oldphase = pI.phase; 1998 if (paintSelection) { 1999 pI.phase = PaintActionSelection; 2000 r->node->paint(pI, _tx + r->left - r->node->xPos() + r->node->marginLeft(), 2001 _ty + r->startY - r->node->yPos() + r->node->marginTop()); 2002 } else { 2003 pI.phase = PaintActionElementBackground; 2004 r->node->paint(pI, 2005 _tx + r->left - r->node->xPos() + r->node->marginLeft(), 2006 _ty + r->startY - r->node->yPos() + r->node->marginTop()); 2007 pI.phase = PaintActionChildBackgrounds; 2008 r->node->paint(pI, 2009 _tx + r->left - r->node->xPos() + r->node->marginLeft(), 2010 _ty + r->startY - r->node->yPos() + r->node->marginTop()); 2011 pI.phase = PaintActionFloat; 2012 r->node->paint(pI, 2013 _tx + r->left - r->node->xPos() + r->node->marginLeft(), 2014 _ty + r->startY - r->node->yPos() + r->node->marginTop()); 2015 pI.phase = PaintActionForeground; 2016 r->node->paint(pI, 2017 _tx + r->left - r->node->xPos() + r->node->marginLeft(), 2018 _ty + r->startY - r->node->yPos() + r->node->marginTop()); 2019 pI.phase = PaintActionOutline; 2020 r->node->paint(pI, 2021 _tx + r->left - r->node->xPos() + r->node->marginLeft(), 2022 _ty + r->startY - r->node->yPos() + r->node->marginTop()); 2023 } 2024 pI.phase = oldphase; 2025 } 2026 } 2027 } 2028 2029 void RenderBlock::insertPositionedObject(RenderObject *o) 2030 { 2031 // Create the list of special objects if we don't aleady have one 2032 if (!m_positionedObjects) { 2033 m_positionedObjects = new QList<RenderObject *>; 2034 } 2035 2036 // Create the special object entry & append it to the list 2037 m_positionedObjects->append(o); 2038 o->setInPosObjectList(); 2039 } 2040 2041 void RenderBlock::removePositionedObject(RenderObject *o) 2042 { 2043 if (m_positionedObjects) { 2044 m_positionedObjects->removeAll(o); 2045 if (m_positionedObjects->isEmpty()) { 2046 delete m_positionedObjects; 2047 m_positionedObjects = nullptr; 2048 } 2049 } 2050 o->setInPosObjectList(false); 2051 } 2052 2053 void RenderBlock::insertFloatingObject(RenderObject *o) 2054 { 2055 // Create the list of special objects if we don't aleady have one 2056 if (!m_floatingObjects) { 2057 m_floatingObjects = new QList<FloatingObject *>; 2058 } else { 2059 // Don't insert the object again if it's already in the list 2060 QListIterator<FloatingObject *> it(*m_floatingObjects); 2061 FloatingObject *f; 2062 while (it.hasNext()) { 2063 f = it.next(); 2064 if (f->node == o) { 2065 return; 2066 } 2067 } 2068 } 2069 2070 // Create the special object entry & append it to the list 2071 2072 FloatingObject *newObj; 2073 if (o->isFloating()) { 2074 // floating object 2075 o->layoutIfNeeded(); 2076 2077 if (o->style()->floating() & FLEFT) { 2078 newObj = new FloatingObject(FloatingObject::FloatLeft); 2079 } else { 2080 newObj = new FloatingObject(FloatingObject::FloatRight); 2081 } 2082 2083 newObj->startY = -500000; 2084 newObj->endY = -500000; 2085 newObj->width = o->width() + o->marginLeft() + o->marginRight(); 2086 } else { 2087 // We should never get here, as insertFloatingObject() should only ever be called with floating 2088 // objects. 2089 KHTMLAssert(false); 2090 newObj = nullptr; // keep gcc's uninitialized variable warnings happy 2091 } 2092 2093 newObj->node = o; 2094 2095 m_floatingObjects->append(newObj); 2096 } 2097 2098 void RenderBlock::removeFloatingObject(RenderObject *o) 2099 { 2100 if (m_floatingObjects) { 2101 QMutableListIterator<FloatingObject *> it(*m_floatingObjects); 2102 while (it.hasNext()) { 2103 if (it.next()->node == o) { 2104 delete it.peekPrevious(); 2105 it.remove(); 2106 } 2107 } 2108 } 2109 } 2110 2111 void RenderBlock::positionNewFloats() 2112 { 2113 if (!m_floatingObjects) { 2114 return; 2115 } 2116 QListIterator<FloatingObject *> it(*m_floatingObjects); 2117 it.toBack(); 2118 if (!it.hasPrevious() || it.previous()->startY != -500000) { 2119 return; 2120 } 2121 FloatingObject *lastFloat; 2122 while (1) { 2123 lastFloat = it.hasPrevious() ? it.previous() : nullptr; 2124 if (!lastFloat || lastFloat->startY != -500000) { 2125 if (lastFloat) { 2126 it.next(); 2127 } 2128 break; 2129 } 2130 } 2131 int y = m_height; 2132 2133 // the float can not start above the y position of the last positioned float. 2134 if (lastFloat && lastFloat->startY > y) { 2135 y = lastFloat->startY; 2136 } 2137 2138 KHTMLAssert(it.hasNext()); 2139 FloatingObject *f = it.next(); 2140 2141 while (f) { 2142 //skip elements copied from elsewhere and positioned elements 2143 if (f->node->containingBlock() != this) { 2144 f = it.hasNext() ? it.next() : nullptr; 2145 continue; 2146 } 2147 2148 RenderObject *o = f->node; 2149 int _height = o->height() + o->marginTop() + o->marginBottom(); 2150 2151 // floats avoid page-breaks 2152 if (canvas()->pagedMode()) { 2153 int top = y; 2154 int bottom = y + o->height(); 2155 if (crossesPageBreak(top, bottom) && o->height() < canvas()->pageHeight()) { 2156 int newY = pageTopAfter(top); 2157 #ifdef PAGE_DEBUG 2158 qCDebug(KHTML_LOG) << renderName() << " clearing float " << newY - y << "px"; 2159 #endif 2160 y = newY; 2161 } 2162 } 2163 2164 int ro = rightOffset(); // Constant part of right offset. 2165 int lo = leftOffset(); // Constant part of left offset. 2166 int fwidth = f->width; // The width we look for. 2167 //qCDebug(KHTML_LOG) << " Object width: " << fwidth << " available width: " << ro - lo; 2168 2169 // in quirk mode, floated auto-width tables try to fit within remaining linewidth 2170 bool ftQuirk = o->isTable() && style()->htmlHacks() && o->style()->width().isAuto(); 2171 if (ftQuirk) { 2172 fwidth = qMin(o->minWidth() + o->marginLeft() + o->marginRight(), fwidth); 2173 } 2174 2175 if (ro - lo < fwidth) { 2176 fwidth = ro - lo; // Never look for more than what will be available. 2177 } 2178 2179 if (o->style()->clear() & CLEFT) { 2180 y = qMax(leftBottom(), y); 2181 } 2182 if (o->style()->clear() & CRIGHT) { 2183 y = qMax(rightBottom(), y); 2184 } 2185 2186 bool canClearLine; 2187 if (o->style()->floating() & FLEFT) { 2188 int heightRemainingLeft = 1; 2189 int heightRemainingRight = 1; 2190 int fx = leftRelOffset(y, lo, false, &heightRemainingLeft, &canClearLine); 2191 if (canClearLine) { 2192 while (rightRelOffset(y, ro, false, &heightRemainingRight) - fx < fwidth) { 2193 y += qMin(heightRemainingLeft, heightRemainingRight); 2194 fx = leftRelOffset(y, lo, false, &heightRemainingLeft); 2195 } 2196 } 2197 if (ftQuirk && (rightRelOffset(y, ro, false) - fx < f->width)) { 2198 o->setPos(o->xPos(), y + o->marginTop()); 2199 o->setChildNeedsLayout(true, false); 2200 o->layoutIfNeeded(); 2201 _height = o->height() + o->marginTop() + o->marginBottom(); 2202 f->width = o->width() + o->marginLeft() + o->marginRight(); 2203 } 2204 f->left = fx; 2205 //qCDebug(KHTML_LOG) << "positioning left aligned float at (" << fx + o->marginLeft() << "/" << y + o->marginTop() << ") fx=" << fx; 2206 o->setPos(fx + o->marginLeft(), y + o->marginTop()); 2207 } else { 2208 int heightRemainingLeft = 1; 2209 int heightRemainingRight = 1; 2210 int fx = rightRelOffset(y, ro, false, &heightRemainingRight, &canClearLine); 2211 if (canClearLine) { 2212 while (fx - leftRelOffset(y, lo, false, &heightRemainingLeft) < fwidth) { 2213 y += qMin(heightRemainingLeft, heightRemainingRight); 2214 fx = rightRelOffset(y, ro, false, &heightRemainingRight); 2215 } 2216 } 2217 if (ftQuirk && (fx - leftRelOffset(y, lo, false) < f->width)) { 2218 o->setPos(o->xPos(), y + o->marginTop()); 2219 o->setChildNeedsLayout(true, false); 2220 o->layoutIfNeeded(); 2221 _height = o->height() + o->marginTop() + o->marginBottom(); 2222 f->width = o->width() + o->marginLeft() + o->marginRight(); 2223 } 2224 f->left = fx - f->width; 2225 //qCDebug(KHTML_LOG) << "positioning right aligned float at (" << fx - o->marginRight() - o->width() << "/" << y + o->marginTop() << ")"; 2226 o->setPos(fx - o->marginRight() - o->width(), y + o->marginTop()); 2227 } 2228 2229 if (m_layer && hasOverflowClip()) { 2230 if (o->xPos() + o->width() > m_overflowWidth) { 2231 m_overflowWidth = o->xPos() + o->width(); 2232 } else if (o->xPos() < m_overflowLeft) { 2233 m_overflowLeft = o->xPos(); 2234 } 2235 } 2236 2237 f->startY = y; 2238 f->endY = f->startY + _height; 2239 2240 //qCDebug(KHTML_LOG) << "floatingObject x/y= (" << f->left << "/" << f->startY << "-" << f->width << "/" << f->endY - f->startY << ")"; 2241 2242 f = it.hasNext() ? it.next() : nullptr; 2243 } 2244 } 2245 2246 void RenderBlock::newLine() 2247 { 2248 positionNewFloats(); 2249 // set y position 2250 int newY = 0; 2251 switch (m_clearStatus) { 2252 case CLEFT: 2253 newY = leftBottom(); 2254 break; 2255 case CRIGHT: 2256 newY = rightBottom(); 2257 break; 2258 case CBOTH: 2259 newY = floatBottom(); 2260 default: 2261 break; 2262 } 2263 if (m_height < newY) { 2264 // qCDebug(KHTML_LOG) << "adjusting y position"; 2265 m_height = newY; 2266 } 2267 m_clearStatus = CNONE; 2268 } 2269 2270 int 2271 RenderBlock::leftOffset() const 2272 { 2273 int left = borderLeft() + paddingLeft(); 2274 if (m_layer && scrollsOverflowY() && m_layer->hasReversedScrollbar()) { 2275 left += m_layer->verticalScrollbarWidth(); 2276 } 2277 return left; 2278 } 2279 2280 int 2281 RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, int *heightRemaining, bool *canClearLine) const 2282 { 2283 int left = fixedOffset; 2284 if (canClearLine) { 2285 *canClearLine = true; 2286 } 2287 2288 if (m_floatingObjects) { 2289 if (heightRemaining) { 2290 *heightRemaining = 1; 2291 } 2292 FloatingObject *r; 2293 QListIterator<FloatingObject *> it(*m_floatingObjects); 2294 while (it.hasNext()) { 2295 r = it.next(); 2296 //qCDebug(KHTML_LOG) <<(void *)this << " left: sy, ey, x, w " << r->startY << "," << r->endY << "," << r->left << "," << r->width << " "; 2297 if (r->startY <= y && r->endY > y && 2298 r->type == FloatingObject::FloatLeft && 2299 r->left + r->width > left) { 2300 left = r->left + r->width; 2301 if (heightRemaining) { 2302 *heightRemaining = r->endY - y; 2303 } 2304 if (canClearLine) { 2305 *canClearLine = (r->node->style()->floating() != FLEFT_ALIGN); 2306 } 2307 } 2308 } 2309 } 2310 2311 if (applyTextIndent && m_firstLine && style()->direction() == LTR) { 2312 int cw = 0; 2313 if (style()->textIndent().isPercent()) { 2314 cw = containingBlock()->contentWidth(); 2315 } 2316 left += style()->textIndent().minWidth(cw); 2317 } 2318 2319 //qCDebug(KHTML_LOG) << "leftOffset(" << y << ") = " << left; 2320 return left; 2321 } 2322 2323 int 2324 RenderBlock::rightOffset() const 2325 { 2326 int right = m_width - borderRight() - paddingRight(); 2327 if (m_layer && scrollsOverflowY() && !m_layer->hasReversedScrollbar()) { 2328 right -= m_layer->verticalScrollbarWidth(); 2329 } 2330 return right; 2331 } 2332 2333 int 2334 RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, int *heightRemaining, bool *canClearLine) const 2335 { 2336 int right = fixedOffset; 2337 if (canClearLine) { 2338 *canClearLine = true; 2339 } 2340 2341 if (m_floatingObjects) { 2342 if (heightRemaining) { 2343 *heightRemaining = 1; 2344 } 2345 FloatingObject *r; 2346 QListIterator<FloatingObject *> it(*m_floatingObjects); 2347 while (it.hasNext()) { 2348 r = it.next(); 2349 //qCDebug(KHTML_LOG) << "right: sy, ey, x, w " << r->startY << "," << r->endY << "," << r->left << "," << r->width << " "; 2350 if (r->startY <= y && r->endY > y && 2351 r->type == FloatingObject::FloatRight && 2352 r->left < right) { 2353 right = r->left; 2354 if (heightRemaining) { 2355 *heightRemaining = r->endY - y; 2356 } 2357 if (canClearLine) { 2358 *canClearLine = (r->node->style()->floating() != FRIGHT_ALIGN); 2359 } 2360 } 2361 } 2362 } 2363 2364 if (applyTextIndent && m_firstLine && style()->direction() == RTL) { 2365 int cw = 0; 2366 if (style()->textIndent().isPercent()) { 2367 cw = containingBlock()->contentWidth(); 2368 } 2369 right -= style()->textIndent().minWidth(cw); 2370 } 2371 2372 //qCDebug(KHTML_LOG) << "rightOffset(" << y << ") = " << right; 2373 return right; 2374 } 2375 2376 unsigned short 2377 RenderBlock::lineWidth(int y, bool *canClearLine) const 2378 { 2379 //qCDebug(KHTML_LOG) << "lineWidth(" << y << ")=" << rightOffset(y) - leftOffset(y); 2380 int result; 2381 if (canClearLine) { 2382 bool rightCanClearLine; 2383 bool leftCanClearLine; 2384 result = rightOffset(y, &rightCanClearLine) - leftOffset(y, &leftCanClearLine); 2385 *canClearLine = rightCanClearLine && leftCanClearLine; 2386 } else { 2387 result = rightOffset(y) - leftOffset(y); 2388 } 2389 return (result < 0) ? 0 : result; 2390 } 2391 2392 int 2393 RenderBlock::nearestFloatBottom(int height) const 2394 { 2395 if (!m_floatingObjects) { 2396 return 0; 2397 } 2398 2399 int bottom = INT_MAX; 2400 FloatingObject *r; 2401 QListIterator<FloatingObject *> it(*m_floatingObjects); 2402 while (it.hasNext()) { 2403 r = it.next(); 2404 if (r->endY > height) { 2405 bottom = qMin(r->endY, bottom); 2406 } 2407 } 2408 2409 return bottom == INT_MAX ? 0 : bottom; 2410 } 2411 2412 int RenderBlock::floatBottom() const 2413 { 2414 if (!m_floatingObjects) { 2415 return 0; 2416 } 2417 int bottom = 0; 2418 FloatingObject *r; 2419 QListIterator<FloatingObject *> it(*m_floatingObjects); 2420 while (it.hasNext()) { 2421 r = it.next(); 2422 if (r->endY > bottom) { 2423 bottom = r->endY; 2424 } 2425 } 2426 return bottom; 2427 } 2428 2429 int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) const 2430 { 2431 int bottom = RenderFlow::lowestPosition(includeOverflowInterior, includeSelf); 2432 if (!includeOverflowInterior && hasOverflowClip()) { 2433 return bottom; 2434 } 2435 if (includeSelf && m_overflowHeight > bottom) { 2436 bottom = m_overflowHeight; 2437 } 2438 2439 if (m_floatingObjects) { 2440 FloatingObject *r; 2441 QListIterator<FloatingObject *> it(*m_floatingObjects); 2442 while (it.hasNext()) { 2443 r = it.next(); 2444 if (!r->noPaint) { 2445 int lp = r->startY + r->node->marginTop() + r->node->lowestPosition(false); 2446 bottom = qMax(bottom, lp); 2447 } 2448 } 2449 } 2450 bottom = qMax(bottom, lowestAbsolutePosition()); 2451 2452 if (!includeSelf && lastLineBox()) { 2453 int lp = lastLineBox()->yPos() + lastLineBox()->height(); 2454 bottom = qMax(bottom, lp); 2455 } 2456 2457 return bottom; 2458 } 2459 2460 int RenderBlock::lowestAbsolutePosition() const 2461 { 2462 if (!m_positionedObjects) { 2463 return 0; 2464 } 2465 2466 // Fixed positioned objects do not scroll and thus should not constitute 2467 // part of the lowest position. 2468 int bottom = 0; 2469 RenderObject *r; 2470 QListIterator<RenderObject *> it(*m_positionedObjects); 2471 while (it.hasNext()) { 2472 r = it.next(); 2473 if (r->style()->position() == PFIXED) { 2474 continue; 2475 } 2476 int lp = r->yPos() + r->lowestPosition(false); 2477 bottom = qMax(bottom, lp); 2478 } 2479 return bottom; 2480 } 2481 2482 int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const 2483 { 2484 int right = RenderFlow::rightmostPosition(includeOverflowInterior, includeSelf); 2485 if (!includeOverflowInterior && hasOverflowClip()) { 2486 return right; 2487 } 2488 if (includeSelf && m_overflowWidth > right) { 2489 right = m_overflowWidth; 2490 } 2491 2492 if (m_floatingObjects) { 2493 FloatingObject *r; 2494 QListIterator<FloatingObject *> it(*m_floatingObjects); 2495 while (it.hasNext()) { 2496 r = it.next(); 2497 if (!r->noPaint) { 2498 int rp = r->left + r->node->marginLeft() + r->node->rightmostPosition(false); 2499 right = qMax(right, rp); 2500 } 2501 } 2502 } 2503 right = qMax(right, rightmostAbsolutePosition()); 2504 2505 if (!includeSelf && firstLineBox()) { 2506 for (InlineRunBox *currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) { 2507 int rp = currBox->xPos() + currBox->width(); 2508 right = qMax(right, rp); 2509 } 2510 } 2511 2512 return right; 2513 } 2514 2515 int RenderBlock::rightmostAbsolutePosition() const 2516 { 2517 if (!m_positionedObjects) { 2518 return 0; 2519 } 2520 int right = 0; 2521 RenderObject *r; 2522 QListIterator<RenderObject *> it(*m_positionedObjects); 2523 while (it.hasNext()) { 2524 r = it.next(); 2525 if (r->style()->position() == PFIXED) { 2526 continue; 2527 } 2528 int rp = r->xPos() + r->rightmostPosition(false); 2529 right = qMax(right, rp); 2530 } 2531 return right; 2532 } 2533 2534 int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const 2535 { 2536 int left = RenderFlow::leftmostPosition(includeOverflowInterior, includeSelf); 2537 if (!includeOverflowInterior && hasOverflowClip()) { 2538 return left; 2539 } 2540 2541 if (includeSelf && m_overflowLeft < left) { 2542 left = m_overflowLeft; 2543 } 2544 2545 if (m_floatingObjects) { 2546 FloatingObject *r; 2547 QListIterator<FloatingObject *> it(*m_floatingObjects); 2548 while (it.hasNext()) { 2549 r = it.next(); 2550 if (!r->noPaint) { 2551 int lp = r->left + r->node->marginLeft() + r->node->leftmostPosition(false); 2552 left = qMin(left, lp); 2553 } 2554 } 2555 } 2556 left = qMin(left, leftmostAbsolutePosition()); 2557 2558 if (!includeSelf && firstLineBox()) { 2559 for (InlineRunBox *currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) { 2560 left = qMin(left, (int)currBox->xPos()); 2561 } 2562 } 2563 2564 return left; 2565 } 2566 2567 int RenderBlock::leftmostAbsolutePosition() const 2568 { 2569 if (!m_positionedObjects) { 2570 return 0; 2571 } 2572 int left = 0; 2573 RenderObject *r; 2574 QListIterator<RenderObject *> it(*m_positionedObjects); 2575 while (it.hasNext()) { 2576 r = it.next(); 2577 if (r->style()->position() == PFIXED) { 2578 continue; 2579 } 2580 int lp = r->xPos() + r->leftmostPosition(false); 2581 left = qMin(left, lp); 2582 } 2583 return left; 2584 } 2585 2586 int RenderBlock::highestPosition(bool includeOverflowInterior, bool includeSelf) const 2587 { 2588 int top = RenderFlow::highestPosition(includeOverflowInterior, includeSelf); 2589 if (!includeOverflowInterior && hasOverflowClip()) { 2590 return top; 2591 } 2592 2593 if (includeSelf && m_overflowTop < top) { 2594 top = m_overflowTop; 2595 } 2596 2597 if (m_floatingObjects) { 2598 FloatingObject *r; 2599 QListIterator<FloatingObject *> it(*m_floatingObjects); 2600 while (it.hasNext()) { 2601 r = it.next(); 2602 if (!r->noPaint) { 2603 int hp = r->startY + r->node->marginTop() + r->node->highestPosition(false); 2604 top = qMin(top, hp); 2605 } 2606 } 2607 } 2608 top = qMin(top, highestAbsolutePosition()); 2609 2610 if (!includeSelf && firstLineBox()) { 2611 top = qMin(top, (int)firstLineBox()->yPos()); 2612 } 2613 2614 return top; 2615 } 2616 2617 int RenderBlock::highestAbsolutePosition() const 2618 { 2619 if (!m_positionedObjects) { 2620 return 0; 2621 } 2622 int top = 0; 2623 RenderObject *r; 2624 QListIterator<RenderObject *> it(*m_positionedObjects); 2625 while (it.hasNext()) { 2626 r = it.next(); 2627 if (r->style()->position() == PFIXED) { 2628 continue; 2629 } 2630 int hp = r->yPos() + r->highestPosition(false); 2631 hp = qMin(top, hp); 2632 } 2633 return top; 2634 } 2635 2636 int 2637 RenderBlock::leftBottom() 2638 { 2639 if (!m_floatingObjects) { 2640 return 0; 2641 } 2642 int bottom = 0; 2643 FloatingObject *r; 2644 QListIterator<FloatingObject *> it(*m_floatingObjects); 2645 while (it.hasNext()) { 2646 r = it.next(); 2647 if (r->endY > bottom && r->type == FloatingObject::FloatLeft) { 2648 bottom = r->endY; 2649 } 2650 } 2651 return bottom; 2652 } 2653 2654 int 2655 RenderBlock::rightBottom() 2656 { 2657 if (!m_floatingObjects) { 2658 return 0; 2659 } 2660 int bottom = 0; 2661 FloatingObject *r; 2662 QListIterator<FloatingObject *> it(*m_floatingObjects); 2663 while (it.hasNext()) { 2664 r = it.next(); 2665 if (r->endY > bottom && r->type == FloatingObject::FloatRight) { 2666 bottom = r->endY; 2667 } 2668 } 2669 return bottom; 2670 } 2671 2672 void 2673 RenderBlock::clearFloats() 2674 { 2675 if (m_floatingObjects) { 2676 QListIterator<FloatingObject *> it(*m_floatingObjects); 2677 while (it.hasNext()) { 2678 delete it.next(); 2679 } 2680 m_floatingObjects->clear(); 2681 } 2682 2683 // we are done if the element defines a new block formatting context 2684 if (flowAroundFloats() || isRoot() || isCanvas() || isFloatingOrPositioned() || isTableCell()) { 2685 return; 2686 } 2687 2688 RenderObject *prev = previousSibling(); 2689 2690 // find the element to copy the floats from 2691 // pass non-flows 2692 // pass fAF's 2693 bool parentHasFloats = false; 2694 while (prev) { 2695 if (!prev->isRenderBlock() || prev->isFloatingOrPositioned() || prev->flowAroundFloats()) { 2696 if (prev->isFloating() && parent()->isRenderBlock()) { 2697 parentHasFloats = true; 2698 } 2699 prev = prev->previousSibling(); 2700 } else { 2701 break; 2702 } 2703 } 2704 2705 int offset = m_y; 2706 if (parentHasFloats) { 2707 addOverHangingFloats(static_cast<RenderBlock *>(parent()), 2708 parent()->borderLeft() + parent()->paddingLeft(), offset, false); 2709 } 2710 2711 int xoffset = 0; 2712 if (prev) { 2713 if (prev->isTableCell()) { 2714 return; 2715 } 2716 offset -= prev->yPos(); 2717 } else { 2718 prev = parent(); 2719 if (!prev) { 2720 return; 2721 } 2722 xoffset += prev->borderLeft() + prev->paddingLeft(); 2723 } 2724 //qCDebug(KHTML_LOG) << "RenderBlock::clearFloats found previous "<< (void *)this << " prev=" << (void *)prev; 2725 2726 // add overhanging special objects from the previous RenderBlock 2727 if (!prev->isRenderBlock()) { 2728 return; 2729 } 2730 RenderBlock *flow = static_cast<RenderBlock *>(prev); 2731 if (!flow->m_floatingObjects) { 2732 return; 2733 } 2734 if (flow->floatBottom() > offset) { 2735 addOverHangingFloats(flow, xoffset, offset, false); 2736 } 2737 } 2738 2739 void RenderBlock::addOverHangingFloats(RenderBlock *flow, int xoff, int offset, bool child) 2740 { 2741 #ifdef DEBUG_LAYOUT 2742 qCDebug(KHTML_LOG) << (void *)this << ": adding overhanging floats xoff=" << xoff << " offset=" << offset << " child=" << child; 2743 #endif 2744 2745 // Prevent floats from being added to the canvas by the root element, e.g., <html>. 2746 if (!flow->m_floatingObjects || (child && flow->isRoot())) { 2747 return; 2748 } 2749 2750 // if I am clear of my floats, don't add them 2751 // the CSS spec also mentions that child floats 2752 // are not cleared. 2753 if (!child && style()->clear() == CBOTH) { 2754 return; 2755 } 2756 2757 QListIterator<FloatingObject *> it(*flow->m_floatingObjects); 2758 FloatingObject *r; 2759 while (it.hasNext()) { 2760 r = it.next(); 2761 2762 if (!child && r->type == FloatingObject::FloatLeft && style()->clear() == CLEFT) { 2763 continue; 2764 } 2765 if (!child && r->type == FloatingObject::FloatRight && style()->clear() == CRIGHT) { 2766 continue; 2767 } 2768 2769 if ((!child && r->endY > offset) || 2770 (child && flow->yPos() + r->endY > height())) { 2771 if (child && !r->crossedLayer) { 2772 if (flow->enclosingLayer() == enclosingLayer()) { 2773 // Set noPaint to true only if we didn't cross layers. 2774 r->noPaint = true; 2775 } else { 2776 r->crossedLayer = true; 2777 } 2778 } 2779 2780 // don't insert it twice! 2781 if (!containsFloat(r->node)) { 2782 FloatingObject *floatingObj = new FloatingObject(KDE_CAST_BF_ENUM(FloatingObject::Type, r->type)); 2783 floatingObj->startY = r->startY - offset; 2784 floatingObj->endY = r->endY - offset; 2785 floatingObj->left = r->left - xoff; 2786 // Applying the child's margin makes no sense in the case where the child was passed in. 2787 // since his own margin was added already through the subtraction of the |xoff| variable 2788 // above. |xoff| will equal -flow->marginLeft() in this case, so it's already been taken 2789 // into account. Only apply this code if |child| is false, since otherwise the left margin 2790 // will get applied twice. -dwh 2791 if (!child && flow != parent()) { 2792 floatingObj->left += flow->marginLeft(); 2793 } 2794 if (!child) { 2795 floatingObj->left -= marginLeft(); 2796 floatingObj->noPaint = true; 2797 } else { 2798 floatingObj->noPaint = (r->crossedLayer || !r->noPaint); 2799 floatingObj->crossedLayer = r->crossedLayer; 2800 } 2801 2802 floatingObj->width = r->width; 2803 floatingObj->node = r->node; 2804 if (!m_floatingObjects) { 2805 m_floatingObjects = new QList<FloatingObject *>; 2806 } 2807 m_floatingObjects->append(floatingObj); 2808 #ifdef DEBUG_LAYOUT 2809 qCDebug(KHTML_LOG) << "addOverHangingFloats x/y= (" << floatingObj->left << "/" << floatingObj->startY << "-" << floatingObj->width << "/" << floatingObj->endY - floatingObj->startY << ")"; 2810 #endif 2811 } 2812 } 2813 } 2814 } 2815 2816 bool RenderBlock::containsFloat(RenderObject *o) const 2817 { 2818 if (m_floatingObjects) { 2819 QListIterator<FloatingObject *> it(*m_floatingObjects); 2820 while (it.hasNext()) { 2821 if (it.next()->node == o) { 2822 return true; 2823 } 2824 } 2825 } 2826 return false; 2827 } 2828 2829 void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderObject *floatToRemove) 2830 { 2831 dirtyFormattingContext(false); 2832 setChildNeedsLayout(true); 2833 2834 if (floatToRemove) { 2835 removeFloatingObject(floatToRemove); 2836 } 2837 2838 // Iterate over our children and mark them as needed. 2839 if (!childrenInline()) { 2840 for (RenderObject *child = firstChild(); child; child = child->nextSibling()) { 2841 if (isBlockFlow() && !child->isFloatingOrPositioned() && 2842 ((floatToRemove ? child->containsFloat(floatToRemove) : child->hasFloats()) || child->usesLineWidth())) { 2843 child->markAllDescendantsWithFloatsForLayout(floatToRemove); 2844 } 2845 } 2846 } 2847 } 2848 2849 int RenderBlock::getClearDelta(RenderObject *child, int yPos) 2850 { 2851 if (!hasFloats()) { 2852 return 0; 2853 } 2854 2855 //qCDebug(KHTML_LOG) << "getClearDelta on child " << child << " oldheight=" << m_height; 2856 bool clearSet = child->style()->clear() != CNONE; 2857 int bottom = 0; 2858 switch (child->style()->clear()) { 2859 case CNONE: 2860 break; 2861 case CLEFT: 2862 bottom = leftBottom(); 2863 break; 2864 case CRIGHT: 2865 bottom = rightBottom(); 2866 break; 2867 case CBOTH: 2868 bottom = floatBottom(); 2869 break; 2870 } 2871 2872 // We also clear floats if we are too big to sit on the same line as 2873 // a float, and happen to flow around floats. 2874 int result = clearSet ? qMax(0, bottom - yPos) : 0; 2875 if (!result && child->flowAroundFloats()) { 2876 bool canClear = true; 2877 bool needsRecalc = child->usesLineWidth(); 2878 int cury = yPos; 2879 int childw = 0; 2880 int aw = contentWidth(); 2881 #if 0 2882 // this is a silly Gecko compatibility hack - enable only if it becomes 2883 // necessary, and then check regularly with new Gecko versions 2884 if (!style()->hasBorder()) { 2885 RenderObject *ps = child; 2886 while ((ps = ps->previousSibling())) { 2887 if (!ps->isFloating() && !ps->isPositioned()) { 2888 break; 2889 } 2890 } 2891 if (!ps) { 2892 return 0; 2893 } 2894 } 2895 #endif 2896 while (true) { 2897 int curw = lineWidth(cury, &canClear); 2898 if (!canClear || curw == aw) { 2899 return cury - yPos; 2900 } 2901 if (!childw || needsRecalc) { 2902 int oy = child->yPos(); 2903 int ow = child->width(); 2904 child->setPos(child->xPos(), cury); 2905 child->calcWidth(); 2906 childw = child->width(); 2907 child->setPos(child->xPos(), oy); 2908 child->setWidth(ow); 2909 } 2910 if (childw <= curw) { 2911 return cury - yPos; 2912 } 2913 if (!(cury = nearestFloatBottom(cury))) { 2914 return 0; 2915 } 2916 } 2917 } 2918 return result; 2919 } 2920 2921 bool RenderBlock::isPointInScrollbar(int _x, int _y, int _tx, int _ty) 2922 { 2923 if (!scrollsOverflow() || !m_layer) { 2924 return false; 2925 } 2926 2927 if (m_layer->verticalScrollbarWidth()) { 2928 bool rtl = QApplication::isRightToLeft(); 2929 QRect vertRect(_tx + (rtl ? borderLeft() : width() - borderRight() - m_layer->verticalScrollbarWidth()), 2930 _ty + borderTop() - borderTopExtra(), 2931 m_layer->verticalScrollbarWidth(), 2932 height() + borderTopExtra() + borderBottomExtra() - borderTop() - borderBottom()); 2933 if (vertRect.contains(_x, _y)) { 2934 RenderLayer::gScrollBar = m_layer->verticalScrollbar(); 2935 return true; 2936 } 2937 } 2938 2939 if (m_layer->horizontalScrollbarHeight()) { 2940 QRect horizRect(_tx + borderLeft(), 2941 _ty + height() - borderBottom() + borderBottomExtra() - m_layer->horizontalScrollbarHeight(), 2942 width() - borderLeft() - borderRight(), 2943 m_layer->horizontalScrollbarHeight()); 2944 if (horizRect.contains(_x, _y)) { 2945 RenderLayer::gScrollBar = m_layer->horizontalScrollbar(); 2946 return true; 2947 } 2948 } 2949 2950 return false; 2951 } 2952 2953 bool RenderBlock::nodeAtPoint(NodeInfo &info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inBox) 2954 { 2955 bool inScrollbar = isPointInScrollbar(_x, _y, _tx + xPos(), _ty + yPos()); 2956 if (inScrollbar && hitTestAction != HitTestChildrenOnly) { 2957 inBox = true; 2958 } 2959 2960 if (hitTestAction != HitTestSelfOnly && m_floatingObjects && !inScrollbar) { 2961 int stx = _tx + xPos(); 2962 int sty = _ty + yPos(); 2963 if (hasOverflowClip() && m_layer) { 2964 m_layer->subtractScrollOffset(stx, sty); 2965 } 2966 FloatingObject *o; 2967 QListIterator<FloatingObject *> it(*m_floatingObjects); 2968 it.toBack(); 2969 while (it.hasPrevious()) { 2970 o = it.previous(); 2971 if (!o->noPaint && !o->node->layer()) 2972 inBox |= o->node->nodeAtPoint(info, _x, _y, 2973 stx + o->left + o->node->marginLeft() - o->node->xPos(), 2974 sty + o->startY + o->node->marginTop() - o->node->yPos(), HitTestAll); 2975 } 2976 } 2977 inBox |= RenderFlow::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inBox); 2978 return inBox; 2979 } 2980 2981 RenderPosition RenderBlock::positionForBox(InlineBox *box, bool start) const 2982 { 2983 if (!box) { 2984 return RenderPosition(); 2985 } 2986 2987 if (!box->object()->element()) { 2988 return RenderPosition(element(), start ? caretMinOffset() : caretMaxOffset()); 2989 } 2990 2991 if (!box->isInlineTextBox()) { 2992 return RenderPosition(box->object()->element(), start ? box->object()->caretMinOffset() : box->object()->caretMaxOffset()); 2993 } 2994 2995 InlineTextBox *textBox = static_cast<InlineTextBox *>(box); 2996 return RenderPosition(box->object()->element(), start ? textBox->start() : textBox->start() + textBox->len()); 2997 } 2998 2999 RenderPosition RenderBlock::positionForRenderer(RenderObject *renderer, bool start) const 3000 { 3001 if (!renderer) { 3002 return RenderPosition(element(), 0); 3003 } 3004 3005 NodeImpl *node = renderer->element() ? renderer->element() : element(); 3006 if (!node) { 3007 return RenderPosition(); 3008 } 3009 3010 long offset = start ? node->caretMinOffset() : node->caretMaxOffset(); 3011 return RenderPosition(node, offset); 3012 } 3013 3014 RenderPosition RenderBlock::positionForCoordinates(int _x, int _y) 3015 { 3016 if (isTable()) { 3017 return RenderFlow::positionForCoordinates(_x, _y); 3018 } 3019 3020 int absx, absy; 3021 absolutePosition(absx, absy); 3022 3023 int top = absy + borderTop() + paddingTop(); 3024 int bottom = top + contentHeight(); 3025 3026 if (_y < top) 3027 // y coordinate is above block 3028 { 3029 return positionForRenderer(firstLeafChild(), true); 3030 } 3031 3032 if (_y >= bottom) 3033 // y coordinate is below block 3034 { 3035 return positionForRenderer(lastLeafChild(), false); 3036 } 3037 3038 if (childrenInline()) { 3039 if (!firstRootBox()) { 3040 return Position(element(), 0); 3041 } 3042 3043 if (_y >= top && _y < absy + firstRootBox()->topOverflow()) 3044 // y coordinate is above first root line box 3045 { 3046 return positionForBox(firstRootBox()->firstLeafChild(), true); 3047 } 3048 3049 // look for the closest line box in the root box which is at the passed-in y coordinate 3050 for (RootInlineBox *root = firstRootBox(); root; root = root->nextRootBox()) { 3051 top = absy + root->topOverflow(); 3052 // set the bottom based on whether there is a next root box 3053 if (root->nextRootBox()) { 3054 bottom = absy + root->nextRootBox()->topOverflow(); 3055 } else { 3056 bottom = absy + root->bottomOverflow(); 3057 } 3058 // check if this root line box is located at this y coordinate 3059 if (_y >= top && _y < bottom && root->firstChild()) { 3060 InlineBox *closestBox = root->closestLeafChildForXPos(_x, absx); 3061 if (closestBox) { 3062 // pass the box a y position that is inside it 3063 return closestBox->object()->positionForCoordinates(_x, absy + closestBox->m_y); 3064 } 3065 } 3066 } 3067 3068 if (lastRootBox()) 3069 // y coordinate is below last root line box 3070 { 3071 return positionForBox(lastRootBox()->lastLeafChild(), false); 3072 } 3073 3074 return RenderPosition(element(), 0); 3075 } 3076 3077 // see if any child blocks exist at this y coordinate 3078 for (RenderObject *renderer = firstChild(); renderer; renderer = renderer->nextSibling()) { 3079 if (renderer->isFloatingOrPositioned()) { 3080 continue; 3081 } 3082 renderer->absolutePosition(absx, top); 3083 RenderObject *next = renderer->nextSibling(); 3084 while (next && next->isFloatingOrPositioned()) { 3085 next = next->nextSibling(); 3086 } 3087 if (next) { 3088 next->absolutePosition(absx, bottom); 3089 } else { 3090 bottom = top + contentHeight(); 3091 } 3092 if (_y >= top && _y < bottom) { 3093 return renderer->positionForCoordinates(_x, _y); 3094 } 3095 } 3096 3097 // pass along to the first child 3098 if (firstChild()) { 3099 return firstChild()->positionForCoordinates(_x, _y); 3100 } 3101 3102 // still no luck...return this render object's element and offset 0 3103 return RenderPosition(element(), 0); 3104 } 3105 3106 void RenderBlock::calcMinMaxWidth() 3107 { 3108 KHTMLAssert(!minMaxKnown()); 3109 3110 #ifdef DEBUG_LAYOUT 3111 qCDebug(KHTML_LOG) << renderName() << "(RenderBlock)::calcMinMaxWidth() this=" << this; 3112 #endif 3113 if (!isTableCell() && style()->width().isFixed() && style()->width().isPositive()) { 3114 m_minWidth = m_maxWidth = calcContentWidth(style()->width().value()); 3115 } else { 3116 m_minWidth = 0; 3117 m_maxWidth = 0; 3118 3119 bool noWrap = !style()->autoWrap(); 3120 if (childrenInline()) { 3121 calcInlineMinMaxWidth(); 3122 } else { 3123 calcBlockMinMaxWidth(); 3124 } 3125 3126 if (m_maxWidth < m_minWidth) { 3127 m_maxWidth = m_minWidth; 3128 } 3129 3130 if (noWrap && childrenInline()) { 3131 m_minWidth = m_maxWidth; 3132 3133 // A horizontal marquee with inline children has no minimum width. 3134 if (style()->overflowX() == OMARQUEE && m_layer && m_layer->marquee() && 3135 m_layer->marquee()->isHorizontal() && !m_layer->marquee()->isUnfurlMarquee()) { 3136 m_minWidth = 0; 3137 } 3138 } 3139 3140 if (isTableCell()) { 3141 Length w = static_cast<RenderTableCell *>(this)->styleOrColWidth(); 3142 if (w.isFixed() && w.isPositive()) { 3143 m_maxWidth = qMax((int)m_minWidth, calcContentWidth(w.value())); 3144 } 3145 } 3146 } 3147 3148 if (style()->minWidth().isFixed() && style()->minWidth().isPositive()) { 3149 m_maxWidth = qMax(m_maxWidth, (int)calcContentWidth(style()->minWidth().value())); 3150 m_minWidth = qMax(m_minWidth, (short)calcContentWidth(style()->minWidth().value())); 3151 } 3152 3153 if (style()->maxWidth().isFixed() && !style()->maxWidth().isUndefined()) { 3154 m_maxWidth = qMin(m_maxWidth, (int)calcContentWidth(style()->maxWidth().value())); 3155 m_minWidth = qMin(m_minWidth, (short)calcContentWidth(style()->maxWidth().value())); 3156 } 3157 3158 int toAdd = 0; 3159 toAdd = borderLeft() + borderRight() + paddingLeft() + paddingRight(); 3160 3161 m_minWidth += toAdd; 3162 m_maxWidth += toAdd; 3163 3164 setMinMaxKnown(); 3165 3166 //qCDebug(KHTML_LOG) << "Text::calcMinMaxWidth(" << this << "): min = " << m_minWidth << " max = " << m_maxWidth; 3167 // ### compare with min/max width set in style sheet... 3168 } 3169 3170 // bidi.cpp defines the following functions too. 3171 // Maybe these should not be static, after all... 3172 3173 static int getBPMWidth(int childValue, Length cssUnit) 3174 { 3175 if (!cssUnit.isAuto()) { 3176 return (cssUnit.isFixed() ? cssUnit.value() : childValue); 3177 } 3178 return 0; 3179 } 3180 3181 static int getBorderPaddingMargin(RenderObject *child, bool endOfInline) 3182 { 3183 RenderStyle *cstyle = child->style(); 3184 int result = 0; 3185 bool leftSide = (cstyle->direction() == LTR) ? !endOfInline : endOfInline; 3186 result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()), 3187 (leftSide ? cstyle->marginLeft() : 3188 cstyle->marginRight())); 3189 result += getBPMWidth((leftSide ? child->paddingLeft() : child->paddingRight()), 3190 (leftSide ? cstyle->paddingLeft() : 3191 cstyle->paddingRight())); 3192 result += leftSide ? child->borderLeft() : child->borderRight(); 3193 return result; 3194 } 3195 3196 static void stripTrailingSpace(bool preserveWS, 3197 int &inlineMax, int &inlineMin, 3198 RenderObject *trailingSpaceChild) 3199 { 3200 if (!preserveWS && trailingSpaceChild && trailingSpaceChild->isText()) { 3201 // Collapse away the trailing space at the end of a block. 3202 RenderText *t = static_cast<RenderText *>(trailingSpaceChild); 3203 const Font *f = t->htmlFont(false); 3204 QChar space[1]; space[0] = ' '; 3205 int spaceWidth = f->charWidth(space, 1, 0, true/*fast algo*/); 3206 inlineMax -= spaceWidth; 3207 if (inlineMin > inlineMax) { 3208 inlineMin = inlineMax; 3209 } 3210 } 3211 } 3212 3213 void RenderBlock::calcInlineMinMaxWidth() 3214 { 3215 int inlineMax = 0; 3216 int inlineMin = 0; 3217 3218 int cw = containingBlock()->contentWidth(); 3219 3220 // If we are at the start of a line, we want to ignore all white-space. 3221 // Also strip spaces if we previously had text that ended in a trailing space. 3222 bool stripFrontSpaces = true; 3223 3224 bool isTcQuirk = isTableCell() && style()->htmlHacks() && style()->width().isAuto(); 3225 3226 RenderObject *trailingSpaceChild = nullptr; 3227 3228 bool autoWrap, oldAutoWrap; 3229 autoWrap = oldAutoWrap = style()->autoWrap(); 3230 3231 InlineMinMaxIterator childIterator(this, this); 3232 bool addedTextIndent = false; // Only gets added in once. 3233 RenderObject *prevFloat = nullptr; 3234 while (RenderObject *child = childIterator.next()) { 3235 autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() : child->style()->autoWrap(); 3236 3237 if (!child->isBR()) { 3238 // Step One: determine whether or not we need to go ahead and 3239 // terminate our current line. Each discrete chunk can become 3240 // the new min-width, if it is the widest chunk seen so far, and 3241 // it can also become the max-width. 3242 3243 // Children fall into three categories: 3244 // (1) An inline flow object. These objects always have a min/max of 0, 3245 // and are included in the iteration solely so that their margins can 3246 // be added in. 3247 // 3248 // (2) An inline non-text non-flow object, e.g., an inline replaced element. 3249 // These objects can always be on a line by themselves, so in this situation 3250 // we need to go ahead and break the current line, and then add in our own 3251 // margins and min/max width on its own line, and then terminate the line. 3252 // 3253 // (3) A text object. Text runs can have breakable characters at the start, 3254 // the middle or the end. They may also lose whitespace off the front if 3255 // we're already ignoring whitespace. In order to compute accurate min-width 3256 // information, we need three pieces of information. 3257 // (a) the min-width of the first non-breakable run. Should be 0 if the text string 3258 // starts with whitespace. 3259 // (b) the min-width of the last non-breakable run. Should be 0 if the text string 3260 // ends with whitespace. 3261 // (c) the min/max width of the string (trimmed for whitespace). 3262 // 3263 // If the text string starts with whitespace, then we need to go ahead and 3264 // terminate our current line (unless we're already in a whitespace stripping 3265 // mode. 3266 // 3267 // If the text string has a breakable character in the middle, but didn't start 3268 // with whitespace, then we add the width of the first non-breakable run and 3269 // then end the current line. We then need to use the intermediate min/max width 3270 // values (if any of them are larger than our current min/max). We then look at 3271 // the width of the last non-breakable run and use that to start a new line 3272 // (unless we end in whitespace). 3273 RenderStyle *cstyle = child->style(); 3274 int childMin = 0; 3275 int childMax = 0; 3276 3277 if (!child->isText()) { 3278 // Case (1) and (2). Inline replaced and inline flow elements. 3279 if (child->isInlineFlow()) { 3280 // Add in padding/border/margin from the appropriate side of 3281 // the element. 3282 int bpm = getBorderPaddingMargin(child, childIterator.endOfInline); 3283 childMin += bpm; 3284 childMax += bpm; 3285 3286 inlineMin += childMin; 3287 inlineMax += childMax; 3288 if (child->isWordBreak()) { 3289 // End a line and start a new line. 3290 m_minWidth = qMax(inlineMin, (int)m_minWidth); 3291 inlineMin = 0; 3292 } 3293 } else { 3294 // Inline replaced elements add in their margins to their min/max values. 3295 int margins = 0; 3296 LengthType type = cstyle->marginLeft().type(); 3297 if (type == Fixed) { 3298 margins += cstyle->marginLeft().value(); 3299 } 3300 type = cstyle->marginRight().type(); 3301 if (type == Fixed) { 3302 margins += cstyle->marginRight().value(); 3303 } 3304 childMin += margins; 3305 childMax += margins; 3306 } 3307 } 3308 3309 if (!child->isRenderInline() && !child->isText()) { 3310 // Case (2). Inline replaced elements and floats. 3311 3312 // Common wrapping quirk 3313 bool qBreak = isTcQuirk && !child->isFloating(); 3314 3315 childMin += child->minWidth(); 3316 childMax += child->maxWidth(); 3317 3318 // Check our "clear" setting. 3319 bool clearPreviousFloat = false; 3320 if (child->isFloating()) { 3321 if (prevFloat && 3322 (((prevFloat->style()->floating() & FLEFT) && (child->style()->clear() & CLEFT)) || 3323 ((prevFloat->style()->floating() & FRIGHT) && (child->style()->clear() & CRIGHT)))) { 3324 clearPreviousFloat = true; 3325 } 3326 prevFloat = child; 3327 } 3328 3329 if ((!qBreak && (autoWrap || oldAutoWrap)) || clearPreviousFloat) { 3330 if (m_minWidth < inlineMin) { 3331 m_minWidth = inlineMin; 3332 } 3333 inlineMin = 0; 3334 } 3335 3336 // If we're supposed to clear the previous float, then terminate maxwidth as well. 3337 if (clearPreviousFloat) { 3338 m_maxWidth = qMax(inlineMax, m_maxWidth); 3339 inlineMax = 0; 3340 } 3341 3342 // Add in text-indent. This is added in only once. 3343 int ti = 0; 3344 if (!addedTextIndent) { 3345 addedTextIndent = true; 3346 ti = style()->textIndent().minWidth(cw); 3347 childMin += ti; 3348 childMax += ti; 3349 } 3350 3351 // Add our width to the max. 3352 inlineMax += childMax; 3353 3354 if ((!autoWrap && !child->isFloating()) || qBreak) { 3355 inlineMin += childMin; 3356 } else { 3357 // Now check our line. 3358 m_minWidth = qMax(childMin, (int)m_minWidth); 3359 3360 // Now start a new line. 3361 inlineMin = 0; 3362 } 3363 3364 // We are no longer stripping whitespace at the start of 3365 // a line. 3366 if (!child->isFloating()) { 3367 stripFrontSpaces = false; 3368 trailingSpaceChild = nullptr; 3369 } 3370 } else if (child->isText()) { 3371 // Case (3). Text. 3372 RenderText *t = static_cast<RenderText *>(child); 3373 3374 // Determine if we have a breakable character. Pass in 3375 // whether or not we should ignore any spaces at the front 3376 // of the string. If those are going to be stripped out, 3377 // then they shouldn't be considered in the breakable char 3378 // check. 3379 bool hasBreakableChar, hasBreak; 3380 int beginMin, endMin; 3381 bool beginWS, endWS; 3382 int beginMax, endMax; 3383 t->trimmedMinMaxWidth(beginMin, beginWS, endMin, endWS, hasBreakableChar, 3384 hasBreak, beginMax, endMax, 3385 childMin, childMax, stripFrontSpaces); 3386 3387 // This text object is insignificant and will not be rendered. Just 3388 // continue. 3389 if (!hasBreak && childMax == 0) { 3390 continue; 3391 } 3392 3393 if (stripFrontSpaces) { 3394 trailingSpaceChild = child; 3395 } else { 3396 trailingSpaceChild = nullptr; 3397 } 3398 3399 // Add in text-indent. This is added in only once. 3400 int ti = 0; 3401 if (!addedTextIndent) { 3402 addedTextIndent = true; 3403 ti = style()->textIndent().minWidth(cw); 3404 childMin += ti; beginMin += ti; 3405 childMax += ti; beginMax += ti; 3406 } 3407 3408 // If we have no breakable characters at all, 3409 // then this is the easy case. We add ourselves to the current 3410 // min and max and continue. 3411 if (!hasBreakableChar) { 3412 inlineMin += childMin; 3413 } else { 3414 // We have a breakable character. Now we need to know if 3415 // we start and end with whitespace. 3416 if (beginWS) { 3417 // Go ahead and end the current line. 3418 if (m_minWidth < inlineMin) { 3419 m_minWidth = inlineMin; 3420 } 3421 } else { 3422 inlineMin += beginMin; 3423 if (m_minWidth < inlineMin) { 3424 m_minWidth = inlineMin; 3425 } 3426 childMin -= ti; 3427 } 3428 3429 inlineMin = childMin; 3430 3431 if (endWS) { 3432 // We end in whitespace, which means we can go ahead 3433 // and end our current line. 3434 if (m_minWidth < inlineMin) { 3435 m_minWidth = inlineMin; 3436 } 3437 inlineMin = 0; 3438 } else { 3439 if (m_minWidth < inlineMin) { 3440 m_minWidth = inlineMin; 3441 } 3442 inlineMin = endMin; 3443 } 3444 } 3445 3446 if (hasBreak) { 3447 inlineMax += beginMax; 3448 if (m_maxWidth < inlineMax) { 3449 m_maxWidth = inlineMax; 3450 } 3451 if (m_maxWidth < childMax) { 3452 m_maxWidth = childMax; 3453 } 3454 inlineMax = endMax; 3455 } else { 3456 inlineMax += childMax; 3457 } 3458 } 3459 3460 // Ignore spaces after a list marker. 3461 if (child->isListMarker()) { 3462 stripFrontSpaces = true; 3463 } 3464 } else { 3465 if (m_minWidth < inlineMin) { 3466 m_minWidth = inlineMin; 3467 } 3468 if (m_maxWidth < inlineMax) { 3469 m_maxWidth = inlineMax; 3470 } 3471 inlineMin = inlineMax = 0; 3472 stripFrontSpaces = true; 3473 trailingSpaceChild = nullptr; 3474 } 3475 3476 oldAutoWrap = autoWrap; 3477 } 3478 3479 stripTrailingSpace(style()->preserveWS(), inlineMax, inlineMin, trailingSpaceChild); 3480 3481 if (m_minWidth < inlineMin) { 3482 m_minWidth = inlineMin; 3483 } 3484 if (m_maxWidth < inlineMax) { 3485 m_maxWidth = inlineMax; 3486 } 3487 // qCDebug(KHTML_LOG) << "m_minWidth=" << m_minWidth 3488 // << " m_maxWidth=" << m_maxWidth; 3489 } 3490 3491 // Use a very large value (in effect infinite). 3492 #define BLOCK_MAX_WIDTH 15000 3493 3494 void RenderBlock::calcBlockMinMaxWidth() 3495 { 3496 bool nowrap = !style()->autoWrap(); 3497 3498 RenderObject *child = firstChild(); 3499 int floatLeftWidth = 0, floatRightWidth = 0; 3500 3501 while (child != nullptr) { 3502 // positioned children don't affect the minmaxwidth 3503 if (child->isPositioned()) { 3504 child = child->nextSibling(); 3505 continue; 3506 } 3507 3508 if (child->isFloating() || child->flowAroundFloats()) { 3509 int floatTotalWidth = floatLeftWidth + floatRightWidth; 3510 if (child->style()->clear() & CLEFT) { 3511 m_maxWidth = qMax(floatTotalWidth, m_maxWidth); 3512 floatLeftWidth = 0; 3513 } 3514 if (child->style()->clear() & CRIGHT) { 3515 m_maxWidth = qMax(floatTotalWidth, m_maxWidth); 3516 floatRightWidth = 0; 3517 } 3518 } 3519 3520 Length ml = child->style()->marginLeft(); 3521 Length mr = child->style()->marginRight(); 3522 3523 // Call calcWidth on the child to ensure that our margins are 3524 // up to date. This method can be called before the child has actually 3525 // calculated its margins (which are computed inside calcWidth). 3526 if (ml.isPercent() || mr.isPercent()) { 3527 calcWidth(); 3528 } 3529 3530 // A margin basically has three types: fixed, percentage, and auto (variable). 3531 // Auto margins simply become 0 when computing min/max width. 3532 // Fixed margins can be added in as is. 3533 // Percentage margins are computed as a percentage of the width we calculated in 3534 // the calcWidth call above. In this case we use the actual cached margin values on 3535 // the RenderObject itself. 3536 int margin = 0, marginLeft = 0, marginRight = 0; 3537 if (ml.isFixed()) { 3538 marginLeft += ml.value(); 3539 } else if (ml.isPercent()) { 3540 marginLeft += child->marginLeft(); 3541 } 3542 3543 if (mr.isFixed()) { 3544 marginRight += mr.value(); 3545 } else if (mr.isPercent()) { 3546 marginRight += child->marginRight(); 3547 } 3548 3549 margin = marginLeft + marginRight; 3550 3551 int w = child->minWidth() + margin; 3552 if (m_minWidth < w) { 3553 m_minWidth = w; 3554 } 3555 3556 // IE ignores tables for calculation of nowrap. Makes some sense. 3557 if (nowrap && !child->isTable() && m_maxWidth < w) { 3558 m_maxWidth = w; 3559 } 3560 3561 w = child->maxWidth() + margin; 3562 3563 if (!child->isFloating()) { 3564 if (child->flowAroundFloats()) { 3565 // Determine a left and right max value based on whether or not the floats can fit in the 3566 // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin 3567 // is smaller than the float width. 3568 int maxLeft = marginLeft > 0 ? qMax(floatLeftWidth, marginLeft) : floatLeftWidth + marginLeft; 3569 int maxRight = marginRight > 0 ? qMax(floatRightWidth, marginRight) : floatRightWidth + marginRight; 3570 w = child->maxWidth() + maxLeft + maxRight; 3571 w = qMax(w, floatLeftWidth + floatRightWidth); 3572 } else { 3573 m_maxWidth = qMax(floatLeftWidth + floatRightWidth, m_maxWidth); 3574 } 3575 floatLeftWidth = floatRightWidth = 0; 3576 } 3577 3578 if (child->isFloating()) { 3579 if (style()->floating() & FLEFT) { 3580 floatLeftWidth += w; 3581 } else { 3582 floatRightWidth += w; 3583 } 3584 } else if (m_maxWidth < w) { 3585 m_maxWidth = w; 3586 } 3587 3588 // A very specific WinIE quirk. 3589 // Example: 3590 /* 3591 <div style="position:absolute; width:100px; top:50px;"> 3592 <div style="position:absolute;left:0px;top:50px;height:50px;background-color:green"> 3593 <table style="width:100%"><tr><td></table> 3594 </div> 3595 </div> 3596 */ 3597 // In the above example, the inner absolute positioned block should have a computed width 3598 // of 100px because of the table. 3599 // We can achieve this effect by making the maxwidth of blocks that contain tables 3600 // with percentage widths be infinite (as long as they are not inside a table cell). 3601 if (style()->htmlHacks() && child->style()->width().isPercent() && 3602 !isTableCell() && child->isTable() && m_maxWidth < BLOCK_MAX_WIDTH) { 3603 RenderBlock *cb = containingBlock(); 3604 while (!cb->isCanvas() && !cb->isTableCell()) { 3605 cb = cb->containingBlock(); 3606 } 3607 if (!cb->isTableCell()) { 3608 m_maxWidth = BLOCK_MAX_WIDTH; 3609 } 3610 } 3611 child = child->nextSibling(); 3612 } 3613 m_maxWidth = qMax(floatLeftWidth + floatRightWidth, m_maxWidth); 3614 } 3615 3616 void RenderBlock::close() 3617 { 3618 if (lastChild() && lastChild()->isAnonymousBlock()) { 3619 lastChild()->close(); 3620 } 3621 updateFirstLetter(); 3622 RenderFlow::close(); 3623 } 3624 3625 int RenderBlock::getBaselineOfFirstLineBox() 3626 { 3627 if (m_firstLineBox) { 3628 return m_firstLineBox->yPos() + m_firstLineBox->baseline(); 3629 } 3630 3631 if (isInline()) { 3632 return -1; // We're inline and had no line box, so we have no baseline we can return. 3633 } 3634 3635 for (RenderObject *curr = firstChild(); curr; curr = curr->nextSibling()) { 3636 int result = curr->getBaselineOfFirstLineBox(); 3637 if (result != -1) { 3638 return curr->yPos() + result; // Translate to our coordinate space. 3639 } 3640 } 3641 3642 return -1; 3643 } 3644 3645 InlineFlowBox *RenderBlock::getFirstLineBox() 3646 { 3647 if (m_firstLineBox) { 3648 return m_firstLineBox; 3649 } 3650 3651 if (isInline()) { 3652 return nullptr; // We're inline and had no line box, so we have no baseline we can return. 3653 } 3654 3655 for (RenderObject *curr = firstChild(); curr; curr = curr->nextSibling()) { 3656 InlineFlowBox *result = curr->getFirstLineBox(); 3657 if (result) { 3658 return result; 3659 } 3660 } 3661 3662 return nullptr; 3663 } 3664 3665 bool RenderBlock::inRootBlockContext() const 3666 { 3667 if (isTableCell() || isFloatingOrPositioned() || hasOverflowClip()) { 3668 return false; 3669 } 3670 3671 if (isRoot() || isCanvas()) { 3672 return true; 3673 } 3674 3675 return containingBlock()->inRootBlockContext(); 3676 } 3677 3678 const char *RenderBlock::renderName() const 3679 { 3680 if (isFloating()) { 3681 return "RenderBlock (floating)"; 3682 } 3683 if (isPositioned()) { 3684 return "RenderBlock (positioned)"; 3685 } 3686 if (isAnonymousBlock() && m_avoidPageBreak) { 3687 return "RenderBlock (avoidPageBreak)"; 3688 } 3689 if (isAnonymousBlock()) { 3690 return "RenderBlock (anonymous)"; 3691 } else if (isAnonymous()) { 3692 return "RenderBlock (generated)"; 3693 } 3694 if (isRelPositioned()) { 3695 return "RenderBlock (relative positioned)"; 3696 } 3697 if (style() && style()->display() == COMPACT) { 3698 return "RenderBlock (compact)"; 3699 } 3700 if (style() && style()->display() == RUN_IN) { 3701 return "RenderBlock (run-in)"; 3702 } 3703 return "RenderBlock"; 3704 } 3705 3706 #ifdef ENABLE_DUMP 3707 void RenderBlock::printTree(int indent) const 3708 { 3709 RenderFlow::printTree(indent); 3710 3711 if (m_floatingObjects) { 3712 QListIterator<FloatingObject *> it(*m_floatingObjects); 3713 FloatingObject *r; 3714 while (it.hasNext()) { 3715 r = it.next(); 3716 QString s; 3717 s.fill(' ', indent); 3718 qCDebug(KHTML_LOG) << s << renderName() << ": " << 3719 (r->type == FloatingObject::FloatLeft ? "FloatLeft" : "FloatRight") << 3720 "[" << r->node->renderName() << ": " << (void *)r->node << "] (" << r->startY << " - " << r->endY << ")" << "width: " << r->width; 3721 } 3722 } 3723 } 3724 3725 void RenderBlock::dump(QTextStream &stream, const QString &ind) const 3726 { 3727 RenderFlow::dump(stream, ind); 3728 3729 if (m_childrenInline) { 3730 stream << QLatin1String(" childrenInline"); 3731 } 3732 // FIXME: currently only print pre to not mess up regression 3733 if (style()->preserveWS()) { 3734 stream << " pre"; 3735 } 3736 if (m_firstLine) { 3737 stream << QLatin1String(" firstLine"); 3738 } 3739 3740 if (m_floatingObjects && !m_floatingObjects->isEmpty()) { 3741 stream << QLatin1String(" special("); 3742 QListIterator<FloatingObject *> it(*m_floatingObjects); 3743 FloatingObject *r; 3744 bool first = true; 3745 while (it.hasNext()) { 3746 r = it.next(); 3747 if (!first) { 3748 stream << QLatin1Char(','); 3749 } 3750 stream << r->node->renderName(); 3751 first = false; 3752 } 3753 stream << QLatin1Char(')'); 3754 } 3755 3756 // ### EClear m_clearStatus 3757 } 3758 #endif 3759 3760 #undef DEBUG 3761 #undef DEBUG_LAYOUT 3762 #undef BOX_DEBUG 3763 3764 } // namespace khtml 3765