File indexing completed on 2024-04-28 15:23:57

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
0005  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
0006  *           (C) 2002-2007 Apple Computer, Inc.
0007  *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
0008  *           (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
0009  *           (C) 2007 Germain Garand (germain@ebooksfrance.org)
0010  *           (C) 2008 Fredrik Höglund (fredrik@kde.org)
0011  *
0012  * This library is free software; you can redistribute it and/or
0013  * modify it under the terms of the GNU Library General Public
0014  * License as published by the Free Software Foundation; either
0015  * version 2 of the License, or (at your option) any later version.
0016  *
0017  * This library is distributed in the hope that it will be useful,
0018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0020  * Library General Public License for more details.
0021  *
0022  * You should have received a copy of the GNU Library General Public License
0023  * along with this library; see the file COPYING.LIB.  If not, write to
0024  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0025  * Boston, MA 02110-1301, USA.
0026  *
0027  */
0028 // -------------------------------------------------------------------------
0029 //#define DEBUG_LAYOUT
0030 //#define CLIP_DEBUG
0031 
0032 #include <QPainter>
0033 
0034 #include "misc/loader.h"
0035 #include "rendering/render_form.h"
0036 #include "rendering/render_replaced.h"
0037 #include "rendering/render_canvas.h"
0038 #include "rendering/render_table.h"
0039 #include "rendering/render_inline.h"
0040 #include "rendering/render_block.h"
0041 #include "rendering/render_line.h"
0042 #include "rendering/render_layer.h"
0043 #include "xml/dom_nodeimpl.h"
0044 #include "xml/dom_docimpl.h"
0045 #include "xml/dom2_eventsimpl.h"
0046 #include "html/html_elementimpl.h"
0047 
0048 #include <QWheelEvent>
0049 #include <khtmlview.h>
0050 #include "khtml_debug.h"
0051 #include <assert.h>
0052 
0053 using namespace DOM;
0054 using namespace khtml;
0055 
0056 #define TABLECELLMARGIN -0x4000
0057 
0058 RenderBox::RenderBox(DOM::NodeImpl *node)
0059     : RenderContainer(node)
0060 {
0061     m_minWidth = -1;
0062     m_maxWidth = -1;
0063     m_width = m_height = 0;
0064     m_x = 0;
0065     m_y = 0;
0066     m_marginTop = 0;
0067     m_marginBottom = 0;
0068     m_marginLeft = 0;
0069     m_marginRight = 0;
0070     m_staticX = 0;
0071     m_staticY = 0;
0072 
0073     m_placeHolderBox = nullptr;
0074     m_layer = nullptr;
0075 }
0076 
0077 RenderBlock *RenderBox::createAnonymousBlock()
0078 {
0079     RenderStyle *newStyle = new RenderStyle();
0080     newStyle->inheritFrom(style());
0081     newStyle->setDisplay(BLOCK);
0082 
0083     RenderBlock *newBox = new(renderArena()) RenderBlock(document() /* anonymous*/);
0084     newBox->setStyle(newStyle);
0085     return newBox;
0086 }
0087 
0088 void RenderBox::restructureParentFlow()
0089 {
0090     if (!parent() || parent()->childrenInline() == isInline()) {
0091         return;
0092     }
0093     // We have gone from not affecting the inline status of the parent flow to suddenly
0094     // having an impact.  See if there is a mismatch between the parent flow's
0095     // childrenInline() state and our state.
0096     if (!isInline()) {
0097         if (parent()->isRenderInline()) {
0098             // We have to split the parent flow.
0099             RenderInline *parentInline = static_cast<RenderInline *>(parent());
0100             RenderBlock *newBox = parentInline->createAnonymousBlock();
0101 
0102             RenderFlow *oldContinuation = parent()->continuation();
0103             parentInline->setContinuation(newBox);
0104 
0105             RenderObject *beforeChild = nextSibling();
0106             parent()->removeChildNode(this);
0107             parentInline->splitFlow(beforeChild, newBox, this, oldContinuation);
0108         } else if (parent()->isRenderBlock()) {
0109             RenderBlock *p = static_cast<RenderBlock *>(parent());
0110             p->makeChildrenNonInline();
0111             if (p->isAnonymousBlock() && p->parent()) {
0112                 p->parent()->removeSuperfluousAnonymousBlockChild(p);
0113             }
0114             // we might be deleted now
0115         }
0116     } else {
0117         // An anonymous block must be made to wrap this inline.
0118         RenderBlock *box = createAnonymousBlock();
0119         parent()->insertChildNode(box, this);
0120         box->appendChildNode(parent()->removeChildNode(this));
0121     }
0122 }
0123 
0124 static inline bool overflowAppliesTo(RenderObject *o)
0125 {
0126     // css 2.1-11.1.1
0127     // 1) overflow only applies to non-replaced block-level elements, table cells, and inline-block elements
0128     if (o->isRenderBlock() || o->isTableRow() || o->isTableSection())
0129         // 2) overflow on root applies to the viewport (cf. KHTMLView::layout)
0130         if (!o->isRoot())
0131             // 3) overflow on body may apply to the viewport...
0132             if (!o->isBody()
0133                     // ...but only for HTML documents...
0134                     || !o->document()->isHTMLDocument()
0135                     // ...and only when the root has a visible overflow
0136                     || !o->document()->documentElement()->renderer()
0137                     || !o->document()->documentElement()->renderer()->style()
0138                     ||  o->document()->documentElement()->renderer()->style()->hidesOverflow()) {
0139                 return true;
0140             }
0141 
0142     return false;
0143 }
0144 
0145 void RenderBox::setStyle(RenderStyle *_style)
0146 {
0147     bool affectsParent = style() && isFloatingOrPositioned() &&
0148                          (!_style->isFloating() && _style->position() != PABSOLUTE && _style->position() != PFIXED) &&
0149                          parent() && (parent()->isBlockFlow() || parent()->isInlineFlow());
0150 
0151     RenderContainer::setStyle(_style);
0152 
0153     // The root always paints its background/border.
0154     if (isRoot()) {
0155         setShouldPaintBackgroundOrBorder(true);
0156     }
0157 
0158     switch (_style->display()) {
0159     case INLINE:
0160     case INLINE_BLOCK:
0161     case INLINE_TABLE:
0162         setInline(true);
0163         break;
0164     case RUN_IN:
0165         if (isInline() && parent() && parent()->childrenInline()) {
0166             break;
0167         }
0168     default:
0169         setInline(false);
0170     }
0171 
0172     switch (_style->position()) {
0173     case PABSOLUTE:
0174     case PFIXED:
0175         setPositioned(true);
0176         break;
0177     default:
0178         setPositioned(false);
0179         if (!isTableCell() && _style->isFloating()) {
0180             setFloating(true);
0181         }
0182 
0183         if (_style->position() == PRELATIVE) {
0184             setRelPositioned(true);
0185         }
0186     }
0187 
0188     if (overflowAppliesTo(this) && _style->hidesOverflow()) {
0189         setHasOverflowClip();
0190     }
0191 
0192     if (requiresLayer()) {
0193         if (!m_layer) {
0194             m_layer = new(renderArena()) RenderLayer(this);
0195             m_layer->insertOnlyThisLayer();
0196             if (parent() && containingBlock()) {
0197                 m_layer->updateLayerPosition();
0198             }
0199         }
0200     } else if (m_layer && !isCanvas()) {
0201         m_layer->removeOnlyThisLayer();
0202         m_layer = nullptr;
0203     }
0204 
0205     if (m_layer) {
0206         m_layer->styleChanged();
0207     }
0208 
0209     if (style()->outlineWidth() > 0 && style()->outlineSize() > maximalOutlineSize(PaintActionOutline)) {
0210         static_cast<RenderCanvas *>(document()->renderer())->setMaximalOutlineSize(style()->outlineSize());
0211     }
0212     if (affectsParent) {
0213         restructureParentFlow();
0214     }
0215 }
0216 
0217 RenderBox::~RenderBox()
0218 {
0219     //qCDebug(KHTML_LOG) << "Element destructor: this=" << nodeName().string();
0220 }
0221 
0222 void RenderBox::detach()
0223 {
0224     RenderLayer *layer = m_layer;
0225     RenderArena *arena = renderArena();
0226 
0227     detachRemainingChildren();
0228     RenderContainer::detach();
0229 
0230     if (layer) {
0231         layer->detach(arena);
0232     }
0233 }
0234 
0235 void RenderBox::detachRemainingChildren()
0236 {
0237     while (firstChild()) {
0238         if (firstChild()->style()->styleType() == RenderStyle::FIRST_LETTER && !firstChild()->isText()) {
0239             // First letters are destroyed by their remaining text fragment.
0240             // We have to remove their references to parent here, however,
0241             // since it may be destroyed once we get to them
0242             firstChild()->remove();
0243         } else {
0244             // Destroy any (most likely anonymous) children remaining in the render tree
0245             if (firstChild()->element()) {
0246                 firstChild()->element()->setRenderer(nullptr);
0247             }
0248             firstChild()->detach();
0249         }
0250     }
0251 }
0252 
0253 void RenderBox::removeChild(RenderObject *oldChild)
0254 {
0255     // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode
0256     // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on
0257     // layout anyway).
0258     oldChild->removeFromObjectLists();
0259 
0260     removeChildNode(oldChild);
0261 }
0262 
0263 InlineBox *RenderBox::createInlineBox(bool /*makePlaceHolderBox*/, bool /*isRootLineBox*/)
0264 {
0265     if (m_placeHolderBox) {
0266         m_placeHolderBox->detach(renderArena(), true/*noRemove*/);
0267     }
0268     return (m_placeHolderBox = new(renderArena()) PlaceHolderBox(this));
0269 }
0270 
0271 void RenderBox::deleteInlineBoxes(RenderArena * /*arena*/)
0272 {
0273     if (m_placeHolderBox) {
0274         m_placeHolderBox->detach(renderArena(), true /*noRemove*/);
0275         m_placeHolderBox = nullptr;
0276     }
0277 }
0278 
0279 void RenderBox::dirtyInlineBoxes(bool fullLayout, bool /*isRootLineBox*/)
0280 {
0281     if (m_placeHolderBox) {
0282         if (fullLayout) {
0283             m_placeHolderBox->detach(renderArena(), true /*noRemove*/);
0284             m_placeHolderBox = nullptr;
0285         } else {
0286             m_placeHolderBox->dirtyInlineBoxes();
0287         }
0288     }
0289 }
0290 
0291 short RenderBox::contentWidth() const
0292 {
0293     short w = m_width - style()->borderLeftWidth() - style()->borderRightWidth();
0294     w -= paddingLeft() + paddingRight();
0295 
0296     if (m_layer && scrollsOverflowY()) {
0297         w -= m_layer->verticalScrollbarWidth();
0298     }
0299 
0300     //qCDebug(KHTML_LOG) << "RenderBox::contentWidth(2) = " << w;
0301     return w;
0302 }
0303 
0304 int RenderBox::contentHeight() const
0305 {
0306     int h = m_height - style()->borderTopWidth() - style()->borderBottomWidth();
0307     h -= paddingTop() + paddingBottom();
0308 
0309     if (m_layer && scrollsOverflowX()) {
0310         h -= m_layer->horizontalScrollbarHeight();
0311     }
0312 
0313     return h;
0314 }
0315 
0316 void RenderBox::setPos(int xPos, int yPos)
0317 {
0318     m_x = xPos; m_y = yPos;
0319 }
0320 
0321 short RenderBox::width() const
0322 {
0323     return m_width;
0324 }
0325 
0326 int RenderBox::height() const
0327 {
0328     return m_height;
0329 }
0330 
0331 void RenderBox::setWidth(int width)
0332 {
0333     m_width = width;
0334 }
0335 
0336 void RenderBox::setHeight(int height)
0337 {
0338     m_height = height;
0339 }
0340 
0341 int RenderBox::calcBoxHeight(int h) const
0342 {
0343     if (style()->boxSizing() == CONTENT_BOX) {
0344         h += borderTop() + borderBottom() + paddingTop() + paddingBottom();
0345     }
0346 
0347     return h;
0348 }
0349 
0350 int RenderBox::calcBoxWidth(int w) const
0351 {
0352     if (style()->boxSizing() == CONTENT_BOX) {
0353         w += borderLeft() + borderRight() + paddingLeft() + paddingRight();
0354     }
0355 
0356     return w;
0357 }
0358 
0359 int RenderBox::calcContentHeight(int h) const
0360 {
0361     if (style()->boxSizing() == BORDER_BOX) {
0362         h -= borderTop() + borderBottom() + paddingTop() + paddingBottom();
0363     }
0364 
0365     return qMax(0, h);
0366 }
0367 
0368 int RenderBox::calcContentWidth(int w) const
0369 {
0370     if (style()->boxSizing() == BORDER_BOX) {
0371         w -= borderLeft() + borderRight() + paddingLeft() + paddingRight();
0372     }
0373 
0374     return qMax(0, w);
0375 }
0376 
0377 // --------------------- painting stuff -------------------------------
0378 
0379 void RenderBox::paint(PaintInfo &i, int _tx, int _ty)
0380 {
0381     _tx += m_x;
0382     _ty += m_y;
0383 
0384     if (hasOverflowClip() && m_layer) {
0385         m_layer->subtractScrollOffset(_tx, _ty);
0386     }
0387 
0388     // default implementation. Just pass things through to the children
0389     for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
0390         child->paint(i, _tx, _ty);
0391     }
0392 }
0393 
0394 void RenderBox::paintRootBoxDecorations(PaintInfo &paintInfo, int _tx, int _ty)
0395 {
0396     //qCDebug(KHTML_LOG) << renderName() << "::paintRootBoxDecorations()" << _tx << "/" << _ty;
0397     const BackgroundLayer *bgLayer = style()->backgroundLayers();
0398     QColor bgColor = style()->backgroundColor();
0399     if (document()->isHTMLDocument() && !style()->hasBackground()) {
0400         // Locate the <body> element using the DOM.  This is easier than trying
0401         // to crawl around a render tree with potential :before/:after content and
0402         // anonymous blocks created by inline <body> tags etc.  We can locate the <body>
0403         // render object very easily via the DOM.
0404         HTMLElementImpl *body = document()->body();
0405         RenderObject *bodyObject = (body && body->id() == ID_BODY) ? body->renderer() : nullptr;
0406 
0407         if (bodyObject) {
0408             bgLayer = bodyObject->style()->backgroundLayers();
0409             bgColor = bodyObject->style()->backgroundColor();
0410         }
0411     }
0412 
0413     if (!bgColor.isValid() && canvas()->view()) {
0414         bgColor = canvas()->view()->palette().color(QPalette::Active, QPalette::Base);
0415     }
0416 
0417     int w = width();
0418     int h = height();
0419 
0420     //    qCDebug(KHTML_LOG) << "width = " << w;
0421 
0422     int rw, rh;
0423     if (canvas()->view()) {
0424         rw = canvas()->view()->contentsWidth();
0425         rh = canvas()->view()->contentsHeight();
0426     } else {
0427         rw = canvas()->docWidth();
0428         rh = canvas()->docHeight();
0429     }
0430 
0431     //    qCDebug(KHTML_LOG) << "rw = " << rw;
0432 
0433     int bx = _tx - marginLeft();
0434     int by = _ty - marginTop();
0435     int bw = qMax(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw);
0436     int bh = qMax(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh);
0437 
0438     // CSS2 14.2:
0439     // " The background of the box generated by the root element covers the entire canvas."
0440     // hence, paint the background even in the margin areas (unlike for every other element!)
0441     // I just love these little inconsistencies .. :-( (Dirk)
0442     QRect cr = paintInfo.r.intersected(QRect(bx, by, bw, bh));
0443     paintAllBackgrounds(paintInfo.p, bgColor, bgLayer, cr, bx, by, bw, bh);
0444 
0445     if (style()->hasBorder()) {
0446         paintBorder(paintInfo.p, _tx, _ty, w, h, style());
0447     }
0448 }
0449 
0450 void RenderBox::paintBoxDecorations(PaintInfo &paintInfo, int _tx, int _ty)
0451 {
0452     //qCDebug(KHTML_LOG) << renderName() << "::paintDecorations()";
0453 
0454     if (isRoot()) {
0455         return paintRootBoxDecorations(paintInfo, _tx, _ty);
0456     }
0457 
0458     int w = width();
0459     int h = height() + borderTopExtra() + borderBottomExtra();
0460     _ty -= borderTopExtra();
0461     QRect cr = QRect(_tx, _ty, w, h).intersected(paintInfo.r);
0462 
0463     // The <body> only paints its background if the root element has defined a background
0464     // independent of the body.  Go through the DOM to get to the root element's render object,
0465     // since the root could be inline and wrapped in an anonymous block.
0466 
0467     if (!isBody() || !document()->isHTMLDocument() || document()->documentElement()->renderer()->style()->hasBackground()) {
0468         paintAllBackgrounds(paintInfo.p, style()->backgroundColor(), style()->backgroundLayers(), cr, _tx, _ty, w, h);
0469     }
0470 
0471     if (style()->hasBorder()) {
0472         paintBorder(paintInfo.p, _tx, _ty, w, h, style());
0473     }
0474 }
0475 
0476 void RenderBox::paintAllBackgrounds(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, QRect clipr, int _tx, int _ty, int w, int height)
0477 {
0478     if (!bgLayer) {
0479         return;
0480     }
0481     paintAllBackgrounds(p, c, bgLayer->next(), clipr, _tx, _ty, w, height);
0482     paintOneBackground(p, c, bgLayer, clipr, _tx, _ty, w, height);
0483 }
0484 
0485 void RenderBox::paintOneBackground(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, QRect clipr, int _tx, int _ty, int w, int height)
0486 {
0487     paintBackgroundExtended(p, c, bgLayer, clipr, _tx, _ty, w, height,
0488                             borderLeft(), borderRight(), paddingLeft(), paddingRight(),
0489                             borderTop(), borderBottom(), paddingTop(), paddingBottom());
0490 }
0491 
0492 static void calculateBackgroundSize(const BackgroundLayer *bgLayer, int &scaledWidth, int &scaledHeight)
0493 {
0494     CachedImage *bg = bgLayer->backgroundImage();
0495 
0496     if (bgLayer->isBackgroundSizeSet()) {
0497         if (bgLayer->backgroundSize().type == BGSLENGTH) {
0498             int w = scaledWidth;
0499             int h = scaledHeight;
0500 
0501             Length bgWidth = bgLayer->backgroundSize().width;
0502             Length bgHeight = bgLayer->backgroundSize().height;
0503 
0504             if (bgWidth.isFixed()) {
0505                 w = bgWidth.value();
0506             } else if (bgWidth.isPercent()) {
0507                 w = bgWidth.width(scaledWidth);
0508             }
0509 
0510             if (bgHeight.isFixed()) {
0511                 h = bgHeight.value();
0512             } else if (bgHeight.isPercent()) {
0513                 h = bgHeight.width(scaledHeight);
0514             }
0515 
0516             // If one of the values is auto we have to use the appropriate
0517             // scale to maintain our aspect ratio.
0518             if (bgWidth.isAuto() && !bgHeight.isAuto()) {
0519                 w = bg->pixmap_size().width() * h / bg->pixmap_size().height();
0520             } else if (!bgWidth.isAuto() && bgHeight.isAuto()) {
0521                 h = bg->pixmap_size().height() * w / bg->pixmap_size().width();
0522             } else if (bgWidth.isAuto() && bgHeight.isAuto()) {
0523                 // If both width and height are auto, we just want to use the image's
0524                 // intrinsic size.
0525                 w = bg->pixmap_size().width();
0526                 h = bg->pixmap_size().height();
0527             }
0528             scaledWidth = qMax(1, w);
0529             scaledHeight = qMax(1, h);
0530         } else {
0531             // 'cover' and 'contain' scaling ratio
0532             Q_ASSERT(bgLayer->backgroundSize().type == BGSCONTAIN ||
0533                      bgLayer->backgroundSize().type == BGSCOVER);
0534             float iw = bg->pixmap_size().width();
0535             float ih = bg->pixmap_size().height();
0536             float w = scaledWidth / iw;
0537             float h = scaledHeight / ih;
0538             float r = (bgLayer->backgroundSize().type == BGSCONTAIN) ? qMin(w, h) : qMax(w, h);
0539             scaledWidth = qMax(1, static_cast<int>(iw * r));
0540             scaledHeight = qMax(1, static_cast<int>(ih * r));
0541         }
0542     } else {
0543         scaledWidth = bg->pixmap_size().width();
0544         scaledHeight = bg->pixmap_size().height();
0545     }
0546 }
0547 
0548 void RenderBox::paintBackgroundExtended(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, QRect clipr,
0549                                         int _tx, int _ty, int w, int h,
0550                                         int bleft, int bright, int pleft, int pright, int btop, int bbottom, int ptop, int pbottom)
0551 {
0552     if (!clipr.isValid()) {
0553         return;
0554     }
0555 
0556     bool needRestore = false;
0557 
0558     if (bgLayer->backgroundClip() != BGBORDER) {
0559         // Clip to the padding or content boxes as necessary.
0560         bool includePadding = bgLayer->backgroundClip() == BGCONTENT;
0561         int x = _tx + bleft + (includePadding ? pleft : 0);
0562         int y = _ty + btop + (includePadding ? ptop : 0);
0563         int width = w - bleft - bright - (includePadding ? pleft + pright : 0);
0564         int height = h - btop - bbottom - (includePadding ? ptop + pbottom : 0);
0565         p->save();
0566         p->setClipRect(QRect(x, y, width, height));
0567         needRestore = true;
0568     }
0569 
0570     CachedImage *bg = bgLayer->backgroundImage();
0571     bool shouldPaintBackgroundImage = bg && bg->isComplete() && !bg->isTransparent() && !bg->isErrorImage() && canvas()->printImages();
0572     QColor bgColor = c;
0573 
0574     // the "bottom" of the page can't be transparent
0575     // unless this is a subframe
0576     if (!bgLayer->next() && isRoot()) {
0577         KHTMLView *v = canvas()->view();
0578         if (bgColor.alpha() != 255) {
0579             if (v && v->m_kwp->isRedirected()) {
0580                 RenderStyle *ws = v->m_kwp->renderWidget()->style();
0581                 // only set static background on transparent subframes if the underlying RenderWidget background
0582                 // has something to show through.
0583                 if (!ws || !ws->backgroundColor().isValid() || ws->backgroundColor().alpha() != 255
0584                         || ws->hasBackgroundImage()) {
0585                     v->setHasStaticBackground();
0586                 }
0587             } else {
0588                 if (bgColor.alpha() == 0) {
0589                     bgColor = p->background().color();
0590                 }
0591                 bgColor.setAlpha(255);
0592             }
0593         }
0594     }
0595 
0596     // Create the border-radius clip path
0597     QPainterPath path = borderRadiusClipPath(bgLayer, _tx, _ty, w, h, bleft, bright, btop, bbottom, pleft, pright, ptop, pbottom);
0598     if (!path.isEmpty()) {
0599         // Avoid saving the painter state twice
0600         if (!needRestore) {
0601             p->save();
0602             needRestore = true;
0603         }
0604         p->setRenderHint(QPainter::Antialiasing, true);
0605     }
0606 
0607     // Paint the color first underneath all images.
0608     if (!bgLayer->next() && bgColor.isValid() && qAlpha(bgColor.rgba()) > 0) {
0609         if (!path.isEmpty()) {
0610             p->fillPath(path, bgColor);
0611         } else {
0612             p->fillRect(clipr.x(), clipr.y(), clipr.width(), clipr.height(), bgColor);
0613         }
0614     }
0615 
0616     // If we're one of the higher-up layers, make sure the color we
0617     // pass to CachedImage::tiled_pixmap below is invalid, so it
0618     // doesn't preblend upper-layers with the color.
0619     if (bgLayer->next()) {
0620         bgColor = QColor();
0621     }
0622 
0623     // no progressive loading of the background image
0624     if (shouldPaintBackgroundImage) {
0625         int sx = 0;
0626         int sy = 0;
0627         int rw = 0;
0628         int rh = 0;
0629         int cw, ch;
0630         int cx, cy;
0631         int scaledImageWidth, scaledImageHeight;
0632 
0633         // CSS2 chapter 14.2.1
0634 
0635         if (bgLayer->backgroundAttachment() != BGAFIXED) {
0636             //scroll
0637             int hpab = 0, vpab = 0, left = 0, top = 0; // Init to 0 for background-origin of 'border'
0638             if (bgLayer->backgroundOrigin() != BGBORDER) {
0639                 hpab += bleft + bright;
0640                 vpab += btop + bbottom;
0641                 left += bleft;
0642                 top += btop;
0643                 if (bgLayer->backgroundOrigin() == BGCONTENT) {
0644                     hpab += pleft + pright;
0645                     vpab += ptop + pbottom;
0646                     left += pleft;
0647                     top += ptop;
0648                 }
0649             }
0650 
0651             int pw, ph;
0652             pw = w - hpab;
0653             ph = h - vpab;
0654             if (isRoot()) {
0655                 // the root's background box 'spills out' to cover the whole canvas, so we have to
0656                 // go back to its true edge for the purpose of computing background-size
0657                 // and honouring background-origin
0658                 rw = width() - hpab;
0659                 rh = height() - vpab;
0660                 left += marginLeft();
0661                 hpab += marginLeft() + marginRight();
0662                 vpab += marginTop() + marginBottom();
0663                 top += marginTop();
0664                 scaledImageWidth = rw;
0665                 scaledImageHeight = rh;
0666             } else {
0667                 scaledImageWidth = pw;
0668                 scaledImageHeight = ph;
0669             }
0670             calculateBackgroundSize(bgLayer, scaledImageWidth, scaledImageHeight);
0671 
0672             EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
0673             if (bgr == NO_REPEAT || bgr == REPEAT_Y) {
0674                 cw = scaledImageWidth;
0675                 int xPosition;
0676                 if (isRoot()) {
0677                     xPosition = bgLayer->backgroundXPosition().minWidthRounded(rw - scaledImageWidth);
0678                 } else {
0679                     xPosition = bgLayer->backgroundXPosition().minWidthRounded(pw - scaledImageWidth);
0680                 }
0681                 if (xPosition >= 0) {
0682                     cx = _tx + xPosition;
0683                     cw = qMin(scaledImageWidth, pw - xPosition);
0684                 } else {
0685                     cx = _tx;
0686                     if (scaledImageWidth > 0) {
0687                         sx = -xPosition;
0688                         cw = qMin(scaledImageWidth + xPosition, pw);
0689                     }
0690                 }
0691                 cx += left;
0692             } else {
0693                 // repeat over x
0694                 cw = w;
0695                 cx = _tx;
0696                 if (scaledImageWidth > 0) {
0697                     int xPosition;
0698                     if (isRoot()) {
0699                         xPosition = bgLayer->backgroundXPosition().minWidthRounded(rw - scaledImageWidth);
0700                     } else {
0701                         xPosition = bgLayer->backgroundXPosition().minWidthRounded(pw - scaledImageWidth);
0702                     }
0703                     sx = scaledImageWidth - (xPosition % scaledImageWidth);
0704                     sx -= left % scaledImageWidth;
0705                 }
0706             }
0707             if (bgr == NO_REPEAT || bgr == REPEAT_X) {
0708                 ch = scaledImageHeight;
0709                 int yPosition;
0710                 if (isRoot()) {
0711                     yPosition = bgLayer->backgroundYPosition().minWidthRounded(rh - scaledImageHeight);
0712                 } else {
0713                     yPosition = bgLayer->backgroundYPosition().minWidthRounded(ph - scaledImageHeight);
0714                 }
0715                 if (yPosition >= 0) {
0716                     cy = _ty + yPosition;
0717                     ch = qMin(ch, ph - yPosition);
0718                 } else {
0719                     cy = _ty;
0720                     if (scaledImageHeight > 0) {
0721                         sy = -yPosition;
0722                         ch = qMin(scaledImageHeight + yPosition, ph);
0723                     }
0724                 }
0725 
0726                 cy += top;
0727             } else {
0728                 // repeat over y
0729                 ch = h;
0730                 cy = _ty;
0731                 if (scaledImageHeight > 0) {
0732                     int yPosition;
0733                     if (isRoot()) {
0734                         yPosition = bgLayer->backgroundYPosition().minWidthRounded(rh - scaledImageHeight);
0735                     } else {
0736                         yPosition = bgLayer->backgroundYPosition().minWidthRounded(ph - scaledImageHeight);
0737                     }
0738                     sy = scaledImageHeight - (yPosition % scaledImageHeight);
0739                     sy -= top % scaledImageHeight;
0740                 }
0741             }
0742             if (layer() && bgLayer->backgroundAttachment() == BGALOCAL) {
0743                 layer()->scrollOffset(sx, sy);
0744             }
0745         } else {
0746             //fixed
0747             QRect fix = getFixedBackgroundImageRect(bgLayer, sx, sy, scaledImageWidth, scaledImageHeight);
0748             QRect ele(_tx, _ty, w, h);
0749             QRect b = fix.intersected(ele);
0750 
0751             //qCDebug(KHTML_LOG) <<" ele is " << ele << " b is " << b << " fix is " << fix;
0752             sx += b.x() - fix.x();
0753             sy += b.y() - fix.y();
0754             cx = b.x(); cy = b.y(); cw = b.width(); ch = b.height();
0755 
0756             if (canvas()->pagedMode() && scaledImageHeight > 0) {
0757                 sy = (sy - viewRect().y()) % scaledImageHeight;
0758             }
0759         }
0760         // restrict painting to repaint-clip
0761         if (cy < clipr.y()) {
0762             ch -= (clipr.y() - cy);
0763             sy += (clipr.y() - cy);
0764             cy = clipr.y();
0765         }
0766         if (cx < clipr.x()) {
0767             cw -= (clipr.x() - cx);
0768             sx += (clipr.x() - cx);
0769             cx = clipr.x();
0770         }
0771         ch = qMin(ch, clipr.height());
0772         cw = qMin(cw, clipr.width());
0773 
0774 //         qCDebug(KHTML_LOG) << " drawTiledPixmap(" << cx << ", " << cy << ", " << cw << ", " << ch << ", " << sx << ", " << sy << ")";
0775         if (cw > 0 && ch > 0) {
0776             // Note that the reason we don't simply set the path as the clip path here before calling
0777             // p->drawTiledPixmap() is that QX11PaintEngine doesn't support anti-aliased clipping.
0778             if (!path.isEmpty()) {
0779                 QBrush brush(bg->tiled_pixmap(bgColor, scaledImageWidth, scaledImageHeight));
0780                 brush.setTransform(QTransform(1, 0, 0, 1, cx - sx, cy - sy));
0781                 QPainterPath cpath;
0782                 cpath.addRect(cx, cy, cw, ch);
0783                 p->fillPath(path.intersected(cpath), brush);
0784             } else {
0785                 p->drawTiledPixmap(cx, cy, cw, ch, bg->tiled_pixmap(bgColor, scaledImageWidth, scaledImageHeight), sx, sy);
0786             }
0787         }
0788     }
0789 
0790     if (needRestore) {
0791         p->restore();    // Undo the background clip and/or the anti-aliasing hint
0792     }
0793 
0794 }
0795 
0796 QRect RenderBox::getFixedBackgroundImageRect(const BackgroundLayer *bgLayer, int &sx, int &sy, int &scaledImageWidth, int &scaledImageHeight)
0797 {
0798     int cx, cy, cw, ch;
0799     QRect vr = viewRect();
0800     int pw = vr.width();
0801     int ph = vr.height();
0802     scaledImageWidth = pw;
0803     scaledImageHeight = ph;
0804     calculateBackgroundSize(bgLayer, scaledImageWidth, scaledImageHeight);
0805     EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
0806 
0807     int xPosition = bgLayer->backgroundXPosition().minWidthRounded(pw - scaledImageWidth);
0808     if (bgr == NO_REPEAT || bgr == REPEAT_Y) {
0809         cw = qMin(scaledImageWidth, pw - xPosition);
0810         cx = vr.x() + xPosition;
0811     } else {
0812         cw = pw;
0813         cx = vr.x();
0814         if (scaledImageWidth > 0) {
0815             sx = scaledImageWidth - xPosition % scaledImageWidth;
0816         }
0817     }
0818 
0819     int yPosition = bgLayer->backgroundYPosition().minWidthRounded(ph - scaledImageHeight);
0820     if (bgr == NO_REPEAT || bgr == REPEAT_X) {
0821         ch = qMin(scaledImageHeight, ph - yPosition);
0822         cy = vr.y() + yPosition;
0823     } else {
0824         ch = ph;
0825         cy = vr.y();
0826         if (scaledImageHeight > 0) {
0827             sy = scaledImageHeight - yPosition % scaledImageHeight;
0828         }
0829     }
0830     return QRect(cx, cy, cw, ch);
0831 }
0832 
0833 void RenderBox::outlineBox(QPainter *p, int _tx, int _ty, const char *color)
0834 {
0835     p->setPen(QPen(QColor(color), 1, Qt::DotLine));
0836     p->setBrush(Qt::NoBrush);
0837     p->drawRect(_tx, _ty, m_width, m_height);
0838 }
0839 
0840 QPainterPath RenderBox::borderRadiusClipPath(const BackgroundLayer *bgLayer, int _tx, int _ty, int w, int h,
0841         int borderLeft, int borderRight, int borderTop, int borderBottom,
0842         int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) const
0843 {
0844     QPainterPath path;
0845 
0846     if (style()->hasBorderRadius()) {
0847         QPoint topLeftRadii, topRightRadii, bottomLeftRadii, bottomRightRadii;
0848         calcBorderRadii(topLeftRadii, topRightRadii, bottomLeftRadii, bottomRightRadii, w, h);
0849 
0850         // CSS Backgrounds and Borders Module Level 3 (https://www.w3.org/TR/2014/CR-css3-background-20140909/), chapter 5.3:
0851         // "A box's backgrounds, but not its border-image, are clipped to the appropriate curve (as determined by 'background-clip')."
0852         int adjustTop = 0, adjustLeft = 0, adjustRight = 0, adjustBottom = 0;
0853         bool clipInner = true;
0854         switch (bgLayer->backgroundClip()) {
0855             case BGCONTENT:
0856                 adjustTop += paddingTop;
0857                 adjustLeft += paddingLeft;
0858                 adjustRight += paddingRight;
0859                 adjustBottom += paddingBottom;
0860                 // No break
0861             case BGPADDING:
0862                 adjustTop += borderTop;
0863                 adjustLeft += borderLeft;
0864                 adjustRight += borderRight;
0865                 adjustBottom += borderBottom;
0866                 break;
0867             default: // BGBORDER
0868                 clipInner = false;
0869                 break;
0870         }
0871         // The clip bounding rect
0872         QRect rect(_tx, _ty, w, h);
0873         if (clipInner) {
0874             rect.adjust(adjustLeft, adjustTop, -adjustRight, -adjustBottom);
0875 
0876             topLeftRadii.rx() = qMax(0, topLeftRadii.x() - adjustLeft);
0877             bottomLeftRadii.rx() = qMax(0, bottomLeftRadii.x() - adjustLeft);
0878             topRightRadii.rx() = qMax(0, topRightRadii.x() - adjustRight);
0879             bottomRightRadii.rx() = qMax(0, bottomRightRadii.x() - adjustRight);
0880             topLeftRadii.ry() = qMax(0, topLeftRadii.y() - adjustTop);
0881             topRightRadii.ry() = qMax(0, topRightRadii.y() - adjustTop);
0882             bottomLeftRadii.ry() = qMax(0, bottomLeftRadii.y() - adjustBottom);
0883             bottomRightRadii.ry() = qMax(0, bottomRightRadii.y() - adjustBottom);
0884         }
0885 
0886         // Top right corner
0887         if (!topRightRadii.isNull()) {
0888             const QRect r(rect.x() + rect.width() - topRightRadii.x() * 2, rect.y(), topRightRadii.x() * 2, topRightRadii.y() * 2);
0889             path.arcMoveTo(r, 0);
0890             path.arcTo(r, 0, 90);
0891         } else {
0892             path.moveTo(rect.x() + rect.width(), rect.y());
0893         }
0894 
0895         // Top left corner
0896         if (!topLeftRadii.isNull()) {
0897             const QRect r(rect.x(), rect.y(), topLeftRadii.x() * 2, topLeftRadii.y() * 2);
0898             path.arcTo(r, 90, 90);
0899         } else {
0900             path.lineTo(rect.x(), rect.y());
0901         }
0902 
0903         // Bottom left corner
0904         if (!bottomLeftRadii.isNull()) {
0905             const QRect r(rect.x(), rect.y() + rect.height() - bottomLeftRadii.y() * 2, bottomLeftRadii.x() * 2, bottomLeftRadii.y() * 2);
0906             path.arcTo(r, 180, 90);
0907         } else {
0908             path.lineTo(rect.x(), rect.y() + rect.height());
0909         }
0910 
0911         // Bottom right corner
0912         if (!bottomRightRadii.isNull()) {
0913             const QRect r(rect.x() + rect.width() - bottomRightRadii.x() * 2, rect.y() + rect.height() - bottomRightRadii.y() * 2,
0914                           bottomRightRadii.x() * 2, bottomRightRadii.y() * 2);
0915             path.arcTo(r, 270, 90);
0916         } else {
0917             path.lineTo(rect.x() + rect.width(), rect.y() + rect.height());
0918         }
0919 
0920         path.closeSubpath();
0921     }
0922 
0923     return path;
0924 }
0925 
0926 QRect RenderBox::overflowClipRect(int tx, int ty)
0927 {
0928     // XXX When overflow-clip (CSS3) is implemented, we'll obtain the property
0929     // here.
0930     int bl = borderLeft(), bt = borderTop(), bb = borderBottom(), br = borderRight();
0931     int clipx = tx + bl;
0932     int clipy = ty + bt;
0933     int clipw = m_width - bl - br;
0934     int cliph = m_height - bt - bb + borderTopExtra() + borderBottomExtra();
0935 
0936     // Subtract out scrollbars if we have them.
0937     if (m_layer) {
0938         if (m_layer->hasReversedScrollbar()) {
0939             clipx += m_layer->verticalScrollbarWidth();
0940         }
0941         clipw -= m_layer->verticalScrollbarWidth();
0942         cliph -= m_layer->horizontalScrollbarHeight();
0943     }
0944 
0945     return QRect(clipx, clipy, clipw, cliph);
0946 }
0947 
0948 QRect RenderBox::clipRect(int tx, int ty)
0949 {
0950     // Clipping applies to the entire box, including the borders, so we
0951     // don't have to do anything about them or margins
0952     int clipw = m_width;
0953     int cliph = m_height;
0954 
0955     bool rtl = (style()->direction() == RTL);
0956 
0957     int clipleft = 0;
0958     int clipright = clipw;
0959     int cliptop = 0;
0960     int clipbottom = cliph;
0961 
0962     if (style()->hasClip() && style()->position() == PABSOLUTE) {
0963         // the only case we use the clip property according to CSS 2.1
0964         if (!style()->clipLeft().isAuto()) {
0965             int c = style()->clipLeft().width(clipw);
0966             if (rtl) {
0967                 clipleft = clipw - c;
0968             } else {
0969                 clipleft = c;
0970             }
0971         }
0972         if (!style()->clipRight().isAuto()) {
0973             int w = style()->clipRight().width(clipw);
0974             if (rtl) {
0975                 clipright = clipw - w;
0976             } else {
0977                 clipright = w;
0978             }
0979         }
0980         if (!style()->clipTop().isAuto()) {
0981             cliptop = style()->clipTop().width(cliph);
0982         }
0983         if (!style()->clipBottom().isAuto()) {
0984             clipbottom = style()->clipBottom().width(cliph);
0985         }
0986     }
0987     int clipx = tx + clipleft;
0988     int clipy = ty + cliptop;
0989     clipw = clipright - clipleft;
0990     cliph = clipbottom - cliptop;
0991 
0992     //qCDebug(KHTML_LOG) << "setting clip("<<clipx<<","<<clipy<<","<<clipw<<","<<cliph<<")";
0993 
0994     return QRect(clipx, clipy, clipw, cliph);
0995 }
0996 
0997 void RenderBox::close()
0998 {
0999     setNeedsLayoutAndMinMaxRecalc();
1000 }
1001 
1002 short RenderBox::containingBlockWidth(RenderObject *providedCB) const
1003 {
1004     if (isCanvas() && canvas()->view()) {
1005         if (canvas()->pagedMode()) {
1006             return canvas()->width();
1007         } else {
1008             return canvas()->view()->visibleWidth();
1009         }
1010     }
1011 
1012     RenderObject *cb = providedCB ? providedCB : containingBlock();
1013     if (isRenderBlock() && cb->isTable() && static_cast<RenderTable *>(cb)->caption() == this) {
1014         //captions are not affected by table border or padding
1015         return cb->width();
1016     }
1017     if (isPositioned()) {
1018         // cf. 10.1.4 - use padding edge
1019         if (cb->isInlineFlow()) {
1020             // 10.1.4.1
1021             int l, r;
1022             InlineFlowBox *firstLineBox = static_cast<const RenderFlow *>(cb)->firstLineBox();
1023             InlineFlowBox *lastLineBox = static_cast<const RenderFlow *>(cb)->lastLineBox();
1024             if (!lastLineBox) {
1025                 return 0;
1026             }
1027             if (cb->style()->direction() == RTL) {
1028                 l = lastLineBox->xPos() + lastLineBox->borderLeft();
1029                 r = firstLineBox->xPos() + firstLineBox->width() - firstLineBox->borderRight();
1030             } else {
1031                 l = firstLineBox->xPos() + firstLineBox->borderLeft();
1032                 r = lastLineBox->xPos() + lastLineBox->width() - lastLineBox->borderRight();
1033             }
1034             return qMax(0, r - l);
1035         }
1036         // 10.1.4.2
1037         return cb->contentWidth() + cb->paddingLeft() + cb->paddingRight();
1038     } else if (usesLineWidth()) {
1039         assert(cb->isRenderBlock());
1040         return static_cast<RenderBlock *>(cb)->lineWidth(m_y);
1041     } else {
1042         return cb->contentWidth();
1043     }
1044 }
1045 
1046 bool RenderBox::absolutePosition(int &_xPos, int &_yPos, bool f) const
1047 {
1048     if (style()->position() == PFIXED) {
1049         f = true;
1050     }
1051     RenderObject *o = container();
1052     if (o && o->absolutePosition(_xPos, _yPos, f)) {
1053         if (o->layer()) {
1054             if (o->hasOverflowClip()) {
1055                 o->layer()->subtractScrollOffset(_xPos, _yPos);
1056             }
1057             if (isPositioned()) {
1058                 o->layer()->checkInlineRelOffset(this, _xPos, _yPos);
1059             }
1060         }
1061 
1062         if (!isInline() || isReplaced()) {
1063             _xPos += xPos(),
1064                      _yPos += yPos();
1065         }
1066 
1067         if (isRelPositioned()) {
1068             relativePositionOffset(_xPos, _yPos);
1069         }
1070         return true;
1071     } else {
1072         _xPos = 0;
1073         _yPos = 0;
1074         return false;
1075     }
1076 }
1077 
1078 void RenderBox::position(InlineBox *box, int /*from*/, int /*len*/, bool /*reverse*/)
1079 {
1080     if (isPositioned()) {
1081         // Cache the x position only if we were an INLINE type originally.
1082         bool wasInline = style()->isOriginalDisplayInlineType();
1083 
1084         if (wasInline && hasStaticX()) {
1085             // The value is cached in the xPos of the box.  We only need this value if
1086             // our object was inline originally, since otherwise it would have ended up underneath
1087             // the inlines.
1088             m_staticX = box->xPos();
1089         } else if (!wasInline && hasStaticY()) {
1090             // Our object was a block originally, so we make our normal flow position be
1091             // just below the line box (as though all the inlines that came before us got
1092             // wrapped in an anonymous block, which is what would have happened had we been
1093             // in flow).  This value was cached in the yPos() of the box.
1094             m_staticY = box->yPos();
1095         }
1096     } else if (isReplaced()) {
1097         setPos(box->xPos(), box->yPos());
1098     }
1099 }
1100 
1101 void RenderBox::repaint(Priority prior)
1102 {
1103     int ow = style() ? style()->outlineSize() : 0;
1104     if (isInline() && !isReplaced()) {
1105         RenderObject *p = parent();
1106         Q_ASSERT(p);
1107         while (p->isInline() && !p->isReplaced()) {
1108             p = p->parent();
1109         }
1110         int xoff = p->hasOverflowClip() ? 0 : p->overflowLeft();
1111         int yoff = p->hasOverflowClip() ? 0 : p->overflowTop();
1112         p->repaintRectangle(-ow + xoff, -ow + yoff, p->effectiveWidth() + ow * 2, p->effectiveHeight() + ow * 2, prior);
1113     } else {
1114         int xoff = hasOverflowClip() ? 0 : overflowLeft();
1115         int yoff = hasOverflowClip() ? 0 : overflowTop();
1116         repaintRectangle(-ow + xoff, -ow + yoff, effectiveWidth() + ow * 2, effectiveHeight() + ow * 2, prior);
1117     }
1118 }
1119 
1120 void RenderBox::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
1121 {
1122     x += m_x;
1123     y += m_y;
1124 
1125     // Apply the relative position offset when invalidating a rectangle.  The layer
1126     // is translated, but the render box isn't, so we need to do this to get the
1127     // right dirty rect.  Since this is called from RenderObject::setStyle, the relative position
1128     // flag on the RenderObject has been cleared, so use the one on the style().
1129     if (style()->position() == PRELATIVE && m_layer) {
1130         relativePositionOffset(x, y);
1131     }
1132 
1133     if (style()->position() == PFIXED) {
1134         f = true;
1135     }
1136 
1137     // qCDebug(KHTML_LOG) << "RenderBox(" <<this << ", " << renderName() << ")::repaintRectangle (" << x << "/" << y << ") (" << w << "/" << h << ")";
1138     RenderObject *o = container();
1139     if (o) {
1140         if (o->layer()) {
1141             if (o->style()->hidesOverflow() && o->layer() && !o->isInlineFlow()) {
1142                 o->layer()->subtractScrollOffset(x, y);    // For overflow:auto/scroll/hidden.
1143             }
1144             if (style()->position() == PABSOLUTE) {
1145                 o->layer()->checkInlineRelOffset(this, x, y);
1146             }
1147         }
1148         o->repaintRectangle(x, y, w, h, p, f);
1149     }
1150 }
1151 
1152 void RenderBox::relativePositionOffset(int &tx, int &ty) const
1153 {
1154     if (!style()->left().isAuto()) {
1155         if (!style()->right().isAuto() && containingBlock()->style()->direction() == RTL) {
1156             tx -= style()->right().width(containingBlockWidth());
1157         } else {
1158             tx +=  style()->left().width(containingBlockWidth());
1159         }
1160     } else if (!style()->right().isAuto()) {
1161         tx -= style()->right().width(containingBlockWidth());
1162     }
1163     if (!style()->top().isAuto()) {
1164         if (style()->top().isPercent()) {
1165             double p = style()->top().percent();
1166             bool neg = p < 0.0;
1167             int ph = calcPercentageHeight(Length((neg ? -p : p), Percent));
1168             if (ph != -1) {
1169                 ty += neg ? -ph : ph;
1170             }
1171         } else {
1172             ty += style()->top().width(containingBlockHeight());
1173         }
1174     } else if (!style()->bottom().isAuto()) {
1175         if (style()->bottom().isPercent()) {
1176             double p = style()->bottom().percent();
1177             bool neg = p < 0.0;
1178             int ph = calcPercentageHeight(Length((neg ? -p : p), Percent));
1179             if (ph != -1) {
1180                 ty -= neg ? -ph : ph;
1181             }
1182         } else {
1183             ty -= style()->bottom().width(containingBlockHeight());
1184         }
1185     }
1186 }
1187 
1188 void RenderBox::calcWidth()
1189 {
1190 #ifdef DEBUG_LAYOUT
1191     qCDebug(KHTML_LOG) << "RenderBox(" << renderName() << ")::calcWidth()";
1192 #endif
1193     if (isPositioned()) {
1194         calcAbsoluteHorizontal();
1195     } else {
1196         bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable();
1197         Length w;
1198         if (treatAsReplaced) {
1199             w = Length(calcReplacedWidth(), Fixed);
1200         } else {
1201             w = style()->width();
1202         }
1203 
1204         Length ml = style()->marginLeft();
1205         Length mr = style()->marginRight();
1206 
1207         int cw = containingBlockWidth();
1208         if (cw < 0) {
1209             cw = 0;
1210         }
1211 
1212         m_marginLeft = 0;
1213         m_marginRight = 0;
1214 
1215         if (isInline() && !isInlineBlockOrInlineTable()) {
1216             // just calculate margins
1217             m_marginLeft = ml.minWidth(cw);
1218             m_marginRight = mr.minWidth(cw);
1219             if (treatAsReplaced) {
1220                 m_width = w.width(cw) + borderLeft() + borderRight() + paddingLeft() + paddingRight();
1221                 m_width = qMax(m_width, m_minWidth);
1222             }
1223 
1224             return;
1225         } else {
1226             LengthType widthType, minWidthType, maxWidthType;
1227             if (treatAsReplaced) {
1228                 m_width = w.width(cw) + borderLeft() + borderRight() + paddingLeft() + paddingRight();
1229                 widthType = w.type();
1230             } else {
1231                 m_width = calcWidthUsing(Width, cw, widthType);
1232                 int minW = calcWidthUsing(MinWidth, cw, minWidthType);
1233                 int maxW = style()->maxWidth().isUndefined() ?
1234                            m_width : calcWidthUsing(MaxWidth, cw, maxWidthType);
1235 
1236                 if (m_width > maxW) {
1237                     m_width = maxW;
1238                     widthType = maxWidthType;
1239                 }
1240                 if (m_width < minW) {
1241                     m_width = minW;
1242                     widthType = minWidthType;
1243                 }
1244                 if (short iw = intrinsicWidth()) {
1245                     // some elements (e.g. Fieldset) have pseudo-replaced behaviour in quirk mode
1246                     if (m_width < iw) {
1247                         m_width = iw;
1248                         widthType = Fixed;
1249                     }
1250                 }
1251             }
1252 
1253             if (widthType == Auto) {
1254                 //          qCDebug(KHTML_LOG) << "variable";
1255                 m_marginLeft = ml.minWidth(cw);
1256                 m_marginRight = mr.minWidth(cw);
1257             } else {
1258 //              qCDebug(KHTML_LOG) << "non-variable " << w.type << ","<< w.value;
1259                 calcHorizontalMargins(ml, mr, cw);
1260             }
1261         }
1262 
1263         if (cw && cw != m_width + m_marginLeft + m_marginRight && !isFloating() && !isInline()) {
1264             if (containingBlock()->style()->direction() == LTR) {
1265                 m_marginRight = cw - m_width - m_marginLeft;
1266             } else {
1267                 m_marginLeft = cw - m_width - m_marginRight;
1268             }
1269         }
1270     }
1271 
1272 #ifdef DEBUG_LAYOUT
1273     qCDebug(KHTML_LOG) << "RenderBox::calcWidth(): m_width=" << m_width << " containingBlockWidth()=" << containingBlockWidth();
1274     qCDebug(KHTML_LOG) << "m_marginLeft=" << m_marginLeft << " m_marginRight=" << m_marginRight;
1275 #endif
1276 }
1277 
1278 int RenderBox::calcWidthUsing(WidthType widthType, int cw, LengthType &lengthType)
1279 {
1280     int width = m_width;
1281     Length w;
1282     if (widthType == Width) {
1283         w = style()->width();
1284     } else if (widthType == MinWidth) {
1285         w = style()->minWidth();
1286     } else {
1287         w = style()->maxWidth();
1288     }
1289 
1290     lengthType = w.type();
1291 
1292     if (lengthType == Auto) {
1293         int marginLeft = style()->marginLeft().minWidth(cw);
1294         int marginRight = style()->marginRight().minWidth(cw);
1295         if (cw) {
1296             width = cw - marginLeft - marginRight;
1297         }
1298 
1299         // size to max width?
1300         if (sizesToMaxWidth()) {
1301             width = qMax(width, (int)m_minWidth);
1302             width = qMin(width, (int)m_maxWidth);
1303         }
1304     } else {
1305         width = calcBoxWidth(w.width(cw));
1306     }
1307 
1308     return width;
1309 }
1310 
1311 void RenderBox::calcHorizontalMargins(const Length &ml, const Length &mr, int cw)
1312 {
1313     if (isFloating() || isInline()) { // Inline blocks/tables and floats don't have their margins increased.
1314         m_marginLeft = ml.minWidth(cw);
1315         m_marginRight = mr.minWidth(cw);
1316     } else {
1317         if ((ml.isAuto() && mr.isAuto() && m_width < cw) ||
1318                 (!ml.isAuto() && !mr.isAuto() &&
1319                  containingBlock()->style()->textAlign() == KHTML_CENTER)) {
1320             m_marginLeft = (cw - m_width) / 2;
1321             if (m_marginLeft < 0) {
1322                 m_marginLeft = 0;
1323             }
1324             m_marginRight = cw - m_width - m_marginLeft;
1325         } else if ((mr.isAuto() && m_width < cw) ||
1326                    (!ml.isAuto() && containingBlock()->style()->direction() == RTL &&
1327                     containingBlock()->style()->textAlign() == KHTML_LEFT)) {
1328             m_marginLeft = ml.width(cw);
1329             m_marginRight = cw - m_width - m_marginLeft;
1330         } else if ((ml.isAuto() && m_width < cw) ||
1331                    (!mr.isAuto() && containingBlock()->style()->direction() == LTR &&
1332                     containingBlock()->style()->textAlign() == KHTML_RIGHT)) {
1333             m_marginRight = mr.width(cw);
1334             m_marginLeft = cw - m_width - m_marginRight;
1335         } else {
1336             // this makes auto margins 0 if we failed a m_width<cw test above (css2.1, 10.3.3)
1337             m_marginLeft = ml.minWidth(cw);
1338             m_marginRight = mr.minWidth(cw);
1339         }
1340     }
1341 }
1342 
1343 void RenderBox::calcHeight()
1344 {
1345 
1346 #ifdef DEBUG_LAYOUT
1347     qCDebug(KHTML_LOG) << "RenderBox::calcHeight()";
1348 #endif
1349 
1350     //cell height is managed by table, inline elements do not have a height property.
1351     if (isTableCell() || (isInline() && !isReplaced())) {
1352         return;
1353     }
1354 
1355     if (isPositioned()) {
1356         calcAbsoluteVertical();
1357     } else {
1358         calcVerticalMargins();
1359 
1360         // For tables, calculate margins only
1361         if (isTable()) {
1362             return;
1363         }
1364 
1365         Length h;
1366         bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable();
1367         bool checkMinMaxHeight = false;
1368 
1369         if (treatAsReplaced) {
1370             h = Length(calcReplacedHeight(), Fixed);
1371         } else {
1372             h = style()->height();
1373             checkMinMaxHeight = true;
1374         }
1375 
1376         int height;
1377         if (checkMinMaxHeight) {
1378             height = calcHeightUsing(style()->height());
1379             if (height == -1) {
1380                 height = m_height;
1381             }
1382             int minH = calcHeightUsing(style()->minHeight()); // Leave as -1 if unset.
1383             int maxH = style()->maxHeight().isUndefined() ? height : calcHeightUsing(style()->maxHeight());
1384             if (maxH == -1) {
1385                 maxH = height;
1386             }
1387             height = qMin(maxH, height);
1388             height = qMax(minH, height);
1389         } else {
1390             // The only times we don't check min/max height are when a fixed length has
1391             // been given as an override.  Just use that.
1392             height = h.value() + borderTop() + borderBottom() + paddingTop() + paddingBottom();
1393         }
1394 
1395         m_height = height;
1396     }
1397 
1398     // Unfurling marquees override with the furled height.
1399     if (style()->overflowX() == OMARQUEE && m_layer && m_layer->marquee() &&
1400             m_layer->marquee()->isUnfurlMarquee() && !m_layer->marquee()->isHorizontal()) {
1401         m_layer->marquee()->setEnd(m_height);
1402         m_height = qMin(m_height, m_layer->marquee()->unfurlPos());
1403     }
1404 
1405 }
1406 
1407 int RenderBox::calcHeightUsing(const Length &h)
1408 {
1409     int height = -1;
1410     if (!h.isAuto()) {
1411         if (h.isFixed()) {
1412             height = h.value();
1413         } else if (h.isPercent()) {
1414             height = calcPercentageHeight(h);
1415         }
1416         if (height != -1) {
1417             height = calcBoxHeight(height);
1418             return height;
1419         }
1420     }
1421     return height;
1422 }
1423 
1424 int RenderBox::calcImplicitContentHeight() const
1425 {
1426     assert(hasImplicitHeight());
1427 
1428     RenderBlock *cb = containingBlock();
1429     // padding-box height
1430     int ch = cb->height() - cb->borderTop() - cb->borderBottom();
1431     int top = style()->top().width(ch);
1432     int bottom = style()->bottom().width(ch);
1433 
1434     return ch - top - bottom - borderTop() - borderBottom() - paddingTop() - paddingBottom();
1435 }
1436 
1437 int RenderBox::calcPercentageHeight(const Length &height) const
1438 {
1439     int result = -1;
1440     RenderBlock *cb = containingBlock();
1441     // In quirk mode, table cells violate what the CSS spec says to do with heights.
1442     if (cb->isTableCell() && style()->htmlHacks()) {
1443         result = static_cast<RenderTableCell *>(cb)->cellPercentageHeight();
1444     }
1445 
1446     // Otherwise we only use our percentage height if our containing block had a specified
1447     // height.
1448     else if (cb->style()->height().isFixed()) {
1449         result = cb->calcContentHeight(cb->style()->height().value());
1450     } else if (cb->style()->height().isPercent()) {
1451         // We need to recur and compute the percentage height for our containing block.
1452         result = cb->calcPercentageHeight(cb->style()->height());
1453         if (result != -1) {
1454             result = cb->calcContentHeight(result);
1455         }
1456     } else if (cb->isCanvas()) {
1457         if (!canvas()->pagedMode()) {
1458             result = static_cast<RenderCanvas *>(cb)->viewportHeight();
1459         } else {
1460             result = static_cast<RenderCanvas *>(cb)->height();
1461         }
1462         result -= cb->style()->borderTopWidth() - cb->style()->borderBottomWidth();
1463         result -= cb->paddingTop() + cb->paddingBottom();
1464     } else if (cb->isBody() && style()->htmlHacks() &&
1465                cb->style()->height().isAuto() && !cb->isFloatingOrPositioned()) {
1466         int margins = cb->collapsedMarginTop() + cb->collapsedMarginBottom();
1467         int visHeight = canvas()->viewportHeight();
1468         RenderObject *p = cb->parent();
1469         result = visHeight - (margins + p->marginTop() + p->marginBottom() +
1470                               p->borderTop() + p->borderBottom() +
1471                               p->paddingTop() + p->paddingBottom());
1472     } else if (cb->isRoot() && style()->htmlHacks() && cb->style()->height().isAuto()) {
1473         int visHeight = canvas()->viewportHeight();
1474         result = visHeight - (marginTop() + marginBottom() +
1475                               borderTop() + borderBottom() +
1476                               paddingTop() + paddingBottom());
1477     } else if (isPositioned()) {
1478         // "10.5 - Note that the height of the containing block of an absolutely positioned element is independent
1479         //  of the size of the element itself, and thus a percentage height on such an element can always be resolved."
1480         //
1481         // take the used height - at the padding edge since we are positioned (10.1)
1482         result = cb->height() - cb->borderTop() - cb->borderBottom();
1483     } else if (cb->hasImplicitHeight()) {
1484         result = cb->calcImplicitContentHeight();
1485     } else if (cb->isAnonymousBlock() || style()->htmlHacks()) {
1486         // IE quirk.
1487         assert(cb->style()->height().isAuto());
1488         result = cb->calcPercentageHeight(cb->style()->height());
1489         if (result != -1) {
1490             result = cb->calcContentHeight(result);
1491         }
1492     }
1493 
1494     if (result != -1) {
1495         result = height.width(result);
1496         if (cb->isTableCell() && style()->boxSizing() != BORDER_BOX) {
1497             result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom());
1498             result = qMax(0, result);
1499         }
1500     }
1501     return result;
1502 }
1503 
1504 short RenderBox::calcReplacedWidth() const
1505 {
1506     int width = calcReplacedWidthUsing(Width);
1507     int minW = calcReplacedWidthUsing(MinWidth);
1508     int maxW = style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(MaxWidth);
1509 
1510     if (width > maxW) {
1511         width = maxW;
1512     }
1513 
1514     if (width < minW) {
1515         width = minW;
1516     }
1517 
1518     return width;
1519 }
1520 
1521 int RenderBox::calcReplacedWidthUsing(WidthType widthType) const
1522 {
1523     Length w;
1524     if (widthType == Width) {
1525         w = style()->width();
1526     } else if (widthType == MinWidth) {
1527         w = style()->minWidth();
1528     } else {
1529         w = style()->maxWidth();
1530     }
1531 
1532     switch (w.type()) {
1533     case Fixed:
1534         return calcContentWidth(w.value());
1535     case Percent: {
1536         const int cw = containingBlockWidth();
1537         if (cw > 0) {
1538             int result = calcContentWidth(w.minWidth(cw));
1539             return result;
1540         }
1541     }
1542     // fall through
1543     default:
1544         return intrinsicWidth();
1545     }
1546 }
1547 
1548 int RenderBox::calcReplacedHeight() const
1549 {
1550     int height = calcReplacedHeightUsing(Height);
1551     int minH = calcReplacedHeightUsing(MinHeight);
1552     int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(MaxHeight);
1553 
1554     if (height > maxH) {
1555         height = maxH;
1556     }
1557 
1558     if (height < minH) {
1559         height = minH;
1560     }
1561 
1562     return height;
1563 }
1564 
1565 int RenderBox::calcReplacedHeightUsing(HeightType heightType) const
1566 {
1567     Length h;
1568     if (heightType == Height) {
1569         h = style()->height();
1570     } else if (heightType == MinHeight) {
1571         h = style()->minHeight();
1572     } else {
1573         h = style()->maxHeight();
1574     }
1575     switch (h.type()) {
1576     case Fixed:
1577         return calcContentHeight(h.value());
1578     case Percent: {
1579         int th = calcPercentageHeight(h);
1580         if (th != -1) {
1581             return calcContentHeight(th);
1582         }
1583         // fall through
1584     }
1585     default:
1586         return intrinsicHeight();
1587     };
1588 }
1589 
1590 int RenderBox::availableHeight() const
1591 {
1592     return availableHeightUsing(style()->height());
1593 }
1594 
1595 int RenderBox::availableHeightUsing(const Length &h) const
1596 {
1597     if (h.isFixed()) {
1598         return calcContentHeight(h.value());
1599     }
1600 
1601     if (isCanvas()) {
1602         if (static_cast<const RenderCanvas *>(this)->pagedMode()) {
1603             return static_cast<const RenderCanvas *>(this)->pageHeight();
1604         } else {
1605             return static_cast<const RenderCanvas *>(this)->viewportHeight();
1606         }
1607     }
1608 
1609     // We need to stop here, since we don't want to increase the height of the table
1610     // artificially.  We're going to rely on this cell getting expanded to some new
1611     // height, and then when we lay out again we'll use the calculation below.
1612     if (isTableCell() && (h.isAuto() || h.isPercent())) {
1613         const RenderTableCell *tableCell = static_cast<const RenderTableCell *>(this);
1614         return tableCell->cellPercentageHeight() -
1615                (borderTop() + borderBottom() + paddingTop() + paddingBottom());
1616     }
1617 
1618     if (h.isPercent()) {
1619         return calcContentHeight(h.width(containingBlock()->availableHeight()));
1620     }
1621 
1622     // Check for implicit height
1623     if (hasImplicitHeight()) {
1624         return calcImplicitContentHeight();
1625     }
1626 
1627     return containingBlock()->availableHeight();
1628 }
1629 
1630 int RenderBox::availableWidth() const
1631 {
1632     return availableWidthUsing(style()->width());
1633 }
1634 
1635 int RenderBox::availableWidthUsing(const Length &w) const
1636 {
1637     if (w.isFixed()) {
1638         return calcContentWidth(w.value());
1639     }
1640 
1641     if (isCanvas()) {
1642         return static_cast<const RenderCanvas *>(this)->viewportWidth();
1643     }
1644 
1645     if (w.isPercent()) {
1646         return calcContentWidth(w.width(containingBlock()->availableWidth()));
1647     }
1648 
1649     return containingBlock()->availableWidth();
1650 }
1651 
1652 void RenderBox::calcVerticalMargins()
1653 {
1654     if (isTableCell()) {
1655         // table margins are basically infinite
1656         m_marginTop = TABLECELLMARGIN;
1657         m_marginBottom = TABLECELLMARGIN;
1658         return;
1659     }
1660 
1661     Length tm = style()->marginTop();
1662     Length bm = style()->marginBottom();
1663 
1664     // margins are calculated with respect to the _width_ of
1665     // the containing block (8.3)
1666     int cw = containingBlock()->contentWidth();
1667 
1668     m_marginTop = tm.minWidth(cw);
1669     m_marginBottom = bm.minWidth(cw);
1670 }
1671 
1672 void RenderBox::setStaticX(short staticX)
1673 {
1674     m_staticX = staticX;
1675 }
1676 
1677 void RenderBox::setStaticY(int staticY)
1678 {
1679     m_staticY = staticY;
1680 }
1681 
1682 void RenderBox::calcAbsoluteHorizontal()
1683 {
1684     if (isReplaced()) {
1685         calcAbsoluteHorizontalReplaced();
1686         return;
1687     }
1688 
1689     // QUESTIONS
1690     // FIXME 1: Which RenderObject's 'direction' property should used: the
1691     // containing block (cb) as the spec seems to imply, the parent (parent()) as
1692     // was previously done in calculating the static distances, or ourself, which
1693     // was also previously done for deciding what to override when you had
1694     // over-constrained margins?  Also note that the container block is used
1695     // in similar situations in other parts of the RenderBox class (see calcWidth()
1696     // and calcHorizontalMargins()). For now we are using the parent for quirks
1697     // mode and the containing block for strict mode.
1698 
1699     // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater
1700     // than or less than the computed m_width.  Be careful of box-sizing and
1701     // percentage issues.
1702 
1703     // The following is based off of the W3C Working Draft from April 11, 2006 of
1704     // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
1705     // <https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
1706     // (block-style-comments in this function and in calcAbsoluteHorizontalValues()
1707     // correspond to text from the spec)
1708 
1709     // We don't use containingBlock(), since we may be positioned by an enclosing
1710     // relative positioned inline.
1711     RenderObject *containerBlock = container();
1712 
1713     const int containerWidth = containingBlockWidth(containerBlock);
1714 
1715     // To match WinIE, in quirks mode use the parent's 'direction' property
1716     // instead of the container block's.
1717     EDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
1718 
1719     const int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight();
1720     const Length marginLeft = style()->marginLeft();
1721     const Length marginRight = style()->marginRight();
1722     Length left = style()->left();
1723     Length right = style()->right();
1724 
1725     /*---------------------------------------------------------------------------*\
1726      * For the purposes of this section and the next, the term "static position"
1727      * (of an element) refers, roughly, to the position an element would have had
1728      * in the normal flow. More precisely:
1729      *
1730      * * The static position for 'left' is the distance from the left edge of the
1731      *   containing block to the left margin edge of a hypothetical box that would
1732      *   have been the first box of the element if its 'position' property had
1733      *   been 'static' and 'float' had been 'none'. The value is negative if the
1734      *   hypothetical box is to the left of the containing block.
1735      * * The static position for 'right' is the distance from the right edge of the
1736      *   containing block to the right margin edge of the same hypothetical box as
1737      *   above. The value is positive if the hypothetical box is to the left of the
1738      *   containing block's edge.
1739      *
1740      * But rather than actually calculating the dimensions of that hypothetical box,
1741      * user agents are free to make a guess at its probable position.
1742      *
1743      * For the purposes of calculating the static position, the containing block of
1744      * fixed positioned elements is the initial containing block instead of the
1745      * viewport, and all scrollable boxes should be assumed to be scrolled to their
1746      * origin.
1747     \*---------------------------------------------------------------------------*/
1748 
1749     // Calculate the static distance if needed.
1750     if (left.isAuto() && right.isAuto()) {
1751         if (containerDirection == LTR) {
1752             // 'm_staticX' should already have been set through layout of the parent.
1753             int staticPosition = m_staticX - containerBlock->borderLeft();
1754             for (RenderObject *po = parent(); po && po != containerBlock; po = po->parent()) {
1755                 staticPosition += po->xPos();
1756             }
1757             left = Length(staticPosition, Fixed);
1758         } else {
1759             RenderObject *po = parent();
1760             // 'm_staticX' should already have been set through layout of the parent.
1761             int staticPosition = m_staticX + containerWidth + containerBlock->borderRight() - po->width();
1762             for (; po && po != containerBlock; po = po->parent()) {
1763                 staticPosition -= po->xPos();
1764             }
1765             right = Length(staticPosition, Fixed);
1766         }
1767     }
1768 
1769     // Calculate constraint equation values for 'width' case.
1770     calcAbsoluteHorizontalValues(style()->width(), containerBlock, containerDirection,
1771                                  containerWidth, bordersPlusPadding,
1772                                  left, right, marginLeft, marginRight,
1773                                  m_width, m_marginLeft, m_marginRight, m_x);
1774     // Calculate constraint equation values for 'max-width' case.calcContentWidth(width.width(containerWidth));
1775     if (!style()->maxWidth().isUndefined()) {
1776         short maxWidth;
1777         short maxMarginLeft;
1778         short maxMarginRight;
1779         short maxXPos;
1780 
1781         calcAbsoluteHorizontalValues(style()->maxWidth(), containerBlock, containerDirection,
1782                                      containerWidth, bordersPlusPadding,
1783                                      left, right, marginLeft, marginRight,
1784                                      maxWidth, maxMarginLeft, maxMarginRight, maxXPos);
1785 
1786         if (m_width > maxWidth) {
1787             m_width = maxWidth;
1788             m_marginLeft = maxMarginLeft;
1789             m_marginRight = maxMarginRight;
1790             m_x = maxXPos;
1791         }
1792     }
1793 
1794     // Calculate constraint equation values for 'min-width' case.
1795     if (!style()->minWidth().isZero()) {
1796         short minWidth;
1797         short minMarginLeft;
1798         short minMarginRight;
1799         short minXPos;
1800 
1801         calcAbsoluteHorizontalValues(style()->minWidth(), containerBlock, containerDirection,
1802                                      containerWidth, bordersPlusPadding,
1803                                      left, right, marginLeft, marginRight,
1804                                      minWidth, minMarginLeft, minMarginRight, minXPos);
1805 
1806         if (m_width < minWidth) {
1807             m_width = minWidth;
1808             m_marginLeft = minMarginLeft;
1809             m_marginRight = minMarginRight;
1810             m_x = minXPos;
1811         }
1812     }
1813 
1814     if (short iw = intrinsicWidth()) {
1815         // some elements (e.g. Fieldset) have pseudo-replaced behaviour in quirk mode
1816         if (m_width < iw - bordersPlusPadding)
1817             calcAbsoluteHorizontalValues(Length(iw - bordersPlusPadding, Fixed), containerBlock, containerDirection,
1818                                          containerWidth, bordersPlusPadding,
1819                                          left, right, marginLeft, marginRight,
1820                                          m_width, m_marginLeft, m_marginRight, m_x);
1821     }
1822 
1823     // Put m_width into correct form.
1824     m_width += bordersPlusPadding;
1825 }
1826 
1827 void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderObject *containerBlock, EDirection containerDirection,
1828         const int containerWidth, const int bordersPlusPadding,
1829         const Length left, const Length right, const Length marginLeft, const Length marginRight,
1830         short &widthValue, short &marginLeftValue, short &marginRightValue, short &xPos)
1831 {
1832     // 'left' and 'right' cannot both be 'auto' because one would of been
1833     // converted to the static postion already
1834     assert(!(left.isAuto() && right.isAuto()));
1835 
1836     int leftValue = 0;
1837 
1838     const bool widthIsAuto = width.isAuto();
1839     const bool leftIsAuto = left.isAuto();
1840     const bool rightIsAuto = right.isAuto();
1841 
1842     if (!leftIsAuto && !widthIsAuto && !rightIsAuto) {
1843         /*-----------------------------------------------------------------------*\
1844          * If none of the three is 'auto': If both 'margin-left' and 'margin-
1845          * right' are 'auto', solve the equation under the extra constraint that
1846          * the two margins get equal values, unless this would make them negative,
1847          * in which case when direction of the containing block is 'ltr' ('rtl'),
1848          * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
1849          * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
1850          * solve the equation for that value. If the values are over-constrained,
1851          * ignore the value for 'left' (in case the 'direction' property of the
1852          * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
1853          * and solve for that value.
1854         \*-----------------------------------------------------------------------*/
1855         // NOTE:  It is not necessary to solve for 'right' in the over constrained
1856         // case because the value is not used for any further calculations.
1857 
1858         leftValue = left.width(containerWidth);
1859         widthValue = calcContentWidth(width.width(containerWidth));
1860 
1861         const int availableSpace = containerWidth - (leftValue + widthValue + right.width(containerWidth) + bordersPlusPadding);
1862 
1863         // Margins are now the only unknown
1864         if (marginLeft.isAuto() && marginRight.isAuto()) {
1865             // Both margins auto, solve for equality
1866             if (availableSpace >= 0) {
1867                 marginLeftValue = availableSpace / 2; // split the diference
1868                 marginRightValue = availableSpace - marginLeftValue;  // account for odd valued differences
1869             } else {
1870                 // see FIXME 1
1871                 if (containerDirection == LTR) {
1872                     marginLeftValue = 0;
1873                     marginRightValue = availableSpace; // will be negative
1874                 } else {
1875                     marginLeftValue = availableSpace; // will be negative
1876                     marginRightValue = 0;
1877                 }
1878             }
1879         } else if (marginLeft.isAuto()) {
1880             // Solve for left margin
1881             marginRightValue = marginRight.width(containerWidth);
1882             marginLeftValue = availableSpace - marginRightValue;
1883         } else if (marginRight.isAuto()) {
1884             // Solve for right margin
1885             marginLeftValue = marginLeft.width(containerWidth);
1886             marginRightValue = availableSpace - marginLeftValue;
1887         } else {
1888             // Over-constrained, solve for left if direction is RTL
1889             marginLeftValue = marginLeft.width(containerWidth);
1890             marginRightValue = marginRight.width(containerWidth);
1891 
1892             // see FIXME 1 -- used to be "this->style()->direction()"
1893             if (containerDirection == RTL) {
1894                 leftValue = (availableSpace + leftValue) - marginLeftValue - marginRightValue;
1895             }
1896         }
1897     } else {
1898         /*--------------------------------------------------------------------*\
1899          * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
1900          * to 0, and pick the one of the following six rules that applies.
1901          *
1902          * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
1903          *    width is shrink-to-fit. Then solve for 'left'
1904          *
1905          *              OMIT RULE 2 AS IT SHOULD NEVER BE HIT
1906          * ------------------------------------------------------------------
1907          * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
1908          *    the 'direction' property of the containing block is 'ltr' set
1909          *    'left' to the static position, otherwise set 'right' to the
1910          *    static position. Then solve for 'left' (if 'direction is 'rtl')
1911          *    or 'right' (if 'direction' is 'ltr').
1912          * ------------------------------------------------------------------
1913          *
1914          * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
1915          *    width is shrink-to-fit . Then solve for 'right'
1916          * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
1917          *    for 'left'
1918          * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
1919          *    for 'width'
1920          * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
1921          *    for 'right'
1922          *
1923          * Calculation of the shrink-to-fit width is similar to calculating the
1924          * width of a table cell using the automatic table layout algorithm.
1925          * Roughly: calculate the preferred width by formatting the content
1926          * without breaking lines other than where explicit line breaks occur,
1927          * and also calculate the preferred minimum width, e.g., by trying all
1928          * possible line breaks. CSS 2.1 does not define the exact algorithm.
1929          * Thirdly, calculate the available width: this is found by solving
1930          * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
1931          * to 0.
1932          *
1933          * Then the shrink-to-fit width is:
1934          * min(max(preferred minimum width, available width), preferred width).
1935         \*--------------------------------------------------------------------*/
1936         // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
1937         // because the value is not used for any further calculations.
1938 
1939         // Calculate margins, 'auto' margins are ignored.
1940         marginLeftValue = marginLeft.minWidth(containerWidth);
1941         marginRightValue = marginRight.minWidth(containerWidth);
1942 
1943         const int availableSpace = containerWidth - (marginLeftValue + marginRightValue + bordersPlusPadding);
1944 
1945         // FIXME: Is there a faster way to find the correct case?
1946         // Use rule/case that applies.
1947         if (leftIsAuto && widthIsAuto && !rightIsAuto) {
1948             // RULE 1: (use shrink-to-fit for width, and solve of left)
1949             int rightValue = right.width(containerWidth);
1950 
1951             // FIXME: would it be better to have shrink-to-fit in one step?
1952             int preferredWidth = m_maxWidth - bordersPlusPadding;
1953             int preferredMinWidth = m_minWidth - bordersPlusPadding;
1954             int availableWidth = availableSpace - rightValue;
1955             widthValue = qMin(qMax(preferredMinWidth, availableWidth), preferredWidth);
1956             leftValue = availableSpace - (widthValue + rightValue);
1957         } else if (!leftIsAuto && widthIsAuto && rightIsAuto) {
1958             // RULE 3: (use shrink-to-fit for width, and no need solve of right)
1959             leftValue = left.width(containerWidth);
1960 
1961             // FIXME: would it be better to have shrink-to-fit in one step?
1962             int preferredWidth = m_maxWidth - bordersPlusPadding;
1963             int preferredMinWidth = m_minWidth - bordersPlusPadding;
1964             int availableWidth = availableSpace - leftValue;
1965             widthValue = qMin(qMax(preferredMinWidth, availableWidth), preferredWidth);
1966         } else if (leftIsAuto && !width.isAuto() && !rightIsAuto) {
1967             // RULE 4: (solve for left)
1968             widthValue = calcContentWidth(width.width(containerWidth));
1969             leftValue = availableSpace - (widthValue + right.width(containerWidth));
1970         } else if (!leftIsAuto && widthIsAuto && !rightIsAuto) {
1971             // RULE 5: (solve for width)
1972             leftValue = left.width(containerWidth);
1973             widthValue = availableSpace - (leftValue + right.width(containerWidth));
1974         } else if (!leftIsAuto && !widthIsAuto && rightIsAuto) {
1975             // RULE 6: (no need solve for right)
1976             leftValue = left.width(containerWidth);
1977             widthValue = calcContentWidth(width.width(containerWidth));
1978         }
1979     }
1980 
1981     // Use computed values to calculate the horizontal position.
1982     int calculatedHorizontalPosition = leftValue + marginLeftValue + containerBlock->borderLeft();
1983     xPos = qBound((int)SHRT_MIN, calculatedHorizontalPosition, (int)SHRT_MAX);
1984 }
1985 
1986 void RenderBox::calcAbsoluteVertical()
1987 {
1988     if (isReplaced()) {
1989         calcAbsoluteVerticalReplaced();
1990         return;
1991     }
1992 
1993     // The following is based off of the W3C Working Draft from April 11, 2006 of
1994     // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
1995     // <https://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
1996     // (block-style-comments in this function and in calcAbsoluteVerticalValues()
1997     // correspond to text from the spec)
1998 
1999     // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2000     const RenderObject *containerBlock = container();
2001     const int containerHeight = containerBlock->height() - containerBlock->borderTop() - containerBlock->borderBottom();
2002 
2003     const int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom();
2004     const Length marginTop = style()->marginTop();
2005     const Length marginBottom = style()->marginBottom();
2006     Length top = style()->top();
2007     Length bottom = style()->bottom();
2008 
2009     /*---------------------------------------------------------------------------*\
2010      * For the purposes of this section and the next, the term "static position"
2011      * (of an element) refers, roughly, to the position an element would have had
2012      * in the normal flow. More precisely, the static position for 'top' is the
2013      * distance from the top edge of the containing block to the top margin edge
2014      * of a hypothetical box that would have been the first box of the element if
2015      * its 'position' property had been 'static' and 'float' had been 'none'. The
2016      * value is negative if the hypothetical box is above the containing block.
2017      *
2018      * But rather than actually calculating the dimensions of that hypothetical
2019      * box, user agents are free to make a guess at its probable position.
2020      *
2021      * For the purposes of calculating the static position, the containing block
2022      * of fixed positioned elements is the initial containing block instead of
2023      * the viewport.
2024     \*---------------------------------------------------------------------------*/
2025 
2026     // Calculate the static distance if needed.
2027     if (top.isAuto() && bottom.isAuto()) {
2028         // m_staticY should already have been set through layout of the parent()
2029         int staticTop = m_staticY - containerBlock->borderTop();
2030         for (RenderObject *po = parent(); po && po != containerBlock; po = po->parent()) {
2031             staticTop += po->yPos();
2032         }
2033         top.setValue(Fixed, staticTop);
2034     }
2035 
2036     int height; // Needed to compute overflow.
2037 
2038     // Calculate constraint equation values for 'height' case.
2039     calcAbsoluteVerticalValues(style()->height(), containerBlock, containerHeight, bordersPlusPadding,
2040                                top, bottom, marginTop, marginBottom,
2041                                height, m_marginTop, m_marginBottom, m_y);
2042 
2043     // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
2044     // see FIXME 2
2045 
2046     // Calculate constraint equation values for 'max-height' case.
2047     if (!style()->maxHeight().isUndefined()) {
2048         int maxHeight;
2049         short maxMarginTop;
2050         short maxMarginBottom;
2051         int maxYPos;
2052 
2053         calcAbsoluteVerticalValues(style()->maxHeight(), containerBlock, containerHeight, bordersPlusPadding,
2054                                    top, bottom, marginTop, marginBottom,
2055                                    maxHeight, maxMarginTop, maxMarginBottom, maxYPos);
2056 
2057         if (height > maxHeight) {
2058             height = maxHeight;
2059             m_marginTop = maxMarginTop;
2060             m_marginBottom = maxMarginBottom;
2061             m_y = maxYPos;
2062         }
2063     }
2064 
2065     // Calculate constraint equation values for 'min-height' case.
2066     if (!style()->minHeight().isZero()) {
2067         int minHeight;
2068         short minMarginTop;
2069         short minMarginBottom;
2070         int minYPos;
2071 
2072         calcAbsoluteVerticalValues(style()->minHeight(), containerBlock, containerHeight, bordersPlusPadding,
2073                                    top, bottom, marginTop, marginBottom,
2074                                    minHeight, minMarginTop, minMarginBottom, minYPos);
2075 
2076         if (height < minHeight) {
2077             height = minHeight;
2078             m_marginTop = minMarginTop;
2079             m_marginBottom = minMarginBottom;
2080             m_y = minYPos;
2081         }
2082     }
2083 
2084     height += bordersPlusPadding;
2085 
2086     // Set final height value.
2087     m_height = height;
2088 }
2089 
2090 void RenderBox::calcAbsoluteVerticalValues(Length height, const RenderObject *containerBlock,
2091         const int containerHeight, const int bordersPlusPadding,
2092         const Length top, const Length bottom, const Length marginTop, const Length marginBottom,
2093         int &heightValue, short &marginTopValue, short &marginBottomValue, int &yPos)
2094 {
2095     // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
2096     // converted to the static position in calcAbsoluteVertical()
2097     assert(!(top.isAuto() && bottom.isAuto()));
2098 
2099     int contentHeight = m_height - bordersPlusPadding;
2100 
2101     int topValue = 0;
2102 
2103     bool heightIsAuto = height.isAuto();
2104     bool topIsAuto = top.isAuto();
2105     bool bottomIsAuto = bottom.isAuto();
2106 
2107     if (isTable() && heightIsAuto) {
2108         // Height is never unsolved for tables. "auto" means shrink to fit.
2109         // Use our height instead.
2110         heightValue = contentHeight;
2111         heightIsAuto = false;
2112     } else if (!heightIsAuto) {
2113         heightValue = calcContentHeight(height.width(containerHeight));
2114         if (contentHeight > heightValue) {
2115             if (!isTable()) {
2116                 contentHeight = heightValue;
2117             } else {
2118                 heightValue = contentHeight;
2119             }
2120         }
2121     }
2122 
2123     if (!topIsAuto && !heightIsAuto && !bottomIsAuto) {
2124         /*-----------------------------------------------------------------------*\
2125          * If none of the three are 'auto': If both 'margin-top' and 'margin-
2126          * bottom' are 'auto', solve the equation under the extra constraint that
2127          * the two margins get equal values. If one of 'margin-top' or 'margin-
2128          * bottom' is 'auto', solve the equation for that value. If the values
2129          * are over-constrained, ignore the value for 'bottom' and solve for that
2130          * value.
2131         \*-----------------------------------------------------------------------*/
2132         // NOTE:  It is not necessary to solve for 'bottom' in the over constrained
2133         // case because the value is not used for any further calculations.
2134 
2135         topValue = top.width(containerHeight);
2136 
2137         const int availableSpace = containerHeight - (topValue + heightValue + bottom.width(containerHeight) + bordersPlusPadding);
2138 
2139         // Margins are now the only unknown
2140         if (marginTop.isAuto() && marginBottom.isAuto()) {
2141             // Both margins auto, solve for equality
2142             // NOTE: This may result in negative values.
2143             marginTopValue = availableSpace / 2; // split the diference
2144             marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences
2145         } else if (marginTop.isAuto()) {
2146             // Solve for top margin
2147             marginBottomValue = marginBottom.width(containerHeight);
2148             marginTopValue = availableSpace - marginBottomValue;
2149         } else if (marginBottom.isAuto()) {
2150             // Solve for bottom margin
2151             marginTopValue = marginTop.width(containerHeight);
2152             marginBottomValue = availableSpace - marginTopValue;
2153         } else {
2154             // Over-constrained, (no need solve for bottom)
2155             marginTopValue = marginTop.width(containerHeight);
2156             marginBottomValue = marginBottom.width(containerHeight);
2157         }
2158     } else {
2159         /*--------------------------------------------------------------------*\
2160          * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
2161          * to 0, and pick the one of the following six rules that applies.
2162          *
2163          * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
2164          *    the height is based on the content, and solve for 'top'.
2165          *
2166          *              OMIT RULE 2 AS IT SHOULD NEVER BE HIT
2167          * ------------------------------------------------------------------
2168          * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
2169          *    set 'top' to the static position, and solve for 'bottom'.
2170          * ------------------------------------------------------------------
2171          *
2172          * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
2173          *    the height is based on the content, and solve for 'bottom'.
2174          * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
2175          *    solve for 'top'.
2176          * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
2177          *    solve for 'height'.
2178          * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
2179          *    solve for 'bottom'.
2180         \*--------------------------------------------------------------------*/
2181         // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
2182         // because the value is not used for any further calculations.
2183 
2184         // Calculate margins, 'auto' margins are ignored.
2185         marginTopValue = marginTop.minWidth(containerHeight);
2186         marginBottomValue = marginBottom.minWidth(containerHeight);
2187 
2188         const int availableSpace = containerHeight - (marginTopValue + marginBottomValue + bordersPlusPadding);
2189 
2190         // Use rule/case that applies.
2191         if (topIsAuto && heightIsAuto && !bottomIsAuto) {
2192             // RULE 1: (height is content based, solve of top)
2193             heightValue = contentHeight;
2194             topValue = availableSpace - (heightValue + bottom.width(containerHeight));
2195         } else if (topIsAuto && !heightIsAuto && bottomIsAuto) {
2196             // RULE 2: (shouldn't happen)
2197         } else if (!topIsAuto && heightIsAuto && bottomIsAuto) {
2198             // RULE 3: (height is content based, no need solve of bottom)
2199             heightValue = contentHeight;
2200             topValue = top.width(containerHeight);
2201         } else if (topIsAuto && !heightIsAuto && !bottomIsAuto) {
2202             // RULE 4: (solve of top)
2203             topValue = availableSpace - (heightValue + bottom.width(containerHeight));
2204         } else if (!topIsAuto && heightIsAuto && !bottomIsAuto) {
2205             // RULE 5: (solve of height)
2206             topValue = top.width(containerHeight);
2207             heightValue = qMax(0, availableSpace - (topValue + bottom.width(containerHeight)));
2208         } else if (!topIsAuto && !heightIsAuto && bottomIsAuto) {
2209             // RULE 6: (no need solve of bottom)
2210             topValue = top.width(containerHeight);
2211         }
2212     }
2213 
2214     // Use computed values to calculate the vertical position.
2215     yPos = topValue + marginTopValue + containerBlock->borderTop();
2216 }
2217 
2218 void RenderBox::calcAbsoluteHorizontalReplaced()
2219 {
2220     // The following is based off of the W3C Working Draft from April 11, 2006 of
2221     // CSS 2.1: Section 10.3.8 "Absolutly positioned, replaced elements"
2222     // <https://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
2223     // (block-style-comments in this function correspond to text from the spec and
2224     // the numbers correspond to numbers in spec)
2225 
2226     // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2227     RenderObject *containerBlock = container();
2228 
2229     const int containerWidth = containingBlockWidth(containerBlock);
2230 
2231     // To match WinIE, in quirks mode use the parent's 'direction' property
2232     // instead of the container block's.
2233     EDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
2234 
2235     // Variables to solve.
2236     Length left = style()->left();
2237     Length right = style()->right();
2238     Length marginLeft = style()->marginLeft();
2239     Length marginRight = style()->marginRight();
2240 
2241     /*-----------------------------------------------------------------------*\
2242      * 1. The used value of 'width' is determined as for inline replaced
2243      *    elements.
2244     \*-----------------------------------------------------------------------*/
2245     // NOTE: This value of width is FINAL in that the min/max width calculations
2246     // are dealt with in calcReplacedWidth().  This means that the steps to produce
2247     // correct max/min in the non-replaced version, are not necessary.
2248     m_width = calcReplacedWidth() + borderLeft() + borderRight() + paddingLeft() + paddingRight();
2249     const int availableSpace = containerWidth - m_width;
2250 
2251     /*-----------------------------------------------------------------------*\
2252      * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
2253      *    of the containing block is 'ltr', set 'left' to the static position;
2254      *    else if 'direction' is 'rtl', set 'right' to the static position.
2255     \*-----------------------------------------------------------------------*/
2256     if (left.isAuto() && right.isAuto()) {
2257         // see FIXME 1
2258         if (containerDirection == LTR) {
2259             // 'm_staticX' should already have been set through layout of the parent.
2260             int staticPosition = m_staticX - containerBlock->borderLeft();
2261             for (RenderObject *po = parent(); po && po != containerBlock; po = po->parent()) {
2262                 staticPosition += po->xPos();
2263             }
2264             left.setValue(Fixed, staticPosition);
2265         } else {
2266             RenderObject *po = parent();
2267             // 'm_staticX' should already have been set through layout of the parent.
2268             int staticPosition = m_staticX + containerWidth + containerBlock->borderRight() - po->width();
2269             for (; po && po != containerBlock; po = po->parent()) {
2270                 staticPosition -= po->xPos();
2271             }
2272             right.setValue(Fixed, staticPosition);
2273         }
2274     }
2275 
2276     /*-----------------------------------------------------------------------*\
2277      * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
2278      *    or 'margin-right' with '0'.
2279     \*-----------------------------------------------------------------------*/
2280     if (left.isAuto() || right.isAuto()) {
2281         if (marginLeft.isAuto()) {
2282             marginLeft.setValue(Fixed, 0);
2283         }
2284         if (marginRight.isAuto()) {
2285             marginRight.setValue(Fixed, 0);
2286         }
2287     }
2288 
2289     /*-----------------------------------------------------------------------*\
2290      * 4. If at this point both 'margin-left' and 'margin-right' are still
2291      *    'auto', solve the equation under the extra constraint that the two
2292      *    margins must get equal values, unless this would make them negative,
2293      *    in which case when the direction of the containing block is 'ltr'
2294      *    ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
2295      *    'margin-right' ('margin-left').
2296     \*-----------------------------------------------------------------------*/
2297     int leftValue = 0;
2298     int rightValue = 0;
2299 
2300     if (marginLeft.isAuto() && marginRight.isAuto()) {
2301         // 'left' and 'right' cannot be 'auto' due to step 3
2302         assert(!(left.isAuto() && right.isAuto()));
2303 
2304         leftValue = left.width(containerWidth);
2305         rightValue = right.width(containerWidth);
2306 
2307         int difference = availableSpace - (leftValue + rightValue);
2308         if (difference > 0) {
2309             m_marginLeft = difference / 2; // split the diference
2310             m_marginRight = difference - m_marginLeft; // account for odd valued differences
2311         } else {
2312             // see FIXME 1
2313             if (containerDirection == LTR) {
2314                 m_marginLeft = 0;
2315                 m_marginRight = difference;  // will be negative
2316             } else {
2317                 m_marginLeft = difference;  // will be negative
2318                 m_marginRight = 0;
2319             }
2320         }
2321 
2322         /*-----------------------------------------------------------------------*\
2323          * 5. If at this point there is an 'auto' left, solve the equation for
2324          *    that value.
2325         \*-----------------------------------------------------------------------*/
2326     } else if (left.isAuto()) {
2327         m_marginLeft = marginLeft.width(containerWidth);
2328         m_marginRight = marginRight.width(containerWidth);
2329         rightValue = right.width(containerWidth);
2330 
2331         // Solve for 'left'
2332         leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight);
2333     } else if (right.isAuto()) {
2334         m_marginLeft = marginLeft.width(containerWidth);
2335         m_marginRight = marginRight.width(containerWidth);
2336         leftValue = left.width(containerWidth);
2337 
2338         // Solve for 'right'
2339         rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight);
2340     } else if (marginLeft.isAuto()) {
2341         m_marginRight = marginRight.width(containerWidth);
2342         leftValue = left.width(containerWidth);
2343         rightValue = right.width(containerWidth);
2344 
2345         // Solve for 'margin-left'
2346         m_marginLeft = availableSpace - (leftValue + rightValue + m_marginRight);
2347     } else if (marginRight.isAuto()) {
2348         m_marginLeft = marginLeft.width(containerWidth);
2349         leftValue = left.width(containerWidth);
2350         rightValue = right.width(containerWidth);
2351 
2352         // Solve for 'margin-right'
2353         m_marginRight = availableSpace - (leftValue + rightValue + m_marginLeft);
2354     }
2355 
2356     /*-----------------------------------------------------------------------*\
2357      * 6. If at this point the values are over-constrained, ignore the value
2358      *    for either 'left' (in case the 'direction' property of the
2359      *    containing block is 'rtl') or 'right' (in case 'direction' is
2360      *    'ltr') and solve for that value.
2361     \*-----------------------------------------------------------------------*/
2362     else  {
2363         m_marginLeft = marginLeft.width(containerWidth);
2364         m_marginRight = marginRight.width(containerWidth);
2365         if (containerDirection  == LTR) {
2366             leftValue = left.width(containerWidth);
2367             rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight);
2368         } else {
2369             rightValue = right.width(containerWidth);
2370             leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight);
2371         }
2372     }
2373 
2374     int totalWidth = m_width + leftValue + rightValue +  m_marginLeft + m_marginRight;
2375     if (totalWidth > containerWidth && (containerDirection == RTL)) {
2376         leftValue = containerWidth - (totalWidth - leftValue);
2377     }
2378 
2379     // Use computed values to calculate the horizontal position.
2380     int calculatedHorizontalPosition = leftValue + m_marginLeft + containerBlock->borderLeft();
2381     m_x = qBound((int)SHRT_MIN, calculatedHorizontalPosition, (int)SHRT_MAX);
2382 }
2383 
2384 void RenderBox::calcAbsoluteVerticalReplaced()
2385 {
2386     // The following is based off of the W3C Working Draft from April 11, 2006 of
2387     // CSS 2.1: Section 10.6.5 "Absolutly positioned, replaced elements"
2388     // <https://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
2389     // (block-style-comments in this function correspond to text from the spec and
2390     // the numbers correspond to numbers in spec)
2391 
2392     // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2393     const RenderObject *containerBlock = container();
2394     const int containerHeight = containerBlock->height() - containerBlock->borderTop() - containerBlock->borderBottom();
2395 
2396     // Variables to solve.
2397     Length top = style()->top();
2398     Length bottom = style()->bottom();
2399     Length marginTop = style()->marginTop();
2400     Length marginBottom = style()->marginBottom();
2401 
2402     /*-----------------------------------------------------------------------*\
2403      * 1. The used value of 'height' is determined as for inline replaced
2404      *    elements.
2405     \*-----------------------------------------------------------------------*/
2406     // NOTE: This value of height is FINAL in that the min/max height calculations
2407     // are dealt with in calcReplacedHeight().  This means that the steps to produce
2408     // correct max/min in the non-replaced version, are not necessary.
2409     m_height = calcReplacedHeight() + borderTop() + borderBottom() + paddingTop() + paddingBottom();
2410     const int availableSpace = containerHeight - m_height;
2411 
2412     /*-----------------------------------------------------------------------*\
2413      * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
2414      *    with the element's static position.
2415     \*-----------------------------------------------------------------------*/
2416     if (top.isAuto() && bottom.isAuto()) {
2417         // m_staticY should already have been set through layout of the parent().
2418         int staticTop = m_staticY - containerBlock->borderTop();
2419         for (RenderObject *po = parent(); po && po != containerBlock; po = po->parent()) {
2420             staticTop += po->yPos();
2421         }
2422         top.setValue(Fixed, staticTop);
2423     }
2424 
2425     /*-----------------------------------------------------------------------*\
2426      * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
2427      *    'margin-bottom' with '0'.
2428     \*-----------------------------------------------------------------------*/
2429     // FIXME: The spec. says that this step should only be taken when bottom is
2430     // auto, but if only top is auto, this makes step 4 impossible.
2431     if (top.isAuto() || bottom.isAuto()) {
2432         if (marginTop.isAuto()) {
2433             marginTop.setValue(Fixed, 0);
2434         }
2435         if (marginBottom.isAuto()) {
2436             marginBottom.setValue(Fixed, 0);
2437         }
2438     }
2439 
2440     /*-----------------------------------------------------------------------*\
2441      * 4. If at this point both 'margin-top' and 'margin-bottom' are still
2442      *    'auto', solve the equation under the extra constraint that the two
2443      *    margins must get equal values.
2444     \*-----------------------------------------------------------------------*/
2445     int topValue = 0;
2446     int bottomValue = 0;
2447 
2448     if (marginTop.isAuto() && marginBottom.isAuto()) {
2449         // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combinded.
2450         assert(!(top.isAuto() || bottom.isAuto()));
2451 
2452         topValue = top.width(containerHeight);
2453         bottomValue = bottom.width(containerHeight);
2454 
2455         int difference = availableSpace - (topValue + bottomValue);
2456         // NOTE: This may result in negative values.
2457         m_marginTop =  difference / 2; // split the difference
2458         m_marginBottom = difference - m_marginTop; // account for odd valued differences
2459 
2460         /*-----------------------------------------------------------------------*\
2461          * 5. If at this point there is only one 'auto' left, solve the equation
2462          *    for that value.
2463         \*-----------------------------------------------------------------------*/
2464     } else if (top.isAuto()) {
2465         m_marginTop = marginTop.width(containerHeight);
2466         m_marginBottom = marginBottom.width(containerHeight);
2467         bottomValue = bottom.width(containerHeight);
2468 
2469         // Solve for 'top'
2470         topValue = availableSpace - (bottomValue + m_marginTop + m_marginBottom);
2471     } else if (bottom.isAuto()) {
2472         m_marginTop = marginTop.width(containerHeight);
2473         m_marginBottom = marginBottom.width(containerHeight);
2474         topValue = top.width(containerHeight);
2475 
2476         // Solve for 'bottom'
2477         // NOTE: It is not necessary to solve for 'bottom' because we don't ever
2478         // use the value.
2479     } else if (marginTop.isAuto()) {
2480         m_marginBottom = marginBottom.width(containerHeight);
2481         topValue = top.width(containerHeight);
2482         bottomValue = bottom.width(containerHeight);
2483 
2484         // Solve for 'margin-top'
2485         m_marginTop = availableSpace - (topValue + bottomValue + m_marginBottom);
2486     } else if (marginBottom.isAuto()) {
2487         m_marginTop = marginTop.width(containerHeight);
2488         topValue = top.width(containerHeight);
2489         bottomValue = bottom.width(containerHeight);
2490 
2491         // Solve for 'margin-bottom'
2492         m_marginBottom = availableSpace - (topValue + bottomValue + m_marginTop);
2493     }
2494 
2495     /*-----------------------------------------------------------------------*\
2496      * 6. If at this point the values are over-constrained, ignore the value
2497      *    for 'bottom' and solve for that value.
2498     \*-----------------------------------------------------------------------*/
2499     else {
2500         m_marginTop = marginTop.width(containerHeight);
2501         m_marginBottom = marginBottom.width(containerHeight);
2502         topValue = top.width(containerHeight);
2503 
2504         // Solve for 'bottom'
2505         // NOTE: It is not necessary to solve for 'bottom' because we don't ever
2506         // use the value.
2507     }
2508 
2509     // Use computed values to calculate the vertical position.
2510     m_y = topValue + m_marginTop + containerBlock->borderTop();
2511 }
2512 
2513 int RenderBox::highestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2514 {
2515     return includeSelf ? 0 : m_height;
2516 }
2517 
2518 int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2519 {
2520     return includeSelf ? m_height : 0;
2521     if (!includeSelf || !m_width) {
2522         return 0;
2523     }
2524     int bottom = m_height;
2525     if (isRelPositioned()) {
2526         int x = 0;
2527         relativePositionOffset(x, bottom);
2528     }
2529     return bottom;
2530 }
2531 
2532 int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2533 {
2534     if (!includeSelf || !m_height) {
2535         return 0;
2536     }
2537     int right = m_width;
2538     if (isRelPositioned()) {
2539         int y = 0;
2540         relativePositionOffset(right, y);
2541     }
2542     return right;
2543 }
2544 
2545 int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2546 {
2547     if (!includeSelf || !m_height) {
2548         return m_width;
2549     }
2550     int left = 0;
2551     if (isRelPositioned()) {
2552         int y = 0;
2553         relativePositionOffset(left, y);
2554     }
2555     return left;
2556 }
2557 
2558 int RenderBox::pageTopAfter(int y) const
2559 {
2560     RenderObject *cb = container();
2561     if (cb) {
2562         return cb->pageTopAfter(y + yPos()) - yPos();
2563     } else {
2564         return 0;
2565     }
2566 }
2567 
2568 int RenderBox::crossesPageBreak(int t, int b) const
2569 {
2570     RenderObject *cb = container();
2571     if (cb) {
2572         return cb->crossesPageBreak(yPos() + t, yPos() + b);
2573     } else {
2574         return false;
2575     }
2576 }
2577 
2578 bool RenderBox::handleEvent(const DOM::EventImpl &e)
2579 {
2580     KHTMLAssert(scrollsOverflow());
2581     bool accepted = false;
2582 
2583     switch (e.id()) {
2584     case EventImpl::KHTML_MOUSEWHEEL_EVENT: {
2585 
2586         const MouseEventImpl &me = static_cast<const MouseEventImpl &>(e);
2587         Qt::MouseButtons buttons = Qt::NoButton;
2588         Qt::KeyboardModifiers state = Qt::NoModifier;
2589         Qt::Orientation orient = Qt::Vertical;
2590 
2591         switch (me.button()) {
2592         case 0:
2593             buttons = Qt::LeftButton;
2594             break;
2595         case 1:
2596             buttons = Qt::MidButton;
2597             break;
2598         case 2:
2599             buttons = Qt::RightButton;
2600             break;
2601         default:
2602             break;
2603         }
2604 
2605         if (me.orientation() == MouseEventImpl::OHorizontal) {
2606             orient = Qt::Horizontal;
2607         }
2608 
2609         int absx = 0;
2610         int absy = 0;
2611         absolutePosition(absx, absy);
2612         absx += borderLeft() + paddingLeft();
2613         absy += borderTop() + paddingTop();
2614 
2615         QPoint p(me.clientX() - absx + canvas()->view()->contentsX(),
2616                  me.clientY() - absy + canvas()->view()->contentsY());
2617 
2618         QWheelEvent we(p, -me.detail() * 40, buttons, state, orient);
2619         KHTMLAssert(layer());
2620         KHTMLView *v = document()->view();
2621         if (((orient == Qt::Vertical && (v->contentsHeight() > v->visibleHeight()))  ||
2622                 (orient == Qt::Horizontal && (v->contentsWidth() > v->visibleWidth()))) &&
2623                 v->isScrollingFromMouseWheel()) {
2624             // don't propagate wheel events to overflows if heuristics say the view is being scrolled by mouse wheel
2625             accepted = false;
2626             break;
2627         }
2628 
2629         bool d = (we.delta() < 0);
2630         if (orient == Qt::Vertical) {
2631             if (QScrollBar *vsb = layer()->verticalScrollbar()) {
2632                 if ((d && vsb->value() != vsb->maximum()) || (!d && vsb->value() != vsb->minimum())) {
2633                     QApplication::sendEvent(vsb, &we);
2634                 }
2635                 accepted = true;
2636             }
2637         } else {
2638             if (QScrollBar *hsb = layer()->horizontalScrollbar()) {
2639                 if ((d && hsb->value() != hsb->maximum()) || (!d && hsb->value() != hsb->minimum())) {
2640                     QApplication::sendEvent(hsb, &we);
2641                 }
2642                 accepted = true;
2643             }
2644         }
2645         break;
2646     }
2647     case EventImpl::KEYDOWN_EVENT:
2648     case EventImpl::KEYUP_EVENT:
2649         break;
2650     case EventImpl::KEYPRESS_EVENT: {
2651         if (!e.isKeyRelatedEvent()) {
2652             break;
2653         }
2654         const KeyEventBaseImpl &domKeyEv = static_cast<const KeyEventBaseImpl &>(e);
2655 
2656         QKeyEvent *const ke = domKeyEv.qKeyEvent();
2657         QScrollBar *vbar = layer()->verticalScrollbar();
2658         QScrollBar *hbar = layer()->horizontalScrollbar();
2659         switch (ke->key()) {
2660         case Qt::Key_PageUp:
2661             if (vbar) {
2662                 vbar->triggerAction(QScrollBar::SliderPageStepSub);
2663             }
2664             break;
2665         case Qt::Key_PageDown:
2666             if (vbar) {
2667                 vbar->triggerAction(QScrollBar::SliderPageStepAdd);
2668             }
2669             break;
2670         case Qt::Key_Up:
2671             if (vbar) {
2672                 vbar->triggerAction(QScrollBar::SliderSingleStepSub);
2673             }
2674             break;
2675         case Qt::Key_Down:
2676             if (vbar) {
2677                 vbar->triggerAction(QScrollBar::SliderSingleStepAdd);
2678             }
2679             break;
2680         case Qt::Key_Left:
2681             if (hbar) {
2682                 hbar->triggerAction(QScrollBar::SliderSingleStepSub);
2683             }
2684             break;
2685         case Qt::Key_Right:
2686             if (hbar) {
2687                 hbar->triggerAction(QScrollBar::SliderSingleStepAdd);
2688             }
2689             break;
2690         default:
2691             break;
2692         }
2693         break;
2694     }
2695     default:
2696         break;
2697     }
2698     if (accepted) {
2699         return true;
2700     }
2701     return RenderContainer::handleEvent(e);
2702 }
2703 
2704 void RenderBox::caretPos(int /*offset*/, int flags, int &_x, int &_y, int &width, int &height) const
2705 {
2706 #if 0
2707     _x = -1;
2708 
2709     // propagate it downwards to its children, someone will feel responsible
2710     RenderObject *child = firstChild();
2711 //    if (child) qCDebug(KHTML_LOG) << "delegating caretPos to " << child->renderName();
2712     if (child) {
2713         child->caretPos(offset, override, _x, _y, width, height);
2714     }
2715 
2716     // if not, use the extents of this box. offset 0 means left, offset 1 means
2717     // right
2718     if (_x == -1) {
2719         //qCDebug(KHTML_LOG) << "no delegation";
2720         _x = xPos() + (offset == 0 ? 0 : m_width);
2721         _y = yPos();
2722         height = m_height;
2723         width = override && offset == 0 ? m_width : 1;
2724 
2725         // If height of box is smaller than font height, use the latter one,
2726         // otherwise the caret might become invisible.
2727         // FIXME: ignoring :first-line, missing good reason to take care of
2728         int fontHeight = style()->fontMetrics().height();
2729         if (fontHeight > height) {
2730             height = fontHeight;
2731         }
2732 
2733         int absx, absy;
2734 
2735         RenderObject *cb = containingBlock();
2736 
2737         if (cb && cb != this && cb->absolutePosition(absx, absy)) {
2738             //qCDebug(KHTML_LOG) << "absx=" << absx << " absy=" << absy;
2739             _x += absx;
2740             _y += absy;
2741         } else {
2742             // we don't know our absolute position, and there is no point returning
2743             // just a relative one
2744             _x = _y = -1;
2745         }
2746     }
2747 #endif
2748 
2749     _x = xPos();
2750     _y = yPos();
2751 //     qCDebug(KHTML_LOG) << "_x " << _x << " _y " << _y;
2752     width = 1;      // no override is indicated in boxes
2753 
2754     RenderBlock *cb = containingBlock();
2755 
2756     // Place caret outside the border
2757     if (flags & CFOutside) {
2758 
2759         RenderStyle *s = element() && element()->parent()
2760                          && element()->parent()->renderer()
2761                          ? element()->parent()->renderer()->style()
2762                          : cb->style();
2763 
2764         const QFontMetrics &fm = s->fontMetrics();
2765         height = fm.height();
2766 
2767         bool rtl = s->direction() == RTL;
2768         bool outsideEnd = flags & CFOutsideEnd;
2769 
2770         if (outsideEnd) {
2771             _x += this->width();
2772         } else {
2773             _x--;
2774         }
2775 
2776         int hl = fm.leading() / 2;
2777         if (!isReplaced() || style()->display() == BLOCK) {
2778             if (!outsideEnd ^ rtl) {
2779                 _y -= hl;
2780             } else {
2781                 _y += qMax(this->height() - fm.ascent() - hl, 0);
2782             }
2783         } else {
2784             _y += baselinePosition(false) - fm.ascent() - hl;
2785         }
2786 
2787         // Place caret inside the element
2788     } else {
2789         const QFontMetrics &fm = style()->fontMetrics();
2790         height = fm.height();
2791 
2792         RenderStyle *s = style();
2793 
2794         _x += borderLeft() + paddingLeft();
2795         _y += borderTop() + paddingTop();
2796 
2797         // ### regard direction
2798         switch (s->textAlign()) {
2799         case LEFT:
2800         case KHTML_LEFT:
2801         case TAAUTO:    // ### find out what this does
2802         case JUSTIFY:
2803             break;
2804         case CENTER:
2805         case KHTML_CENTER:
2806             _x += contentWidth() / 2;
2807             break;
2808         case KHTML_RIGHT:
2809         case RIGHT:
2810             _x += contentWidth();
2811             break;
2812         }
2813     }
2814 
2815     int absx, absy;
2816     if (cb && cb != this && cb->absolutePosition(absx, absy)) {
2817 //         qCDebug(KHTML_LOG) << "absx=" << absx << " absy=" << absy;
2818         _x += absx;
2819         _y += absy;
2820     } else {
2821         // we don't know our absolute position, and there is no point returning
2822         // just a relative one
2823         _x = _y = -1;
2824     }
2825 }
2826 
2827 #undef DEBUG_LAYOUT