KHtml

render_block.cpp
1 /*
2  * This file is part of the render object implementation for KHTML.
3  *
4  * Copyright (C) 1999-2003 Lars Knoll ([email protected])
5  * (C) 1999-2003 Antti Koivisto ([email protected])
6  * (C) 2002-2003 Dirk Mueller ([email protected])
7  * (C) 2003-2009 Apple Computer, Inc.
8  * (C) 2004-2009 Germain Garand ([email protected])
9  * (C) 2005 Allan Sandfeld Jensen ([email protected])
10  * (C) 2006 Charles Samuels ([email protected])
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB. If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  *
27  */
28 
29 //#define DEBUG
30 //#define DEBUG_LAYOUT
31 //#define BOX_DEBUG
32 //#define FLOAT_DEBUG
33 //#define PAGE_DEBUG
34 
35 #include "render_block.h"
36 
37 #include "khtml_debug.h"
38 #include <limits.h>
39 #include "render_text.h"
40 #include "render_table.h"
41 #include "render_canvas.h"
42 #include "render_layer.h"
43 #include "rendering/render_position.h"
44 
45 #include <xml/dom_nodeimpl.h>
46 #include <xml/dom_docimpl.h>
47 #include <xml/dom_selection.h>
48 #include <html/html_formimpl.h>
49 
50 #include <khtmlview.h>
51 #include <khtml_part.h>
52 
53 using namespace DOM;
54 
55 namespace khtml
56 {
57 
58 // -------------------------------------------------------------------------------------------------------
59 
60 // Our MarginInfo state used when laying out block children.
61 RenderBlock::MarginInfo::MarginInfo(RenderBlock *block, int top, int bottom)
62 {
63  // Whether or not we can collapse our own margins with our children. We don't do this
64  // if we had any border/padding (obviously), if we're the root or HTML elements, or if
65  // we're positioned, floating, a table cell.
66  m_canCollapseWithChildren = !block->isCanvas() && !block->isRoot() && !block->isPositioned() &&
67  !block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable();
68 
69  m_canCollapseTopWithChildren = m_canCollapseWithChildren && (top == 0) /*&& block->style()->marginTopCollapse() != MSEPARATE */;
70 
71  // If any height other than auto is specified in CSS, then we don't collapse our bottom
72  // margins with our children's margins. To do otherwise would be to risk odd visual
73  // effects when the children overflow out of the parent block and yet still collapse
74  // with it. We also don't collapse if we have any bottom border/padding.
75  m_canCollapseBottomWithChildren = m_canCollapseWithChildren && (bottom == 0) &&
76  (block->style()->height().isAuto() && block->style()->height().isZero()) /*&& block->style()->marginBottomCollapse() != MSEPARATE*/;
77 
78  m_quirkContainer = block->isTableCell() || block->isBody() /*|| block->style()->marginTopCollapse() == MDISCARD ||
79  block->style()->marginBottomCollapse() == MDISCARD*/;
80 
81  m_atTopOfBlock = true;
82  m_atBottomOfBlock = false;
83 
84  m_posMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(true) : 0;
85  m_negMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(false) : 0;
86 
87  m_selfCollapsingBlockClearedFloat = false;
88 
89  m_topQuirk = m_bottomQuirk = m_determinedTopQuirk = false;
90 }
91 
92 // -------------------------------------------------------------------------------------------------------
93 
94 RenderBlock::RenderBlock(DOM::NodeImpl *node)
95  : RenderFlow(node)
96 {
97  m_childrenInline = true;
98  m_floatingObjects = nullptr;
99  m_positionedObjects = nullptr;
100  m_firstLine = false;
101  m_avoidPageBreak = false;
102  m_clearStatus = CNONE;
103  m_maxTopPosMargin = m_maxTopNegMargin = m_maxBottomPosMargin = m_maxBottomNegMargin = 0;
104  m_topMarginQuirk = m_bottomMarginQuirk = false;
105  m_overflowHeight = m_overflowWidth = 0;
106  m_overflowLeft = m_overflowTop = 0;
107 }
108 
109 RenderBlock::~RenderBlock()
110 {
111  if (m_floatingObjects) {
112  QListIterator<FloatingObject *> it(*m_floatingObjects);
113  while (it.hasNext()) {
114  delete it.next();
115  }
116  }
117  delete m_floatingObjects;
118  delete m_positionedObjects;
119 }
120 
121 void RenderBlock::setStyle(RenderStyle *_style)
122 {
123  setReplaced(_style->isDisplayReplacedType());
124 
125  RenderFlow::setStyle(_style);
126 
127  // ### we could save this call when the change only affected
128  // non inherited properties
129  RenderObject *child = firstChild();
130  while (child != nullptr) {
131  if (child->isAnonymousBlock()) {
132  RenderStyle *newStyle = new RenderStyle();
133  newStyle->inheritFrom(style());
134  newStyle->setDisplay(BLOCK);
135  child->setStyle(newStyle);
136  }
137  child = child->nextSibling();
138  }
139 
140  if (attached()) {
141  // Update generated content and ::inside
142  updateReplacedContent();
143  // Update pseudos for :before and :after
144  updatePseudoChildren();
145  }
146 
147  // handled by close() during parsing
148  // ### remove close move upto updatePseudo
149  if (!document()->parsing()) {
150  updateFirstLetter();
151  }
152 }
153 
154 // Attach handles initial setStyle that requires parent nodes
155 void RenderBlock::attach()
156 {
157  RenderFlow::attach();
158 
159  updateReplacedContent();
160  updatePseudoChildren();
161 }
162 
163 static inline bool isFirstLetterPunct(const QChar *c)
164 {
165  // CSS2.1/3 definition for ::first-letter doesn't include Pc or Pd.
166  if (c->isPunct()) {
167  QChar::Category cat = c->category();
168  return cat != QChar::Punctuation_Connector &&
170  }
171  return false;
172 }
173 
174 void RenderBlock::updateFirstLetter()
175 {
176  // Only blocks with inline-children can generate a first-letter
177  if (!childrenInline() || !firstChild()) {
178  return;
179  }
180 
181  // Don't recurse
182  if (style()->styleType() == RenderStyle::FIRST_LETTER) {
183  return;
184  }
185 
186  // The first-letter style is inheritable.
187  RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LETTER);
188  RenderObject *o = this;
189  while (o && !pseudoStyle) {
190  // ### We should ignore empty preceding siblings
191  if (o->parent() && o->parent()->firstChild() == this) {
192  o = o->parent();
193  } else {
194  break;
195  }
196  pseudoStyle = o->style()->getPseudoStyle(RenderStyle::FIRST_LETTER);
197  };
198 
199  // FIXME: Currently we don't delete first-letters, this is
200  // handled instead in NodeImpl::diff by issuing Detach on first-letter changes.
201  if (!pseudoStyle) {
202  return;
203  }
204 
205  // Drill into inlines looking for our first text child.
206  RenderObject *firstText = firstChild();
207  while (firstText && firstText->needsLayout() && !firstText->isFloating() && !firstText->isRenderBlock() && !firstText->isReplaced() && !firstText->isText())
208  // ### We should skip first children with only white-space and punctuation
209  {
210  firstText = firstText->firstChild();
211  }
212 
213  if (firstText && firstText->isText() && !firstText->isBR()) {
214  RenderObject *firstLetterObject = nullptr;
215  // Find the old first-letter
216  if (firstText->parent()->style()->styleType() == RenderStyle::FIRST_LETTER) {
217  firstLetterObject = firstText->parent();
218  }
219 
220  // Force inline display (except for floating first-letters)
221  pseudoStyle->setDisplay(pseudoStyle->isFloating() ? BLOCK : INLINE);
222  pseudoStyle->setPosition(PSTATIC); // CSS2 says first-letter can't be positioned.
223 
224  if (firstLetterObject != nullptr) {
225  firstLetterObject->setStyle(pseudoStyle);
226  RenderStyle *newStyle = new RenderStyle();
227  newStyle->inheritFrom(pseudoStyle);
228  firstText->setStyle(newStyle);
229  return;
230  }
231 
232  RenderText *textObj = static_cast<RenderText *>(firstText);
233  RenderObject *firstLetterContainer = firstText->parent();
234 
235  firstLetterObject = RenderFlow::createFlow(node(), pseudoStyle, renderArena());
236  firstLetterObject->setIsAnonymous(true);
237  firstLetterContainer->addChild(firstLetterObject, firstLetterContainer->firstChild());
238 
239  // if this object is the result of a :begin, then the text may have not been
240  // generated yet if it is a counter
241  if (textObj->recalcMinMax()) {
242  textObj->recalcMinMaxWidths();
243  }
244 
245  // The original string is going to be either a generated content string or a DOM node's
246  // string. We want the original string before it got transformed in case first-letter has
247  // no text-transform or a different text-transform applied to it.
248  DOMStringImpl *oldText = textObj->originalString();
249  if (!oldText) {
250  oldText = textObj->string();
251  }
252  // ### In theory a first-letter can stretch across multiple text objects, if they only contain
253  // punctuation and white-space
254  if (oldText->l >= 1) {
255  oldText->ref();
256  // begin: we need skip leading whitespace so that RenderBlock::findNextLineBreak
257  // won't think we're continuing from a previous run
258  unsigned int begin = 0; // the position that first-letter begins
259  unsigned int length = 0; // the position that "the rest" begins
260  while (length < oldText->l && (oldText->s + length)->isSpace()) {
261  length++;
262  }
263  begin = length;
264  while (length < oldText->l &&
265  (isFirstLetterPunct(oldText->s + length) || (oldText->s + length)->isSpace())) {
266  length++;
267  }
268  if (length < oldText->l &&
269  !((oldText->s + length)->isSpace() || isFirstLetterPunct(oldText->s + length))) {
270  length++;
271  }
272  while (length < oldText->l && isFirstLetterPunct(oldText->s + length)) {
273  length++;
274  }
275 
276  // we need to generated a remainingText object even if no text is left
277  // because it holds the place and style for the old textObj
278  RenderTextFragment *remainingText =
279  new(renderArena()) RenderTextFragment(textObj->node(), oldText, length, oldText->l - length);
280  remainingText->setIsAnonymous(textObj->isAnonymous());
281  remainingText->setStyle(textObj->style());
282  if (remainingText->element()) {
283  remainingText->element()->setRenderer(remainingText);
284  }
285 
286  RenderObject *nextObj = textObj->nextSibling();
287  textObj->detach();
288  firstLetterContainer->addChild(remainingText, nextObj);
289 
290  RenderTextFragment *letter =
291  new(renderArena()) RenderTextFragment(remainingText->node(), oldText, begin, length - begin);
292  letter->setIsAnonymous(remainingText->isAnonymous());
293  RenderStyle *newStyle = new RenderStyle();
294  newStyle->inheritFrom(pseudoStyle);
295  letter->setStyle(newStyle);
296  firstLetterObject->addChild(letter);
297  oldText->deref();
298 
299  remainingText->setFirstLetter(letter);
300  }
301  firstLetterObject->close();
302  }
303 }
304 
305 void RenderBlock::addChildToFlow(RenderObject *newChild, RenderObject *beforeChild)
306 {
307  // Make sure we don't append things after :after-generated content if we have it.
308  if (!beforeChild && lastChild() && lastChild()->style()->styleType() == RenderStyle::AFTER) {
309  beforeChild = lastChild();
310  }
311 
312  bool madeBoxesNonInline = false;
313 
314  // If the requested beforeChild is not one of our children, then this is most likely because
315  // there is an anonymous block box within this object that contains the beforeChild. So
316  // just insert the child into the anonymous block box instead of here. This may also be
317  // needed in cases of things like anonymous tables.
318  if (beforeChild && beforeChild->parent() != this) {
319 
320  KHTMLAssert(beforeChild->parent());
321 
322  // In the special case where we are prepending a block-level element before
323  // something contained inside an anonymous block, we can just prepend it before
324  // the anonymous block.
325  if (!newChild->isInline() && beforeChild->parent()->isAnonymousBlock() &&
326  beforeChild->parent()->parent() == this &&
327  beforeChild->parent()->firstChild() == beforeChild) {
328  return addChildToFlow(newChild, beforeChild->parent());
329  }
330 
331  // Otherwise find our kid inside which the beforeChild is, and delegate to it.
332  // This may be many levels deep due to anonymous tables, table sections, etc.
333  RenderObject *responsible = beforeChild->parent();
334  while (responsible->parent() != this) {
335  responsible = responsible->parent();
336  }
337 
338  return responsible->addChild(newChild, beforeChild);
339  }
340 
341  // prevent elements that haven't received a layout yet from getting painted by pushing
342  // them far above the top of the page
343  if (!newChild->isInline()) {
344  newChild->setPos(newChild->xPos(), -500000);
345  }
346 
347  // A block has to either have all of its children inline, or all of its children as blocks.
348  // So, if our children are currently inline and a block child has to be inserted, we move all our
349  // inline children into anonymous block boxes
350  if (m_childrenInline && !newChild->isInline() && !newChild->isFloatingOrPositioned()) {
351  // This is a block with inline content. Wrap the inline content in anonymous blocks.
352  makeChildrenNonInline(beforeChild);
353  madeBoxesNonInline = true;
354 
355  if (beforeChild && beforeChild->parent() != this) {
356  beforeChild = beforeChild->parent();
357  KHTMLAssert(beforeChild->isAnonymousBlock());
358  KHTMLAssert(beforeChild->parent() == this);
359  }
360  } else if (!m_childrenInline && !newChild->isFloatingOrPositioned()) {
361  // If we're inserting an inline child but all of our children are blocks, then we have to make sure
362  // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise
363  // a new one is created and inserted into our list of children in the appropriate position.
364  if (newChild->isInline()) {
365  if (beforeChild) {
366  if (beforeChild->previousSibling() && beforeChild->previousSibling()->isAnonymousBlock()) {
367  beforeChild->previousSibling()->addChild(newChild);
368  return;
369  }
370  } else {
371  if (m_last && m_last->isAnonymousBlock()) {
372  m_last->addChild(newChild);
373  return;
374  }
375  }
376 
377  // no suitable existing anonymous box - create a new one
378  RenderBlock *newBox = createAnonymousBlock();
379  RenderBox::addChild(newBox, beforeChild);
380  newBox->addChild(newChild);
381 
382  //the above may actually destroy newBox in case an anonymous
383  //table got created, and made the anonymous block redundant.
384  //so look up what to hide indirectly.
385  RenderObject *toHide = newChild;
386  while (toHide->parent() != this) {
387  toHide = toHide->parent();
388  }
389 
390  toHide->setPos(toHide->xPos(), -500000);
391  return;
392  } else {
393  // We are adding another block child... if the current last child is an anonymous box
394  // then it needs to be closed.
395  // ### get rid of the closing thing altogether this will only work during initial parsing
396  if (lastChild() && lastChild()->isAnonymous()) {
397  lastChild()->close();
398  }
399  }
400  }
401 
402  RenderBox::addChild(newChild, beforeChild);
403  // ### care about aligned stuff
404 
405  if (madeBoxesNonInline && isAnonymousBlock()) {
406  parent()->removeSuperfluousAnonymousBlockChild(this);
407  }
408  // we might be deleted now
409 }
410 
411 static void getInlineRun(RenderObject *start, RenderObject *stop,
412  RenderObject *&inlineRunStart,
413  RenderObject *&inlineRunEnd)
414 {
415  // Beginning at |start| we find the largest contiguous run of inlines that
416  // we can. We denote the run with start and end points, |inlineRunStart|
417  // and |inlineRunEnd|. Note that these two values may be the same if
418  // we encounter only one inline.
419  //
420  // We skip any non-inlines we encounter as long as we haven't found any
421  // inlines yet.
422  //
423  //
424  // |stop| indicates a non-inclusive stop point. Regardless of whether |stop|
425  // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered
426  // a non-inline.
427 
428  RenderObject *curr = start;
429  bool sawInline;
430  do {
431  while (curr && !(curr->isInline() || curr->isFloatingOrPositioned())) {
432  curr = curr->nextSibling();
433  }
434 
435  inlineRunStart = inlineRunEnd = curr;
436 
437  if (!curr) {
438  return; // No more inline children to be found.
439  }
440 
441  sawInline = curr->isInline();
442 
443  curr = curr->nextSibling();
444  while (curr && (curr->isInline() || curr->isFloatingOrPositioned()) && (curr != stop)) {
445  inlineRunEnd = curr;
446  if (curr->isInline()) {
447  sawInline = true;
448  }
449  curr = curr->nextSibling();
450  }
451  } while (!sawInline);
452 
453 }
454 
455 void RenderBlock::deleteLineBoxTree()
456 {
457  InlineFlowBox *line = m_firstLineBox;
458  InlineFlowBox *nextLine;
459  while (line) {
460  nextLine = line->nextFlowBox();
461  line->deleteLine(renderArena());
462  line = nextLine;
463  }
464  m_firstLineBox = m_lastLineBox = nullptr;
465 }
466 
467 short RenderBlock::baselinePosition(bool firstLine) const
468 {
469  // CSS2.1-10.8.1 "The baseline of an 'inline-block' is the baseline of its last line box
470  // in the normal flow, unless it has either no in-flow line boxes or if its 'overflow'
471  // property has a computed value other than 'visible', in which case the baseline is the bottom margin edge."
472 
473  if (isReplaced() && !hasOverflowClip() && !needsLayout()) {
474  int res = getBaselineOfLastLineBox();
475  if (res != -1) {
476  return res + marginTop();
477  }
478  }
479  return RenderBox::baselinePosition(firstLine);
480 }
481 
482 int RenderBlock::getBaselineOfLastLineBox() const
483 {
484  if (!isBlockFlow()) {
485  return -1;
486  }
487 
488  if (childrenInline()) {
489 // if (!firstLineBox() && hasLineIfEmpty())
490 // return RenderFlow::baselinePosition(true) + borderTop() + paddingTop();
491  if (lastLineBox()) {
492  return lastLineBox()->yPos() + lastLineBox()->baseline();
493  }
494  return -1;
495  } else {
496 // bool haveNormalFlowChild = false;
497  for (RenderObject *curr = lastChild(); curr; curr = curr->previousSibling()) {
498  if (!curr->isFloatingOrPositioned() && curr->isBlockFlow()) {
499 // haveNormalFlowChild = true;
500  int result = static_cast<RenderBlock *>(curr)->getBaselineOfLastLineBox();
501  if (result != -1) {
502  return curr->yPos() + result; // Translate to our coordinate space.
503  }
504  }
505  }
506 // if (!haveNormalFlowChild && isRenderButton()) // hasLineIfEmpty()
507 // return RenderFlow::baselinePosition(true) + borderTop() + paddingTop();
508  }
509 
510  return -1;
511 }
512 
513 void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint)
514 {
515  // makeChildrenNonInline takes a block whose children are *all* inline and it
516  // makes sure that inline children are coalesced under anonymous
517  // blocks. If |insertionPoint| is defined, then it represents the insertion point for
518  // the new block child that is causing us to have to wrap all the inlines. This
519  // means that we cannot coalesce inlines before |insertionPoint| with inlines following
520  // |insertionPoint|, because the new child is going to be inserted in between the inlines,
521  // splitting them.
522  KHTMLAssert(isReplacedBlock() || !isInline());
523  KHTMLAssert(!insertionPoint || insertionPoint->parent() == this);
524 
525  deleteLineBoxTree();
526 
527  m_childrenInline = false;
528 
529  RenderObject *child = firstChild();
530 
531  while (child) {
532  RenderObject *inlineRunStart, *inlineRunEnd;
533  getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd);
534 
535  if (!inlineRunStart) {
536  break;
537  }
538 
539  child = inlineRunEnd->nextSibling();
540 
541  RenderBlock *box = createAnonymousBlock();
542  insertChildNode(box, inlineRunStart);
543  RenderObject *o = inlineRunStart;
544  while (o != inlineRunEnd) {
545  RenderObject *no = o;
546  o = no->nextSibling();
547  box->appendChildNode(removeChildNode(no));
548  }
549  box->appendChildNode(removeChildNode(inlineRunEnd));
550  box->close();
551  box->setPos(box->xPos(), -500000);
552  }
553 }
554 
555 void RenderBlock::makePageBreakAvoidBlocks()
556 {
557  KHTMLAssert(!childrenInline());
558  KHTMLAssert(canvas()->pagedMode());
559 
560  RenderObject *breakAfter = firstChild();
561  RenderObject *breakBefore = breakAfter ? breakAfter->nextSibling() : nullptr;
562 
563  RenderBlock *pageRun = nullptr;
564 
565  // ### Should follow margin-collapsing rules, skipping self-collapsing blocks
566  // and exporting page-breaks from first/last child when collapsing with parent margin.
567  while (breakAfter) {
568  if (breakAfter->isRenderBlock() && !breakAfter->childrenInline()) {
569  static_cast<RenderBlock *>(breakAfter)->makePageBreakAvoidBlocks();
570  }
571  EPageBreak pbafter = breakAfter->style()->pageBreakAfter();
572  EPageBreak pbbefore = breakBefore ? breakBefore->style()->pageBreakBefore() : PBALWAYS;
573  if ((pbafter == PBAVOID && pbbefore == PBAVOID) ||
574  (pbafter == PBAVOID && pbbefore == PBAUTO) ||
575  (pbafter == PBAUTO && pbbefore == PBAVOID)) {
576  if (!pageRun) {
577  pageRun = createAnonymousBlock();
578  pageRun->m_avoidPageBreak = true;
579  pageRun->setChildrenInline(false);
580  }
581  pageRun->appendChildNode(removeChildNode(breakAfter));
582  } else {
583  if (pageRun) {
584  pageRun->appendChildNode(removeChildNode(breakAfter));
585  pageRun->close();
586  insertChildNode(pageRun, breakBefore);
587  pageRun = nullptr;
588  }
589  }
590  breakAfter = breakBefore;
591  breakBefore = breakBefore ? breakBefore->nextSibling() : nullptr;
592  }
593 
594  // recurse into positioned block children as well.
595  if (m_positionedObjects) {
596  RenderObject *obj;
597  QListIterator<RenderObject *> it(*m_positionedObjects);
598  while (it.hasNext()) {
599  obj = it.next();
600  if (obj->isRenderBlock() && !obj->childrenInline()) {
601  static_cast<RenderBlock *>(obj)->makePageBreakAvoidBlocks();
602  }
603  }
604  }
605 
606  // recurse into floating block children.
607  if (m_floatingObjects) {
608  FloatingObject *obj;
609  QListIterator<FloatingObject *> it(*m_floatingObjects);
610  while (it.hasNext()) {
611  obj = it.next();
612  if (obj->node->isRenderBlock() && !obj->node->childrenInline()) {
613  static_cast<RenderBlock *>(obj->node)->makePageBreakAvoidBlocks();
614  }
615  }
616  }
617 }
618 
619 void RenderBlock::removeChild(RenderObject *oldChild)
620 {
621  // If this child is a block, and if our previous and next siblings are
622  // both anonymous blocks with inline content, then we can go ahead and
623  // fold the inline content back together.
624  RenderObject *prev = oldChild->previousSibling();
625  RenderObject *next = oldChild->nextSibling();
626  RenderObject *lc = nullptr;
627  bool mergedBlocks = false;
628  bool checkContinuationMerge = false;
629  if (!documentBeingDestroyed() && !isInline() && !oldChild->isInline() && !oldChild->continuation()) {
630  if (prev && prev->isAnonymousBlock() && prev->childrenInline() &&
631  next && next->isAnonymousBlock() && next->childrenInline()) {
632  // Take all the children out of the |next| block and put them in
633  // the |prev| block.
634  RenderObject *o = next->firstChild();
635  while (o) {
636  RenderObject *no = o;
637  o = no->nextSibling();
638  prev->appendChildNode(next->removeChildNode(no));
639  }
640 
641  // Detach the now-empty block.
642  static_cast<RenderBlock *>(next)->deleteLineBoxTree();
643  next->detach();
644 
645  mergedBlocks = true;
646  }
647 
648  // Check if there are continuations we could merge
649  checkContinuationMerge = (mergedBlocks || (!prev && !next)) && continuation() && isAnonymousBlock() && continuation()->isRenderInline() &&
650  previousSibling() && previousSibling()->isAnonymousBlock() && (lc = previousSibling()->lastChild());
651 
652  if (checkContinuationMerge) {
653  while (lc->lastChild() && lc->continuation()) {
654  lc = lc->lastChild();
655  }
656  checkContinuationMerge = lc->isRenderInline() && lc->continuation() && (lc->continuation() == this);
657  }
658  if (checkContinuationMerge) {
659  RenderObject *prev = lc->parent();
660  RenderObject *cont = continuation()->parent();
661  while (prev && cont) {
662  if (prev == previousSibling() && cont == nextSibling() && cont->isAnonymousBlock()) {
663  break;
664  }
665  if (!prev->continuation() || prev->continuation() != cont) {
666  checkContinuationMerge = false;
667  break;
668  }
669  prev = prev->parent();
670  cont = cont->parent();
671  }
672  }
673  }
674 
675  RenderFlow::removeChild(oldChild);
676 
677  if (mergedBlocks && prev && !prev->previousSibling() && !prev->nextSibling()) {
678  // The remerge has knocked us down to containing only a single anonymous
679  // box. We can go ahead and pull the content right back up into our
680  // box.
681  RenderBlock *anonBlock = static_cast<RenderBlock *>(prev);
682  m_childrenInline = true;
683  RenderObject *o = anonBlock->firstChild();
684  while (o) {
685  RenderObject *no = o;
686  o = no->nextSibling();
687  appendChildNode(anonBlock->removeChildNode(no));
688  }
689 
690  // Detach the now-empty block.
691  anonBlock->deleteLineBoxTree();
692  anonBlock->detach();
693  }
694  if (checkContinuationMerge && ((!prev && !next) || m_childrenInline)) {
695  // |oldChild| was a block that split an inline into continuations.
696  // Now that we only have inline content left, we may merge back those continuations
697  // into a single inline.
698  assert(lc->isRenderInline());
699  RenderFlow *prev = static_cast<RenderFlow *>(lc);
700  while (RenderFlow *next = prev->continuation()) {
701  RenderObject *o = next->firstChild();
702  while (o) {
703  RenderObject *no = o;
704  o = no->nextSibling();
705  prev->appendChildNode(next->removeChildNode(no));
706  }
707  prev->setContinuation(next->continuation());
708  next->setContinuation(nullptr);
709  if (next != this) {
710  next->detach();
711  prev = static_cast<RenderFlow *>(prev->parent());
712  assert(!prev || prev->isRenderInline() || prev->isRenderBlock());
713  }
714  }
715  assert(nextSibling() && nextSibling()->isAnonymousBlock());
716  if (!nextSibling()->firstChild()) {
717  static_cast<RenderBlock *>(nextSibling())->deleteLineBoxTree();
718  nextSibling()->detach();
719  }
720  deleteLineBoxTree();
721  detach();
722  }
723 }
724 
725 bool RenderBlock::isSelfCollapsingBlock() const
726 {
727  // We are not self-collapsing if we
728  // (a) have a non-zero height according to layout (an optimization to avoid wasting time)
729  // (b) are a table,
730  // (c) have border/padding,
731  // (d) have a min-height
732  if (m_height > 0 ||
733  isTable() || (borderBottom() + paddingBottom() + borderTop() + paddingTop()) != 0 ||
734  style()->minHeight().isPositive()) {
735  return false;
736  }
737 
738  bool hasAutoHeight = style()->height().isAuto();
739  if (style()->height().isPercent() && !style()->htmlHacks()) {
740  hasAutoHeight = true;
741  for (RenderBlock *cb = containingBlock(); !cb->isCanvas(); cb = cb->containingBlock()) {
742  if (cb->style()->height().isFixed() || cb->isTableCell()) {
743  hasAutoHeight = false;
744  }
745  }
746  }
747 
748  // If the height is 0 or auto, then whether or not we are a self-collapsing block depends
749  // on whether we have content that is all self-collapsing or not.
750  if (hasAutoHeight || ((style()->height().isFixed() || style()->height().isPercent()) && style()->height().isZero())) {
751  // If the block has inline children, see if we generated any line boxes. If we have any
752  // line boxes, then we can't be self-collapsing, since we have content.
753  if (childrenInline()) {
754  return !firstLineBox();
755  }
756 
757  // Whether or not we collapse is dependent on whether all our normal flow children
758  // are also self-collapsing.
759  for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
760  if (child->isFloatingOrPositioned()) {
761  continue;
762  }
763  if (!child->isSelfCollapsingBlock()) {
764  return false;
765  }
766  }
767  return true;
768  }
769  return false;
770 }
771 
772 void RenderBlock::layout()
773 {
774  // Table cells call layoutBlock directly, so don't add any logic here. Put code into
775  // layoutBlock().
776  layoutBlock(false);
777 }
778 
779 void RenderBlock::layoutBlock(bool relayoutChildren)
780 {
781  if (isInline() && !isReplacedBlock()) {
782  setNeedsLayout(false);
783  return;
784  }
785  // qCDebug(KHTML_LOG) << renderName() << " " << this << "::layoutBlock() start";
786  // QTime t;
787  // t.start();
788  KHTMLAssert(needsLayout());
789  KHTMLAssert(minMaxKnown());
790 
791  if (canvas()->pagedMode()) {
792  relayoutChildren = true;
793  }
794 
795  if (markedForRepaint()) {
796  repaintDuringLayout();
797  setMarkedForRepaint(false);
798  }
799 
800  if (!relayoutChildren && posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
801  // All we have to is lay out our positioned objects.
802  layoutPositionedObjects(relayoutChildren);
803  if (hasOverflowClip()) {
804  m_layer->checkScrollbarsAfterLayout();
805  }
806  setNeedsLayout(false);
807  return;
808  }
809 
810  int oldWidth = m_width;
811 
812  calcWidth();
813  m_overflowWidth = m_width;
814  m_overflowLeft = 0;
815  if (style()->direction() == LTR) {
816  int cw = 0;
817  if (style()->textIndent().isPercent()) {
818  cw = containingBlock()->contentWidth();
819  }
820  m_overflowLeft = qMin(0, style()->textIndent().minWidth(cw));
821  }
822 
823  if (oldWidth != m_width) {
824  relayoutChildren = true;
825  }
826 
827  // qCDebug(KHTML_LOG) << floatingObjects << "," << oldWidth << ","
828  // << m_width << ","<< needsLayout() << "," << isAnonymousBox() << ","
829  // << isPositioned();
830 
831 #ifdef DEBUG_LAYOUT
832  qCDebug(KHTML_LOG) << renderName() << "(RenderBlock) " << this << " ::layout() width=" << m_width << ", needsLayout=" << needsLayout();
833  if (containingBlock() == static_cast<RenderObject *>(this)) {
834  qCDebug(KHTML_LOG) << renderName() << ": containingBlock == this";
835  }
836 #endif
837 
838  clearFloats();
839 
840  int previousHeight = m_height;
841  m_height = 0;
842  m_overflowHeight = 0;
843  m_clearStatus = CNONE;
844 
845  // We use four values, maxTopPos, maxPosNeg, maxBottomPos, and maxBottomNeg, to track
846  // our current maximal positive and negative margins. These values are used when we
847  // are collapsed with adjacent blocks, so for example, if you have block A and B
848  // collapsing together, then you'd take the maximal positive margin from both A and B
849  // and subtract it from the maximal negative margin from both A and B to get the
850  // true collapsed margin. This algorithm is recursive, so when we finish layout()
851  // our block knows its current maximal positive/negative values.
852  //
853  // Start out by setting our margin values to our current margins. Table cells have
854  // no margins, so we don't fill in the values for table cells.
855  if (!isTableCell()) {
856  initMaxMarginValues();
857 
858  m_topMarginQuirk = style()->marginTop().isQuirk();
859  m_bottomMarginQuirk = style()->marginBottom().isQuirk();
860 
861  if (element() && element()->id() == ID_FORM && static_cast<HTMLFormElementImpl *>(element())->isMalformed())
862  // See if this form is malformed (i.e., unclosed). If so, don't give the form
863  // a bottom margin.
864  {
865  m_maxBottomPosMargin = m_maxBottomNegMargin = 0;
866  }
867  }
868 
869  if (scrollsOverflow() && m_layer) {
870  // For overflow:scroll blocks, ensure we have both scrollbars in place always.
871  if (style()->overflowX() == OSCROLL) {
872  m_layer->showScrollbar(Qt::Horizontal, true);
873  }
874  if (style()->overflowY() == OSCROLL) {
875  m_layer->showScrollbar(Qt::Vertical, true);
876  }
877  }
878 
879  setContainsPageBreak(false);
880 
881  if (childrenInline()) {
882  layoutInlineChildren(relayoutChildren);
883  } else {
884  layoutBlockChildren(relayoutChildren);
885  }
886 
887  // Expand our intrinsic height to encompass floats.
888  int toAdd = borderBottom() + paddingBottom();
889  if (m_layer && scrollsOverflowX() && style()->height().isAuto()) {
890  toAdd += m_layer->horizontalScrollbarHeight();
891  }
892  if (floatBottom() + toAdd > m_height && (isFloatingOrPositioned() || flowAroundFloats())) {
893  m_overflowHeight = m_height = floatBottom() + toAdd;
894  }
895 
896  int oldHeight = m_height;
897  calcHeight();
898  if (oldHeight != m_height) {
899  m_overflowHeight -= toAdd;
900  if (m_layer && scrollsOverflowY()) {
901  // overflow-height only includes padding-bottom when it scrolls
902  m_overflowHeight += paddingBottom();
903  }
904  // If the block got expanded in size, then increase our overflowheight to match.
905  if (m_overflowHeight < m_height) {
906  m_overflowHeight = m_height;
907  }
908  }
909  if (previousHeight != m_height) {
910  relayoutChildren = true;
911  }
912 
913  if (isTableCell()) {
914  // Table cells need to grow to accommodate both overhanging floats and
915  // blocks that have overflowed content.
916  // Check for an overhanging float first.
917  // FIXME: This needs to look at the last flow, not the last child.
918  if (lastChild() && lastChild()->hasOverhangingFloats() && !lastChild()->hasOverflowClip()) {
919  KHTMLAssert(lastChild()->isRenderBlock());
920  m_height = lastChild()->yPos() + static_cast<RenderBlock *>(lastChild())->floatBottom();
921  m_height += borderBottom() + paddingBottom();
922  }
923 
924  if (m_overflowHeight > m_height && !hasOverflowClip()) {
925  m_height = m_overflowHeight + borderBottom() + paddingBottom();
926  }
927  }
928 
929  if (hasOverhangingFloats() && ((isFloating() && style()->height().isAuto()) || isTableCell())) {
930  m_height = floatBottom();
931  m_height += borderBottom() + paddingBottom();
932  }
933 
934  if (canvas()->pagedMode()) {
935 #ifdef PAGE_DEBUG
936  qCDebug(KHTML_LOG) << renderName() << " Page Bottom: " << pageTopAfter(0);
937  qCDebug(KHTML_LOG) << renderName() << " Bottom: " << m_height;
938 #endif
939  bool needsPageBreak = false;
940  int xpage = crossesPageBreak(0, m_height);
941  if (xpage) {
942  needsPageBreak = true;
943 #ifdef PAGE_DEBUG
944  qCDebug(KHTML_LOG) << renderName() << " crosses to page " << xpage;
945 #endif
946  }
947  if (needsPageBreak && !containsPageBreak()) {
948  setNeedsPageClear(true);
949 #ifdef PAGE_DEBUG
950  qCDebug(KHTML_LOG) << renderName() << " marked for page-clear";
951 #endif
952  }
953  }
954 
955  layoutPositionedObjects(relayoutChildren);
956 
957  // Always ensure our overflow width/height are at least as large as our width/height.
958  m_overflowWidth = qMax(m_overflowWidth, (int)m_width);
959  m_overflowHeight = qMax(m_overflowHeight, m_height);
960 
961  // Update our scrollbars if we're overflow:auto/scroll now that we know if
962  // we overflow or not.
963  if (hasOverflowClip() && m_layer) {
964  m_layer->checkScrollbarsAfterLayout();
965  }
966 
967  setNeedsLayout(false);
968 }
969 
970 void RenderBlock::adjustPositionedBlock(RenderObject *child, const MarginInfo &marginInfo)
971 {
972  if (child->isBox() && child->hasStaticX()) {
973  if (style()->direction() == LTR) {
974  static_cast<RenderBox *>(child)->setStaticX(borderLeft() + paddingLeft());
975  } else {
976  static_cast<RenderBox *>(child)->setStaticX(borderRight() + paddingRight());
977  }
978  }
979 
980  if (child->isBox() && child->hasStaticY()) {
981  int y = m_height;
982  if (!marginInfo.canCollapseWithTop()) {
983  child->calcVerticalMargins();
984  int marginTop = child->marginTop();
985  int collapsedTopPos = marginInfo.posMargin();
986  int collapsedTopNeg = marginInfo.negMargin();
987  if (marginTop > 0) {
988  if (marginTop > collapsedTopPos) {
989  collapsedTopPos = marginTop;
990  }
991  } else {
992  if (-marginTop > collapsedTopNeg) {
993  collapsedTopNeg = -marginTop;
994  }
995  }
996  y += (collapsedTopPos - collapsedTopNeg) - marginTop;
997  }
998  static_cast<RenderBox *>(child)->setStaticY(y);
999  }
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 
bool isRightToLeft()
QString & fill(QChar ch, int size)
This file is part of the HTML rendering engine for KDE.
QTextStream & right(QTextStream &stream)
This class is khtml&#39;s main class.
Definition: khtml_part.h:208
MESSAGECORE_EXPORT KMime::Content * next(KMime::Content *node, bool allowChildren=true)
KGuiItem cont()
QChar::Category category() const const
const QList< QKeySequence > & begin()
QTextStream & left(QTextStream &stream)
void append(const T &value)
Type type(const QSqlDatabase &db)
MESSAGECORE_EXPORT KMime::Content * firstChild(const KMime::Content *node)
bool isPunct() const const
This library provides a full-featured HTML parser and widget.
bool isCaretMode() const
Returns whether caret mode is on/off.
Content * parent() const
Horizontal
MESSAGECORE_EXPORT KMime::Content * nextSibling(const KMime::Content *node)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:06 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.