1 /**
2 * This file is part of the DOM implementation for KDE.
3 *
4 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2002-2007 Apple Computer, Inc.
7 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
8 * (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
9 * (C) 2007 Germain Garand (germain@ebooksfrance.org)
10 * (C) 2008 Fredrik Höglund (fredrik@kde.org)
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public License
23 * along with this library; see the file COPYING.LIB. If not, write to
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 * Boston, MA 02110-1301, USA.
26 *
27 */
28 // -------------------------------------------------------------------------
29 //#define DEBUG_LAYOUT
30 //#define CLIP_DEBUG
31
32 #include <QPainter>
33
34 #include "misc/loader.h"
35 #include "rendering/render_form.h"
36 #include "rendering/render_replaced.h"
37 #include "rendering/render_canvas.h"
38 #include "rendering/render_table.h"
39 #include "rendering/render_inline.h"
40 #include "rendering/render_block.h"
41 #include "rendering/render_line.h"
42 #include "rendering/render_layer.h"
43 #include "xml/dom_nodeimpl.h"
44 #include "xml/dom_docimpl.h"
45 #include "xml/dom2_eventsimpl.h"
46 #include "html/html_elementimpl.h"
47
48 #include <QWheelEvent>
49 #include <khtmlview.h>
50 #include "khtml_debug.h"
51 #include <assert.h>
52
53 using namespace DOM;
54 using namespace khtml;
55
56 #define TABLECELLMARGIN -0x4000
57
RenderBox(DOM::NodeImpl * node)58 RenderBox::RenderBox(DOM::NodeImpl *node)
59 : RenderContainer(node)
60 {
61 m_minWidth = -1;
62 m_maxWidth = -1;
63 m_width = m_height = 0;
64 m_x = 0;
65 m_y = 0;
66 m_marginTop = 0;
67 m_marginBottom = 0;
68 m_marginLeft = 0;
69 m_marginRight = 0;
70 m_staticX = 0;
71 m_staticY = 0;
72
73 m_placeHolderBox = nullptr;
74 m_layer = nullptr;
75 }
76
createAnonymousBlock()77 RenderBlock *RenderBox::createAnonymousBlock()
78 {
79 RenderStyle *newStyle = new RenderStyle();
80 newStyle->inheritFrom(style());
81 newStyle->setDisplay(BLOCK);
82
83 RenderBlock *newBox = new(renderArena()) RenderBlock(document() /* anonymous*/);
84 newBox->setStyle(newStyle);
85 return newBox;
86 }
87
restructureParentFlow()88 void RenderBox::restructureParentFlow()
89 {
90 if (!parent() || parent()->childrenInline() == isInline()) {
91 return;
92 }
93 // We have gone from not affecting the inline status of the parent flow to suddenly
94 // having an impact. See if there is a mismatch between the parent flow's
95 // childrenInline() state and our state.
96 if (!isInline()) {
97 if (parent()->isRenderInline()) {
98 // We have to split the parent flow.
99 RenderInline *parentInline = static_cast<RenderInline *>(parent());
100 RenderBlock *newBox = parentInline->createAnonymousBlock();
101
102 RenderFlow *oldContinuation = parent()->continuation();
103 parentInline->setContinuation(newBox);
104
105 RenderObject *beforeChild = nextSibling();
106 parent()->removeChildNode(this);
107 parentInline->splitFlow(beforeChild, newBox, this, oldContinuation);
108 } else if (parent()->isRenderBlock()) {
109 RenderBlock *p = static_cast<RenderBlock *>(parent());
110 p->makeChildrenNonInline();
111 if (p->isAnonymousBlock() && p->parent()) {
112 p->parent()->removeSuperfluousAnonymousBlockChild(p);
113 }
114 // we might be deleted now
115 }
116 } else {
117 // An anonymous block must be made to wrap this inline.
118 RenderBlock *box = createAnonymousBlock();
119 parent()->insertChildNode(box, this);
120 box->appendChildNode(parent()->removeChildNode(this));
121 }
122 }
123
overflowAppliesTo(RenderObject * o)124 static inline bool overflowAppliesTo(RenderObject *o)
125 {
126 // css 2.1-11.1.1
127 // 1) overflow only applies to non-replaced block-level elements, table cells, and inline-block elements
128 if (o->isRenderBlock() || o->isTableRow() || o->isTableSection())
129 // 2) overflow on root applies to the viewport (cf. KHTMLView::layout)
130 if (!o->isRoot())
131 // 3) overflow on body may apply to the viewport...
132 if (!o->isBody()
133 // ...but only for HTML documents...
134 || !o->document()->isHTMLDocument()
135 // ...and only when the root has a visible overflow
136 || !o->document()->documentElement()->renderer()
137 || !o->document()->documentElement()->renderer()->style()
138 || o->document()->documentElement()->renderer()->style()->hidesOverflow()) {
139 return true;
140 }
141
142 return false;
143 }
144
setStyle(RenderStyle * _style)145 void RenderBox::setStyle(RenderStyle *_style)
146 {
147 bool affectsParent = style() && isFloatingOrPositioned() &&
148 (!_style->isFloating() && _style->position() != PABSOLUTE && _style->position() != PFIXED) &&
149 parent() && (parent()->isBlockFlow() || parent()->isInlineFlow());
150
151 RenderContainer::setStyle(_style);
152
153 // The root always paints its background/border.
154 if (isRoot()) {
155 setShouldPaintBackgroundOrBorder(true);
156 }
157
158 switch (_style->display()) {
159 case INLINE:
160 case INLINE_BLOCK:
161 case INLINE_TABLE:
162 setInline(true);
163 break;
164 case RUN_IN:
165 if (isInline() && parent() && parent()->childrenInline()) {
166 break;
167 }
168 default:
169 setInline(false);
170 }
171
172 switch (_style->position()) {
173 case PABSOLUTE:
174 case PFIXED:
175 setPositioned(true);
176 break;
177 default:
178 setPositioned(false);
179 if (!isTableCell() && _style->isFloating()) {
180 setFloating(true);
181 }
182
183 if (_style->position() == PRELATIVE) {
184 setRelPositioned(true);
185 }
186 }
187
188 if (overflowAppliesTo(this) && _style->hidesOverflow()) {
189 setHasOverflowClip();
190 }
191
192 if (requiresLayer()) {
193 if (!m_layer) {
194 m_layer = new(renderArena()) RenderLayer(this);
195 m_layer->insertOnlyThisLayer();
196 if (parent() && containingBlock()) {
197 m_layer->updateLayerPosition();
198 }
199 }
200 } else if (m_layer && !isCanvas()) {
201 m_layer->removeOnlyThisLayer();
202 m_layer = nullptr;
203 }
204
205 if (m_layer) {
206 m_layer->styleChanged();
207 }
208
209 if (style()->outlineWidth() > 0 && style()->outlineSize() > maximalOutlineSize(PaintActionOutline)) {
210 static_cast<RenderCanvas *>(document()->renderer())->setMaximalOutlineSize(style()->outlineSize());
211 }
212 if (affectsParent) {
213 restructureParentFlow();
214 }
215 }
216
~RenderBox()217 RenderBox::~RenderBox()
218 {
219 //qCDebug(KHTML_LOG) << "Element destructor: this=" << nodeName().string();
220 }
221
detach()222 void RenderBox::detach()
223 {
224 RenderLayer *layer = m_layer;
225 RenderArena *arena = renderArena();
226
227 detachRemainingChildren();
228 RenderContainer::detach();
229
230 if (layer) {
231 layer->detach(arena);
232 }
233 }
234
detachRemainingChildren()235 void RenderBox::detachRemainingChildren()
236 {
237 while (firstChild()) {
238 if (firstChild()->style()->styleType() == RenderStyle::FIRST_LETTER && !firstChild()->isText()) {
239 // First letters are destroyed by their remaining text fragment.
240 // We have to remove their references to parent here, however,
241 // since it may be destroyed once we get to them
242 firstChild()->remove();
243 } else {
244 // Destroy any (most likely anonymous) children remaining in the render tree
245 if (firstChild()->element()) {
246 firstChild()->element()->setRenderer(nullptr);
247 }
248 firstChild()->detach();
249 }
250 }
251 }
252
removeChild(RenderObject * oldChild)253 void RenderBox::removeChild(RenderObject *oldChild)
254 {
255 // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode
256 // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on
257 // layout anyway).
258 oldChild->removeFromObjectLists();
259
260 removeChildNode(oldChild);
261 }
262
createInlineBox(bool,bool)263 InlineBox *RenderBox::createInlineBox(bool /*makePlaceHolderBox*/, bool /*isRootLineBox*/)
264 {
265 if (m_placeHolderBox) {
266 m_placeHolderBox->detach(renderArena(), true/*noRemove*/);
267 }
268 return (m_placeHolderBox = new(renderArena()) PlaceHolderBox(this));
269 }
270
deleteInlineBoxes(RenderArena *)271 void RenderBox::deleteInlineBoxes(RenderArena * /*arena*/)
272 {
273 if (m_placeHolderBox) {
274 m_placeHolderBox->detach(renderArena(), true /*noRemove*/);
275 m_placeHolderBox = nullptr;
276 }
277 }
278
dirtyInlineBoxes(bool fullLayout,bool)279 void RenderBox::dirtyInlineBoxes(bool fullLayout, bool /*isRootLineBox*/)
280 {
281 if (m_placeHolderBox) {
282 if (fullLayout) {
283 m_placeHolderBox->detach(renderArena(), true /*noRemove*/);
284 m_placeHolderBox = nullptr;
285 } else {
286 m_placeHolderBox->dirtyInlineBoxes();
287 }
288 }
289 }
290
contentWidth() const291 short RenderBox::contentWidth() const
292 {
293 short w = m_width - style()->borderLeftWidth() - style()->borderRightWidth();
294 w -= paddingLeft() + paddingRight();
295
296 if (m_layer && scrollsOverflowY()) {
297 w -= m_layer->verticalScrollbarWidth();
298 }
299
300 //qCDebug(KHTML_LOG) << "RenderBox::contentWidth(2) = " << w;
301 return w;
302 }
303
contentHeight() const304 int RenderBox::contentHeight() const
305 {
306 int h = m_height - style()->borderTopWidth() - style()->borderBottomWidth();
307 h -= paddingTop() + paddingBottom();
308
309 if (m_layer && scrollsOverflowX()) {
310 h -= m_layer->horizontalScrollbarHeight();
311 }
312
313 return h;
314 }
315
setPos(int xPos,int yPos)316 void RenderBox::setPos(int xPos, int yPos)
317 {
318 m_x = xPos; m_y = yPos;
319 }
320
width() const321 short RenderBox::width() const
322 {
323 return m_width;
324 }
325
height() const326 int RenderBox::height() const
327 {
328 return m_height;
329 }
330
setWidth(int width)331 void RenderBox::setWidth(int width)
332 {
333 m_width = width;
334 }
335
setHeight(int height)336 void RenderBox::setHeight(int height)
337 {
338 m_height = height;
339 }
340
calcBoxHeight(int h) const341 int RenderBox::calcBoxHeight(int h) const
342 {
343 if (style()->boxSizing() == CONTENT_BOX) {
344 h += borderTop() + borderBottom() + paddingTop() + paddingBottom();
345 }
346
347 return h;
348 }
349
calcBoxWidth(int w) const350 int RenderBox::calcBoxWidth(int w) const
351 {
352 if (style()->boxSizing() == CONTENT_BOX) {
353 w += borderLeft() + borderRight() + paddingLeft() + paddingRight();
354 }
355
356 return w;
357 }
358
calcContentHeight(int h) const359 int RenderBox::calcContentHeight(int h) const
360 {
361 if (style()->boxSizing() == BORDER_BOX) {
362 h -= borderTop() + borderBottom() + paddingTop() + paddingBottom();
363 }
364
365 return qMax(0, h);
366 }
367
calcContentWidth(int w) const368 int RenderBox::calcContentWidth(int w) const
369 {
370 if (style()->boxSizing() == BORDER_BOX) {
371 w -= borderLeft() + borderRight() + paddingLeft() + paddingRight();
372 }
373
374 return qMax(0, w);
375 }
376
377 // --------------------- painting stuff -------------------------------
378
paint(PaintInfo & i,int _tx,int _ty)379 void RenderBox::paint(PaintInfo &i, int _tx, int _ty)
380 {
381 _tx += m_x;
382 _ty += m_y;
383
384 if (hasOverflowClip() && m_layer) {
385 m_layer->subtractScrollOffset(_tx, _ty);
386 }
387
388 // default implementation. Just pass things through to the children
389 for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
390 child->paint(i, _tx, _ty);
391 }
392 }
393
paintRootBoxDecorations(PaintInfo & paintInfo,int _tx,int _ty)394 void RenderBox::paintRootBoxDecorations(PaintInfo &paintInfo, int _tx, int _ty)
395 {
396 //qCDebug(KHTML_LOG) << renderName() << "::paintRootBoxDecorations()" << _tx << "/" << _ty;
397 const BackgroundLayer *bgLayer = style()->backgroundLayers();
398 QColor bgColor = style()->backgroundColor();
399 if (document()->isHTMLDocument() && !style()->hasBackground()) {
400 // Locate the <body> element using the DOM. This is easier than trying
401 // to crawl around a render tree with potential :before/:after content and
402 // anonymous blocks created by inline <body> tags etc. We can locate the <body>
403 // render object very easily via the DOM.
404 HTMLElementImpl *body = document()->body();
405 RenderObject *bodyObject = (body && body->id() == ID_BODY) ? body->renderer() : nullptr;
406
407 if (bodyObject) {
408 bgLayer = bodyObject->style()->backgroundLayers();
409 bgColor = bodyObject->style()->backgroundColor();
410 }
411 }
412
413 if (!bgColor.isValid() && canvas()->view()) {
414 bgColor = canvas()->view()->palette().color(QPalette::Active, QPalette::Base);
415 }
416
417 int w = width();
418 int h = height();
419
420 // qCDebug(KHTML_LOG) << "width = " << w;
421
422 int rw, rh;
423 if (canvas()->view()) {
424 rw = canvas()->view()->contentsWidth();
425 rh = canvas()->view()->contentsHeight();
426 } else {
427 rw = canvas()->docWidth();
428 rh = canvas()->docHeight();
429 }
430
431 // qCDebug(KHTML_LOG) << "rw = " << rw;
432
433 int bx = _tx - marginLeft();
434 int by = _ty - marginTop();
435 int bw = qMax(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw);
436 int bh = qMax(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh);
437
438 // CSS2 14.2:
439 // " The background of the box generated by the root element covers the entire canvas."
440 // hence, paint the background even in the margin areas (unlike for every other element!)
441 // I just love these little inconsistencies .. :-( (Dirk)
442 QRect cr = paintInfo.r.intersected(QRect(bx, by, bw, bh));
443 paintAllBackgrounds(paintInfo.p, bgColor, bgLayer, cr, bx, by, bw, bh);
444
445 if (style()->hasBorder()) {
446 paintBorder(paintInfo.p, _tx, _ty, w, h, style());
447 }
448 }
449
paintBoxDecorations(PaintInfo & paintInfo,int _tx,int _ty)450 void RenderBox::paintBoxDecorations(PaintInfo &paintInfo, int _tx, int _ty)
451 {
452 //qCDebug(KHTML_LOG) << renderName() << "::paintDecorations()";
453
454 if (isRoot()) {
455 return paintRootBoxDecorations(paintInfo, _tx, _ty);
456 }
457
458 int w = width();
459 int h = height() + borderTopExtra() + borderBottomExtra();
460 _ty -= borderTopExtra();
461 QRect cr = QRect(_tx, _ty, w, h).intersected(paintInfo.r);
462
463 // The <body> only paints its background if the root element has defined a background
464 // independent of the body. Go through the DOM to get to the root element's render object,
465 // since the root could be inline and wrapped in an anonymous block.
466
467 if (!isBody() || !document()->isHTMLDocument() || document()->documentElement()->renderer()->style()->hasBackground()) {
468 paintAllBackgrounds(paintInfo.p, style()->backgroundColor(), style()->backgroundLayers(), cr, _tx, _ty, w, h);
469 }
470
471 if (style()->hasBorder()) {
472 paintBorder(paintInfo.p, _tx, _ty, w, h, style());
473 }
474 }
475
paintAllBackgrounds(QPainter * p,const QColor & c,const BackgroundLayer * bgLayer,QRect clipr,int _tx,int _ty,int w,int height)476 void RenderBox::paintAllBackgrounds(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, QRect clipr, int _tx, int _ty, int w, int height)
477 {
478 if (!bgLayer) {
479 return;
480 }
481 paintAllBackgrounds(p, c, bgLayer->next(), clipr, _tx, _ty, w, height);
482 paintOneBackground(p, c, bgLayer, clipr, _tx, _ty, w, height);
483 }
484
paintOneBackground(QPainter * p,const QColor & c,const BackgroundLayer * bgLayer,QRect clipr,int _tx,int _ty,int w,int height)485 void RenderBox::paintOneBackground(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, QRect clipr, int _tx, int _ty, int w, int height)
486 {
487 paintBackgroundExtended(p, c, bgLayer, clipr, _tx, _ty, w, height,
488 borderLeft(), borderRight(), paddingLeft(), paddingRight(),
489 borderTop(), borderBottom(), paddingTop(), paddingBottom());
490 }
491
calculateBackgroundSize(const BackgroundLayer * bgLayer,int & scaledWidth,int & scaledHeight)492 static void calculateBackgroundSize(const BackgroundLayer *bgLayer, int &scaledWidth, int &scaledHeight)
493 {
494 CachedImage *bg = bgLayer->backgroundImage();
495
496 if (bgLayer->isBackgroundSizeSet()) {
497 if (bgLayer->backgroundSize().type == BGSLENGTH) {
498 int w = scaledWidth;
499 int h = scaledHeight;
500
501 Length bgWidth = bgLayer->backgroundSize().width;
502 Length bgHeight = bgLayer->backgroundSize().height;
503
504 if (bgWidth.isFixed()) {
505 w = bgWidth.value();
506 } else if (bgWidth.isPercent()) {
507 w = bgWidth.width(scaledWidth);
508 }
509
510 if (bgHeight.isFixed()) {
511 h = bgHeight.value();
512 } else if (bgHeight.isPercent()) {
513 h = bgHeight.width(scaledHeight);
514 }
515
516 // If one of the values is auto we have to use the appropriate
517 // scale to maintain our aspect ratio.
518 if (bgWidth.isAuto() && !bgHeight.isAuto()) {
519 w = bg->pixmap_size().width() * h / bg->pixmap_size().height();
520 } else if (!bgWidth.isAuto() && bgHeight.isAuto()) {
521 h = bg->pixmap_size().height() * w / bg->pixmap_size().width();
522 } else if (bgWidth.isAuto() && bgHeight.isAuto()) {
523 // If both width and height are auto, we just want to use the image's
524 // intrinsic size.
525 w = bg->pixmap_size().width();
526 h = bg->pixmap_size().height();
527 }
528 scaledWidth = qMax(1, w);
529 scaledHeight = qMax(1, h);
530 } else {
531 // 'cover' and 'contain' scaling ratio
532 Q_ASSERT(bgLayer->backgroundSize().type == BGSCONTAIN ||
533 bgLayer->backgroundSize().type == BGSCOVER);
534 float iw = bg->pixmap_size().width();
535 float ih = bg->pixmap_size().height();
536 float w = scaledWidth / iw;
537 float h = scaledHeight / ih;
538 float r = (bgLayer->backgroundSize().type == BGSCONTAIN) ? qMin(w, h) : qMax(w, h);
539 scaledWidth = qMax(1, static_cast<int>(iw * r));
540 scaledHeight = qMax(1, static_cast<int>(ih * r));
541 }
542 } else {
543 scaledWidth = bg->pixmap_size().width();
544 scaledHeight = bg->pixmap_size().height();
545 }
546 }
547
paintBackgroundExtended(QPainter * p,const QColor & c,const BackgroundLayer * bgLayer,QRect clipr,int _tx,int _ty,int w,int h,int bleft,int bright,int pleft,int pright,int btop,int bbottom,int ptop,int pbottom)548 void RenderBox::paintBackgroundExtended(QPainter *p, const QColor &c, const BackgroundLayer *bgLayer, QRect clipr,
549 int _tx, int _ty, int w, int h,
550 int bleft, int bright, int pleft, int pright, int btop, int bbottom, int ptop, int pbottom)
551 {
552 if (!clipr.isValid()) {
553 return;
554 }
555
556 bool needRestore = false;
557
558 if (bgLayer->backgroundClip() != BGBORDER) {
559 // Clip to the padding or content boxes as necessary.
560 bool includePadding = bgLayer->backgroundClip() == BGCONTENT;
561 int x = _tx + bleft + (includePadding ? pleft : 0);
562 int y = _ty + btop + (includePadding ? ptop : 0);
563 int width = w - bleft - bright - (includePadding ? pleft + pright : 0);
564 int height = h - btop - bbottom - (includePadding ? ptop + pbottom : 0);
565 p->save();
566 p->setClipRect(QRect(x, y, width, height));
567 needRestore = true;
568 }
569
570 CachedImage *bg = bgLayer->backgroundImage();
571 bool shouldPaintBackgroundImage = bg && bg->isComplete() && !bg->isTransparent() && !bg->isErrorImage() && canvas()->printImages();
572 QColor bgColor = c;
573
574 // the "bottom" of the page can't be transparent
575 // unless this is a subframe
576 if (!bgLayer->next() && isRoot()) {
577 KHTMLView *v = canvas()->view();
578 if (bgColor.alpha() != 255) {
579 if (v && v->m_kwp->isRedirected()) {
580 RenderStyle *ws = v->m_kwp->renderWidget()->style();
581 // only set static background on transparent subframes if the underlying RenderWidget background
582 // has something to show through.
583 if (!ws || !ws->backgroundColor().isValid() || ws->backgroundColor().alpha() != 255
584 || ws->hasBackgroundImage()) {
585 v->setHasStaticBackground();
586 }
587 } else {
588 if (bgColor.alpha() == 0) {
589 bgColor = p->background().color();
590 }
591 bgColor.setAlpha(255);
592 }
593 }
594 }
595
596 // Create the border-radius clip path
597 QPainterPath path = borderRadiusClipPath(bgLayer, _tx, _ty, w, h, bleft, bright, btop, bbottom, pleft, pright, ptop, pbottom);
598 if (!path.isEmpty()) {
599 // Avoid saving the painter state twice
600 if (!needRestore) {
601 p->save();
602 needRestore = true;
603 }
604 p->setRenderHint(QPainter::Antialiasing, true);
605 }
606
607 // Paint the color first underneath all images.
608 if (!bgLayer->next() && bgColor.isValid() && qAlpha(bgColor.rgba()) > 0) {
609 if (!path.isEmpty()) {
610 p->fillPath(path, bgColor);
611 } else {
612 p->fillRect(clipr.x(), clipr.y(), clipr.width(), clipr.height(), bgColor);
613 }
614 }
615
616 // If we're one of the higher-up layers, make sure the color we
617 // pass to CachedImage::tiled_pixmap below is invalid, so it
618 // doesn't preblend upper-layers with the color.
619 if (bgLayer->next()) {
620 bgColor = QColor();
621 }
622
623 // no progressive loading of the background image
624 if (shouldPaintBackgroundImage) {
625 int sx = 0;
626 int sy = 0;
627 int rw = 0;
628 int rh = 0;
629 int cw, ch;
630 int cx, cy;
631 int scaledImageWidth, scaledImageHeight;
632
633 // CSS2 chapter 14.2.1
634
635 if (bgLayer->backgroundAttachment() != BGAFIXED) {
636 //scroll
637 int hpab = 0, vpab = 0, left = 0, top = 0; // Init to 0 for background-origin of 'border'
638 if (bgLayer->backgroundOrigin() != BGBORDER) {
639 hpab += bleft + bright;
640 vpab += btop + bbottom;
641 left += bleft;
642 top += btop;
643 if (bgLayer->backgroundOrigin() == BGCONTENT) {
644 hpab += pleft + pright;
645 vpab += ptop + pbottom;
646 left += pleft;
647 top += ptop;
648 }
649 }
650
651 int pw, ph;
652 pw = w - hpab;
653 ph = h - vpab;
654 if (isRoot()) {
655 // the root's background box 'spills out' to cover the whole canvas, so we have to
656 // go back to its true edge for the purpose of computing background-size
657 // and honouring background-origin
658 rw = width() - hpab;
659 rh = height() - vpab;
660 left += marginLeft();
661 hpab += marginLeft() + marginRight();
662 vpab += marginTop() + marginBottom();
663 top += marginTop();
664 scaledImageWidth = rw;
665 scaledImageHeight = rh;
666 } else {
667 scaledImageWidth = pw;
668 scaledImageHeight = ph;
669 }
670 calculateBackgroundSize(bgLayer, scaledImageWidth, scaledImageHeight);
671
672 EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
673 if (bgr == NO_REPEAT || bgr == REPEAT_Y) {
674 cw = scaledImageWidth;
675 int xPosition;
676 if (isRoot()) {
677 xPosition = bgLayer->backgroundXPosition().minWidthRounded(rw - scaledImageWidth);
678 } else {
679 xPosition = bgLayer->backgroundXPosition().minWidthRounded(pw - scaledImageWidth);
680 }
681 if (xPosition >= 0) {
682 cx = _tx + xPosition;
683 cw = qMin(scaledImageWidth, pw - xPosition);
684 } else {
685 cx = _tx;
686 if (scaledImageWidth > 0) {
687 sx = -xPosition;
688 cw = qMin(scaledImageWidth + xPosition, pw);
689 }
690 }
691 cx += left;
692 } else {
693 // repeat over x
694 cw = w;
695 cx = _tx;
696 if (scaledImageWidth > 0) {
697 int xPosition;
698 if (isRoot()) {
699 xPosition = bgLayer->backgroundXPosition().minWidthRounded(rw - scaledImageWidth);
700 } else {
701 xPosition = bgLayer->backgroundXPosition().minWidthRounded(pw - scaledImageWidth);
702 }
703 sx = scaledImageWidth - (xPosition % scaledImageWidth);
704 sx -= left % scaledImageWidth;
705 }
706 }
707 if (bgr == NO_REPEAT || bgr == REPEAT_X) {
708 ch = scaledImageHeight;
709 int yPosition;
710 if (isRoot()) {
711 yPosition = bgLayer->backgroundYPosition().minWidthRounded(rh - scaledImageHeight);
712 } else {
713 yPosition = bgLayer->backgroundYPosition().minWidthRounded(ph - scaledImageHeight);
714 }
715 if (yPosition >= 0) {
716 cy = _ty + yPosition;
717 ch = qMin(ch, ph - yPosition);
718 } else {
719 cy = _ty;
720 if (scaledImageHeight > 0) {
721 sy = -yPosition;
722 ch = qMin(scaledImageHeight + yPosition, ph);
723 }
724 }
725
726 cy += top;
727 } else {
728 // repeat over y
729 ch = h;
730 cy = _ty;
731 if (scaledImageHeight > 0) {
732 int yPosition;
733 if (isRoot()) {
734 yPosition = bgLayer->backgroundYPosition().minWidthRounded(rh - scaledImageHeight);
735 } else {
736 yPosition = bgLayer->backgroundYPosition().minWidthRounded(ph - scaledImageHeight);
737 }
738 sy = scaledImageHeight - (yPosition % scaledImageHeight);
739 sy -= top % scaledImageHeight;
740 }
741 }
742 if (layer() && bgLayer->backgroundAttachment() == BGALOCAL) {
743 layer()->scrollOffset(sx, sy);
744 }
745 } else {
746 //fixed
747 QRect fix = getFixedBackgroundImageRect(bgLayer, sx, sy, scaledImageWidth, scaledImageHeight);
748 QRect ele(_tx, _ty, w, h);
749 QRect b = fix.intersected(ele);
750
751 //qCDebug(KHTML_LOG) <<" ele is " << ele << " b is " << b << " fix is " << fix;
752 sx += b.x() - fix.x();
753 sy += b.y() - fix.y();
754 cx = b.x(); cy = b.y(); cw = b.width(); ch = b.height();
755
756 if (canvas()->pagedMode() && scaledImageHeight > 0) {
757 sy = (sy - viewRect().y()) % scaledImageHeight;
758 }
759 }
760 // restrict painting to repaint-clip
761 if (cy < clipr.y()) {
762 ch -= (clipr.y() - cy);
763 sy += (clipr.y() - cy);
764 cy = clipr.y();
765 }
766 if (cx < clipr.x()) {
767 cw -= (clipr.x() - cx);
768 sx += (clipr.x() - cx);
769 cx = clipr.x();
770 }
771 ch = qMin(ch, clipr.height());
772 cw = qMin(cw, clipr.width());
773
774 // qCDebug(KHTML_LOG) << " drawTiledPixmap(" << cx << ", " << cy << ", " << cw << ", " << ch << ", " << sx << ", " << sy << ")";
775 if (cw > 0 && ch > 0) {
776 // Note that the reason we don't simply set the path as the clip path here before calling
777 // p->drawTiledPixmap() is that QX11PaintEngine doesn't support anti-aliased clipping.
778 if (!path.isEmpty()) {
779 QBrush brush(bg->tiled_pixmap(bgColor, scaledImageWidth, scaledImageHeight));
780 brush.setTransform(QTransform(1, 0, 0, 1, cx - sx, cy - sy));
781 QPainterPath cpath;
782 cpath.addRect(cx, cy, cw, ch);
783 p->fillPath(path.intersected(cpath), brush);
784 } else {
785 p->drawTiledPixmap(cx, cy, cw, ch, bg->tiled_pixmap(bgColor, scaledImageWidth, scaledImageHeight), sx, sy);
786 }
787 }
788 }
789
790 if (needRestore) {
791 p->restore(); // Undo the background clip and/or the anti-aliasing hint
792 }
793
794 }
795
getFixedBackgroundImageRect(const BackgroundLayer * bgLayer,int & sx,int & sy,int & scaledImageWidth,int & scaledImageHeight)796 QRect RenderBox::getFixedBackgroundImageRect(const BackgroundLayer *bgLayer, int &sx, int &sy, int &scaledImageWidth, int &scaledImageHeight)
797 {
798 int cx, cy, cw, ch;
799 QRect vr = viewRect();
800 int pw = vr.width();
801 int ph = vr.height();
802 scaledImageWidth = pw;
803 scaledImageHeight = ph;
804 calculateBackgroundSize(bgLayer, scaledImageWidth, scaledImageHeight);
805 EBackgroundRepeat bgr = bgLayer->backgroundRepeat();
806
807 int xPosition = bgLayer->backgroundXPosition().minWidthRounded(pw - scaledImageWidth);
808 if (bgr == NO_REPEAT || bgr == REPEAT_Y) {
809 cw = qMin(scaledImageWidth, pw - xPosition);
810 cx = vr.x() + xPosition;
811 } else {
812 cw = pw;
813 cx = vr.x();
814 if (scaledImageWidth > 0) {
815 sx = scaledImageWidth - xPosition % scaledImageWidth;
816 }
817 }
818
819 int yPosition = bgLayer->backgroundYPosition().minWidthRounded(ph - scaledImageHeight);
820 if (bgr == NO_REPEAT || bgr == REPEAT_X) {
821 ch = qMin(scaledImageHeight, ph - yPosition);
822 cy = vr.y() + yPosition;
823 } else {
824 ch = ph;
825 cy = vr.y();
826 if (scaledImageHeight > 0) {
827 sy = scaledImageHeight - yPosition % scaledImageHeight;
828 }
829 }
830 return QRect(cx, cy, cw, ch);
831 }
832
outlineBox(QPainter * p,int _tx,int _ty,const char * color)833 void RenderBox::outlineBox(QPainter *p, int _tx, int _ty, const char *color)
834 {
835 p->setPen(QPen(QColor(color), 1, Qt::DotLine));
836 p->setBrush(Qt::NoBrush);
837 p->drawRect(_tx, _ty, m_width, m_height);
838 }
839
borderRadiusClipPath(const BackgroundLayer * bgLayer,int _tx,int _ty,int w,int h,int borderLeft,int borderRight,int borderTop,int borderBottom,int paddingLeft,int paddingRight,int paddingTop,int paddingBottom) const840 QPainterPath RenderBox::borderRadiusClipPath(const BackgroundLayer *bgLayer, int _tx, int _ty, int w, int h,
841 int borderLeft, int borderRight, int borderTop, int borderBottom,
842 int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) const
843 {
844 QPainterPath path;
845
846 if (style()->hasBorderRadius()) {
847 QPoint topLeftRadii, topRightRadii, bottomLeftRadii, bottomRightRadii;
848 calcBorderRadii(topLeftRadii, topRightRadii, bottomLeftRadii, bottomRightRadii, w, h);
849
850 // CSS Backgrounds and Borders Module Level 3 (https://www.w3.org/TR/2014/CR-css3-background-20140909/), chapter 5.3:
851 // "A box's backgrounds, but not its border-image, are clipped to the appropriate curve (as determined by 'background-clip')."
852 int adjustTop = 0, adjustLeft = 0, adjustRight = 0, adjustBottom = 0;
853 bool clipInner = true;
854 switch (bgLayer->backgroundClip()) {
855 case BGCONTENT:
856 adjustTop += paddingTop;
857 adjustLeft += paddingLeft;
858 adjustRight += paddingRight;
859 adjustBottom += paddingBottom;
860 // No break
861 case BGPADDING:
862 adjustTop += borderTop;
863 adjustLeft += borderLeft;
864 adjustRight += borderRight;
865 adjustBottom += borderBottom;
866 break;
867 default: // BGBORDER
868 clipInner = false;
869 break;
870 }
871 // The clip bounding rect
872 QRect rect(_tx, _ty, w, h);
873 if (clipInner) {
874 rect.adjust(adjustLeft, adjustTop, -adjustRight, -adjustBottom);
875
876 topLeftRadii.rx() = qMax(0, topLeftRadii.x() - adjustLeft);
877 bottomLeftRadii.rx() = qMax(0, bottomLeftRadii.x() - adjustLeft);
878 topRightRadii.rx() = qMax(0, topRightRadii.x() - adjustRight);
879 bottomRightRadii.rx() = qMax(0, bottomRightRadii.x() - adjustRight);
880 topLeftRadii.ry() = qMax(0, topLeftRadii.y() - adjustTop);
881 topRightRadii.ry() = qMax(0, topRightRadii.y() - adjustTop);
882 bottomLeftRadii.ry() = qMax(0, bottomLeftRadii.y() - adjustBottom);
883 bottomRightRadii.ry() = qMax(0, bottomRightRadii.y() - adjustBottom);
884 }
885
886 // Top right corner
887 if (!topRightRadii.isNull()) {
888 const QRect r(rect.x() + rect.width() - topRightRadii.x() * 2, rect.y(), topRightRadii.x() * 2, topRightRadii.y() * 2);
889 path.arcMoveTo(r, 0);
890 path.arcTo(r, 0, 90);
891 } else {
892 path.moveTo(rect.x() + rect.width(), rect.y());
893 }
894
895 // Top left corner
896 if (!topLeftRadii.isNull()) {
897 const QRect r(rect.x(), rect.y(), topLeftRadii.x() * 2, topLeftRadii.y() * 2);
898 path.arcTo(r, 90, 90);
899 } else {
900 path.lineTo(rect.x(), rect.y());
901 }
902
903 // Bottom left corner
904 if (!bottomLeftRadii.isNull()) {
905 const QRect r(rect.x(), rect.y() + rect.height() - bottomLeftRadii.y() * 2, bottomLeftRadii.x() * 2, bottomLeftRadii.y() * 2);
906 path.arcTo(r, 180, 90);
907 } else {
908 path.lineTo(rect.x(), rect.y() + rect.height());
909 }
910
911 // Bottom right corner
912 if (!bottomRightRadii.isNull()) {
913 const QRect r(rect.x() + rect.width() - bottomRightRadii.x() * 2, rect.y() + rect.height() - bottomRightRadii.y() * 2,
914 bottomRightRadii.x() * 2, bottomRightRadii.y() * 2);
915 path.arcTo(r, 270, 90);
916 } else {
917 path.lineTo(rect.x() + rect.width(), rect.y() + rect.height());
918 }
919
920 path.closeSubpath();
921 }
922
923 return path;
924 }
925
overflowClipRect(int tx,int ty)926 QRect RenderBox::overflowClipRect(int tx, int ty)
927 {
928 // XXX When overflow-clip (CSS3) is implemented, we'll obtain the property
929 // here.
930 int bl = borderLeft(), bt = borderTop(), bb = borderBottom(), br = borderRight();
931 int clipx = tx + bl;
932 int clipy = ty + bt;
933 int clipw = m_width - bl - br;
934 int cliph = m_height - bt - bb + borderTopExtra() + borderBottomExtra();
935
936 // Subtract out scrollbars if we have them.
937 if (m_layer) {
938 if (m_layer->hasReversedScrollbar()) {
939 clipx += m_layer->verticalScrollbarWidth();
940 }
941 clipw -= m_layer->verticalScrollbarWidth();
942 cliph -= m_layer->horizontalScrollbarHeight();
943 }
944
945 return QRect(clipx, clipy, clipw, cliph);
946 }
947
clipRect(int tx,int ty)948 QRect RenderBox::clipRect(int tx, int ty)
949 {
950 // Clipping applies to the entire box, including the borders, so we
951 // don't have to do anything about them or margins
952 int clipw = m_width;
953 int cliph = m_height;
954
955 bool rtl = (style()->direction() == RTL);
956
957 int clipleft = 0;
958 int clipright = clipw;
959 int cliptop = 0;
960 int clipbottom = cliph;
961
962 if (style()->hasClip() && style()->position() == PABSOLUTE) {
963 // the only case we use the clip property according to CSS 2.1
964 if (!style()->clipLeft().isAuto()) {
965 int c = style()->clipLeft().width(clipw);
966 if (rtl) {
967 clipleft = clipw - c;
968 } else {
969 clipleft = c;
970 }
971 }
972 if (!style()->clipRight().isAuto()) {
973 int w = style()->clipRight().width(clipw);
974 if (rtl) {
975 clipright = clipw - w;
976 } else {
977 clipright = w;
978 }
979 }
980 if (!style()->clipTop().isAuto()) {
981 cliptop = style()->clipTop().width(cliph);
982 }
983 if (!style()->clipBottom().isAuto()) {
984 clipbottom = style()->clipBottom().width(cliph);
985 }
986 }
987 int clipx = tx + clipleft;
988 int clipy = ty + cliptop;
989 clipw = clipright - clipleft;
990 cliph = clipbottom - cliptop;
991
992 //qCDebug(KHTML_LOG) << "setting clip("<<clipx<<","<<clipy<<","<<clipw<<","<<cliph<<")";
993
994 return QRect(clipx, clipy, clipw, cliph);
995 }
996
close()997 void RenderBox::close()
998 {
999 setNeedsLayoutAndMinMaxRecalc();
1000 }
1001
containingBlockWidth(RenderObject * providedCB) const1002 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
absolutePosition(int & _xPos,int & _yPos,bool f) const1046 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
position(InlineBox * box,int,int,bool)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
repaint(Priority prior)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
repaintRectangle(int x,int y,int w,int h,Priority p,bool f)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
relativePositionOffset(int & tx,int & ty) const1152 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
calcWidth()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
calcWidthUsing(WidthType widthType,int cw,LengthType & lengthType)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
calcHorizontalMargins(const Length & ml,const Length & mr,int cw)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
calcHeight()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
calcHeightUsing(const Length & h)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
calcImplicitContentHeight() const1424 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
calcPercentageHeight(const Length & height) const1437 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
calcReplacedWidth() const1504 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
calcReplacedWidthUsing(WidthType widthType) const1521 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
calcReplacedHeight() const1548 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
calcReplacedHeightUsing(HeightType heightType) const1565 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
availableHeight() const1590 int RenderBox::availableHeight() const
1591 {
1592 return availableHeightUsing(style()->height());
1593 }
1594
availableHeightUsing(const Length & h) const1595 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
availableWidth() const1630 int RenderBox::availableWidth() const
1631 {
1632 return availableWidthUsing(style()->width());
1633 }
1634
availableWidthUsing(const Length & w) const1635 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
calcVerticalMargins()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
setStaticX(short staticX)1672 void RenderBox::setStaticX(short staticX)
1673 {
1674 m_staticX = staticX;
1675 }
1676
setStaticY(int staticY)1677 void RenderBox::setStaticY(int staticY)
1678 {
1679 m_staticY = staticY;
1680 }
1681
calcAbsoluteHorizontal()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
calcAbsoluteHorizontalValues(Length width,const RenderObject * containerBlock,EDirection containerDirection,const int containerWidth,const int bordersPlusPadding,const Length left,const Length right,const Length marginLeft,const Length marginRight,short & widthValue,short & marginLeftValue,short & marginRightValue,short & xPos)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
calcAbsoluteVertical()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
calcAbsoluteVerticalValues(Length height,const RenderObject * containerBlock,const int containerHeight,const int bordersPlusPadding,const Length top,const Length bottom,const Length marginTop,const Length marginBottom,int & heightValue,short & marginTopValue,short & marginBottomValue,int & yPos)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
calcAbsoluteHorizontalReplaced()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
calcAbsoluteVerticalReplaced()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
highestPosition(bool,bool includeSelf) const2513 int RenderBox::highestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2514 {
2515 return includeSelf ? 0 : m_height;
2516 }
2517
lowestPosition(bool,bool includeSelf) const2518 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
rightmostPosition(bool,bool includeSelf) const2532 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
leftmostPosition(bool,bool includeSelf) const2545 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
pageTopAfter(int y) const2558 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
crossesPageBreak(int t,int b) const2568 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
handleEvent(const DOM::EventImpl & e)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
caretPos(int,int flags,int & _x,int & _y,int & width,int & height) const2704 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
2828