KHtml

render_box.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999-2003 Lars Knoll ([email protected])
5  * (C) 1999 Antti Koivisto ([email protected])
6  * (C) 2002-2007 Apple Computer, Inc.
7  * (C) 2005 Allan Sandfeld Jensen ([email protected])
8  * (C) 2006 Samuel Weinig ([email protected])
9  * (C) 2007 Germain Garand ([email protected])
10  * (C) 2008 Fredrik Höglund ([email protected])
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB. If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  *
27  */
28 // -------------------------------------------------------------------------
29 //#define DEBUG_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 
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 
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 
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 
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 
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 
217 RenderBox::~RenderBox()
218 {
219  //qCDebug(KHTML_LOG) << "Element destructor: this=" << nodeName().string();
220 }
221 
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 
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 
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 
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 
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 
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 
291 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 
304 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 
316 void RenderBox::setPos(int xPos, int yPos)
317 {
318  m_x = xPos; m_y = yPos;
319 }
320 
321 short RenderBox::width() const
322 {
323  return m_width;
324 }
325 
326 int RenderBox::height() const
327 {
328  return m_height;
329 }
330 
331 void RenderBox::setWidth(int width)
332 {
333  m_width = width;
334 }
335 
336 void RenderBox::setHeight(int height)
337 {
338  m_height = height;
339 }
340 
341 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 
350 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 
359 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 
368 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 
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 
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 
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 
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 
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 
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 
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  }
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 
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 
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 
840 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 
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 
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 
997 void RenderBox::close()
998 {
999  setNeedsLayoutAndMinMaxRecalc();
1000 }
1001 
1002 short RenderBox::containingBlockWidth(RenderObject *providedCB) const
1003 {
1004  if (isCanvas() && canvas()->view()) {
1005  if (canvas()->pagedMode()) {
1006  return canvas()->width();
1007  } else {
1008  return canvas()->view()->visibleWidth();
1009  }
1010  }
1011 
1012  RenderObject *cb = providedCB ? providedCB : containingBlock();
1013  if (isRenderBlock() && cb->isTable() && static_cast<RenderTable *>(cb)->caption() == this) {
1014  //captions are not affected by table border or padding
1015  return cb->width();
1016  }
1017  if (isPositioned()) {
1018  // cf. 10.1.4 - use padding edge
1019  if (cb->isInlineFlow()) {
1020  // 10.1.4.1
1021  int l, r;
1022  InlineFlowBox *firstLineBox = static_cast<const RenderFlow *>(cb)->firstLineBox();
1023  InlineFlowBox *lastLineBox = static_cast<const RenderFlow *>(cb)->lastLineBox();
1024  if (!lastLineBox) {
1025  return 0;
1026  }
1027  if (cb->style()->direction() == RTL) {
1028  l = lastLineBox->xPos() + lastLineBox->borderLeft();
1029  r = firstLineBox->xPos() + firstLineBox->width() - firstLineBox->borderRight();
1030  } else {
1031  l = firstLineBox->xPos() + firstLineBox->borderLeft();
1032  r = lastLineBox->xPos() + lastLineBox->width() - lastLineBox->borderRight();
1033  }
1034  return qMax(0, r - l);
1035  }
1036  // 10.1.4.2
1037  return cb->contentWidth() + cb->paddingLeft() + cb->paddingRight();
1038  } else if (usesLineWidth()) {
1039  assert(cb->isRenderBlock());
1040  return static_cast<RenderBlock *>(cb)->lineWidth(m_y);
1041  } else {
1042  return cb->contentWidth();
1043  }
1044 }
1045 
1046 bool RenderBox::absolutePosition(int &_xPos, int &_yPos, bool f) const
1047 {
1048  if (style()->position() == PFIXED) {
1049  f = true;
1050  }
1051  RenderObject *o = container();
1052  if (o && o->absolutePosition(_xPos, _yPos, f)) {
1053  if (o->layer()) {
1054  if (o->hasOverflowClip()) {
1055  o->layer()->subtractScrollOffset(_xPos, _yPos);
1056  }
1057  if (isPositioned()) {
1058  o->layer()->checkInlineRelOffset(this, _xPos, _yPos);
1059  }
1060  }
1061 
1062  if (!isInline() || isReplaced()) {
1063  _xPos += xPos(),
1064  _yPos += yPos();
1065  }
1066 
1067  if (isRelPositioned()) {
1068  relativePositionOffset(_xPos, _yPos);
1069  }
1070  return true;
1071  } else {
1072  _xPos = 0;
1073  _yPos = 0;
1074  return false;
1075  }
1076 }
1077 
1078 void RenderBox::position(InlineBox *box, int /*from*/, int /*len*/, bool /*reverse*/)
1079 {
1080  if (isPositioned()) {
1081  // Cache the x position only if we were an INLINE type originally.
1082  bool wasInline = style()->isOriginalDisplayInlineType();
1083 
1084  if (wasInline && hasStaticX()) {
1085  // The value is cached in the xPos of the box. We only need this value if
1086  // our object was inline originally, since otherwise it would have ended up underneath
1087  // the inlines.
1088  m_staticX = box->xPos();
1089  } else if (!wasInline && hasStaticY()) {
1090  // Our object was a block originally, so we make our normal flow position be
1091  // just below the line box (as though all the inlines that came before us got
1092  // wrapped in an anonymous block, which is what would have happened had we been
1093  // in flow). This value was cached in the yPos() of the box.
1094  m_staticY = box->yPos();
1095  }
1096  } else if (isReplaced()) {
1097  setPos(box->xPos(), box->yPos());
1098  }
1099 }
1100 
1101 void RenderBox::repaint(Priority prior)
1102 {
1103  int ow = style() ? style()->outlineSize() : 0;
1104  if (isInline() && !isReplaced()) {
1105  RenderObject *p = parent();
1106  Q_ASSERT(p);
1107  while (p->isInline() && !p->isReplaced()) {
1108  p = p->parent();
1109  }
1110  int xoff = p->hasOverflowClip() ? 0 : p->overflowLeft();
1111  int yoff = p->hasOverflowClip() ? 0 : p->overflowTop();
1112  p->repaintRectangle(-ow + xoff, -ow + yoff, p->effectiveWidth() + ow * 2, p->effectiveHeight() + ow * 2, prior);
1113  } else {
1114  int xoff = hasOverflowClip() ? 0 : overflowLeft();
1115  int yoff = hasOverflowClip() ? 0 : overflowTop();
1116  repaintRectangle(-ow + xoff, -ow + yoff, effectiveWidth() + ow * 2, effectiveHeight() + ow * 2, prior);
1117  }
1118 }
1119 
1120 void RenderBox::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
1121 {
1122  x += m_x;
1123  y += m_y;
1124 
1125  // Apply the relative position offset when invalidating a rectangle. The layer
1126  // is translated, but the render box isn't, so we need to do this to get the
1127  // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
1128  // flag on the RenderObject has been cleared, so use the one on the style().
1129  if (style()->position() == PRELATIVE && m_layer) {
1130  relativePositionOffset(x, y);
1131  }
1132 
1133  if (style()->position() == PFIXED) {
1134  f = true;
1135  }
1136 
1137  // qCDebug(KHTML_LOG) << "RenderBox(" <<this << ", " << renderName() << ")::repaintRectangle (" << x << "/" << y << ") (" << w << "/" << h << ")";
1138  RenderObject *o = container();
1139  if (o) {
1140  if (o->layer()) {
1141  if (o->style()->hidesOverflow() && o->layer() && !o->isInlineFlow()) {
1142  o->layer()->subtractScrollOffset(x, y); // For overflow:auto/scroll/hidden.
1143  }
1144  if (style()->position() == PABSOLUTE) {
1145  o->layer()->checkInlineRelOffset(this, x, y);
1146  }
1147  }
1148  o->repaintRectangle(x, y, w, h, p, f);
1149  }
1150 }
1151 
1152 void RenderBox::relativePositionOffset(int &tx, int &ty) const
1153 {
1154  if (!style()->left().isAuto()) {
1155  if (!style()->right().isAuto() && containingBlock()->style()->direction() == RTL) {
1156  tx -= style()->right().width(containingBlockWidth());
1157  } else {
1158  tx += style()->left().width(containingBlockWidth());
1159  }
1160  } else if (!style()->right().isAuto()) {
1161  tx -= style()->right().width(containingBlockWidth());
1162  }
1163  if (!style()->top().isAuto()) {
1164  if (style()->top().isPercent()) {
1165  double p = style()->top().percent();
1166  bool neg = p < 0.0;
1167  int ph = calcPercentageHeight(Length((neg ? -p : p), Percent));
1168  if (ph != -1) {
1169  ty += neg ? -ph : ph;
1170  }
1171  } else {
1172  ty += style()->top().width(containingBlockHeight());
1173  }
1174  } else if (!style()->bottom().isAuto()) {
1175  if (style()->bottom().isPercent()) {
1176  double p = style()->bottom().percent();
1177  bool neg = p < 0.0;
1178  int ph = calcPercentageHeight(Length((neg ? -p : p), Percent));
1179  if (ph != -1) {
1180  ty -= neg ? -ph : ph;
1181  }
1182  } else {
1183  ty -= style()->bottom().width(containingBlockHeight());
1184  }
1185  }
1186 }
1187 
1188 void RenderBox::calcWidth()
1189 {
1190 #ifdef DEBUG_LAYOUT
1191  qCDebug(KHTML_LOG) << "RenderBox(" << renderName() << ")::calcWidth()";
1192 #endif
1193  if (isPositioned()) {
1194  calcAbsoluteHorizontal();
1195  } else {
1196  bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable();
1197  Length w;
1198  if (treatAsReplaced) {
1199  w = Length(calcReplacedWidth(), Fixed);
1200  } else {
1201  w = style()->width();
1202  }
1203 
1204  Length ml = style()->marginLeft();
1205  Length mr = style()->marginRight();
1206 
1207  int cw = containingBlockWidth();
1208  if (cw < 0) {
1209  cw = 0;
1210  }
1211 
1212  m_marginLeft = 0;
1213  m_marginRight = 0;
1214 
1215  if (isInline() && !isInlineBlockOrInlineTable()) {
1216  // just calculate margins
1217  m_marginLeft = ml.minWidth(cw);
1218  m_marginRight = mr.minWidth(cw);
1219  if (treatAsReplaced) {
1220  m_width = w.width(cw) + borderLeft() + borderRight() + paddingLeft() + paddingRight();
1221  m_width = qMax(m_width, m_minWidth);
1222  }
1223 
1224  return;
1225  } else {
1226  LengthType widthType, minWidthType, maxWidthType;
1227  if (treatAsReplaced) {
1228  m_width = w.width(cw) + borderLeft() + borderRight() + paddingLeft() + paddingRight();
1229  widthType = w.type();
1230  } else {
1231  m_width = calcWidthUsing(Width, cw, widthType);
1232  int minW = calcWidthUsing(MinWidth, cw, minWidthType);
1233  int maxW = style()->maxWidth().isUndefined() ?
1234  m_width : calcWidthUsing(MaxWidth, cw, maxWidthType);
1235 
1236  if (m_width > maxW) {
1237  m_width = maxW;
1238  widthType = maxWidthType;
1239  }
1240  if (m_width < minW) {
1241  m_width = minW;
1242  widthType = minWidthType;
1243  }
1244  if (short iw = intrinsicWidth()) {
1245  // some elements (e.g. Fieldset) have pseudo-replaced behaviour in quirk mode
1246  if (m_width < iw) {
1247  m_width = iw;
1248  widthType = Fixed;
1249  }
1250  }
1251  }
1252 
1253  if (widthType == Auto) {
1254  // qCDebug(KHTML_LOG) << "variable";
1255  m_marginLeft = ml.minWidth(cw);
1256  m_marginRight = mr.minWidth(cw);
1257  } else {
1258 // qCDebug(KHTML_LOG) << "non-variable " << w.type << ","<< w.value;
1259  calcHorizontalMargins(ml, mr, cw);
1260  }
1261  }
1262 
1263  if (cw && cw != m_width + m_marginLeft + m_marginRight && !isFloating() && !isInline()) {
1264  if (containingBlock()->style()->direction() == LTR) {
1265  m_marginRight = cw - m_width - m_marginLeft;
1266  } else {
1267  m_marginLeft = cw - m_width - m_marginRight;
1268  }
1269  }
1270  }
1271 
1272 #ifdef DEBUG_LAYOUT
1273  qCDebug(KHTML_LOG) << "RenderBox::calcWidth(): m_width=" << m_width << " containingBlockWidth()=" << containingBlockWidth();
1274  qCDebug(KHTML_LOG) << "m_marginLeft=" << m_marginLeft << " m_marginRight=" << m_marginRight;
1275 #endif
1276 }
1277 
1278 int RenderBox::calcWidthUsing(WidthType widthType, int cw, LengthType &lengthType)
1279 {
1280  int width = m_width;
1281  Length w;
1282  if (widthType == Width) {
1283  w = style()->width();
1284  } else if (widthType == MinWidth) {
1285  w = style()->minWidth();
1286  } else {
1287  w = style()->maxWidth();
1288  }
1289 
1290  lengthType = w.type();
1291 
1292  if (lengthType == Auto) {
1293  int marginLeft = style()->marginLeft().minWidth(cw);
1294  int marginRight = style()->marginRight().minWidth(cw);
1295  if (cw) {
1296  width = cw - marginLeft - marginRight;
1297  }
1298 
1299  // size to max width?
1300  if (sizesToMaxWidth()) {
1301  width = qMax(width, (int)m_minWidth);
1302  width = qMin(width, (int)m_maxWidth);
1303  }
1304  } else {
1305  width = calcBoxWidth(w.width(cw));
1306  }
1307 
1308  return width;
1309 }
1310 
1311 void RenderBox::calcHorizontalMargins(const Length &ml, const Length &mr, int cw)
1312 {
1313  if (isFloating() || isInline()) { // Inline blocks/tables and floats don't have their margins increased.
1314  m_marginLeft = ml.minWidth(cw);
1315  m_marginRight = mr.minWidth(cw);
1316  } else {
1317  if ((ml.isAuto() && mr.isAuto() && m_width < cw) ||
1318  (!ml.isAuto() && !mr.isAuto() &&
1319  containingBlock()->style()->textAlign() == KHTML_CENTER)) {
1320  m_marginLeft = (cw - m_width) / 2;
1321  if (m_marginLeft < 0) {
1322  m_marginLeft = 0;
1323  }
1324  m_marginRight = cw - m_width - m_marginLeft;
1325  } else if ((mr.isAuto() && m_width < cw) ||
1326  (!ml.isAuto() && containingBlock()->style()->direction() == RTL &&
1327  containingBlock()->style()->textAlign() == KHTML_LEFT)) {
1328  m_marginLeft = ml.width(cw);
1329  m_marginRight = cw - m_width - m_marginLeft;
1330  } else if ((ml.isAuto() && m_width < cw) ||
1331  (!mr.isAuto() && containingBlock()->style()->direction() == LTR &&
1332  containingBlock()->style()->textAlign() == KHTML_RIGHT)) {
1333  m_marginRight = mr.width(cw);
1334  m_marginLeft = cw - m_width - m_marginRight;
1335  } else {
1336  // this makes auto margins 0 if we failed a m_width<cw test above (css2.1, 10.3.3)
1337  m_marginLeft = ml.minWidth(cw);
1338  m_marginRight = mr.minWidth(cw);
1339  }
1340  }
1341 }
1342 
1343 void RenderBox::calcHeight()
1344 {
1345 
1346 #ifdef DEBUG_LAYOUT
1347  qCDebug(KHTML_LOG) << "RenderBox::calcHeight()";
1348 #endif
1349 
1350  //cell height is managed by table, inline elements do not have a height property.
1351  if (isTableCell() || (isInline() && !isReplaced())) {
1352  return;
1353  }
1354 
1355  if (isPositioned()) {
1356  calcAbsoluteVertical();
1357  } else {
1358  calcVerticalMargins();
1359 
1360  // For tables, calculate margins only
1361  if (isTable()) {
1362  return;
1363  }
1364 
1365  Length h;
1366  bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable();
1367  bool checkMinMaxHeight = false;
1368 
1369  if (treatAsReplaced) {
1370  h = Length(calcReplacedHeight(), Fixed);
1371  } else {
1372  h = style()->height();
1373  checkMinMaxHeight = true;
1374  }
1375 
1376  int height;
1377  if (checkMinMaxHeight) {
1378  height = calcHeightUsing(style()->height());
1379  if (height == -1) {
1380  height = m_height;
1381  }
1382  int minH = calcHeightUsing(style()->minHeight()); // Leave as -1 if unset.
1383  int maxH = style()->maxHeight().isUndefined() ? height : calcHeightUsing(style()->maxHeight());
1384  if (maxH == -1) {
1385  maxH = height;
1386  }
1387  height = qMin(maxH, height);
1388  height = qMax(minH, height);
1389  } else {
1390  // The only times we don't check min/max height are when a fixed length has
1391  // been given as an override. Just use that.
1392  height = h.value() + borderTop() + borderBottom() + paddingTop() + paddingBottom();
1393  }
1394 
1395  m_height = height;
1396  }
1397 
1398  // Unfurling marquees override with the furled height.
1399  if (style()->overflowX() == OMARQUEE && m_layer && m_layer->marquee() &&
1400  m_layer->marquee()->isUnfurlMarquee() && !m_layer->marquee()->isHorizontal()) {
1401  m_layer->marquee()->setEnd(m_height);
1402  m_height = qMin(m_height, m_layer->marquee()->unfurlPos());
1403  }
1404 
1405 }
1406 
1407 int RenderBox::calcHeightUsing(const Length &h)
1408 {
1409  int height = -1;
1410  if (!h.isAuto()) {
1411  if (h.isFixed()) {
1412  height = h.value();
1413  } else if (h.isPercent()) {
1414  height = calcPercentageHeight(h);
1415  }
1416  if (height != -1) {
1417  height = calcBoxHeight(height);
1418  return height;
1419  }
1420  }
1421  return height;
1422 }
1423 
1424 int RenderBox::calcImplicitContentHeight() const
1425 {
1426  assert(hasImplicitHeight());
1427 
1428  RenderBlock *cb = containingBlock();
1429  // padding-box height
1430  int ch = cb->height() - cb->borderTop() - cb->borderBottom();
1431  int top = style()->top().width(ch);
1432  int bottom = style()->bottom().width(ch);
1433 
1434  return ch - top - bottom - borderTop() - borderBottom() - paddingTop() - paddingBottom();
1435 }
1436 
1437 int RenderBox::calcPercentageHeight(const Length &height) const
1438 {
1439  int result = -1;
1440  RenderBlock *cb = containingBlock();
1441  // In quirk mode, table cells violate what the CSS spec says to do with heights.
1442  if (cb->isTableCell() && style()->htmlHacks()) {
1443  result = static_cast<RenderTableCell *>(cb)->cellPercentageHeight();
1444  }
1445 
1446  // Otherwise we only use our percentage height if our containing block had a specified
1447  // height.
1448  else if (cb->style()->height().isFixed()) {
1449  result = cb->calcContentHeight(cb->style()->height().value());
1450  } else if (cb->style()->height().isPercent()) {
1451  // We need to recur and compute the percentage height for our containing block.
1452  result = cb->calcPercentageHeight(cb->style()->height());
1453  if (result != -1) {
1454  result = cb->calcContentHeight(result);
1455  }
1456  } else if (cb->isCanvas()) {
1457  if (!canvas()->pagedMode()) {
1458  result = static_cast<RenderCanvas *>(cb)->viewportHeight();
1459  } else {
1460  result = static_cast<RenderCanvas *>(cb)->height();
1461  }
1462  result -= cb->style()->borderTopWidth() - cb->style()->borderBottomWidth();
1463  result -= cb->paddingTop() + cb->paddingBottom();
1464  } else if (cb->isBody() && style()->htmlHacks() &&
1465  cb->style()->height().isAuto() && !cb->isFloatingOrPositioned()) {
1466  int margins = cb->collapsedMarginTop() + cb->collapsedMarginBottom();
1467  int visHeight = canvas()->viewportHeight();
1468  RenderObject *p = cb->parent();
1469  result = visHeight - (margins + p->marginTop() + p->marginBottom() +
1470  p->borderTop() + p->borderBottom() +
1471  p->paddingTop() + p->paddingBottom());
1472  } else if (cb->isRoot() && style()->htmlHacks() && cb->style()->height().isAuto()) {
1473  int visHeight = canvas()->viewportHeight();
1474  result = visHeight - (marginTop() + marginBottom() +
1475  borderTop() + borderBottom() +
1476  paddingTop() + paddingBottom());
1477  } else if (isPositioned()) {
1478  // "10.5 - Note that the height of the containing block of an absolutely positioned element is independent
1479  // of the size of the element itself, and thus a percentage height on such an element can always be resolved."
1480  //
1481  // take the used height - at the padding edge since we are positioned (10.1)
1482  result = cb->height() - cb->borderTop() - cb->borderBottom();
1483  } else if (cb->hasImplicitHeight()) {
1484  result = cb->calcImplicitContentHeight();
1485  } else if (cb->isAnonymousBlock() || style()->htmlHacks()) {
1486  // IE quirk.
1487  assert(cb->style()->height().isAuto());
1488  result = cb->calcPercentageHeight(cb->style()->height());
1489  if (result != -1) {
1490  result = cb->calcContentHeight(result);
1491  }
1492  }
1493 
1494  if (result != -1) {
1495  result = height.width(result);
1496  if (cb->isTableCell() && style()->boxSizing() != BORDER_BOX) {
1497  result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom());
1498  result = qMax(0, result);
1499  }
1500  }
1501  return result;
1502 }
1503 
1504 short RenderBox::calcReplacedWidth() const
1505 {
1506  int width = calcReplacedWidthUsing(Width);
1507  int minW = calcReplacedWidthUsing(MinWidth);
1508  int maxW = style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(MaxWidth);
1509 
1510  if (width > maxW) {
1511  width = maxW;
1512  }
1513 
1514  if (width < minW) {
1515  width = minW;
1516  }
1517 
1518  return width;
1519 }
1520 
1521 int RenderBox::calcReplacedWidthUsing(WidthType widthType) const
1522 {
1523  Length w;
1524  if (widthType == Width) {
1525  w = style()->width();
1526  } else if (widthType == MinWidth) {
1527  w = style()->minWidth();
1528  } else {
1529  w = style()->maxWidth();
1530  }
1531 
1532  switch (w.type()) {
1533  case Fixed:
1534  return calcContentWidth(w.value());
1535  case Percent: {
1536  const int cw = containingBlockWidth();
1537  if (cw > 0) {
1538  int result = calcContentWidth(w.minWidth(cw));
1539  return result;
1540  }
1541  }
1542  // fall through
1543  default:
1544  return intrinsicWidth();
1545  }
1546 }
1547 
1548 int RenderBox::calcReplacedHeight() const
1549 {
1550  int height = calcReplacedHeightUsing(Height);
1551  int minH = calcReplacedHeightUsing(MinHeight);
1552  int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(MaxHeight);
1553 
1554  if (height > maxH) {
1555  height = maxH;
1556  }
1557 
1558  if (height < minH) {
1559  height = minH;
1560  }
1561 
1562  return height;
1563 }
1564 
1565 int RenderBox::calcReplacedHeightUsing(HeightType heightType) const
1566 {
1567  Length h;
1568  if (heightType == Height) {
1569  h = style()->height();
1570  } else if (heightType == MinHeight) {
1571  h = style()->minHeight();
1572  } else {
1573  h = style()->maxHeight();
1574  }
1575  switch (h.type()) {
1576  case Fixed:
1577  return calcContentHeight(h.value());
1578  case Percent: {
1579  int th = calcPercentageHeight(h);
1580  if (th != -1) {
1581  return calcContentHeight(th);
1582  }
1583  // fall through
1584  }
1585  default:
1586  return intrinsicHeight();
1587  };
1588 }
1589 
1590 int RenderBox::availableHeight() const
1591 {
1592  return availableHeightUsing(style()->height());
1593 }
1594 
1595 int RenderBox::availableHeightUsing(const Length &h) const
1596 {
1597  if (h.isFixed()) {
1598  return calcContentHeight(h.value());
1599  }
1600 
1601  if (isCanvas()) {
1602  if (static_cast<const RenderCanvas *>(this)->pagedMode()) {
1603  return static_cast<const RenderCanvas *>(this)->pageHeight();
1604  } else {
1605  return static_cast<const RenderCanvas *>(this)->viewportHeight();
1606  }
1607  }
1608 
1609  // We need to stop here, since we don't want to increase the height of the table
1610  // artificially. We're going to rely on this cell getting expanded to some new
1611  // height, and then when we lay out again we'll use the calculation below.
1612  if (isTableCell() && (h.isAuto() || h.isPercent())) {
1613  const RenderTableCell *tableCell = static_cast<const RenderTableCell *>(this);
1614  return tableCell->cellPercentageHeight() -
1615  (borderTop() + borderBottom() + paddingTop() + paddingBottom());
1616  }
1617 
1618  if (h.isPercent()) {
1619  return calcContentHeight(h.width(containingBlock()->availableHeight()));
1620  }
1621 
1622  // Check for implicit height
1623  if (hasImplicitHeight()) {
1624  return calcImplicitContentHeight();
1625  }
1626 
1627  return containingBlock()->availableHeight();
1628 }
1629 
1630 int RenderBox::availableWidth() const
1631 {
1632  return availableWidthUsing(style()->width());
1633 }
1634 
1635 int RenderBox::availableWidthUsing(const Length &w) const
1636 {
1637  if (w.isFixed()) {
1638  return calcContentWidth(w.value());
1639  }
1640 
1641  if (isCanvas()) {
1642  return static_cast<const RenderCanvas *>(this)->viewportWidth();
1643  }
1644 
1645  if (w.isPercent()) {
1646  return calcContentWidth(w.width(containingBlock()->availableWidth()));
1647  }
1648 
1649  return containingBlock()->availableWidth();
1650 }
1651 
1652 void RenderBox::calcVerticalMargins()
1653 {
1654  if (isTableCell()) {
1655  // table margins are basically infinite
1656  m_marginTop = TABLECELLMARGIN;
1657  m_marginBottom = TABLECELLMARGIN;
1658  return;
1659  }
1660 
1661  Length tm = style()->marginTop();
1662  Length bm = style()->marginBottom();
1663 
1664  // margins are calculated with respect to the _width_ of
1665  // the containing block (8.3)
1666  int cw = containingBlock()->contentWidth();
1667 
1668  m_marginTop = tm.minWidth(cw);
1669  m_marginBottom = bm.minWidth(cw);
1670 }
1671 
1672 void RenderBox::setStaticX(short staticX)
1673 {
1674  m_staticX = staticX;
1675 }
1676 
1677 void RenderBox::setStaticY(int staticY)
1678 {
1679  m_staticY = staticY;
1680 }
1681 
1682 void RenderBox::calcAbsoluteHorizontal()
1683 {
1684  if (isReplaced()) {
1685  calcAbsoluteHorizontalReplaced();
1686  return;
1687  }
1688 
1689  // QUESTIONS
1690  // FIXME 1: Which RenderObject's 'direction' property should used: the
1691  // containing block (cb) as the spec seems to imply, the parent (parent()) as
1692  // was previously done in calculating the static distances, or ourself, which
1693  // was also previously done for deciding what to override when you had
1694  // over-constrained margins? Also note that the container block is used
1695  // in similar situations in other parts of the RenderBox class (see calcWidth()
1696  // and calcHorizontalMargins()). For now we are using the parent for quirks
1697  // mode and the containing block for strict mode.
1698 
1699  // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater
1700  // than or less than the computed m_width. Be careful of box-sizing and
1701  // percentage issues.
1702 
1703  // The following is based off of the W3C Working Draft from April 11, 2006 of
1704  // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
1705  // <https://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
1706  // (block-style-comments in this function and in calcAbsoluteHorizontalValues()
1707  // correspond to text from the spec)
1708 
1709  // We don't use containingBlock(), since we may be positioned by an enclosing
1710  // relative positioned inline.
1711  RenderObject *containerBlock = container();
1712 
1713  const int containerWidth = containingBlockWidth(containerBlock);
1714 
1715  // To match WinIE, in quirks mode use the parent's 'direction' property
1716  // instead of the container block's.
1717  EDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
1718 
1719  const int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight();
1720  const Length marginLeft = style()->marginLeft();
1721  const Length marginRight = style()->marginRight();
1722  Length left = style()->left();
1723  Length right = style()->right();
1724 
1725  /*---------------------------------------------------------------------------*\
1726  * For the purposes of this section and the next, the term "static position"
1727  * (of an element) refers, roughly, to the position an element would have had
1728  * in the normal flow. More precisely:
1729  *
1730  * * The static position for 'left' is the distance from the left edge of the
1731  * containing block to the left margin edge of a hypothetical box that would
1732  * have been the first box of the element if its 'position' property had
1733  * been 'static' and 'float' had been 'none'. The value is negative if the
1734  * hypothetical box is to the left of the containing block.
1735  * * The static position for 'right' is the distance from the right edge of the
1736  * containing block to the right margin edge of the same hypothetical box as
1737  * above. The value is positive if the hypothetical box is to the left of the
1738  * containing block's edge.
1739  *
1740  * But rather than actually calculating the dimensions of that hypothetical box,
1741  * user agents are free to make a guess at its probable position.
1742  *
1743  * For the purposes of calculating the static position, the containing block of
1744  * fixed positioned elements is the initial containing block instead of the
1745  * viewport, and all scrollable boxes should be assumed to be scrolled to their
1746  * origin.
1747  \*---------------------------------------------------------------------------*/
1748 
1749  // Calculate the static distance if needed.
1750  if (left.isAuto() && right.isAuto()) {
1751  if (containerDirection == LTR) {
1752  // 'm_staticX' should already have been set through layout of the parent.
1753  int staticPosition = m_staticX - containerBlock->borderLeft();
1754  for (RenderObject *po = parent(); po && po != containerBlock; po = po->parent()) {
1755  staticPosition += po->xPos();
1756  }
1757  left = Length(staticPosition, Fixed);
1758  } else {
1759  RenderObject *po = parent();
1760  // 'm_staticX' should already have been set through layout of the parent.
1761  int staticPosition = m_staticX + containerWidth + containerBlock->borderRight() - po->width();
1762  for (; po && po != containerBlock; po = po->parent()) {
1763  staticPosition -= po->xPos();
1764  }
1765  right = Length(staticPosition, Fixed);
1766  }
1767  }
1768 
1769  // Calculate constraint equation values for 'width' case.
1770  calcAbsoluteHorizontalValues(style()->width(), containerBlock, containerDirection,
1771  containerWidth, bordersPlusPadding,
1772  left, right, marginLeft, marginRight,
1773  m_width, m_marginLeft, m_marginRight, m_x);
1774  // Calculate constraint equation values for 'max-width' case.calcContentWidth(width.width(containerWidth));
1775  if (!style()->maxWidth().isUndefined()) {
1776  short maxWidth;
1777  short maxMarginLeft;
1778  short maxMarginRight;
1779  short maxXPos;
1780 
1781  calcAbsoluteHorizontalValues(style()->maxWidth(), containerBlock, containerDirection,
1782  containerWidth, bordersPlusPadding,
1783  left, right, marginLeft, marginRight,
1784  maxWidth, maxMarginLeft, maxMarginRight, maxXPos);
1785 
1786  if (m_width > maxWidth) {
1787  m_width = maxWidth;
1788  m_marginLeft = maxMarginLeft;
1789  m_marginRight = maxMarginRight;
1790  m_x = maxXPos;
1791  }
1792  }
1793 
1794  // Calculate constraint equation values for 'min-width' case.
1795  if (!style()->minWidth().isZero()) {
1796  short minWidth;
1797  short minMarginLeft;
1798  short minMarginRight;
1799  short minXPos;
1800 
1801  calcAbsoluteHorizontalValues(style()->minWidth(), containerBlock, containerDirection,
1802  containerWidth, bordersPlusPadding,
1803  left, right, marginLeft, marginRight,
1804  minWidth, minMarginLeft, minMarginRight, minXPos);
1805 
1806  if (m_width < minWidth) {
1807  m_width = minWidth;
1808  m_marginLeft = minMarginLeft;
1809  m_marginRight = minMarginRight;
1810  m_x = minXPos;
1811  }
1812  }
1813 
1814  if (short iw = intrinsicWidth()) {
1815  // some elements (e.g. Fieldset) have pseudo-replaced behaviour in quirk mode
1816  if (m_width < iw - bordersPlusPadding)
1817  calcAbsoluteHorizontalValues(Length(iw - bordersPlusPadding, Fixed), containerBlock, containerDirection,
1818  containerWidth, bordersPlusPadding,
1819  left, right, marginLeft, marginRight,
1820  m_width, m_marginLeft, m_marginRight, m_x);
1821  }
1822 
1823  // Put m_width into correct form.
1824  m_width += bordersPlusPadding;
1825 }
1826 
1827 void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderObject *containerBlock, EDirection containerDirection,
1828  const int containerWidth, const int bordersPlusPadding,
1829  const Length left, const Length right, const Length marginLeft, const Length marginRight,
1830  short &widthValue, short &marginLeftValue, short &marginRightValue, short &xPos)
1831 {
1832  // 'left' and 'right' cannot both be 'auto' because one would of been
1833  // converted to the static postion already
1834  assert(!(left.isAuto() && right.isAuto()));
1835 
1836  int leftValue = 0;
1837 
1838  const bool widthIsAuto = width.isAuto();
1839  const bool leftIsAuto = left.isAuto();
1840  const bool rightIsAuto = right.isAuto();
1841 
1842  if (!leftIsAuto && !widthIsAuto && !rightIsAuto) {
1843  /*-----------------------------------------------------------------------*\
1844  * If none of the three is 'auto': If both 'margin-left' and 'margin-
1845  * right' are 'auto', solve the equation under the extra constraint that
1846  * the two margins get equal values, unless this would make them negative,
1847  * in which case when direction of the containing block is 'ltr' ('rtl'),
1848  * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
1849  * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
1850  * solve the equation for that value. If the values are over-constrained,
1851  * ignore the value for 'left' (in case the 'direction' property of the
1852  * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
1853  * and solve for that value.
1854  \*-----------------------------------------------------------------------*/
1855  // NOTE: It is not necessary to solve for 'right' in the over constrained
1856  // case because the value is not used for any further calculations.
1857 
1858  leftValue = left.width(containerWidth);
1859  widthValue = calcContentWidth(width.width(containerWidth));
1860 
1861  const int availableSpace = containerWidth - (leftValue + widthValue + right.width(containerWidth) + bordersPlusPadding);
1862 
1863  // Margins are now the only unknown
1864  if (marginLeft.isAuto() && marginRight.isAuto()) {
1865  // Both margins auto, solve for equality
1866  if (availableSpace >= 0) {
1867  marginLeftValue = availableSpace / 2; // split the diference
1868  marginRightValue = availableSpace - marginLeftValue; // account for odd valued differences
1869  } else {
1870  // see FIXME 1
1871  if (containerDirection == LTR) {
1872  marginLeftValue = 0;
1873  marginRightValue = availableSpace; // will be negative
1874  } else {
1875  marginLeftValue = availableSpace; // will be negative
1876  marginRightValue = 0;
1877  }
1878  }
1879  } else if (marginLeft.isAuto()) {
1880  // Solve for left margin
1881  marginRightValue = marginRight.width(containerWidth);
1882  marginLeftValue = availableSpace - marginRightValue;
1883  } else if (marginRight.isAuto()) {
1884  // Solve for right margin
1885  marginLeftValue = marginLeft.width(containerWidth);
1886  marginRightValue = availableSpace - marginLeftValue;
1887  } else {
1888  // Over-constrained, solve for left if direction is RTL
1889  marginLeftValue = marginLeft.width(containerWidth);
1890  marginRightValue = marginRight.width(containerWidth);
1891 
1892  // see FIXME 1 -- used to be "this->style()->direction()"
1893  if (containerDirection == RTL) {
1894  leftValue = (availableSpace + leftValue) - marginLeftValue - marginRightValue;
1895  }
1896  }
1897  } else {
1898  /*--------------------------------------------------------------------*\
1899  * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
1900  * to 0, and pick the one of the following six rules that applies.
1901  *
1902  * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
1903  * width is shrink-to-fit. Then solve for 'left'
1904  *
1905  * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
1906  * ------------------------------------------------------------------
1907  * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
1908  * the 'direction' property of the containing block is 'ltr' set
1909  * 'left' to the static position, otherwise set 'right' to the
1910  * static position. Then solve for 'left' (if 'direction is 'rtl')
1911  * or 'right' (if 'direction' is 'ltr').
1912  * ------------------------------------------------------------------
1913  *
1914  * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
1915  * width is shrink-to-fit . Then solve for 'right'
1916  * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
1917  * for 'left'
1918  * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
1919  * for 'width'
1920  * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
1921  * for 'right'
1922  *
1923  * Calculation of the shrink-to-fit width is similar to calculating the
1924  * width of a table cell using the automatic table layout algorithm.
1925  * Roughly: calculate the preferred width by formatting the content
1926  * without breaking lines other than where explicit line breaks occur,
1927  * and also calculate the preferred minimum width, e.g., by trying all
1928  * possible line breaks. CSS 2.1 does not define the exact algorithm.
1929  * Thirdly, calculate the available width: this is found by solving
1930  * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
1931  * to 0.
1932  *
1933  * Then the shrink-to-fit width is:
1934  * min(max(preferred minimum width, available width), preferred width).
1935  \*--------------------------------------------------------------------*/
1936  // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
1937  // because the value is not used for any further calculations.
1938 
1939  // Calculate margins, 'auto' margins are ignored.
1940  marginLeftValue = marginLeft.minWidth(containerWidth);
1941  marginRightValue = marginRight.minWidth(containerWidth);
1942 
1943  const int availableSpace = containerWidth - (marginLeftValue + marginRightValue + bordersPlusPadding);
1944 
1945  // FIXME: Is there a faster way to find the correct case?
1946  // Use rule/case that applies.
1947  if (leftIsAuto && widthIsAuto && !rightIsAuto) {
1948  // RULE 1: (use shrink-to-fit for width, and solve of left)
1949  int rightValue = right.width(containerWidth);
1950 
1951  // FIXME: would it be better to have shrink-to-fit in one step?
1952  int preferredWidth = m_maxWidth - bordersPlusPadding;
1953  int preferredMinWidth = m_minWidth - bordersPlusPadding;
1954  int availableWidth = availableSpace - rightValue;
1955  widthValue = qMin(qMax(preferredMinWidth, availableWidth), preferredWidth);
1956  leftValue = availableSpace - (widthValue + rightValue);
1957  } else if (!leftIsAuto && widthIsAuto && rightIsAuto) {
1958  // RULE 3: (use shrink-to-fit for width, and no need solve of right)
1959  leftValue = left.width(containerWidth);
1960 
1961  // FIXME: would it be better to have shrink-to-fit in one step?
1962  int preferredWidth = m_maxWidth - bordersPlusPadding;
1963  int preferredMinWidth = m_minWidth - bordersPlusPadding;
1964  int availableWidth = availableSpace - leftValue;
1965  widthValue = qMin(qMax(preferredMinWidth, availableWidth), preferredWidth);
1966  } else if (leftIsAuto && !width.isAuto() && !rightIsAuto) {
1967  // RULE 4: (solve for left)
1968  widthValue = calcContentWidth(width.width(containerWidth));
1969  leftValue = availableSpace - (widthValue + right.width(containerWidth));
1970  } else if (!leftIsAuto && widthIsAuto && !rightIsAuto) {
1971  // RULE 5: (solve for width)
1972  leftValue = left.width(containerWidth);
1973  widthValue = availableSpace - (leftValue + right.width(containerWidth));
1974  } else if (!leftIsAuto && !widthIsAuto && rightIsAuto) {
1975  // RULE 6: (no need solve for right)
1976  leftValue = left.width(containerWidth);
1977  widthValue = calcContentWidth(width.width(containerWidth));
1978  }
1979  }
1980 
1981  // Use computed values to calculate the horizontal position.
1982  int calculatedHorizontalPosition = leftValue + marginLeftValue + containerBlock->borderLeft();
1983  xPos = qBound((int)SHRT_MIN, calculatedHorizontalPosition, (int)SHRT_MAX);
1984 }
1985 
1986 void RenderBox::calcAbsoluteVertical()
1987 {
1988  if (isReplaced()) {
1989  calcAbsoluteVerticalReplaced();
1990  return;
1991  }
1992 
1993  // The following is based off of the W3C Working Draft from April 11, 2006 of
1994  // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
1995  // <https://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
1996  // (block-style-comments in this function and in calcAbsoluteVerticalValues()
1997  // correspond to text from the spec)
1998 
1999  // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2000  const RenderObject *containerBlock = container();
2001  const int containerHeight = containerBlock->height() - containerBlock->borderTop() - containerBlock->borderBottom();
2002 
2003  const int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom();
2004  const Length marginTop = style()->marginTop();
2005  const Length marginBottom = style()->marginBottom();
2006  Length top = style()->top();
2007  Length bottom = style()->bottom();
2008 
2009  /*---------------------------------------------------------------------------*\
2010  * For the purposes of this section and the next, the term "static position"
2011  * (of an element) refers, roughly, to the position an element would have had
2012  * in the normal flow. More precisely, the static position for 'top' is the
2013  * distance from the top edge of the containing block to the top margin edge
2014  * of a hypothetical box that would have been the first box of the element if
2015  * its 'position' property had been 'static' and 'float' had been 'none'. The
2016  * value is negative if the hypothetical box is above the containing block.
2017  *
2018  * But rather than actually calculating the dimensions of that hypothetical
2019  * box, user agents are free to make a guess at its probable position.
2020  *
2021  * For the purposes of calculating the static position, the containing block
2022  * of fixed positioned elements is the initial containing block instead of
2023  * the viewport.
2024  \*---------------------------------------------------------------------------*/
2025 
2026  // Calculate the static distance if needed.
2027  if (top.isAuto() && bottom.isAuto()) {
2028  // m_staticY should already have been set through layout of the parent()
2029  int staticTop = m_staticY - containerBlock->borderTop();
2030  for (RenderObject *po = parent(); po && po != containerBlock; po = po->parent()) {
2031  staticTop += po->yPos();
2032  }
2033  top.setValue(Fixed, staticTop);
2034  }
2035 
2036  int height; // Needed to compute overflow.
2037 
2038  // Calculate constraint equation values for 'height' case.
2039  calcAbsoluteVerticalValues(style()->height(), containerBlock, containerHeight, bordersPlusPadding,
2040  top, bottom, marginTop, marginBottom,
2041  height, m_marginTop, m_marginBottom, m_y);
2042 
2043  // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
2044  // see FIXME 2
2045 
2046  // Calculate constraint equation values for 'max-height' case.
2047  if (!style()->maxHeight().isUndefined()) {
2048  int maxHeight;
2049  short maxMarginTop;
2050  short maxMarginBottom;
2051  int maxYPos;
2052 
2053  calcAbsoluteVerticalValues(style()->maxHeight(), containerBlock, containerHeight, bordersPlusPadding,
2054  top, bottom, marginTop, marginBottom,
2055  maxHeight, maxMarginTop, maxMarginBottom, maxYPos);
2056 
2057  if (height > maxHeight) {
2058  height = maxHeight;
2059  m_marginTop = maxMarginTop;
2060  m_marginBottom = maxMarginBottom;
2061  m_y = maxYPos;
2062  }
2063  }
2064 
2065  // Calculate constraint equation values for 'min-height' case.
2066  if (!style()->minHeight().isZero()) {
2067  int minHeight;
2068  short minMarginTop;
2069  short minMarginBottom;
2070  int minYPos;
2071 
2072  calcAbsoluteVerticalValues(style()->minHeight(), containerBlock, containerHeight, bordersPlusPadding,
2073  top, bottom, marginTop, marginBottom,
2074  minHeight, minMarginTop, minMarginBottom, minYPos);
2075 
2076  if (height < minHeight) {
2077  height = minHeight;
2078  m_marginTop = minMarginTop;
2079  m_marginBottom = minMarginBottom;
2080  m_y = minYPos;
2081  }
2082  }
2083 
2084  height += bordersPlusPadding;
2085 
2086  // Set final height value.
2087  m_height = height;
2088 }
2089 
2090 void RenderBox::calcAbsoluteVerticalValues(Length height, const RenderObject *containerBlock,
2091  const int containerHeight, const int bordersPlusPadding,
2092  const Length top, const Length bottom, const Length marginTop, const Length marginBottom,
2093  int &heightValue, short &marginTopValue, short &marginBottomValue, int &yPos)
2094 {
2095  // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
2096  // converted to the static position in calcAbsoluteVertical()
2097  assert(!(top.isAuto() && bottom.isAuto()));
2098 
2099  int contentHeight = m_height - bordersPlusPadding;
2100 
2101  int topValue = 0;
2102 
2103  bool heightIsAuto = height.isAuto();
2104  bool topIsAuto = top.isAuto();
2105  bool bottomIsAuto = bottom.isAuto();
2106 
2107  if (isTable() && heightIsAuto) {
2108  // Height is never unsolved for tables. "auto" means shrink to fit.
2109  // Use our height instead.
2110  heightValue = contentHeight;
2111  heightIsAuto = false;
2112  } else if (!heightIsAuto) {
2113  heightValue = calcContentHeight(height.width(containerHeight));
2114  if (contentHeight > heightValue) {
2115  if (!isTable()) {
2116  contentHeight = heightValue;
2117  } else {
2118  heightValue = contentHeight;
2119  }
2120  }
2121  }
2122 
2123  if (!topIsAuto && !heightIsAuto && !bottomIsAuto) {
2124  /*-----------------------------------------------------------------------*\
2125  * If none of the three are 'auto': If both 'margin-top' and 'margin-
2126  * bottom' are 'auto', solve the equation under the extra constraint that
2127  * the two margins get equal values. If one of 'margin-top' or 'margin-
2128  * bottom' is 'auto', solve the equation for that value. If the values
2129  * are over-constrained, ignore the value for 'bottom' and solve for that
2130  * value.
2131  \*-----------------------------------------------------------------------*/
2132  // NOTE: It is not necessary to solve for 'bottom' in the over constrained
2133  // case because the value is not used for any further calculations.
2134 
2135  topValue = top.width(containerHeight);
2136 
2137  const int availableSpace = containerHeight - (topValue + heightValue + bottom.width(containerHeight) + bordersPlusPadding);
2138 
2139  // Margins are now the only unknown
2140  if (marginTop.isAuto() && marginBottom.isAuto()) {
2141  // Both margins auto, solve for equality
2142  // NOTE: This may result in negative values.
2143  marginTopValue = availableSpace / 2; // split the diference
2144  marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences
2145  } else if (marginTop.isAuto()) {
2146  // Solve for top margin
2147  marginBottomValue = marginBottom.width(containerHeight);
2148  marginTopValue = availableSpace - marginBottomValue;
2149  } else if (marginBottom.isAuto()) {
2150  // Solve for bottom margin
2151  marginTopValue = marginTop.width(containerHeight);
2152  marginBottomValue = availableSpace - marginTopValue;
2153  } else {
2154  // Over-constrained, (no need solve for bottom)
2155  marginTopValue = marginTop.width(containerHeight);
2156  marginBottomValue = marginBottom.width(containerHeight);
2157  }
2158  } else {
2159  /*--------------------------------------------------------------------*\
2160  * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
2161  * to 0, and pick the one of the following six rules that applies.
2162  *
2163  * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
2164  * the height is based on the content, and solve for 'top'.
2165  *
2166  * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
2167  * ------------------------------------------------------------------
2168  * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
2169  * set 'top' to the static position, and solve for 'bottom'.
2170  * ------------------------------------------------------------------
2171  *
2172  * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
2173  * the height is based on the content, and solve for 'bottom'.
2174  * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
2175  * solve for 'top'.
2176  * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
2177  * solve for 'height'.
2178  * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
2179  * solve for 'bottom'.
2180  \*--------------------------------------------------------------------*/
2181  // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
2182  // because the value is not used for any further calculations.
2183 
2184  // Calculate margins, 'auto' margins are ignored.
2185  marginTopValue = marginTop.minWidth(containerHeight);
2186  marginBottomValue = marginBottom.minWidth(containerHeight);
2187 
2188  const int availableSpace = containerHeight - (marginTopValue + marginBottomValue + bordersPlusPadding);
2189 
2190  // Use rule/case that applies.
2191  if (topIsAuto && heightIsAuto && !bottomIsAuto) {
2192  // RULE 1: (height is content based, solve of top)
2193  heightValue = contentHeight;
2194  topValue = availableSpace - (heightValue + bottom.width(containerHeight));
2195  } else if (topIsAuto && !heightIsAuto && bottomIsAuto) {
2196  // RULE 2: (shouldn't happen)
2197  } else if (!topIsAuto && heightIsAuto && bottomIsAuto) {
2198  // RULE 3: (height is content based, no need solve of bottom)
2199  heightValue = contentHeight;
2200  topValue = top.width(containerHeight);
2201  } else if (topIsAuto && !heightIsAuto && !bottomIsAuto) {
2202  // RULE 4: (solve of top)
2203  topValue = availableSpace - (heightValue + bottom.width(containerHeight));
2204  } else if (!topIsAuto && heightIsAuto && !bottomIsAuto) {
2205  // RULE 5: (solve of height)
2206  topValue = top.width(containerHeight);
2207  heightValue = qMax(0, availableSpace - (topValue + bottom.width(containerHeight)));
2208  } else if (!topIsAuto && !heightIsAuto && bottomIsAuto) {
2209  // RULE 6: (no need solve of bottom)
2210  topValue = top.width(containerHeight);
2211  }
2212  }
2213 
2214  // Use computed values to calculate the vertical position.
2215  yPos = topValue + marginTopValue + containerBlock->borderTop();
2216 }
2217 
2218 void RenderBox::calcAbsoluteHorizontalReplaced()
2219 {
2220  // The following is based off of the W3C Working Draft from April 11, 2006 of
2221  // CSS 2.1: Section 10.3.8 "Absolutly positioned, replaced elements"
2222  // <https://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
2223  // (block-style-comments in this function correspond to text from the spec and
2224  // the numbers correspond to numbers in spec)
2225 
2226  // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2227  RenderObject *containerBlock = container();
2228 
2229  const int containerWidth = containingBlockWidth(containerBlock);
2230 
2231  // To match WinIE, in quirks mode use the parent's 'direction' property
2232  // instead of the container block's.
2233  EDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
2234 
2235  // Variables to solve.
2236  Length left = style()->left();
2237  Length right = style()->right();
2238  Length marginLeft = style()->marginLeft();
2239  Length marginRight = style()->marginRight();
2240 
2241  /*-----------------------------------------------------------------------*\
2242  * 1. The used value of 'width' is determined as for inline replaced
2243  * elements.
2244  \*-----------------------------------------------------------------------*/
2245  // NOTE: This value of width is FINAL in that the min/max width calculations
2246  // are dealt with in calcReplacedWidth(). This means that the steps to produce
2247  // correct max/min in the non-replaced version, are not necessary.
2248  m_width = calcReplacedWidth() + borderLeft() + borderRight() + paddingLeft() + paddingRight();
2249  const int availableSpace = containerWidth - m_width;
2250 
2251  /*-----------------------------------------------------------------------*\
2252  * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
2253  * of the containing block is 'ltr', set 'left' to the static position;
2254  * else if 'direction' is 'rtl', set 'right' to the static position.
2255  \*-----------------------------------------------------------------------*/
2256  if (left.isAuto() && right.isAuto()) {
2257  // see FIXME 1
2258  if (containerDirection == LTR) {
2259  // 'm_staticX' should already have been set through layout of the parent.
2260  int staticPosition = m_staticX - containerBlock->borderLeft();
2261  for (RenderObject *po = parent(); po && po != containerBlock; po = po->parent()) {
2262  staticPosition += po->xPos();
2263  }
2264  left.setValue(Fixed, staticPosition);
2265  } else {
2266  RenderObject *po = parent();
2267  // 'm_staticX' should already have been set through layout of the parent.
2268  int staticPosition = m_staticX + containerWidth + containerBlock->borderRight() - po->width();
2269  for (; po && po != containerBlock; po = po->parent()) {
2270  staticPosition -= po->xPos();
2271  }
2272  right.setValue(Fixed, staticPosition);
2273  }
2274  }
2275 
2276  /*-----------------------------------------------------------------------*\
2277  * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
2278  * or 'margin-right' with '0'.
2279  \*-----------------------------------------------------------------------*/
2280  if (left.isAuto() || right.isAuto()) {
2281  if (marginLeft.isAuto()) {
2282  marginLeft.setValue(Fixed, 0);
2283  }
2284  if (marginRight.isAuto()) {
2285  marginRight.setValue(Fixed, 0);
2286  }
2287  }
2288 
2289  /*-----------------------------------------------------------------------*\
2290  * 4. If at this point both 'margin-left' and 'margin-right' are still
2291  * 'auto', solve the equation under the extra constraint that the two
2292  * margins must get equal values, unless this would make them negative,
2293  * in which case when the direction of the containing block is 'ltr'
2294  * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
2295  * 'margin-right' ('margin-left').
2296  \*-----------------------------------------------------------------------*/
2297  int leftValue = 0;
2298  int rightValue = 0;
2299 
2300  if (marginLeft.isAuto() && marginRight.isAuto()) {
2301  // 'left' and 'right' cannot be 'auto' due to step 3
2302  assert(!(left.isAuto() && right.isAuto()));
2303 
2304  leftValue = left.width(containerWidth);
2305  rightValue = right.width(containerWidth);
2306 
2307  int difference = availableSpace - (leftValue + rightValue);
2308  if (difference > 0) {
2309  m_marginLeft = difference / 2; // split the diference
2310  m_marginRight = difference - m_marginLeft; // account for odd valued differences
2311  } else {
2312  // see FIXME 1
2313  if (containerDirection == LTR) {
2314  m_marginLeft = 0;
2315  m_marginRight = difference; // will be negative
2316  } else {
2317  m_marginLeft = difference; // will be negative
2318  m_marginRight = 0;
2319  }
2320  }
2321 
2322  /*-----------------------------------------------------------------------*\
2323  * 5. If at this point there is an 'auto' left, solve the equation for
2324  * that value.
2325  \*-----------------------------------------------------------------------*/
2326  } else if (left.isAuto()) {
2327  m_marginLeft = marginLeft.width(containerWidth);
2328  m_marginRight = marginRight.width(containerWidth);
2329  rightValue = right.width(containerWidth);
2330 
2331  // Solve for 'left'
2332  leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight);
2333  } else if (right.isAuto()) {
2334  m_marginLeft = marginLeft.width(containerWidth);
2335  m_marginRight = marginRight.width(containerWidth);
2336  leftValue = left.width(containerWidth);
2337 
2338  // Solve for 'right'
2339  rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight);
2340  } else if (marginLeft.isAuto()) {
2341  m_marginRight = marginRight.width(containerWidth);
2342  leftValue = left.width(containerWidth);
2343  rightValue = right.width(containerWidth);
2344 
2345  // Solve for 'margin-left'
2346  m_marginLeft = availableSpace - (leftValue + rightValue + m_marginRight);
2347  } else if (marginRight.isAuto()) {
2348  m_marginLeft = marginLeft.width(containerWidth);
2349  leftValue = left.width(containerWidth);
2350  rightValue = right.width(containerWidth);
2351 
2352  // Solve for 'margin-right'
2353  m_marginRight = availableSpace - (leftValue + rightValue + m_marginLeft);
2354  }
2355 
2356  /*-----------------------------------------------------------------------*\
2357  * 6. If at this point the values are over-constrained, ignore the value
2358  * for either 'left' (in case the 'direction' property of the
2359  * containing block is 'rtl') or 'right' (in case 'direction' is
2360  * 'ltr') and solve for that value.
2361  \*-----------------------------------------------------------------------*/
2362  else {
2363  m_marginLeft = marginLeft.width(containerWidth);
2364  m_marginRight = marginRight.width(containerWidth);
2365  if (containerDirection == LTR) {
2366  leftValue = left.width(containerWidth);
2367  rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight);
2368  } else {
2369  rightValue = right.width(containerWidth);
2370  leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight);
2371  }
2372  }
2373 
2374  int totalWidth = m_width + leftValue + rightValue + m_marginLeft + m_marginRight;
2375  if (totalWidth > containerWidth && (containerDirection == RTL)) {
2376  leftValue = containerWidth - (totalWidth - leftValue);
2377  }
2378 
2379  // Use computed values to calculate the horizontal position.
2380  int calculatedHorizontalPosition = leftValue + m_marginLeft + containerBlock->borderLeft();
2381  m_x = qBound((int)SHRT_MIN, calculatedHorizontalPosition, (int)SHRT_MAX);
2382 }
2383 
2384 void RenderBox::calcAbsoluteVerticalReplaced()
2385 {
2386  // The following is based off of the W3C Working Draft from April 11, 2006 of
2387  // CSS 2.1: Section 10.6.5 "Absolutly positioned, replaced elements"
2388  // <https://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
2389  // (block-style-comments in this function correspond to text from the spec and
2390  // the numbers correspond to numbers in spec)
2391 
2392  // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2393  const RenderObject *containerBlock = container();
2394  const int containerHeight = containerBlock->height() - containerBlock->borderTop() - containerBlock->borderBottom();
2395 
2396  // Variables to solve.
2397  Length top = style()->top();
2398  Length bottom = style()->bottom();
2399  Length marginTop = style()->marginTop();
2400  Length marginBottom = style()->marginBottom();
2401 
2402  /*-----------------------------------------------------------------------*\
2403  * 1. The used value of 'height' is determined as for inline replaced
2404  * elements.
2405  \*-----------------------------------------------------------------------*/
2406  // NOTE: This value of height is FINAL in that the min/max height calculations
2407  // are dealt with in calcReplacedHeight(). This means that the steps to produce
2408  // correct max/min in the non-replaced version, are not necessary.
2409  m_height = calcReplacedHeight() + borderTop() + borderBottom() + paddingTop() + paddingBottom();
2410  const int availableSpace = containerHeight - m_height;
2411 
2412  /*-----------------------------------------------------------------------*\
2413  * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
2414  * with the element's static position.
2415  \*-----------------------------------------------------------------------*/
2416  if (top.isAuto() && bottom.isAuto()) {
2417  // m_staticY should already have been set through layout of the parent().
2418  int staticTop = m_staticY - containerBlock->borderTop();
2419  for (RenderObject *po = parent(); po && po != containerBlock; po = po->parent()) {
2420  staticTop += po->yPos();
2421  }
2422  top.setValue(Fixed, staticTop);
2423  }
2424 
2425  /*-----------------------------------------------------------------------*\
2426  * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
2427  * 'margin-bottom' with '0'.
2428  \*-----------------------------------------------------------------------*/
2429  // FIXME: The spec. says that this step should only be taken when bottom is
2430  // auto, but if only top is auto, this makes step 4 impossible.
2431  if (top.isAuto() || bottom.isAuto()) {
2432  if (marginTop.isAuto()) {
2433  marginTop.setValue(Fixed, 0);
2434  }
2435  if (marginBottom.isAuto()) {
2436  marginBottom.setValue(Fixed, 0);
2437  }
2438  }
2439 
2440  /*-----------------------------------------------------------------------*\
2441  * 4. If at this point both 'margin-top' and 'margin-bottom' are still
2442  * 'auto', solve the equation under the extra constraint that the two
2443  * margins must get equal values.
2444  \*-----------------------------------------------------------------------*/
2445  int topValue = 0;
2446  int bottomValue = 0;
2447 
2448  if (marginTop.isAuto() && marginBottom.isAuto()) {
2449  // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combinded.
2450  assert(!(top.isAuto() || bottom.isAuto()));
2451 
2452  topValue = top.width(containerHeight);
2453  bottomValue = bottom.width(containerHeight);
2454 
2455  int difference = availableSpace - (topValue + bottomValue);
2456  // NOTE: This may result in negative values.
2457  m_marginTop = difference / 2; // split the difference
2458  m_marginBottom = difference - m_marginTop; // account for odd valued differences
2459 
2460  /*-----------------------------------------------------------------------*\
2461  * 5. If at this point there is only one 'auto' left, solve the equation
2462  * for that value.
2463  \*-----------------------------------------------------------------------*/
2464  } else if (top.isAuto()) {
2465  m_marginTop = marginTop.width(containerHeight);
2466  m_marginBottom = marginBottom.width(containerHeight);
2467  bottomValue = bottom.width(containerHeight);
2468 
2469  // Solve for 'top'
2470  topValue = availableSpace - (bottomValue + m_marginTop + m_marginBottom);
2471  } else if (bottom.isAuto()) {
2472  m_marginTop = marginTop.width(containerHeight);
2473  m_marginBottom = marginBottom.width(containerHeight);
2474  topValue = top.width(containerHeight);
2475 
2476  // Solve for 'bottom'
2477  // NOTE: It is not necessary to solve for 'bottom' because we don't ever
2478  // use the value.
2479  } else if (marginTop.isAuto()) {
2480  m_marginBottom = marginBottom.width(containerHeight);
2481  topValue = top.width(containerHeight);
2482  bottomValue = bottom.width(containerHeight);
2483 
2484  // Solve for 'margin-top'
2485  m_marginTop = availableSpace - (topValue + bottomValue + m_marginBottom);
2486  } else if (marginBottom.isAuto()) {
2487  m_marginTop = marginTop.width(containerHeight);
2488  topValue = top.width(containerHeight);
2489  bottomValue = bottom.width(containerHeight);
2490 
2491  // Solve for 'margin-bottom'
2492  m_marginBottom = availableSpace - (topValue + bottomValue + m_marginTop);
2493  }
2494 
2495  /*-----------------------------------------------------------------------*\
2496  * 6. If at this point the values are over-constrained, ignore the value
2497  * for 'bottom' and solve for that value.
2498  \*-----------------------------------------------------------------------*/
2499  else {
2500  m_marginTop = marginTop.width(containerHeight);
2501  m_marginBottom = marginBottom.width(containerHeight);
2502  topValue = top.width(containerHeight);
2503 
2504  // Solve for 'bottom'
2505  // NOTE: It is not necessary to solve for 'bottom' because we don't ever
2506  // use the value.
2507  }
2508 
2509  // Use computed values to calculate the vertical position.
2510  m_y = topValue + m_marginTop + containerBlock->borderTop();
2511 }
2512 
2513 int RenderBox::highestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2514 {
2515  return includeSelf ? 0 : m_height;
2516 }
2517 
2518 int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2519 {
2520  return includeSelf ? m_height : 0;
2521  if (!includeSelf || !m_width) {
2522  return 0;
2523  }
2524  int bottom = m_height;
2525  if (isRelPositioned()) {
2526  int x = 0;
2527  relativePositionOffset(x, bottom);
2528  }
2529  return bottom;
2530 }
2531 
2532 int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2533 {
2534  if (!includeSelf || !m_height) {
2535  return 0;
2536  }
2537  int right = m_width;
2538  if (isRelPositioned()) {
2539  int y = 0;
2540  relativePositionOffset(right, y);
2541  }
2542  return right;
2543 }
2544 
2545 int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2546 {
2547  if (!includeSelf || !m_height) {
2548  return m_width;
2549  }
2550  int left = 0;
2551  if (isRelPositioned()) {
2552  int y = 0;
2553  relativePositionOffset(left, y);
2554  }
2555  return left;
2556 }
2557 
2558 int RenderBox::pageTopAfter(int y) const
2559 {
2560  RenderObject *cb = container();
2561  if (cb) {
2562  return cb->pageTopAfter(y + yPos()) - yPos();
2563  } else {
2564  return 0;
2565  }
2566 }
2567 
2568 int RenderBox::crossesPageBreak(int t, int b) const
2569 {
2570  RenderObject *cb = container();
2571  if (cb) {
2572  return cb->crossesPageBreak(yPos() + t, yPos() + b);
2573  } else {
2574  return false;
2575  }
2576 }
2577 
2578 bool RenderBox::handleEvent(const DOM::EventImpl &e)
2579 {
2580  KHTMLAssert(scrollsOverflow());
2581  bool accepted = false;
2582 
2583  switch (e.id()) {
2584  case EventImpl::KHTML_MOUSEWHEEL_EVENT: {
2585 
2586  const MouseEventImpl &me = static_cast<const MouseEventImpl &>(e);
2587  Qt::MouseButtons buttons = Qt::NoButton;
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) {
2663  }
2664  break;
2665  case Qt::Key_PageDown:
2666  if (vbar) {
2668  }
2669  break;
2670  case Qt::Key_Up:
2671  if (vbar) {
2673  }
2674  break;
2675  case Qt::Key_Down:
2676  if (vbar) {
2678  }
2679  break;
2680  case Qt::Key_Left:
2681  if (hbar) {
2683  }
2684  break;
2685  case Qt::Key_Right:
2686  if (hbar) {
2688  }
2689  break;
2690  default:
2691  break;
2692  }
2693  break;
2694  }
2695  default:
2696  break;
2697  }
2698  if (accepted) {
2699  return true;
2700  }
2701  return RenderContainer::handleEvent(e);
2702 }
2703 
2704 void RenderBox::caretPos(int /*offset*/, int flags, int &_x, int &_y, int &width, int &height) const
2705 {
2706 #if 0
2707  _x = -1;
2708 
2709  // propagate it downwards to its children, someone will feel responsible
2710  RenderObject *child = firstChild();
2711 // if (child) qCDebug(KHTML_LOG) << "delegating caretPos to " << child->renderName();
2712  if (child) {
2713  child->caretPos(offset, override, _x, _y, width, height);
2714  }
2715 
2716  // if not, use the extents of this box. offset 0 means left, offset 1 means
2717  // right
2718  if (_x == -1) {
2719  //qCDebug(KHTML_LOG) << "no delegation";
2720  _x = xPos() + (offset == 0 ? 0 : m_width);
2721  _y = yPos();
2722  height = m_height;
2723  width = override && offset == 0 ? m_width : 1;
2724 
2725  // If height of box is smaller than font height, use the latter one,
2726  // otherwise the caret might become invisible.
2727  // FIXME: ignoring :first-line, missing good reason to take care of
2728  int fontHeight = style()->fontMetrics().height();
2729  if (fontHeight > height) {
2730  height = fontHeight;
2731  }
2732 
2733  int absx, absy;
2734 
2735  RenderObject *cb = containingBlock();
2736 
2737  if (cb && cb != this && cb->absolutePosition(absx, absy)) {
2738  //qCDebug(KHTML_LOG) << "absx=" << absx << " absy=" << absy;
2739  _x += absx;
2740  _y += absy;
2741  } else {
2742  // we don't know our absolute position, and there is no point returning
2743  // just a relative one
2744  _x = _y = -1;
2745  }
2746  }
2747 #endif
2748 
2749  _x = xPos();
2750  _y = yPos();
2751 // qCDebug(KHTML_LOG) << "_x " << _x << " _y " << _y;
2752  width = 1; // no override is indicated in boxes
2753 
2754  RenderBlock *cb = containingBlock();
2755 
2756  // Place caret outside the border
2757  if (flags & CFOutside) {
2758 
2759  RenderStyle *s = element() && element()->parent()
2760  && element()->parent()->renderer()
2761  ? element()->parent()->renderer()->style()
2762  : cb->style();
2763 
2764  const QFontMetrics &fm = s->fontMetrics();
2765  height = fm.height();
2766 
2767  bool rtl = s->direction() == RTL;
2768  bool outsideEnd = flags & CFOutsideEnd;
2769 
2770  if (outsideEnd) {
2771  _x += this->width();
2772  } else {
2773  _x--;
2774  }
2775 
2776  int hl = fm.leading() / 2;
2777  if (!isReplaced() || style()->display() == BLOCK) {
2778  if (!outsideEnd ^ rtl) {
2779  _y -= hl;
2780  } else {
2781  _y += qMax(this->height() - fm.ascent() - hl, 0);
2782  }
2783  } else {
2784  _y += baselinePosition(false) - fm.ascent() - hl;
2785  }
2786 
2787  // Place caret inside the element
2788  } else {
2789  const QFontMetrics &fm = style()->fontMetrics();
2790  height = fm.height();
2791 
2792  RenderStyle *s = style();
2793 
2794  _x += borderLeft() + paddingLeft();
2795  _y += borderTop() + paddingTop();
2796 
2797  // ### regard direction
2798  switch (s->textAlign()) {
2799  case LEFT:
2800  case KHTML_LEFT:
2801  case TAAUTO: // ### find out what this does
2802  case JUSTIFY:
2803  break;
2804  case CENTER:
2805  case KHTML_CENTER:
2806  _x += contentWidth() / 2;
2807  break;
2808  case KHTML_RIGHT:
2809  case RIGHT:
2810  _x += contentWidth();
2811  break;
2812  }
2813  }
2814 
2815  int absx, absy;
2816  if (cb && cb != this && cb->absolutePosition(absx, absy)) {
2817 // qCDebug(KHTML_LOG) << "absx=" << absx << " absy=" << absy;
2818  _x += absx;
2819  _y += absy;
2820  } else {
2821  // we don't know our absolute position, and there is no point returning
2822  // just a relative one
2823  _x = _y = -1;
2824  }
2825 }
2826 
2827 #undef DEBUG_LAYOUT
QTextStream & ws(QTextStream &stream)
QString caption()
typedef KeyboardModifiers
int ascent() const const
int & rx()
int & ry()
int width() const const
const QBrush & background() const const
void fillRect(const QRectF &rectangle, const QBrush &brush)
void setRenderHint(QPainter::RenderHint hint, bool on)
void closeSubpath()
QStyle * style() const const
This file is part of the HTML rendering engine for KDE.
QTextStream & right(QTextStream &stream)
void save()
typedef MouseButtons
void setAlpha(int alpha)
virtual void caretPos(int offset, int flags, int &_x, int &_y, int &width, int &height) const
Returns the content coordinates of the caret within this render object.
void moveTo(const QPointF &point)
Renders and displays HTML in a QScrollArea.
Definition: khtmlview.h:97
void drawTiledPixmap(const QRectF &rectangle, const QPixmap &pixmap, const QPointF &position)
int height() const const
int x() const const
int y() const const
Base class for rendering objects that can have children.
a cached image
Definition: loader.h:359
int x() const const
int y() const const
QTextStream & left(QTextStream &stream)
const QColor & color() const const
void drawRect(const QRectF &rectangle)
int visibleWidth() const
Returns the width of the viewport.
Definition: khtmlview.cpp:719
int visibleHeight() const
Returns the height of the viewport.
Definition: khtmlview.cpp:735
void setTransform(const QTransform &matrix)
void fillPath(const QPainterPath &path, const QBrush &brush)
void setPen(const QColor &color)
void lineTo(const QPointF &endPoint)
int effectiveWidth() const
Returns the width that is effectively considered when contemplating the object as a whole – usually ...
int contentsHeight() const
Returns the contents area&#39;s height.
Definition: khtmlview.cpp:693
QTextStream & left(QTextStream &s)
void addRect(const QRectF &rectangle)
bool sendEvent(QObject *receiver, QEvent *event)
MESSAGECORE_EXPORT KMime::Content * firstChild(const KMime::Content *node)
void arcMoveTo(const QRectF &rectangle, qreal angle)
void setBrush(const QBrush &brush)
QRect intersected(const QRect &rectangle) const const
int alpha() const const
int key() const const
bool isValid() const const
int leading() const const
all geometry managing stuff is only in the block elements.
Definition: render_flow.h:44
void restore()
This library provides a full-featured HTML parser and widget.
bool isNull() const const
int width() const const
void setClipRect(const QRectF &rectangle, Qt::ClipOperation operation)
QTextStream & right(QTextStream &s)
void triggerAction(QAbstractSlider::SliderAction action)
bool isEmpty() const const
int height() const const
int height() const const
Base Class for all rendering tree objects.
Orientation
int contentsWidth() const
Returns the contents area&#39;s width.
Definition: khtmlview.cpp:688
int effectiveHeight() const
Returns the height that is effectively considered when contemplating the object as a whole – usually...
void arcTo(const QRectF &rectangle, qreal startAngle, qreal sweepLength)
MESSAGECORE_EXPORT KMime::Content * nextSibling(const KMime::Content *node)
Key_PageUp
QPainterPath intersected(const QPainterPath &p) const const
QRgb rgba() const const
bool isValid() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 22:48:19 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.