File indexing completed on 2024-11-10 09:38:55
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