2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * Copyright (C) 2004 Apple Computer, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 // -------------------------------------------------------------------------
25 //#define DEBUG_LAYOUT
30 #include "rendering/render_box.h"
31 #include "rendering/render_replaced.h"
32 #include "rendering/render_canvas.h"
33 #include "rendering/render_table.h"
34 #include "render_flexbox.h"
35 #include "render_arena.h"
37 #include "misc/htmlhashes.h"
38 #include "xml/dom_nodeimpl.h"
39 #include "xml/dom_docimpl.h"
40 #include "html/html_elementimpl.h"
42 #include "render_line.h"
44 #include <khtmlview.h>
50 using namespace khtml;
52 #define TABLECELLMARGIN -0x4000
54 RenderBox::RenderBox(DOM::NodeImpl* node)
60 m_width = m_height = 0;
70 m_inlineBoxWrapper = 0;
73 void RenderBox::setStyle(RenderStyle *_style)
75 RenderObject::setStyle(_style);
77 // The root always paints its background/border.
79 setShouldPaintBackgroundOrBorder(true);
81 setInline(_style->isDisplayInlineType());
83 switch(_style->position())
92 if (_style->isFloating())
95 if (_style->position() == RELATIVE)
96 setRelPositioned(true);
99 // FIXME: Note that we restrict overflow to blocks for now. One day table bodies and cells
100 // will need to support overflow.
101 // We also handle <body> and <html>, whose overflow applies to the viewport.
102 if (_style->overflow() != OVISIBLE && isBlockFlow() && !isTableCell() && !isRoot() && (!isBody() || !document()->isHTMLDocument()))
103 setHasOverflowClip();
105 if (requiresLayer()) {
107 m_layer = new (renderArena()) RenderLayer(this);
108 m_layer->insertOnlyThisLayer();
109 if (containingBlock())
110 m_layer->updateLayerPositions();
113 else if (m_layer && !isRoot() && !isCanvas()) {
114 m_layer->removeOnlyThisLayer();
119 m_layer->styleChanged();
121 // Set the text color if we're the body.
123 element()->getDocument()->setTextColor(_style->color());
125 if (style()->outlineWidth() > 0 && style()->outlineSize() > maximalOutlineSize(PaintActionOutline))
126 static_cast<RenderCanvas*>(document()->renderer())->setMaximalOutlineSize(style()->outlineSize());
129 RenderBox::~RenderBox()
131 //kdDebug( 6040 ) << "Element destructor: this=" << nodeName().string() << endl;
134 void RenderBox::detach()
136 RenderLayer* layer = m_layer;
137 RenderArena* arena = renderArena();
139 // This must be done before we detach the RenderObject.
141 layer->clearClipRect();
143 if (m_inlineBoxWrapper) {
144 if (!documentBeingDestroyed())
145 m_inlineBoxWrapper->remove();
146 m_inlineBoxWrapper->detach(arena);
147 m_inlineBoxWrapper = 0;
150 RenderObject::detach();
153 layer->detach(arena);
156 int RenderBox::contentWidth() const
158 int w = m_width - borderLeft() - borderRight();
159 w -= paddingLeft() + paddingRight();
161 if (includeScrollbarSize())
162 w -= m_layer->verticalScrollbarWidth();
164 //kdDebug( 6040 ) << "RenderBox::contentWidth(2) = " << w << endl;
168 int RenderBox::contentHeight() const
170 int h = m_height - borderTop() - borderBottom();
171 h -= paddingTop() + paddingBottom();
173 if (includeScrollbarSize())
174 h -= m_layer->horizontalScrollbarHeight();
179 int RenderBox::overrideWidth() const
181 return m_overrideSize == -1 ? m_width : m_overrideSize;
184 int RenderBox::overrideHeight() const
186 return m_overrideSize == -1 ? m_height : m_overrideSize;
189 void RenderBox::setPos( int xPos, int yPos )
191 if (xPos == m_x && yPos == m_y)
192 return; // Optimize for the case where we don't move at all.
194 m_x = xPos; m_y = yPos;
197 int RenderBox::width() const
202 int RenderBox::height() const
208 bool RenderBox::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
209 HitTestAction hitTestAction)
214 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
215 // FIXME: We have to skip over inline flows, since they can show up inside table rows at the moment (a demoted inline <form> for example). If we ever implement a
216 // table-specific hit-test method (which we should do for performance reasons anyway), then we can remove this check.
217 if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction)) {
223 // Check our bounds next. For this purpose always assume that we can only be hit in the
224 // foreground phase (which is true for replaced elements like images).
225 if (hitTestAction != HitTestForeground)
228 QRect boundsRect(_tx, _ty, m_width, m_height);
229 if (boundsRect.contains(_x, _y)) {
236 // --------------------- painting stuff -------------------------------
238 void RenderBox::paint(PaintInfo& i, int _tx, int _ty)
243 // default implementation. Just pass paint through to the children
244 for (RenderObject* child = firstChild(); child; child = child->nextSibling())
245 child->paint(i, _tx, _ty);
248 void RenderBox::paintRootBoxDecorations(PaintInfo& i, int _tx, int _ty)
250 //kdDebug( 6040 ) << renderName() << "::paintBoxDecorations()" << _tx << "/" << _ty << endl;
251 const BackgroundLayer* bgLayer = style()->backgroundLayers();
252 QColor bgColor = style()->backgroundColor();
253 if (document()->isHTMLDocument() && !style()->hasBackground()) {
254 // Locate the <body> element using the DOM. This is easier than trying
255 // to crawl around a render tree with potential :before/:after content and
256 // anonymous blocks created by inline <body> tags etc. We can locate the <body>
257 // render object very easily via the DOM.
258 HTMLElementImpl* body = document()->body();
259 RenderObject* bodyObject = (body && body->id() == ID_BODY) ? body->renderer() : 0;
261 bgLayer = bodyObject->style()->backgroundLayers();
262 bgColor = bodyObject->style()->backgroundColor();
269 // kdDebug(0) << "width = " << w <<endl;
272 if (canvas()->view()) {
273 rw = canvas()->view()->contentsWidth();
274 rh = canvas()->view()->contentsHeight();
277 rw = canvas()->width();
278 rh = canvas()->height();
281 // kdDebug(0) << "rw = " << rw <<endl;
283 int bx = _tx - marginLeft();
284 int by = _ty - marginTop();
285 int bw = kMax(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw);
286 int bh = kMax(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh);
289 // " The background of the box generated by the root element covers the entire canvas."
290 // hence, paint the background even in the margin areas (unlike for every other element!)
291 // I just love these little inconsistencies .. :-( (Dirk)
292 int my = kMax(by, i.r.y());
294 paintBackgrounds(i.p, bgColor, bgLayer, my, i.r.height(), bx, by, bw, bh);
296 if (style()->hasBorder() && style()->display() != INLINE)
297 paintBorder( i.p, _tx, _ty, w, h, style() );
300 void RenderBox::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
302 if (!shouldPaintWithinRoot(i))
305 //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
307 return paintRootBoxDecorations(i, _tx, _ty);
310 int h = height() + borderTopExtra() + borderBottomExtra();
311 _ty -= borderTopExtra();
313 int my = kMax(_ty, i.r.y());
316 mh= kMax(0, h - (i.r.y() - _ty));
318 mh = kMin(i.r.height(), h);
320 // The <body> only paints its background if the root element has defined a background
321 // independent of the body. Go through the DOM to get to the root element's render object,
322 // since the root could be inline and wrapped in an anonymous block.
323 if (!isBody() || !document()->isHTMLDocument() || document()->documentElement()->renderer()->style()->hasBackground())
324 paintBackgrounds(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
326 if (style()->hasBorder())
327 paintBorder(i.p, _tx, _ty, w, h, style());
330 void RenderBox::paintBackgrounds(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int height)
332 if (!bgLayer) return;
333 paintBackgrounds(p, c, bgLayer->next(), clipy, cliph, _tx, _ty, w, height);
334 paintBackground(p, c, bgLayer, clipy, cliph, _tx, _ty, w, height);
337 void RenderBox::paintBackground(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int height)
339 paintBackgroundExtended(p, c, bgLayer, clipy, cliph, _tx, _ty, w, height,
340 borderLeft(), borderRight());
343 void RenderBox::paintBackgroundExtended(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, int clipy, int cliph,
344 int _tx, int _ty, int w, int h,
345 int bleft, int bright)
347 CachedImage* bg = bgLayer->backgroundImage();
348 bool shouldPaintBackgroundImage = bg && bg->pixmap_size() == bg->valid_rect().size() && !bg->isTransparent() && !bg->isErrorImage();
351 // When this style flag is set, change existing background colors and images to a solid white background.
352 // If there's no bg color or image, leave it untouched to avoid affecting transparency.
353 // We don't try to avoid loading the background images, because this style flag is only set
354 // when printing, and at that point we've already loaded the background images anyway. (To avoid
355 // loading the background images we'd have to do this check when applying styles rather than
357 if (style()->forceBackgroundsToWhite()) {
358 // Note that we can't reuse this variable below because the bgColor might be changed
359 bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && qAlpha(bgColor.rgb()) > 0;
360 if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
362 shouldPaintBackgroundImage = false;
366 // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
367 // no background in the child document should show the parent's background.
368 if (!bgLayer->next() && isRoot() && !(bgColor.isValid() && qAlpha(bgColor.rgb()) > 0) && canvas()->view()) {
370 DOM::NodeImpl* elt = document()->ownerElement();
372 if (elt->id() == ID_FRAME)
373 isTransparent = false;
375 // Locate the <body> element using the DOM. This is easier than trying
376 // to crawl around a render tree with potential :before/:after content and
377 // anonymous blocks created by inline <body> tags etc. We can locate the <body>
378 // render object very easily via the DOM.
379 HTMLElementImpl* body = document()->body();
380 isTransparent = !body || body->id() != ID_FRAMESET; // Can't scroll a frameset document anyway.
383 isTransparent = canvas()->view()->isTransparent();
386 canvas()->view()->useSlowRepaints(); // The parent must show behind the child.
388 bgColor = canvas()->view()->palette().active().color(QColorGroup::Base);
391 // Paint the color first underneath all images.
392 if (!bgLayer->next() && bgColor.isValid() && qAlpha(bgColor.rgb()) > 0) {
393 // If we have an alpha and we are painting the root element, go ahead and blend with our default
394 // background color (typically white).
395 if (qAlpha(bgColor.rgb()) < 0xFF && isRoot() && !canvas()->view()->isTransparent())
396 p->fillRect(_tx, clipy, w, cliph, canvas()->view()->palette().active().color(QColorGroup::Base));
397 p->fillRect(_tx, clipy, w, cliph, bgColor);
400 // no progressive loading of the background image
401 if (shouldPaintBackgroundImage) {
406 int vpab = bleft + bright;
407 int hpab = borderTop() + borderBottom();
409 // CSS2 chapter 14.2.1
411 if (bgLayer->backgroundAttachment())
417 int pixw = bg->pixmap_size().width();
418 int pixh = bg->pixmap_size().height();
419 EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
420 if( (bgr == NO_REPEAT || bgr == REPEAT_Y) && w > pixw ) {
422 int xPosition = bgLayer->backgroundXPosition().minWidth(pw-pixw);
424 cx = _tx + xPosition;
441 sx = pixw - ((bgLayer->backgroundXPosition().minWidth(pw-pixw)) % pixw );
446 if( (bgr == NO_REPEAT || bgr == REPEAT_X) && h > pixh ) {
448 int yPosition = bgLayer->backgroundYPosition().minWidth(ph-pixh);
450 cy = _ty + yPosition;
468 sy = pixh - ((bgLayer->backgroundYPosition().minWidth(ph-pixh)) % pixh );
469 sy -= borderTop() % pixh;
476 QRect vr = viewRect();
478 int ph = vr.height();
480 int pixw = bg->pixmap_size().width();
481 int pixh = bg->pixmap_size().height();
482 EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
483 if( (bgr == NO_REPEAT || bgr == REPEAT_Y) && pw > pixw ) {
485 cx = vr.x() + bgLayer->backgroundXPosition().minWidth(pw-pixw);
492 sx = pixw - ((bgLayer->backgroundXPosition().minWidth(pw-pixw)) % pixw );
496 if( (bgr == NO_REPEAT || bgr == REPEAT_X) && ph > pixh ) {
498 cy = vr.y() + bgLayer->backgroundYPosition().minWidth(ph-pixh);
505 sy = pixh - ((bgLayer->backgroundYPosition().minWidth(ph-pixh)) % pixh );
509 QRect fix(cx,cy,cw,ch);
510 QRect ele(_tx,_ty,w,h);
511 QRect b = fix.intersect(ele);
514 cx=b.x();cy=b.y();cw=b.width();ch=b.height();
518 // kdDebug() << "cx="<<cx << " cy="<<cy<< " cw="<<cw << " ch="<<ch << " sx="<<sx << " sy="<<sy << endl;
521 p->drawTiledPixmap(cx, cy, cw, ch, bg->tiled_pixmap(c), sx, sy);
525 void RenderBox::outlineBox(QPainter *p, int _tx, int _ty, const char *color)
527 p->setPen(QPen(QColor(color), 1, Qt::DotLine));
528 p->setBrush( Qt::NoBrush );
529 p->drawRect(_tx, _ty, m_width, m_height);
532 QRect RenderBox::getOverflowClipRect(int tx, int ty)
534 // XXX When overflow-clip (CSS3) is implemented, we'll obtain the property
536 int bl=borderLeft(),bt=borderTop(),bb=borderBottom(),br=borderRight();
539 int clipw = m_width-bl-br;
540 int cliph = m_height-bt-bb;
542 // Subtract out scrollbars if we have them.
544 clipw -= m_layer->verticalScrollbarWidth();
545 cliph -= m_layer->horizontalScrollbarHeight();
547 return QRect(clipx,clipy,clipw,cliph);
550 QRect RenderBox::getClipRect(int tx, int ty)
555 int cliph = m_height;
557 if (!style()->clipLeft().isVariable())
559 int c=style()->clipLeft().width(m_width);
564 if (!style()->clipRight().isVariable())
566 int w = style()->clipRight().width(m_width);
567 clipw -= m_width - w;
570 if (!style()->clipTop().isVariable())
572 int c=style()->clipTop().width(m_height);
576 if (!style()->clipBottom().isVariable())
578 int h = style()->clipBottom().width(m_height);
579 cliph -= m_height - h;
581 //kdDebug( 6040 ) << "setting clip("<<clipx<<","<<clipy<<","<<clipw<<","<<cliph<<")"<<endl;
583 QRect cr(clipx,clipy,clipw,cliph);
587 int RenderBox::containingBlockWidth() const
589 RenderBlock* cb = containingBlock();
593 return cb->lineWidth(m_y);
595 return cb->contentWidth();
598 bool RenderBox::absolutePosition(int &xPos, int &yPos, bool f)
600 if (style()->position() == FIXED)
602 RenderObject *o = container();
603 if (o && o->absolutePosition(xPos, yPos, f)) {
604 if (o->hasOverflowClip())
605 o->layer()->subtractScrollOffset(xPos, yPos);
607 if (!isInline() || isReplaced())
608 xPos += m_x, yPos += m_y;
610 if (isRelPositioned())
611 relativePositionOffset(xPos, yPos);
621 void RenderBox::dirtyLineBoxes(bool fullLayout, bool)
623 if (m_inlineBoxWrapper) {
625 m_inlineBoxWrapper->detach(renderArena());
626 m_inlineBoxWrapper = 0;
629 m_inlineBoxWrapper->dirtyLineBoxes();
633 void RenderBox::position(InlineBox* box, int from, int len, bool reverse)
635 if (isPositioned()) {
636 // Cache the x position only if we were an INLINE type originally.
637 bool wasInline = style()->isOriginalDisplayInlineType();
638 if (wasInline && hasStaticX()) {
639 // The value is cached in the xPos of the box. We only need this value if
640 // our object was inline originally, since otherwise it would have ended up underneath
642 m_staticX = box->xPos();
644 else if (!wasInline && hasStaticY())
645 // Our object was a block originally, so we make our normal flow position be
646 // just below the line box (as though all the inlines that came before us got
647 // wrapped in an anonymous block, which is what would have happened had we been
648 // in flow). This value was cached in the yPos() of the box.
649 m_staticY = box->yPos();
653 box->detach(renderArena());
655 else if (isReplaced()) {
658 m_inlineBoxWrapper = box;
662 // For inline replaced elements, this function returns the inline box that owns us. Enables
663 // the replaced RenderObject to quickly determine what line it is contained on and to easily
664 // iterate over structures on the line.
665 InlineBox* RenderBox::inlineBoxWrapper() const
667 return m_inlineBoxWrapper;
670 void RenderBox::deleteLineBoxWrapper()
672 if (m_inlineBoxWrapper)
673 m_inlineBoxWrapper->detach(renderArena());
674 m_inlineBoxWrapper = 0;
677 void RenderBox::setInlineBoxWrapper(InlineBox* b)
679 m_inlineBoxWrapper = b;
682 QRect RenderBox::getAbsoluteRepaintRect()
684 int ow = style() ? style()->outlineSize() : 0;
685 QRect r(-ow, -ow, overflowWidth(false)+ow*2, overflowHeight(false)+ow*2);
686 computeAbsoluteRepaintRect(r);
690 void RenderBox::computeAbsoluteRepaintRect(QRect& r, bool f)
695 // Apply the relative position offset when invalidating a rectangle. The layer
696 // is translated, but the render box isn't, so we need to do this to get the
697 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
698 // flag on the RenderObject has been cleared, so use the one on the style().
699 if (style()->position() == RELATIVE && m_layer)
700 m_layer->relativePositionOffset(x,y);
702 if (style()->position()==FIXED)
705 RenderObject* o = container();
707 // <body> may not have overflow, since it might be applying its overflow value to the
709 if (o->hasOverflowClip()) {
710 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
711 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
712 // anyway if its size does change.
713 QRect boxRect(0, 0, o->layer()->width(), o->layer()->height());
714 o->layer()->subtractScrollOffset(x,y); // For overflow:auto/scroll/hidden.
715 QRect repaintRect(x, y, r.width(), r.height());
716 r = repaintRect.intersect(boxRect);
724 o->computeAbsoluteRepaintRect(r, f);
728 void RenderBox::repaintDuringLayoutIfMoved(int oldX, int oldY)
732 if (oldX != newX || oldY != newY) {
733 // The child moved. Invalidate the object's old and new positions. We have to do this
734 // since the object may not have gotten a layout.
735 m_x = oldX; m_y = oldY;
737 repaintFloatingDescendants();
738 m_x = newX; m_y = newY;
740 repaintFloatingDescendants();
744 void RenderBox::relativePositionOffset(int &tx, int &ty)
746 if(!style()->left().isVariable())
747 tx += style()->left().width(containingBlockWidth());
748 else if(!style()->right().isVariable())
749 tx -= style()->right().width(containingBlockWidth());
750 if(!style()->top().isVariable())
752 if (!style()->top().isPercent()
753 || containingBlock()->style()->height().isFixed())
754 ty += style()->top().width(containingBlockHeight());
756 else if(!style()->bottom().isVariable())
758 if (!style()->bottom().isPercent()
759 || containingBlock()->style()->height().isFixed())
760 ty -= style()->bottom().width(containingBlockHeight());
764 void RenderBox::calcWidth()
767 kdDebug( 6040 ) << "RenderBox("<<renderName()<<")::calcWidth()" << endl;
771 calcAbsoluteHorizontal();
775 // The parent box is flexing us, so it has increased or decreased our width. Use the width
776 // from the style context.
777 if (m_overrideSize != -1 && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
778 && parent()->isFlexingChildren()) {
779 m_width = m_overrideSize;
783 bool inVerticalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL;
784 bool stretching = parent()->style()->boxAlign() == BSTRETCH;
785 bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable() &&
786 (!inVerticalBox || !stretching);
789 w = Length( calcReplacedWidth(), Fixed );
791 w = style()->width();
793 Length ml = style()->marginLeft();
794 Length mr = style()->marginRight();
796 RenderBlock *cb = containingBlock();
797 int cw = containingBlockWidth();
804 if (isInline() && !isInlineBlockOrInlineTable())
806 // just calculate margins
807 m_marginLeft = ml.minWidth(cw);
808 m_marginRight = mr.minWidth(cw);
811 m_width = w.width(cw);
812 m_width += paddingLeft() + paddingRight() + borderLeft() + borderRight();
813 if(m_width < m_minWidth) m_width = m_minWidth;
819 LengthType widthType, minWidthType, maxWidthType;
820 if (treatAsReplaced) {
821 m_width = w.width(cw);
822 m_width += paddingLeft() + paddingRight() + borderLeft() + borderRight();
825 m_width = calcWidthUsing(Width, cw, widthType);
826 int minW = calcWidthUsing(MinWidth, cw, minWidthType);
827 int maxW = style()->maxWidth().value == UNDEFINED ?
828 m_width : calcWidthUsing(MaxWidth, cw, maxWidthType);
830 if (m_width > maxW) {
832 widthType = maxWidthType;
834 if (m_width < minW) {
836 widthType = minWidthType;
840 if (widthType == Variable) {
841 // kdDebug( 6040 ) << "variable" << endl;
842 m_marginLeft = ml.minWidth(cw);
843 m_marginRight = mr.minWidth(cw);
847 // kdDebug( 6040 ) << "non-variable " << w.type << ","<< w.value << endl;
848 calcHorizontalMargins(ml,mr,cw);
852 if (cw && cw != m_width + m_marginLeft + m_marginRight && !isFloating() && !isInline() &&
853 !cb->isFlexibleBox())
855 if (cb->style()->direction()==LTR)
856 m_marginRight = cw - m_width - m_marginLeft;
858 m_marginLeft = cw - m_width - m_marginRight;
863 kdDebug( 6040 ) << "RenderBox::calcWidth(): m_width=" << m_width << " containingBlockWidth()=" << containingBlockWidth() << endl;
864 kdDebug( 6040 ) << "m_marginLeft=" << m_marginLeft << " m_marginRight=" << m_marginRight << endl;
868 int RenderBox::calcWidthUsing(WidthType widthType, int cw, LengthType& lengthType)
872 if (widthType == Width)
873 w = style()->width();
874 else if (widthType == MinWidth)
875 w = style()->minWidth();
877 w = style()->maxWidth();
881 if (lengthType == Variable) {
882 int marginLeft = style()->marginLeft().minWidth(cw);
883 int marginRight = style()->marginRight().minWidth(cw);
884 if (cw) width = cw - marginLeft - marginRight;
886 if (sizesToMaxWidth()) {
887 if (width < m_minWidth)
889 if (width > m_maxWidth)
896 width += paddingLeft() + paddingRight() + borderLeft() + borderRight();
902 void RenderBox::calcHorizontalMargins(const Length& ml, const Length& mr, int cw)
904 if (isFloating() || isInline()) // Inline blocks/tables and floats don't have their margins increased.
906 m_marginLeft = ml.minWidth(cw);
907 m_marginRight = mr.minWidth(cw);
911 if ( (ml.type == Variable && mr.type == Variable) ||
912 (ml.type != Variable && mr.type != Variable &&
913 containingBlock()->style()->textAlign() == KHTML_CENTER) )
915 m_marginLeft = (cw - m_width)/2;
916 if (m_marginLeft<0) m_marginLeft=0;
917 m_marginRight = cw - m_width - m_marginLeft;
919 else if (mr.type == Variable ||
920 (ml.type != Variable && containingBlock()->style()->direction() == RTL &&
921 containingBlock()->style()->textAlign() == KHTML_LEFT))
923 m_marginLeft = ml.width(cw);
924 m_marginRight = cw - m_width - m_marginLeft;
926 else if (ml.type == Variable ||
927 (mr.type != Variable && containingBlock()->style()->direction() == LTR &&
928 containingBlock()->style()->textAlign() == KHTML_RIGHT))
930 m_marginRight = mr.width(cw);
931 m_marginLeft = cw - m_width - m_marginRight;
935 m_marginLeft = ml.minWidth(cw);
936 m_marginRight = mr.minWidth(cw);
941 void RenderBox::calcHeight()
945 kdDebug( 6040 ) << "RenderBox::calcHeight()" << endl;
948 // Cell height is managed by the table and inline non-replaced elements do not support a height property.
949 if (isTableCell() || (isInline() && !isReplaced()))
953 calcAbsoluteVertical();
956 calcVerticalMargins();
958 // For tables, calculate margins only
963 bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL;
964 bool stretching = parent()->style()->boxAlign() == BSTRETCH;
965 bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable() && (!inHorizontalBox || !stretching);
966 bool checkMinMaxHeight = false;
968 // The parent box is flexing us, so it has increased or decreased our height. We have to
969 // grab our cached flexible height.
970 if (m_overrideSize != -1 && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL
971 && parent()->isFlexingChildren())
972 h = Length(m_overrideSize - borderTop() - borderBottom() - paddingTop() - paddingBottom(), Fixed);
973 else if (treatAsReplaced)
974 h = Length(calcReplacedHeight(), Fixed);
976 h = style()->height();
977 checkMinMaxHeight = true;
980 // Block children of horizontal flexible boxes fill the height of the box.
981 if (h.isVariable() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
982 && parent()->isStretchingChildren()) {
983 h = Length(parent()->contentHeight() - marginTop() - marginBottom() -
984 borderTop() - paddingTop() - borderBottom() - paddingBottom(), Fixed);
985 checkMinMaxHeight = false;
989 if (checkMinMaxHeight) {
990 height = calcHeightUsing(style()->height());
991 int minH = calcHeightUsing(style()->minHeight());
992 int maxH = style()->maxHeight().value == UNDEFINED ? height : calcHeightUsing(style()->maxHeight());
993 height = kMin(maxH, height);
994 height = kMax(minH, height);
997 // The only times we don't check min/max height are when a fixed length has
998 // been given as an override. Just use that.
999 height = h.value + borderTop() + paddingTop() + borderBottom() + paddingBottom();
1003 // Unfurling marquees override with the furled height.
1004 if (style()->overflow() == OMARQUEE && m_layer && m_layer->marquee() &&
1005 m_layer->marquee()->isUnfurlMarquee() && !m_layer->marquee()->isHorizontal()) {
1006 m_layer->marquee()->setEnd(m_height);
1007 m_height = kMin(m_height, m_layer->marquee()->unfurlPos());
1010 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the
1011 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height
1013 if (style()->htmlHacks() && style()->height().isVariable() &&
1014 !isFloatingOrPositioned() && (isRoot() || isBody())) {
1015 int margins = collapsedMarginTop() + collapsedMarginBottom();
1016 int visHeight = canvas()->view()->visibleHeight();
1018 m_height = kMax(m_height, visHeight - margins);
1020 m_height = kMax(m_height, visHeight -
1021 (margins + parent()->marginTop() + parent()->marginBottom() +
1022 parent()->borderTop() + parent()->borderBottom() +
1023 parent()->paddingTop() + parent()->paddingBottom()));
1027 int RenderBox::calcHeightUsing(const Length& h)
1029 if (!h.isVariable()) {
1033 else if (h.isPercent())
1034 height = calcPercentageHeight(h);
1036 height += borderTop() + paddingTop() + borderBottom() + paddingBottom();
1043 int RenderBox::calcPercentageHeight(const Length& height)
1046 bool includeBorderPadding = isTable();
1047 RenderBlock* cb = containingBlock();
1048 if (style()->htmlHacks()) {
1049 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing
1050 // block that may have a specified height and then use it. In strict mode, this violates the
1051 // specification, which states that percentage heights just revert to auto if the containing
1052 // block has an auto height.
1053 for ( ; !cb->isCanvas() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() &&
1054 cb->style()->height().isVariable(); cb = cb->containingBlock());
1057 // Table cells violate what the CSS spec says to do with heights. Basically we
1058 // don't care if the cell specified a height or not. We just always make ourselves
1059 // be a percentage of the cell's current content height.
1060 if (cb->isTableCell()) {
1061 result = cb->overrideSize();
1063 // Normally we would let the cell size intrinsically, but scrolling overflow has to be
1064 // treated differently, since WinIE lets scrolled overflow regions shrink as needed.
1065 // While we can't get all cases right, we can at least detect when the cell has a specified
1066 // height or when the table has a specified height. In these cases we want to initially have
1067 // no size and allow the flexing of the table or the cell to its specified height to cause us
1068 // to grow to fill the space. This could end up being wrong in some cases, but it is
1069 // preferable to the alternative (sizing intrinsically and making the row end up too big).
1070 RenderTableCell* cell = static_cast<RenderTableCell*>(cb);
1071 if (scrollsOverflow() &&
1072 (!cell->style()->height().isVariable() || !cell->table()->style()->height().isVariable()))
1076 includeBorderPadding = true;
1079 // Otherwise we only use our percentage height if our containing block had a specified
1081 else if (cb->style()->height().isFixed())
1082 result = cb->style()->height().value;
1083 else if (cb->style()->height().isPercent())
1084 // We need to recur and compute the percentage height for our containing block.
1085 result = cb->calcPercentageHeight(cb->style()->height());
1086 else if (cb->isCanvas() || (cb->isBody() && style()->htmlHacks())) {
1087 // Don't allow this to affect the block' m_height member variable, since this
1088 // can get called while the block is still laying out its kids.
1089 int oldHeight = cb->height();
1091 result = cb->contentHeight();
1092 cb->setHeight(oldHeight);
1095 result = height.width(result);
1096 if (includeBorderPadding) {
1097 // It is necessary to use the border-box to match WinIE's broken
1098 // box model. This is essential for sizing inside
1099 // table cells using percentage heights.
1100 result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom());
1101 result = kMax(0, result);
1107 int RenderBox::calcReplacedWidth() const
1109 int width = calcReplacedWidthUsing(Width);
1110 int minW = calcReplacedWidthUsing(MinWidth);
1111 int maxW = style()->maxWidth().value == UNDEFINED ? width : calcReplacedWidthUsing(MaxWidth);
1122 int RenderBox::calcReplacedWidthUsing(WidthType widthType) const
1125 if (widthType == Width)
1126 w = style()->width();
1127 else if (widthType == MinWidth)
1128 w = style()->minWidth();
1130 w = style()->maxWidth();
1137 const int cw = containingBlockWidth();
1139 int result = w.minWidth(cw);
1145 return intrinsicWidth();
1149 int RenderBox::calcReplacedHeight() const
1151 int height = calcReplacedHeightUsing(Height);
1152 int minH = calcReplacedHeightUsing(MinHeight);
1153 int maxH = style()->maxHeight().value == UNDEFINED ? height : calcReplacedHeightUsing(MaxHeight);
1164 int RenderBox::calcReplacedHeightUsing(HeightType heightType) const
1167 if (heightType == Height)
1168 h = style()->height();
1169 else if (heightType == MinHeight)
1170 h = style()->minHeight();
1172 h = style()->maxHeight();
1175 return availableHeightUsing(h);
1179 return intrinsicHeight();
1183 int RenderBox::availableHeight() const
1185 return availableHeightUsing(style()->height());
1188 int RenderBox::availableHeightUsing(const Length& h) const
1194 return static_cast<const RenderCanvas*>(this)->viewportHeight();
1196 // We need to stop here, since we don't want to increase the height of the table
1197 // artificially. We're going to rely on this cell getting expanded to some new
1198 // height, and then when we lay out again we'll use the calculation below.
1199 if (isTableCell() && (h.isVariable() || h.isPercent())) {
1200 return overrideSize() - (borderLeft()+borderRight()+paddingLeft()+paddingRight());
1204 return h.width(containingBlock()->availableHeight());
1206 return containingBlock()->availableHeight();
1209 void RenderBox::calcVerticalMargins()
1211 if( isTableCell() ) {
1212 // table margins are basically infinite
1213 m_marginTop = TABLECELLMARGIN;
1214 m_marginBottom = TABLECELLMARGIN;
1218 Length tm = style()->marginTop();
1219 Length bm = style()->marginBottom();
1221 // margins are calculated with respect to the _width_ of
1222 // the containing block (8.3)
1223 int cw = containingBlock()->contentWidth();
1225 m_marginTop = tm.minWidth(cw);
1226 m_marginBottom = bm.minWidth(cw);
1229 void RenderBox::setStaticX(int staticX)
1231 m_staticX = staticX;
1234 void RenderBox::setStaticY(int staticY)
1236 m_staticY = staticY;
1239 void RenderBox::calcAbsoluteHorizontal()
1241 const int AUTO = -666666;
1244 int pab = borderLeft()+ borderRight()+ paddingLeft()+ paddingRight();
1248 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
1249 RenderObject* cb = container();
1250 cw = containingBlockWidth() + cb->paddingLeft() + cb->paddingRight();
1252 if (!style()->left().isVariable())
1253 l = style()->left().width(cw);
1254 if (!style()->right().isVariable())
1255 r = style()->right().width(cw);
1257 int static_distance=0;
1258 if ((parent()->style()->direction()==LTR && (l == AUTO && r == AUTO))
1259 || style()->left().isStatic())
1261 static_distance = m_staticX - cb->borderLeft(); // Should already have been set through layout of the parent().
1262 RenderObject* po = parent();
1263 for (; po && po != cb; po = po->parent())
1264 static_distance += po->xPos();
1266 if (l == AUTO || style()->left().isStatic())
1267 l = static_distance;
1270 else if ((parent()->style()->direction()==RTL && (l==AUTO && r==AUTO ))
1271 || style()->right().isStatic())
1273 RenderObject* po = parent();
1274 static_distance = m_staticX - cb->borderLeft(); // Should already have been set through layout of the parent().
1275 while (po && po!=containingBlock()) {
1276 static_distance+=po->xPos();
1280 if (r==AUTO || style()->right().isStatic())
1281 r = static_distance;
1284 calcAbsoluteHorizontalValues(Width, cb, cw, pab, static_distance, l, r, m_width, m_marginLeft, m_marginRight, m_x);
1286 // Avoid doing any work in the common case (where the values of min-width and max-width are their defaults).
1287 int minW = m_width, minML, minMR, minX;
1288 calcAbsoluteHorizontalValues(MinWidth, cb, cw, pab, static_distance, l, r, minW, minML, minMR, minX);
1290 int maxW = m_width, maxML, maxMR, maxX;
1291 if (style()->maxWidth().value != UNDEFINED)
1292 calcAbsoluteHorizontalValues(MaxWidth, cb, cw, static_distance, pab, l, r, maxW, maxML, maxMR, maxX);
1294 if (m_width > maxW) {
1296 m_marginLeft = maxML;
1297 m_marginRight = maxMR;
1301 if (m_width < minW) {
1303 m_marginLeft = minML;
1304 m_marginRight = minMR;
1309 void RenderBox::calcAbsoluteHorizontalValues(WidthType widthType, RenderObject* cb, int cw, int pab, int static_distance,
1310 int l, int r, int& w, int& ml, int& mr, int& x)
1312 const int AUTO = -666666;
1315 if (!style()->marginLeft().isVariable())
1316 ml = style()->marginLeft().width(cw);
1317 if (!style()->marginRight().isVariable())
1318 mr = style()->marginRight().width(cw);
1321 if (widthType == Width)
1322 width = style()->width();
1323 else if (widthType == MinWidth)
1324 width = style()->minWidth();
1326 width = style()->maxWidth();
1328 if (!width.isVariable())
1329 w = width.width(cw);
1330 else if (isReplaced())
1331 w = intrinsicWidth();
1333 if (l != AUTO && w != AUTO && r != AUTO) {
1334 // left, width, right all given, play with margins
1335 int ot = l + w + r + pab;
1337 if (ml==AUTO && mr==AUTO) {
1338 // both margins auto, solve for equality
1343 // solve for left margin
1346 // solve for right margin
1349 // overconstrained, solve according to dir
1350 if (style()->direction() == LTR)
1351 r = cw - ( l + w + ml + mr + pab);
1353 l = cw - ( r + w + ml + mr + pab);
1358 // one or two of (left, width, right) missing, solve
1360 // auto margins are ignored
1361 if (ml==AUTO) ml = 0;
1362 if (mr==AUTO) mr = 0;
1364 //1. solve left & width.
1365 if (l == AUTO && w == AUTO && r != AUTO) {
1366 // From section 10.3.7 of the CSS2.1 specification.
1367 // "The shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width)."
1368 w = kMin(kMax(m_minWidth - pab, cw - (r + ml + mr + pab)), m_maxWidth - pab);
1369 l = cw - (r + w + ml + mr + pab);
1373 //2. solve left & right. use static positioning.
1374 if (l == AUTO && w != AUTO && r == AUTO) {
1375 if (style()->direction()==RTL) {
1376 r = static_distance;
1377 l = cw - (r + w + ml + mr + pab);
1380 l = static_distance;
1381 r = cw - (l + w + ml + mr + pab);
1384 } //3. solve width & right.
1385 else if (l != AUTO && w == AUTO && r == AUTO) {
1386 // From section 10.3.7 of the CSS2.1 specification.
1387 // "The shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width)."
1388 w = kMin(kMax(m_minWidth - pab, cw - (l + ml + mr + pab)), m_maxWidth - pab);
1389 r = cw - (l + w + ml + mr + pab);
1394 if (l==AUTO && w!=AUTO && r!=AUTO)
1395 l = cw - (r + w + ml + mr + pab);
1398 if (l!=AUTO && w==AUTO && r!=AUTO)
1399 w = cw - (r + l + ml + mr + pab);
1403 if (l!=AUTO && w!=AUTO && r==AUTO)
1404 r = cw - (l + w + ml + mr + pab);
1408 x = l + ml + cb->borderLeft();
1411 void RenderBox::calcAbsoluteVertical()
1413 // css2 spec 10.6.4 & 10.6.5
1416 // http://www.w3.org/Style/css2-updates/REC-CSS2-19980512-errata
1417 // (actually updated 2000-10-24)
1418 // that introduces static-position value for top, left & right
1420 const int AUTO = -666666;
1425 int pab = borderTop()+borderBottom()+paddingTop()+paddingBottom();
1427 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
1428 RenderObject* cb = container();
1429 if (cb->isRoot()) // Even in strict mode (where we don't grow the root to fill the viewport) other browsers
1430 // position as though the root fills the viewport.
1431 ch = cb->availableHeight();
1433 ch = cb->height() - cb->borderTop() - cb->borderBottom();
1435 if (!style()->top().isVariable())
1436 t = style()->top().width(ch);
1437 if (!style()->bottom().isVariable())
1438 b = style()->bottom().width(ch);
1441 calcAbsoluteVerticalValues(Height, cb, ch, pab, t, b, h, mt, mb, y);
1443 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
1444 int minH = h, minMT, minMB, minY;
1445 calcAbsoluteVerticalValues(MinHeight, cb, ch, pab, t, b, minH, minMT, minMB, minY);
1447 int maxH = h, maxMT, maxMB, maxY;
1448 if (style()->maxHeight().value != UNDEFINED)
1449 calcAbsoluteVerticalValues(MaxHeight, cb, ch, pab, t, b, maxH, maxMT, maxMB, maxY);
1465 // If our natural height exceeds the new height once we've set it, then we need to make sure to update
1466 // overflow to track the spillout.
1468 setOverflowHeight(m_height);
1470 // Set our final values.
1473 m_marginBottom = mb;
1477 void RenderBox::calcAbsoluteVerticalValues(HeightType heightType, RenderObject* cb, int ch, int pab,
1478 int t, int b, int& h, int& mt, int& mb, int& y)
1480 const int AUTO = -666666;
1483 if (!style()->marginTop().isVariable())
1484 mt = style()->marginTop().width(ch);
1485 if (!style()->marginBottom().isVariable())
1486 mb = style()->marginBottom().width(ch);
1489 if (heightType == Height)
1490 height = style()->height();
1491 else if (heightType == MinHeight)
1492 height = style()->minHeight();
1494 height = style()->maxHeight();
1496 int ourHeight = m_height;
1498 if (isTable() && height.isVariable())
1499 // Height is never unsolved for tables. "auto" means shrink to fit. Use our
1501 h = ourHeight - pab;
1502 else if (!height.isVariable())
1504 h = height.width(ch);
1505 if (ourHeight - pab > h)
1506 ourHeight = h + pab;
1508 else if (isReplaced())
1509 h = intrinsicHeight();
1512 if ((t == AUTO && b == AUTO) || style()->top().isStatic()) {
1513 // calc hypothetical location in the normal flow
1514 // used for 1) top=static-position
1515 // 2) top, bottom, height are all auto -> calc top -> 3.
1516 // 3) precalc for case 2 below
1517 static_top = m_staticY - cb->borderTop(); // Should already have been set through layout of the parent().
1518 RenderObject* po = parent();
1519 for (; po && po != cb; po = po->parent())
1520 static_top += po->yPos();
1522 if (h == AUTO || style()->top().isStatic())
1526 if (t != AUTO && h != AUTO && b != AUTO) {
1527 // top, height, bottom all given, play with margins
1528 int ot = h + t + b + pab;
1530 if (mt == AUTO && mb == AUTO) {
1531 // both margins auto, solve for equality
1536 // solve for top margin
1539 // solve for bottom margin
1542 // overconstrained, solve for bottom
1543 b = ch - (h + t + mt + mb + pab);
1546 // one or two of (top, height, bottom) missing, solve
1548 // auto margins are ignored
1554 //1. solve top & height. use content height.
1555 if (t == AUTO && h == AUTO && b != AUTO) {
1556 h = ourHeight - pab;
1557 t = ch - (h + b + mt + mb + pab);
1559 else if (t == AUTO && h != AUTO && b == AUTO) //2. solve top & bottom. use static positioning.
1562 b = ch - (h + t + mt + mb + pab);
1564 else if (t != AUTO && h == AUTO && b == AUTO) //3. solve height & bottom. use content height.
1566 h = ourHeight - pab;
1567 b = ch - (h + t + mt + mb + pab);
1571 if (t == AUTO && h != AUTO && b != AUTO)
1572 t = ch - (h + b + mt + mb + pab);
1576 if (t != AUTO && h == AUTO && b != AUTO)
1577 h = ch - (t + b + mt + mb + pab);
1581 if (t != AUTO && h != AUTO && b == AUTO)
1582 b = ch - (h + t + mt + mb + pab);
1585 if (ourHeight < h + pab) //content must still fit
1586 ourHeight = h + pab;
1588 if (hasOverflowClip() && ourHeight > h + pab)
1589 ourHeight = h + pab;
1591 // Do not allow the height to be negative. This can happen when someone specifies both top and bottom
1592 // but the containing block height is less than top, e.g., top:20px, bottom:0, containing block height 16.
1593 ourHeight = kMax(0, ourHeight);
1596 y = t + mt + cb->borderTop();
1599 QRect RenderBox::caretRect(int offset, EAffinity affinity, int *extraWidthToEndOfLine)
1601 // FIXME: Is it OK to check only first child instead of picking
1602 // right child based on offset? Is it OK to pass the same offset
1603 // along to the child instead of offset 0 or whatever?
1605 // propagate it downwards to its children, someone will feel responsible
1606 RenderObject *child = firstChild();
1608 QRect result = child->caretRect(offset, affinity, extraWidthToEndOfLine);
1609 // FIXME: in-band signalling!
1610 if (result.isEmpty())
1616 // if not, use the extents of this box
1617 // offset 0 means left, offset 1 means right
1618 _x = xPos() + (offset == 0 ? 0 : m_width);
1619 InlineBox *box = inlineBoxWrapper();
1621 height = box->root()->bottomOverflow() - box->root()->topOverflow();
1622 _y = box->root()->topOverflow();
1628 // If height of box is smaller than font height, use the latter one,
1629 // otherwise the caret might become invisible.
1631 // Also, if the box is not a replaced element, always use the font height.
1632 // This prevents the "big caret" bug described in:
1633 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point
1635 // FIXME: ignoring :first-line, missing good reason to take care of
1636 int fontHeight = style()->fontMetrics().height();
1637 if (fontHeight > height || !isReplaced())
1638 height = fontHeight;
1641 RenderObject *cb = containingBlock();
1642 if (cb && cb != this && cb->absolutePosition(absx,absy)) {
1647 // we don't know our absolute position, and there is no point returning
1648 // just a relative one
1652 if (extraWidthToEndOfLine)
1653 *extraWidthToEndOfLine = m_width - _x;
1655 return QRect(_x, _y, 1, height);
1658 int RenderBox::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
1660 return includeSelf ? m_height : 0;
1663 int RenderBox::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
1665 return includeSelf ? m_width : 0;
1668 int RenderBox::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
1670 return includeSelf ? 0 : m_width;