1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25 #include "config.h"
26 #include "RenderBox.h"
27
28 #include "CachedImage.h"
29 #include "Chrome.h"
30 #include "ChromeClient.h"
31 #include "Document.h"
32 #include "FrameView.h"
33 #include "GraphicsContext.h"
34 #include "htmlediting.h"
35 #include "HTMLElement.h"
36 #include "HTMLNames.h"
37 #include "ImageBuffer.h"
38 #include "FloatQuad.h"
39 #include "Frame.h"
40 #include "Page.h"
41 #include "RenderArena.h"
42 #include "RenderFlexibleBox.h"
43 #include "RenderInline.h"
44 #include "RenderLayer.h"
45 #include "RenderTableCell.h"
46 #include "RenderTheme.h"
47 #ifdef ANDROID_LAYOUT
48 #include "Settings.h"
49 #endif
50 #include "RenderView.h"
51 #include "TransformState.h"
52 #include <algorithm>
53 #include <math.h>
54
55 #if ENABLE(WML)
56 #include "WMLNames.h"
57 #endif
58
59 using namespace std;
60
61 namespace WebCore {
62
63 using namespace HTMLNames;
64
65 // Used by flexible boxes when flexing this element.
66 typedef WTF::HashMap<const RenderBox*, int> OverrideSizeMap;
67 static OverrideSizeMap* gOverrideSizeMap = 0;
68
69 bool RenderBox::s_hadOverflowClip = false;
70
RenderBox(Node * node)71 RenderBox::RenderBox(Node* node)
72 : RenderBoxModelObject(node)
73 #ifdef ANDROID_LAYOUT
74 , m_visibleWidth(0)
75 #endif
76 , m_marginLeft(0)
77 , m_marginRight(0)
78 , m_marginTop(0)
79 , m_marginBottom(0)
80 , m_minPrefWidth(-1)
81 , m_maxPrefWidth(-1)
82 , m_inlineBoxWrapper(0)
83 {
84 setIsBox();
85 }
86
~RenderBox()87 RenderBox::~RenderBox()
88 {
89 }
90
destroy()91 void RenderBox::destroy()
92 {
93 // A lot of the code in this function is just pasted into
94 // RenderWidget::destroy. If anything in this function changes,
95 // be sure to fix RenderWidget::destroy() as well.
96 if (hasOverrideSize())
97 gOverrideSizeMap->remove(this);
98
99 if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent()))
100 RenderBlock::removePercentHeightDescendant(this);
101
102 RenderBoxModelObject::destroy();
103 }
104
removeFloatingOrPositionedChildFromBlockLists()105 void RenderBox::removeFloatingOrPositionedChildFromBlockLists()
106 {
107 ASSERT(isFloatingOrPositioned());
108
109 if (documentBeingDestroyed())
110 return;
111
112 if (isFloating()) {
113 RenderBlock* outermostBlock = containingBlock();
114 for (RenderBlock* p = outermostBlock; p && !p->isRenderView(); p = p->containingBlock()) {
115 if (p->containsFloat(this))
116 outermostBlock = p;
117 }
118
119 if (outermostBlock) {
120 RenderObject* parent = outermostBlock->parent();
121 if (parent && parent->isFlexibleBox())
122 outermostBlock = toRenderBlock(parent);
123
124 outermostBlock->markAllDescendantsWithFloatsForLayout(this, false);
125 }
126 }
127
128 if (isPositioned()) {
129 RenderObject* p;
130 for (p = parent(); p; p = p->parent()) {
131 if (p->isRenderBlock())
132 toRenderBlock(p)->removePositionedObject(this);
133 }
134 }
135 }
136
styleWillChange(StyleDifference diff,const RenderStyle * newStyle)137 void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
138 {
139 s_hadOverflowClip = hasOverflowClip();
140
141 if (style()) {
142 // The background of the root element or the body element could propagate up to
143 // the canvas. Just dirty the entire canvas when our style changes substantially.
144 if (diff >= StyleDifferenceRepaint && node() &&
145 (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag)))
146 view()->repaint();
147
148 // When a layout hint happens and an object's position style changes, we have to do a layout
149 // to dirty the render tree using the old position value now.
150 if (diff == StyleDifferenceLayout && parent() && style()->position() != newStyle->position()) {
151 markContainingBlocksForLayout();
152 if (style()->position() == StaticPosition)
153 repaint();
154 else if (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition)
155 parent()->setChildNeedsLayout(true);
156 if (isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition))
157 removeFloatingOrPositionedChildFromBlockLists();
158 }
159 }
160
161 RenderBoxModelObject::styleWillChange(diff, newStyle);
162 }
163
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)164 void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
165 {
166 RenderBoxModelObject::styleDidChange(diff, oldStyle);
167
168 if (needsLayout() && oldStyle && (oldStyle->height().isPercent() || oldStyle->minHeight().isPercent() || oldStyle->maxHeight().isPercent()))
169 RenderBlock::removePercentHeightDescendant(this);
170
171 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the
172 // new zoomed coordinate space.
173 if (hasOverflowClip() && oldStyle && style() && oldStyle->effectiveZoom() != style()->effectiveZoom()) {
174 int left = scrollLeft();
175 if (left) {
176 left = (left / oldStyle->effectiveZoom()) * style()->effectiveZoom();
177 setScrollLeft(left);
178 }
179 int top = scrollTop();
180 if (top) {
181 top = (top / oldStyle->effectiveZoom()) * style()->effectiveZoom();
182 setScrollTop(top);
183 }
184 }
185
186 // Set the text color if we're the body.
187 if (isBody())
188 document()->setTextColor(style()->color());
189 }
190
updateBoxModelInfoFromStyle()191 void RenderBox::updateBoxModelInfoFromStyle()
192 {
193 RenderBoxModelObject::updateBoxModelInfoFromStyle();
194
195 bool isRootObject = isRoot();
196 bool isViewObject = isRenderView();
197
198 // The root and the RenderView always paint their backgrounds/borders.
199 if (isRootObject || isViewObject)
200 setHasBoxDecorations(true);
201
202 setPositioned(style()->position() == AbsolutePosition || style()->position() == FixedPosition);
203 setFloating(!isPositioned() && style()->isFloating());
204
205 // We also handle <body> and <html>, whose overflow applies to the viewport.
206 if (style()->overflowX() != OVISIBLE && !isRootObject && (isRenderBlock() || isTableRow() || isTableSection())) {
207 bool boxHasOverflowClip = true;
208 if (isBody()) {
209 // Overflow on the body can propagate to the viewport under the following conditions.
210 // (1) The root element is <html>.
211 // (2) We are the primary <body> (can be checked by looking at document.body).
212 // (3) The root element has visible overflow.
213 if (document()->documentElement()->hasTagName(htmlTag) &&
214 document()->body() == node() &&
215 document()->documentElement()->renderer()->style()->overflowX() == OVISIBLE)
216 boxHasOverflowClip = false;
217 }
218
219 // Check for overflow clip.
220 // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value.
221 if (boxHasOverflowClip) {
222 if (!s_hadOverflowClip)
223 // Erase the overflow
224 repaint();
225 setHasOverflowClip();
226 }
227 }
228
229 setHasTransform(style()->hasTransformRelatedProperty());
230 setHasReflection(style()->boxReflect());
231 }
232
layout()233 void RenderBox::layout()
234 {
235 ASSERT(needsLayout());
236
237 RenderObject* child = firstChild();
238 if (!child) {
239 setNeedsLayout(false);
240 return;
241 }
242
243 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()));
244 while (child) {
245 child->layoutIfNeeded();
246 ASSERT(!child->needsLayout());
247 child = child->nextSibling();
248 }
249 statePusher.pop();
250 setNeedsLayout(false);
251 }
252
253 // More IE extensions. clientWidth and clientHeight represent the interior of an object
254 // excluding border and scrollbar.
clientWidth() const255 int RenderBox::clientWidth() const
256 {
257 return width() - borderLeft() - borderRight() - verticalScrollbarWidth();
258 }
259
clientHeight() const260 int RenderBox::clientHeight() const
261 {
262 return height() - borderTop() - borderBottom() - horizontalScrollbarHeight();
263 }
264
scrollWidth() const265 int RenderBox::scrollWidth() const
266 {
267 if (hasOverflowClip())
268 return layer()->scrollWidth();
269 // For objects with visible overflow, this matches IE.
270 if (style()->direction() == LTR)
271 return max(clientWidth(), rightmostPosition(true, false) - borderLeft());
272 return clientWidth() - min(0, leftmostPosition(true, false) - borderLeft());
273 }
274
scrollHeight() const275 int RenderBox::scrollHeight() const
276 {
277 if (hasOverflowClip())
278 return layer()->scrollHeight();
279 // For objects with visible overflow, this matches IE.
280 return max(clientHeight(), lowestPosition(true, false) - borderTop());
281 }
282
scrollLeft() const283 int RenderBox::scrollLeft() const
284 {
285 return hasOverflowClip() ? layer()->scrollXOffset() : 0;
286 }
287
scrollTop() const288 int RenderBox::scrollTop() const
289 {
290 return hasOverflowClip() ? layer()->scrollYOffset() : 0;
291 }
292
setScrollLeft(int newLeft)293 void RenderBox::setScrollLeft(int newLeft)
294 {
295 if (hasOverflowClip())
296 layer()->scrollToXOffset(newLeft);
297 }
298
setScrollTop(int newTop)299 void RenderBox::setScrollTop(int newTop)
300 {
301 if (hasOverflowClip())
302 layer()->scrollToYOffset(newTop);
303 }
304
absoluteRects(Vector<IntRect> & rects,int tx,int ty)305 void RenderBox::absoluteRects(Vector<IntRect>& rects, int tx, int ty)
306 {
307 rects.append(IntRect(tx, ty, width(), height()));
308 }
309
absoluteQuads(Vector<FloatQuad> & quads)310 void RenderBox::absoluteQuads(Vector<FloatQuad>& quads)
311 {
312 quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height())));
313 }
314
absoluteContentBox() const315 IntRect RenderBox::absoluteContentBox() const
316 {
317 IntRect rect = contentBoxRect();
318 FloatPoint absPos = localToAbsolute(FloatPoint());
319 rect.move(absPos.x(), absPos.y());
320 return rect;
321 }
322
absoluteContentQuad() const323 FloatQuad RenderBox::absoluteContentQuad() const
324 {
325 IntRect rect = contentBoxRect();
326 return localToAbsoluteQuad(FloatRect(rect));
327 }
328
outlineBoundsForRepaint(RenderBoxModelObject * repaintContainer) const329 IntRect RenderBox::outlineBoundsForRepaint(RenderBoxModelObject* repaintContainer) const
330 {
331 IntRect box = borderBoundingBox();
332 adjustRectForOutlineAndShadow(box);
333
334 FloatQuad containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer);
335 box = containerRelativeQuad.enclosingBoundingBox();
336
337 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
338 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
339 box.move(view()->layoutDelta());
340
341 return box;
342 }
343
addFocusRingRects(Vector<IntRect> & rects,int tx,int ty)344 void RenderBox::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty)
345 {
346 if (width() && height())
347 rects.append(IntRect(tx, ty, width(), height()));
348 }
349
reflectionBox() const350 IntRect RenderBox::reflectionBox() const
351 {
352 IntRect result;
353 if (!style()->boxReflect())
354 return result;
355 IntRect box = borderBoxRect();
356 result = box;
357 switch (style()->boxReflect()->direction()) {
358 case ReflectionBelow:
359 result.move(0, box.height() + reflectionOffset());
360 break;
361 case ReflectionAbove:
362 result.move(0, -box.height() - reflectionOffset());
363 break;
364 case ReflectionLeft:
365 result.move(-box.width() - reflectionOffset(), 0);
366 break;
367 case ReflectionRight:
368 result.move(box.width() + reflectionOffset(), 0);
369 break;
370 }
371 return result;
372 }
373
reflectionOffset() const374 int RenderBox::reflectionOffset() const
375 {
376 if (!style()->boxReflect())
377 return 0;
378 if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight)
379 return style()->boxReflect()->offset().calcValue(borderBoxRect().width());
380 return style()->boxReflect()->offset().calcValue(borderBoxRect().height());
381 }
382
reflectedRect(const IntRect & r) const383 IntRect RenderBox::reflectedRect(const IntRect& r) const
384 {
385 if (!style()->boxReflect())
386 return IntRect();
387
388 IntRect box = borderBoxRect();
389 IntRect result = r;
390 switch (style()->boxReflect()->direction()) {
391 case ReflectionBelow:
392 result.setY(box.bottom() + reflectionOffset() + (box.bottom() - r.bottom()));
393 break;
394 case ReflectionAbove:
395 result.setY(box.y() - reflectionOffset() - box.height() + (box.bottom() - r.bottom()));
396 break;
397 case ReflectionLeft:
398 result.setX(box.x() - reflectionOffset() - box.width() + (box.right() - r.right()));
399 break;
400 case ReflectionRight:
401 result.setX(box.right() + reflectionOffset() + (box.right() - r.right()));
402 break;
403 }
404 return result;
405 }
406
verticalScrollbarWidth() const407 int RenderBox::verticalScrollbarWidth() const
408 {
409 return includeVerticalScrollbarSize() ? layer()->verticalScrollbarWidth() : 0;
410 }
411
horizontalScrollbarHeight() const412 int RenderBox::horizontalScrollbarHeight() const
413 {
414 return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0;
415 }
416
scroll(ScrollDirection direction,ScrollGranularity granularity,float multiplier,Node ** stopNode)417 bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node** stopNode)
418 {
419 RenderLayer* l = layer();
420 if (l && l->scroll(direction, granularity, multiplier)) {
421 if (stopNode)
422 *stopNode = node();
423 return true;
424 }
425
426 if (stopNode && *stopNode && *stopNode == node())
427 return true;
428
429 RenderBlock* b = containingBlock();
430 if (b && !b->isRenderView())
431 return b->scroll(direction, granularity, multiplier, stopNode);
432 return false;
433 }
434
canBeScrolledAndHasScrollableArea() const435 bool RenderBox::canBeScrolledAndHasScrollableArea() const
436 {
437 return canBeProgramaticallyScrolled(false) && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth());
438 }
439
canBeProgramaticallyScrolled(bool) const440 bool RenderBox::canBeProgramaticallyScrolled(bool) const
441 {
442 return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->isContentEditable()))) || (node() && node()->isDocumentNode());
443 }
444
autoscroll()445 void RenderBox::autoscroll()
446 {
447 if (layer())
448 layer()->autoscroll();
449 }
450
panScroll(const IntPoint & source)451 void RenderBox::panScroll(const IntPoint& source)
452 {
453 if (layer())
454 layer()->panScrollFromPoint(source);
455 }
456
minPrefWidth() const457 int RenderBox::minPrefWidth() const
458 {
459 if (prefWidthsDirty())
460 const_cast<RenderBox*>(this)->calcPrefWidths();
461
462 return m_minPrefWidth;
463 }
464
maxPrefWidth() const465 int RenderBox::maxPrefWidth() const
466 {
467 if (prefWidthsDirty())
468 const_cast<RenderBox*>(this)->calcPrefWidths();
469
470 return m_maxPrefWidth;
471 }
472
overrideSize() const473 int RenderBox::overrideSize() const
474 {
475 if (!hasOverrideSize())
476 return -1;
477 return gOverrideSizeMap->get(this);
478 }
479
setOverrideSize(int s)480 void RenderBox::setOverrideSize(int s)
481 {
482 if (s == -1) {
483 if (hasOverrideSize()) {
484 setHasOverrideSize(false);
485 gOverrideSizeMap->remove(this);
486 }
487 } else {
488 if (!gOverrideSizeMap)
489 gOverrideSizeMap = new OverrideSizeMap();
490 setHasOverrideSize(true);
491 gOverrideSizeMap->set(this, s);
492 }
493 }
494
overrideWidth() const495 int RenderBox::overrideWidth() const
496 {
497 return hasOverrideSize() ? overrideSize() : width();
498 }
499
overrideHeight() const500 int RenderBox::overrideHeight() const
501 {
502 return hasOverrideSize() ? overrideSize() : height();
503 }
504
calcBorderBoxWidth(int width) const505 int RenderBox::calcBorderBoxWidth(int width) const
506 {
507 int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight();
508 if (style()->boxSizing() == CONTENT_BOX)
509 return width + bordersPlusPadding;
510 return max(width, bordersPlusPadding);
511 }
512
calcBorderBoxHeight(int height) const513 int RenderBox::calcBorderBoxHeight(int height) const
514 {
515 int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom();
516 if (style()->boxSizing() == CONTENT_BOX)
517 return height + bordersPlusPadding;
518 return max(height, bordersPlusPadding);
519 }
520
calcContentBoxWidth(int width) const521 int RenderBox::calcContentBoxWidth(int width) const
522 {
523 if (style()->boxSizing() == BORDER_BOX)
524 width -= (borderLeft() + borderRight() + paddingLeft() + paddingRight());
525 return max(0, width);
526 }
527
calcContentBoxHeight(int height) const528 int RenderBox::calcContentBoxHeight(int height) const
529 {
530 if (style()->boxSizing() == BORDER_BOX)
531 height -= (borderTop() + borderBottom() + paddingTop() + paddingBottom());
532 return max(0, height);
533 }
534
535 // Hit Testing
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int xPos,int yPos,int tx,int ty,HitTestAction action)536 bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action)
537 {
538 tx += x();
539 ty += y();
540
541 // Check kids first.
542 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
543 if (!child->hasLayer() && child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) {
544 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
545 return true;
546 }
547 }
548
549 // Check our bounds next. For this purpose always assume that we can only be hit in the
550 // foreground phase (which is true for replaced elements like images).
551 if (visibleToHitTesting() && action == HitTestForeground && IntRect(tx, ty, width(), height()).contains(xPos, yPos)) {
552 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
553 return true;
554 }
555
556 return false;
557 }
558
559 // --------------------- painting stuff -------------------------------
560
paint(PaintInfo & paintInfo,int tx,int ty)561 void RenderBox::paint(PaintInfo& paintInfo, int tx, int ty)
562 {
563 tx += x();
564 ty += y();
565
566 // default implementation. Just pass paint through to the children
567 PaintInfo childInfo(paintInfo);
568 childInfo.paintingRoot = paintingRootForChildren(paintInfo);
569 for (RenderObject* child = firstChild(); child; child = child->nextSibling())
570 child->paint(childInfo, tx, ty);
571 }
572
paintRootBoxDecorations(PaintInfo & paintInfo,int tx,int ty)573 void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
574 {
575 const FillLayer* bgLayer = style()->backgroundLayers();
576 Color bgColor = style()->backgroundColor();
577 RenderObject* bodyObject = 0;
578 if (!style()->hasBackground() && node() && node()->hasTagName(HTMLNames::htmlTag)) {
579 // Locate the <body> element using the DOM. This is easier than trying
580 // to crawl around a render tree with potential :before/:after content and
581 // anonymous blocks created by inline <body> tags etc. We can locate the <body>
582 // render object very easily via the DOM.
583 HTMLElement* body = document()->body();
584 bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0;
585 if (bodyObject) {
586 bgLayer = bodyObject->style()->backgroundLayers();
587 bgColor = bodyObject->style()->backgroundColor();
588 }
589 }
590
591 int w = width();
592 int h = height();
593
594 int rw;
595 int rh;
596 if (view()->frameView()) {
597 rw = view()->frameView()->contentsWidth();
598 rh = view()->frameView()->contentsHeight();
599 } else {
600 rw = view()->width();
601 rh = view()->height();
602 }
603
604 // CSS2 14.2:
605 // The background of the box generated by the root element covers the entire canvas including
606 // its margins.
607 int bx = tx - marginLeft();
608 int by = ty - marginTop();
609 int bw = max(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw);
610 int bh = max(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh);
611
612 paintFillLayers(paintInfo, bgColor, bgLayer, bx, by, bw, bh, CompositeSourceOver, bodyObject);
613
614 if (style()->hasBorder() && style()->display() != INLINE)
615 paintBorder(paintInfo.context, tx, ty, w, h, style());
616 }
617
paintBoxDecorations(PaintInfo & paintInfo,int tx,int ty)618 void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
619 {
620 if (!shouldPaintWithinRoot(paintInfo))
621 return;
622
623 if (isRoot()) {
624 paintRootBoxDecorations(paintInfo, tx, ty);
625 return;
626 }
627
628 int w = width();
629 int h = height();
630
631 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat
632 // balloon layout is an example of this).
633 borderFitAdjust(tx, w);
634
635 // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
636 // custom shadows of their own.
637 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal);
638
639 // If we have a native theme appearance, paint that before painting our background.
640 // The theme will tell us whether or not we should also paint the CSS background.
641 bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, IntRect(tx, ty, w, h));
642 if (!themePainted) {
643 // The <body> only paints its background if the root element has defined a background
644 // independent of the body. Go through the DOM to get to the root element's render object,
645 // since the root could be inline and wrapped in an anonymous block.
646 if (!isBody() || document()->documentElement()->renderer()->style()->hasBackground())
647 paintFillLayers(paintInfo, style()->backgroundColor(), style()->backgroundLayers(), tx, ty, w, h);
648 if (style()->hasAppearance())
649 theme()->paintDecorations(this, paintInfo, IntRect(tx, ty, w, h));
650 }
651 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset);
652
653 // The theme will tell us whether or not we should also paint the CSS border.
654 if ((!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, IntRect(tx, ty, w, h)))) && style()->hasBorder())
655 paintBorder(paintInfo.context, tx, ty, w, h, style());
656 }
657
paintMask(PaintInfo & paintInfo,int tx,int ty)658 void RenderBox::paintMask(PaintInfo& paintInfo, int tx, int ty)
659 {
660 if (!shouldPaintWithinRoot(paintInfo) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
661 return;
662
663 int w = width();
664 int h = height();
665
666 // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat
667 // balloon layout is an example of this).
668 borderFitAdjust(tx, w);
669
670 paintMaskImages(paintInfo, tx, ty, w, h);
671 }
672
paintMaskImages(const PaintInfo & paintInfo,int tx,int ty,int w,int h)673 void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int tx, int ty, int w, int h)
674 {
675 // Figure out if we need to push a transparency layer to render our mask.
676 bool pushTransparencyLayer = false;
677 bool compositedMask = hasLayer() && layer()->hasCompositedMask();
678 CompositeOperator compositeOp = CompositeSourceOver;
679
680 bool allMaskImagesLoaded = true;
681
682 if (!compositedMask) {
683 StyleImage* maskBoxImage = style()->maskBoxImage().image();
684 const FillLayer* maskLayers = style()->maskLayers();
685
686 // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content.
687 if (maskBoxImage)
688 allMaskImagesLoaded &= maskBoxImage->isLoaded();
689
690 if (maskLayers)
691 allMaskImagesLoaded &= maskLayers->imagesAreLoaded();
692
693 // Before all images have loaded, just use an empty transparency layer as the mask.
694 if (!allMaskImagesLoaded)
695 pushTransparencyLayer = true;
696
697 if (maskBoxImage && maskLayers->hasImage()) {
698 // We have a mask-box-image and mask-image, so need to composite them together before using the result as a mask.
699 pushTransparencyLayer = true;
700 } else {
701 // We have to use an extra image buffer to hold the mask. Multiple mask images need
702 // to composite together using source-over so that they can then combine into a single unified mask that
703 // can be composited with the content using destination-in. SVG images need to be able to set compositing modes
704 // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer
705 // and composite that buffer as the mask.
706 // We have to check that the mask images to be rendered contain at least one image that can be actually used in rendering
707 // before pushing the transparency layer.
708 for (const FillLayer* fillLayer = maskLayers->next(); fillLayer; fillLayer = fillLayer->next()) {
709 if (fillLayer->hasImage() && fillLayer->image()->canRender(style()->effectiveZoom())) {
710 pushTransparencyLayer = true;
711 // We found one image that can be used in rendering, exit the loop
712 break;
713 }
714 }
715 }
716
717 compositeOp = CompositeDestinationIn;
718 if (pushTransparencyLayer) {
719 paintInfo.context->setCompositeOperation(CompositeDestinationIn);
720 paintInfo.context->beginTransparencyLayer(1.0f);
721 compositeOp = CompositeSourceOver;
722 }
723 }
724
725 if (allMaskImagesLoaded) {
726 paintFillLayers(paintInfo, Color(), style()->maskLayers(), tx, ty, w, h, compositeOp);
727 paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp);
728 }
729
730 if (pushTransparencyLayer)
731 paintInfo.context->endTransparencyLayer();
732 }
733
maskClipRect()734 IntRect RenderBox::maskClipRect()
735 {
736 IntRect bbox = borderBoxRect();
737 if (style()->maskBoxImage().image())
738 return bbox;
739
740 IntRect result;
741 for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) {
742 if (maskLayer->image()) {
743 IntRect maskRect;
744 IntPoint phase;
745 IntSize tileSize;
746 calculateBackgroundImageGeometry(maskLayer, bbox.x(), bbox.y(), bbox.width(), bbox.height(), maskRect, phase, tileSize);
747 result.unite(maskRect);
748 }
749 }
750 return result;
751 }
752
paintFillLayers(const PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,int tx,int ty,int width,int height,CompositeOperator op,RenderObject * backgroundObject)753 void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject)
754 {
755 if (!fillLayer)
756 return;
757
758 paintFillLayers(paintInfo, c, fillLayer->next(), tx, ty, width, height, op, backgroundObject);
759 paintFillLayer(paintInfo, c, fillLayer, tx, ty, width, height, op, backgroundObject);
760 }
761
paintFillLayer(const PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,int tx,int ty,int width,int height,CompositeOperator op,RenderObject * backgroundObject)762 void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int width, int height, CompositeOperator op, RenderObject* backgroundObject)
763 {
764 paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, width, height, 0, op, backgroundObject);
765 }
766
imageChanged(WrappedImagePtr image,const IntRect *)767 void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*)
768 {
769 if (!parent())
770 return;
771
772 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) ||
773 (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) {
774 repaint();
775 return;
776 }
777
778 bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true);
779 if (!didFullRepaint)
780 repaintLayerRectsForImage(image, style()->maskLayers(), false);
781 }
782
repaintLayerRectsForImage(WrappedImagePtr image,const FillLayer * layers,bool drawingBackground)783 bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground)
784 {
785 IntRect rendererRect;
786 RenderBox* layerRenderer = 0;
787
788 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
789 if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(style()->effectiveZoom())) {
790 // Now that we know this image is being used, compute the renderer and the rect
791 // if we haven't already
792 if (!layerRenderer) {
793 bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->style()->hasBackground()));
794 if (drawingRootBackground) {
795 layerRenderer = view();
796
797 int rw;
798 int rh;
799
800 if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) {
801 rw = frameView->contentsWidth();
802 rh = frameView->contentsHeight();
803 } else {
804 rw = layerRenderer->width();
805 rh = layerRenderer->height();
806 }
807 rendererRect = IntRect(-layerRenderer->marginLeft(),
808 -layerRenderer->marginTop(),
809 max(layerRenderer->width() + layerRenderer->marginLeft() + layerRenderer->marginRight() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw),
810 max(layerRenderer->height() + layerRenderer->marginTop() + layerRenderer->marginBottom() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh));
811 } else {
812 layerRenderer = this;
813 rendererRect = borderBoxRect();
814 }
815 }
816
817 IntRect repaintRect;
818 IntPoint phase;
819 IntSize tileSize;
820 layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect.x(), rendererRect.y(), rendererRect.width(), rendererRect.height(), repaintRect, phase, tileSize);
821 layerRenderer->repaintRectangle(repaintRect);
822 if (repaintRect == rendererRect)
823 return true;
824 }
825 }
826 return false;
827 }
828
829 #if PLATFORM(MAC)
830
paintCustomHighlight(int tx,int ty,const AtomicString & type,bool behindText)831 void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText)
832 {
833 Frame* frame = document()->frame();
834 if (!frame)
835 return;
836 Page* page = frame->page();
837 if (!page)
838 return;
839
840 InlineBox* boxWrap = inlineBoxWrapper();
841 RootInlineBox* r = boxWrap ? boxWrap->root() : 0;
842 if (r) {
843 FloatRect rootRect(tx + r->x(), ty + r->selectionTop(), r->width(), r->selectionHeight());
844 FloatRect imageRect(tx + x(), rootRect.y(), width(), rootRect.height());
845 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false);
846 } else {
847 FloatRect imageRect(tx + x(), ty + y(), width(), height());
848 page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false);
849 }
850 }
851
852 #endif
853
pushContentsClip(PaintInfo & paintInfo,int tx,int ty)854 bool RenderBox::pushContentsClip(PaintInfo& paintInfo, int tx, int ty)
855 {
856 if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask)
857 return false;
858
859 bool isControlClip = hasControlClip();
860 bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer();
861
862 if (!isControlClip && !isOverflowClip)
863 return false;
864
865 if (paintInfo.phase == PaintPhaseOutline)
866 paintInfo.phase = PaintPhaseChildOutlines;
867 else if (paintInfo.phase == PaintPhaseChildBlockBackground) {
868 paintInfo.phase = PaintPhaseBlockBackground;
869 paintObject(paintInfo, tx, ty);
870 paintInfo.phase = PaintPhaseChildBlockBackgrounds;
871 }
872 IntRect clipRect(isControlClip ? controlClipRect(tx, ty) : overflowClipRect(tx, ty));
873 paintInfo.context->save();
874 if (style()->hasBorderRadius()) {
875 IntSize topLeft, topRight, bottomLeft, bottomRight;
876 IntRect borderRect = IntRect(tx, ty, width(), height());
877 style()->getBorderRadiiForRect(borderRect, topLeft, topRight, bottomLeft, bottomRight);
878
879 paintInfo.context->addRoundedRectClip(borderRect, topLeft, topRight, bottomLeft, bottomRight);
880 }
881
882 paintInfo.context->clip(clipRect);
883 return true;
884 }
885
popContentsClip(PaintInfo & paintInfo,PaintPhase originalPhase,int tx,int ty)886 void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, int tx, int ty)
887 {
888 ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer()));
889
890 paintInfo.context->restore();
891 if (originalPhase == PaintPhaseOutline) {
892 paintInfo.phase = PaintPhaseSelfOutline;
893 paintObject(paintInfo, tx, ty);
894 paintInfo.phase = originalPhase;
895 } else if (originalPhase == PaintPhaseChildBlockBackground)
896 paintInfo.phase = originalPhase;
897 }
898
overflowClipRect(int tx,int ty)899 IntRect RenderBox::overflowClipRect(int tx, int ty)
900 {
901 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property
902 // here.
903
904 int bLeft = borderLeft();
905 int bTop = borderTop();
906
907 int clipX = tx + bLeft;
908 int clipY = ty + bTop;
909 int clipWidth = width() - bLeft - borderRight();
910 int clipHeight = height() - bTop - borderBottom();
911
912 // Subtract out scrollbars if we have them.
913 if (layer()) {
914 clipWidth -= layer()->verticalScrollbarWidth();
915 clipHeight -= layer()->horizontalScrollbarHeight();
916 }
917
918 return IntRect(clipX, clipY, clipWidth, clipHeight);
919 }
920
clipRect(int tx,int ty)921 IntRect RenderBox::clipRect(int tx, int ty)
922 {
923 int clipX = tx;
924 int clipY = ty;
925 int clipWidth = width();
926 int clipHeight = height();
927
928 if (!style()->clipLeft().isAuto()) {
929 int c = style()->clipLeft().calcValue(width());
930 clipX += c;
931 clipWidth -= c;
932 }
933
934 if (!style()->clipRight().isAuto())
935 clipWidth -= width() - style()->clipRight().calcValue(width());
936
937 if (!style()->clipTop().isAuto()) {
938 int c = style()->clipTop().calcValue(height());
939 clipY += c;
940 clipHeight -= c;
941 }
942
943 if (!style()->clipBottom().isAuto())
944 clipHeight -= height() - style()->clipBottom().calcValue(height());
945
946 return IntRect(clipX, clipY, clipWidth, clipHeight);
947 }
948
containingBlockWidthForContent() const949 int RenderBox::containingBlockWidthForContent() const
950 {
951 RenderBlock* cb = containingBlock();
952 if (shrinkToAvoidFloats())
953 return cb->lineWidth(y(), false);
954 return cb->availableWidth();
955 }
956
mapLocalToContainer(RenderBoxModelObject * repaintContainer,bool fixed,bool useTransforms,TransformState & transformState) const957 void RenderBox::mapLocalToContainer(RenderBoxModelObject* repaintContainer, bool fixed, bool useTransforms, TransformState& transformState) const
958 {
959 if (repaintContainer == this)
960 return;
961
962 if (RenderView* v = view()) {
963 if (v->layoutStateEnabled() && !repaintContainer) {
964 LayoutState* layoutState = v->layoutState();
965 IntSize offset = layoutState->m_offset;
966 offset.expand(x(), y());
967 if (style()->position() == RelativePosition && layer())
968 offset += layer()->relativePositionOffset();
969 transformState.move(offset);
970 return;
971 }
972 }
973
974 bool containerSkipped;
975 RenderObject* o = container(repaintContainer, &containerSkipped);
976 if (!o)
977 return;
978
979 bool isFixedPos = style()->position() == FixedPosition;
980 bool hasTransform = hasLayer() && layer()->transform();
981 if (hasTransform) {
982 // If this box has a transform, it acts as a fixed position container for fixed descendants,
983 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
984 fixed &= isFixedPos;
985 } else
986 fixed |= isFixedPos;
987
988 IntSize containerOffset = offsetFromContainer(o);
989
990 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
991 if (useTransforms && shouldUseTransformFromContainer(o)) {
992 TransformationMatrix t;
993 getTransformFromContainer(o, containerOffset, t);
994 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
995 } else
996 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
997
998 if (containerSkipped) {
999 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1000 // to just subtract the delta between the repaintContainer and o.
1001 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1002 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1003 return;
1004 }
1005
1006 o->mapLocalToContainer(repaintContainer, fixed, useTransforms, transformState);
1007 }
1008
mapAbsoluteToLocalPoint(bool fixed,bool useTransforms,TransformState & transformState) const1009 void RenderBox::mapAbsoluteToLocalPoint(bool fixed, bool useTransforms, TransformState& transformState) const
1010 {
1011 // We don't expect absoluteToLocal() to be called during layout (yet)
1012 ASSERT(!view() || !view()->layoutStateEnabled());
1013
1014 bool isFixedPos = style()->position() == FixedPosition;
1015 bool hasTransform = hasLayer() && layer()->transform();
1016 if (hasTransform) {
1017 // If this box has a transform, it acts as a fixed position container for fixed descendants,
1018 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
1019 fixed &= isFixedPos;
1020 } else
1021 fixed |= isFixedPos;
1022
1023 RenderObject* o = container();
1024 if (!o)
1025 return;
1026
1027 o->mapAbsoluteToLocalPoint(fixed, useTransforms, transformState);
1028
1029 IntSize containerOffset = offsetFromContainer(o);
1030
1031 bool preserve3D = useTransforms && (o->style()->preserves3D() || style()->preserves3D());
1032 if (useTransforms && shouldUseTransformFromContainer(o)) {
1033 TransformationMatrix t;
1034 getTransformFromContainer(o, containerOffset, t);
1035 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1036 } else
1037 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1038 }
1039
offsetFromContainer(RenderObject * o) const1040 IntSize RenderBox::offsetFromContainer(RenderObject* o) const
1041 {
1042 ASSERT(o == container());
1043
1044 IntSize offset;
1045 if (isRelPositioned())
1046 offset += relativePositionOffset();
1047
1048 if (!isInline() || isReplaced()) {
1049 RenderBlock* cb;
1050 if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition
1051 && (cb = toRenderBlock(o))->hasColumns()) {
1052 IntRect rect(x(), y(), 1, 1);
1053 cb->adjustRectForColumns(rect);
1054 offset.expand(rect.x(), rect.y());
1055 } else
1056 offset.expand(x(), y());
1057 }
1058
1059 if (o->hasOverflowClip())
1060 offset -= toRenderBox(o)->layer()->scrolledContentOffset();
1061
1062 if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline())
1063 offset += toRenderInline(o)->relativePositionedInlineOffset(this);
1064
1065 return offset;
1066 }
1067
createInlineBox()1068 InlineBox* RenderBox::createInlineBox()
1069 {
1070 return new (renderArena()) InlineBox(this);
1071 }
1072
dirtyLineBoxes(bool fullLayout)1073 void RenderBox::dirtyLineBoxes(bool fullLayout)
1074 {
1075 if (m_inlineBoxWrapper) {
1076 if (fullLayout) {
1077 m_inlineBoxWrapper->destroy(renderArena());
1078 m_inlineBoxWrapper = 0;
1079 } else
1080 m_inlineBoxWrapper->dirtyLineBoxes();
1081 }
1082 }
1083
positionLineBox(InlineBox * box)1084 void RenderBox::positionLineBox(InlineBox* box)
1085 {
1086 if (isPositioned()) {
1087 // Cache the x position only if we were an INLINE type originally.
1088 bool wasInline = style()->isOriginalDisplayInlineType();
1089 if (wasInline && style()->hasStaticX()) {
1090 // The value is cached in the xPos of the box. We only need this value if
1091 // our object was inline originally, since otherwise it would have ended up underneath
1092 // the inlines.
1093 layer()->setStaticX(box->x());
1094 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
1095 } else if (!wasInline && style()->hasStaticY()) {
1096 // Our object was a block originally, so we make our normal flow position be
1097 // just below the line box (as though all the inlines that came before us got
1098 // wrapped in an anonymous block, which is what would have happened had we been
1099 // in flow). This value was cached in the y() of the box.
1100 layer()->setStaticY(box->y());
1101 setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
1102 }
1103
1104 // Nuke the box.
1105 box->remove();
1106 box->destroy(renderArena());
1107 } else if (isReplaced()) {
1108 setLocation(box->x(), box->y());
1109 m_inlineBoxWrapper = box;
1110 }
1111 }
1112
deleteLineBoxWrapper()1113 void RenderBox::deleteLineBoxWrapper()
1114 {
1115 if (m_inlineBoxWrapper) {
1116 if (!documentBeingDestroyed())
1117 m_inlineBoxWrapper->remove();
1118 m_inlineBoxWrapper->destroy(renderArena());
1119 m_inlineBoxWrapper = 0;
1120 }
1121 }
1122
clippedOverflowRectForRepaint(RenderBoxModelObject * repaintContainer)1123 IntRect RenderBox::clippedOverflowRectForRepaint(RenderBoxModelObject* repaintContainer)
1124 {
1125 if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
1126 return IntRect();
1127
1128 IntRect r = visibleOverflowRect();
1129
1130 RenderView* v = view();
1131 if (v) {
1132 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
1133 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
1134 r.move(v->layoutDelta());
1135 }
1136
1137 if (style()) {
1138 if (style()->hasAppearance())
1139 // The theme may wish to inflate the rect used when repainting.
1140 theme()->adjustRepaintRect(this, r);
1141
1142 // We have to use maximalOutlineSize() because a child might have an outline
1143 // that projects outside of our overflowRect.
1144 if (v) {
1145 ASSERT(style()->outlineSize() <= v->maximalOutlineSize());
1146 r.inflate(v->maximalOutlineSize());
1147 }
1148 }
1149 computeRectForRepaint(repaintContainer, r);
1150 return r;
1151 }
1152
computeRectForRepaint(RenderBoxModelObject * repaintContainer,IntRect & rect,bool fixed)1153 void RenderBox::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed)
1154 {
1155 if (RenderView* v = view()) {
1156 // LayoutState is only valid for root-relative repainting
1157 if (v->layoutStateEnabled() && !repaintContainer) {
1158 LayoutState* layoutState = v->layoutState();
1159
1160 if (layer() && layer()->transform())
1161 rect = layer()->transform()->mapRect(rect);
1162
1163 if (style()->position() == RelativePosition && layer())
1164 rect.move(layer()->relativePositionOffset());
1165
1166 rect.move(x(), y());
1167 rect.move(layoutState->m_offset);
1168 if (layoutState->m_clipped)
1169 rect.intersect(layoutState->m_clipRect);
1170 return;
1171 }
1172 }
1173
1174 if (hasReflection())
1175 rect.unite(reflectedRect(rect));
1176
1177 if (repaintContainer == this)
1178 return;
1179
1180 bool containerSkipped;
1181 RenderObject* o = container(repaintContainer, &containerSkipped);
1182 if (!o)
1183 return;
1184
1185 IntPoint topLeft = rect.location();
1186 topLeft.move(x(), y());
1187
1188 if (style()->position() == FixedPosition)
1189 fixed = true;
1190
1191 if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) {
1192 RenderBlock* cb = toRenderBlock(o);
1193 if (cb->hasColumns()) {
1194 IntRect repaintRect(topLeft, rect.size());
1195 cb->adjustRectForColumns(repaintRect);
1196 topLeft = repaintRect.location();
1197 rect = repaintRect;
1198 }
1199 }
1200
1201 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box
1202 // in the parent's coordinate space that encloses us.
1203 if (layer() && layer()->transform()) {
1204 fixed = false;
1205 rect = layer()->transform()->mapRect(rect);
1206 // FIXME: this clobbers topLeft adjustment done for multicol above
1207 topLeft = rect.location();
1208 topLeft.move(x(), y());
1209 }
1210
1211 if (style()->position() == AbsolutePosition && o->isRelPositioned() && o->isRenderInline())
1212 topLeft += toRenderInline(o)->relativePositionedInlineOffset(this);
1213 else if (style()->position() == RelativePosition && layer()) {
1214 // Apply the relative position offset when invalidating a rectangle. The layer
1215 // is translated, but the render box isn't, so we need to do this to get the
1216 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
1217 // flag on the RenderObject has been cleared, so use the one on the style().
1218 topLeft += layer()->relativePositionOffset();
1219 }
1220
1221 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
1222 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
1223 if (o->hasOverflowClip()) {
1224 RenderBox* containerBox = toRenderBox(o);
1225
1226 // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the
1227 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
1228 // anyway if its size does change.
1229 topLeft -= containerBox->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden.
1230
1231 IntRect repaintRect(topLeft, rect.size());
1232 IntRect boxRect(0, 0, containerBox->layer()->width(), containerBox->layer()->height());
1233 rect = intersection(repaintRect, boxRect);
1234 if (rect.isEmpty())
1235 return;
1236 } else
1237 rect.setLocation(topLeft);
1238
1239 if (containerSkipped) {
1240 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
1241 IntSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1242 rect.move(-containerOffset);
1243 return;
1244 }
1245
1246 o->computeRectForRepaint(repaintContainer, rect, fixed);
1247 }
1248
repaintDuringLayoutIfMoved(const IntRect & rect)1249 void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect)
1250 {
1251 int newX = x();
1252 int newY = y();
1253 int newWidth = width();
1254 int newHeight = height();
1255 if (rect.x() != newX || rect.y() != newY) {
1256 // The child moved. Invalidate the object's old and new positions. We have to do this
1257 // since the object may not have gotten a layout.
1258 m_frameRect = rect;
1259 repaint();
1260 repaintOverhangingFloats(true);
1261 m_frameRect = IntRect(newX, newY, newWidth, newHeight);
1262 repaint();
1263 repaintOverhangingFloats(true);
1264 }
1265 }
1266
calcWidth()1267 void RenderBox::calcWidth()
1268 {
1269 #ifdef ANDROID_LAYOUT
1270 if (view()->frameView()) {
1271 const Settings* settings = document()->settings();
1272 ASSERT(settings);
1273 if (settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) {
1274 m_visibleWidth = view()->frameView()->screenWidth();
1275 }
1276 }
1277 #endif
1278
1279 if (isPositioned()) {
1280 calcAbsoluteHorizontal();
1281 return;
1282 }
1283
1284 // If layout is limited to a subtree, the subtree root's width does not change.
1285 if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this)
1286 return;
1287
1288 // The parent box is flexing us, so it has increased or decreased our
1289 // width. Use the width from the style context.
1290 if (hasOverrideSize() && parent()->style()->boxOrient() == HORIZONTAL
1291 && parent()->isFlexibleBox() && parent()->isFlexingChildren()) {
1292 setWidth(overrideSize());
1293 return;
1294 }
1295
1296 bool inVerticalBox = parent()->isFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL);
1297 bool stretching = (parent()->style()->boxAlign() == BSTRETCH);
1298 bool treatAsReplaced = shouldCalculateSizeAsReplaced() && (!inVerticalBox || !stretching);
1299
1300 Length w = (treatAsReplaced) ? Length(calcReplacedWidth(), Fixed) : style()->width();
1301
1302 RenderBlock* cb = containingBlock();
1303 int containerWidth = max(0, containingBlockWidthForContent());
1304
1305 Length marginLeft = style()->marginLeft();
1306 Length marginRight = style()->marginRight();
1307
1308 if (isInline() && !isInlineBlockOrInlineTable()) {
1309 // just calculate margins
1310 m_marginLeft = marginLeft.calcMinValue(containerWidth);
1311 m_marginRight = marginRight.calcMinValue(containerWidth);
1312 #ifdef ANDROID_LAYOUT
1313 if (treatAsReplaced) {
1314 #else
1315 if (treatAsReplaced)
1316 #endif
1317 setWidth(max(w.value() + borderLeft() + borderRight() + paddingLeft() + paddingRight(), minPrefWidth()));
1318
1319 #ifdef ANDROID_LAYOUT
1320 // in SSR mode with replaced box, if the box width is wider than the container width,
1321 // it will be shrinked to fit to the container.
1322 if (containerWidth && (width() + m_marginLeft + m_marginRight) > containerWidth &&
1323 document()->frame()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
1324 m_marginLeft = m_marginRight = 0;
1325 setWidth(containerWidth);
1326 m_minPrefWidth = m_maxPrefWidth = containerWidth;
1327 }
1328 }
1329 #endif
1330 return;
1331 }
1332
1333 // Width calculations
1334 if (treatAsReplaced)
1335 setWidth(w.value() + borderLeft() + borderRight() + paddingLeft() + paddingRight());
1336 else {
1337 // Calculate Width
1338 setWidth(calcWidthUsing(Width, containerWidth));
1339
1340 // Calculate MaxWidth
1341 if (!style()->maxWidth().isUndefined()) {
1342 int maxW = calcWidthUsing(MaxWidth, containerWidth);
1343 if (width() > maxW) {
1344 setWidth(maxW);
1345 w = style()->maxWidth();
1346 }
1347 }
1348
1349 // Calculate MinWidth
1350 int minW = calcWidthUsing(MinWidth, containerWidth);
1351 if (width() < minW) {
1352 setWidth(minW);
1353 w = style()->minWidth();
1354 }
1355 }
1356
1357 if (stretchesToMinIntrinsicWidth()) {
1358 setWidth(max(width(), minPrefWidth()));
1359 w = Length(width(), Fixed);
1360 }
1361
1362 // Margin calculations
1363 if (w.isAuto()) {
1364 m_marginLeft = marginLeft.calcMinValue(containerWidth);
1365 m_marginRight = marginRight.calcMinValue(containerWidth);
1366 } else {
1367 m_marginLeft = 0;
1368 m_marginRight = 0;
1369 calcHorizontalMargins(marginLeft, marginRight, containerWidth);
1370 }
1371 #ifdef ANDROID_LAYOUT
1372 // in SSR mode with non-replaced box, we use ANDROID_SSR_MARGIN_PADDING for left/right margin.
1373 // If the box width is wider than the container width, it will be shrinked to fit to the container.
1374 if (containerWidth && !treatAsReplaced &&
1375 document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
1376 setWidth(width() + m_marginLeft + m_marginRight);
1377 m_marginLeft = m_marginLeft > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginLeft;
1378 m_marginRight = m_marginRight > ANDROID_SSR_MARGIN_PADDING ? ANDROID_SSR_MARGIN_PADDING : m_marginRight;
1379 if (width() > containerWidth) {
1380 m_minPrefWidth = m_maxPrefWidth = containerWidth-(m_marginLeft + m_marginRight);
1381 setWidth(m_minPrefWidth);
1382 } else
1383 setWidth(width() -(m_marginLeft + m_marginRight));
1384 }
1385 #endif
1386
1387 if (containerWidth && containerWidth != (width() + m_marginLeft + m_marginRight)
1388 && !isFloating() && !isInline() && !cb->isFlexibleBox()) {
1389 if (cb->style()->direction() == LTR)
1390 m_marginRight = containerWidth - width() - m_marginLeft;
1391 else
1392 m_marginLeft = containerWidth - width() - m_marginRight;
1393 }
1394 }
1395
calcWidthUsing(WidthType widthType,int cw)1396 int RenderBox::calcWidthUsing(WidthType widthType, int cw)
1397 {
1398 int widthResult = width();
1399 Length w;
1400 if (widthType == Width)
1401 w = style()->width();
1402 else if (widthType == MinWidth)
1403 w = style()->minWidth();
1404 else
1405 w = style()->maxWidth();
1406
1407 if (w.isIntrinsicOrAuto()) {
1408 int marginLeft = style()->marginLeft().calcMinValue(cw);
1409 int marginRight = style()->marginRight().calcMinValue(cw);
1410 if (cw)
1411 widthResult = cw - marginLeft - marginRight;
1412
1413 if (sizesToIntrinsicWidth(widthType)) {
1414 widthResult = max(widthResult, minPrefWidth());
1415 widthResult = min(widthResult, maxPrefWidth());
1416 }
1417 } else
1418 widthResult = calcBorderBoxWidth(w.calcValue(cw));
1419
1420 return widthResult;
1421 }
1422
sizesToIntrinsicWidth(WidthType widthType) const1423 bool RenderBox::sizesToIntrinsicWidth(WidthType widthType) const
1424 {
1425 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
1426 // but they allow text to sit on the same line as the marquee.
1427 if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee()))
1428 return true;
1429
1430 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both
1431 // min-width and width. max-width is only clamped if it is also intrinsic.
1432 Length width = (widthType == MaxWidth) ? style()->maxWidth() : style()->width();
1433 if (width.type() == Intrinsic)
1434 return true;
1435
1436 // Children of a horizontal marquee do not fill the container by default.
1437 // FIXME: Need to deal with MAUTO value properly. It could be vertical.
1438 if (parent()->style()->overflowX() == OMARQUEE) {
1439 EMarqueeDirection dir = parent()->style()->marqueeDirection();
1440 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
1441 return true;
1442 }
1443
1444 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes
1445 // that don't stretch their kids lay out their children at their intrinsic widths.
1446 if (parent()->isFlexibleBox()
1447 && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
1448 return true;
1449
1450 return false;
1451 }
1452
calcHorizontalMargins(const Length & marginLeft,const Length & marginRight,int containerWidth)1453 void RenderBox::calcHorizontalMargins(const Length& marginLeft, const Length& marginRight, int containerWidth)
1454 {
1455 if (isFloating() || isInline()) {
1456 // Inline blocks/tables and floats don't have their margins increased.
1457 m_marginLeft = marginLeft.calcMinValue(containerWidth);
1458 m_marginRight = marginRight.calcMinValue(containerWidth);
1459 return;
1460 }
1461
1462 if ((marginLeft.isAuto() && marginRight.isAuto() && width() < containerWidth)
1463 || (!marginLeft.isAuto() && !marginRight.isAuto() && containingBlock()->style()->textAlign() == WEBKIT_CENTER)) {
1464 m_marginLeft = max(0, (containerWidth - width()) / 2);
1465 m_marginRight = containerWidth - width() - m_marginLeft;
1466 } else if ((marginRight.isAuto() && width() < containerWidth)
1467 || (!marginLeft.isAuto() && containingBlock()->style()->direction() == RTL && containingBlock()->style()->textAlign() == WEBKIT_LEFT)) {
1468 m_marginLeft = marginLeft.calcValue(containerWidth);
1469 m_marginRight = containerWidth - width() - m_marginLeft;
1470 } else if ((marginLeft.isAuto() && width() < containerWidth)
1471 || (!marginRight.isAuto() && containingBlock()->style()->direction() == LTR && containingBlock()->style()->textAlign() == WEBKIT_RIGHT)) {
1472 m_marginRight = marginRight.calcValue(containerWidth);
1473 m_marginLeft = containerWidth - width() - m_marginRight;
1474 } else {
1475 // This makes auto margins 0 if we failed a width() < containerWidth test above (css2.1, 10.3.3).
1476 m_marginLeft = marginLeft.calcMinValue(containerWidth);
1477 m_marginRight = marginRight.calcMinValue(containerWidth);
1478 }
1479 }
1480
calcHeight()1481 void RenderBox::calcHeight()
1482 {
1483 // Cell height is managed by the table and inline non-replaced elements do not support a height property.
1484 if (isTableCell() || (isInline() && !isReplaced()))
1485 return;
1486
1487 Length h;
1488 if (isPositioned())
1489 calcAbsoluteVertical();
1490 else {
1491 calcVerticalMargins();
1492
1493 // For tables, calculate margins only.
1494 if (isTable())
1495 return;
1496
1497 bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL;
1498 bool stretching = parent()->style()->boxAlign() == BSTRETCH;
1499 bool treatAsReplaced = shouldCalculateSizeAsReplaced() && (!inHorizontalBox || !stretching);
1500 bool checkMinMaxHeight = false;
1501
1502 // The parent box is flexing us, so it has increased or decreased our height. We have to
1503 // grab our cached flexible height.
1504 if (hasOverrideSize() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL
1505 && parent()->isFlexingChildren())
1506 h = Length(overrideSize() - borderTop() - borderBottom() - paddingTop() - paddingBottom(), Fixed);
1507 else if (treatAsReplaced)
1508 h = Length(calcReplacedHeight(), Fixed);
1509 else {
1510 h = style()->height();
1511 checkMinMaxHeight = true;
1512 }
1513
1514 // Block children of horizontal flexible boxes fill the height of the box.
1515 if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL
1516 && parent()->isStretchingChildren()) {
1517 h = Length(parentBox()->contentHeight() - marginTop() - marginBottom() -
1518 borderTop() - paddingTop() - borderBottom() - paddingBottom(), Fixed);
1519 checkMinMaxHeight = false;
1520 }
1521
1522 int heightResult;
1523 if (checkMinMaxHeight) {
1524 #ifdef ANDROID_LAYOUT
1525 // in SSR mode, ignore CSS height as layout is so different
1526 if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR)
1527 heightResult = -1;
1528 else
1529 #endif
1530 heightResult = calcHeightUsing(style()->height());
1531 if (heightResult == -1)
1532 heightResult = height();
1533 int minH = calcHeightUsing(style()->minHeight()); // Leave as -1 if unset.
1534 int maxH = style()->maxHeight().isUndefined() ? heightResult : calcHeightUsing(style()->maxHeight());
1535 if (maxH == -1)
1536 maxH = heightResult;
1537 heightResult = min(maxH, heightResult);
1538 heightResult = max(minH, heightResult);
1539 } else {
1540 // The only times we don't check min/max height are when a fixed length has
1541 // been given as an override. Just use that. The value has already been adjusted
1542 // for box-sizing.
1543 heightResult = h.value() + borderTop() + borderBottom() + paddingTop() + paddingBottom();
1544 }
1545
1546 setHeight(heightResult);
1547 }
1548
1549 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the
1550 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height
1551 // is specified. When we're printing, we also need this quirk if the body or root has a percentage
1552 // height since we don't set a height in RenderView when we're printing. So without this quirk, the
1553 // height has nothing to be a percentage of, and it ends up being 0. That is bad.
1554 bool printingNeedsBaseHeight = document()->printing() && h.isPercent()
1555 && (isRoot() || (isBody() && document()->documentElement()->renderer()->style()->height().isPercent()));
1556 if (stretchesToViewHeight() || printingNeedsBaseHeight) {
1557 int margins = collapsedMarginTop() + collapsedMarginBottom();
1558 int visHeight = document()->printing() ? view()->frameView()->visibleHeight() : view()->viewHeight();
1559 if (isRoot())
1560 setHeight(max(height(), visHeight - margins));
1561 else {
1562 int marginsBordersPadding = margins + parentBox()->marginTop() + parentBox()->marginBottom()
1563 + parentBox()->borderTop() + parentBox()->borderBottom()
1564 + parentBox()->paddingTop() + parentBox()->paddingBottom();
1565 setHeight(max(height(), visHeight - marginsBordersPadding));
1566 }
1567 }
1568 }
1569
calcHeightUsing(const Length & h)1570 int RenderBox::calcHeightUsing(const Length& h)
1571 {
1572 int height = -1;
1573 if (!h.isAuto()) {
1574 if (h.isFixed())
1575 height = h.value();
1576 else if (h.isPercent())
1577 height = calcPercentageHeight(h);
1578 if (height != -1) {
1579 height = calcBorderBoxHeight(height);
1580 return height;
1581 }
1582 }
1583 return height;
1584 }
1585
calcPercentageHeight(const Length & height)1586 int RenderBox::calcPercentageHeight(const Length& height)
1587 {
1588 int result = -1;
1589 bool skippedAutoHeightContainingBlock = false;
1590 RenderBlock* cb = containingBlock();
1591 if (style()->htmlHacks()) {
1592 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing
1593 // block that may have a specified height and then use it. In strict mode, this violates the
1594 // specification, which states that percentage heights just revert to auto if the containing
1595 // block has an auto height.
1596 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->height().isAuto()) {
1597 skippedAutoHeightContainingBlock = true;
1598 cb = cb->containingBlock();
1599 cb->addPercentHeightDescendant(this);
1600 }
1601 }
1602
1603 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height
1604 // explicitly specified that can be used for any percentage computations.
1605 bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->height().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto()));
1606
1607 bool includeBorderPadding = isTable();
1608
1609 // Table cells violate what the CSS spec says to do with heights. Basically we
1610 // don't care if the cell specified a height or not. We just always make ourselves
1611 // be a percentage of the cell's current content height.
1612 if (cb->isTableCell()) {
1613 if (!skippedAutoHeightContainingBlock) {
1614 result = cb->overrideSize();
1615 if (result == -1) {
1616 // Normally we would let the cell size intrinsically, but scrolling overflow has to be
1617 // treated differently, since WinIE lets scrolled overflow regions shrink as needed.
1618 // While we can't get all cases right, we can at least detect when the cell has a specified
1619 // height or when the table has a specified height. In these cases we want to initially have
1620 // no size and allow the flexing of the table or the cell to its specified height to cause us
1621 // to grow to fill the space. This could end up being wrong in some cases, but it is
1622 // preferable to the alternative (sizing intrinsically and making the row end up too big).
1623 RenderTableCell* cell = toRenderTableCell(cb);
1624 if (scrollsOverflowY() && (!cell->style()->height().isAuto() || !cell->table()->style()->height().isAuto()))
1625 return 0;
1626 return -1;
1627 }
1628 includeBorderPadding = true;
1629 }
1630 }
1631 // Otherwise we only use our percentage height if our containing block had a specified
1632 // height.
1633 else if (cb->style()->height().isFixed())
1634 result = cb->calcContentBoxHeight(cb->style()->height().value());
1635 else if (cb->style()->height().isPercent() && !isPositionedWithSpecifiedHeight) {
1636 // We need to recur and compute the percentage height for our containing block.
1637 result = cb->calcPercentageHeight(cb->style()->height());
1638 if (result != -1)
1639 result = cb->calcContentBoxHeight(result);
1640 } else if (cb->isRenderView() || (cb->isBody() && style()->htmlHacks()) || isPositionedWithSpecifiedHeight) {
1641 // Don't allow this to affect the block' height() member variable, since this
1642 // can get called while the block is still laying out its kids.
1643 int oldHeight = cb->height();
1644 cb->calcHeight();
1645 result = cb->contentHeight();
1646 cb->setHeight(oldHeight);
1647 } else if (cb->isRoot() && isPositioned())
1648 // Match the positioned objects behavior, which is that positioned objects will fill their viewport
1649 // always. Note we could only hit this case by recurring into calcPercentageHeight on a positioned containing block.
1650 result = cb->calcContentBoxHeight(cb->availableHeight());
1651
1652 if (result != -1) {
1653 result = height.calcValue(result);
1654 if (includeBorderPadding) {
1655 // It is necessary to use the border-box to match WinIE's broken
1656 // box model. This is essential for sizing inside
1657 // table cells using percentage heights.
1658 result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom());
1659 result = max(0, result);
1660 }
1661 }
1662 return result;
1663 }
1664
calcReplacedWidth(bool includeMaxWidth) const1665 int RenderBox::calcReplacedWidth(bool includeMaxWidth) const
1666 {
1667 int width = calcReplacedWidthUsing(style()->width());
1668 int minW = calcReplacedWidthUsing(style()->minWidth());
1669 int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth());
1670
1671 return max(minW, min(width, maxW));
1672 }
1673
calcReplacedWidthUsing(Length width) const1674 int RenderBox::calcReplacedWidthUsing(Length width) const
1675 {
1676 switch (width.type()) {
1677 case Fixed:
1678 return calcContentBoxWidth(width.value());
1679 case Percent: {
1680 const int cw = isPositioned() ? containingBlockWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockWidthForContent();
1681 if (cw > 0)
1682 return calcContentBoxWidth(width.calcMinValue(cw));
1683 }
1684 // fall through
1685 default:
1686 return intrinsicSize().width();
1687 }
1688 }
1689
calcReplacedHeight() const1690 int RenderBox::calcReplacedHeight() const
1691 {
1692 int height = calcReplacedHeightUsing(style()->height());
1693 int minH = calcReplacedHeightUsing(style()->minHeight());
1694 int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight());
1695
1696 return max(minH, min(height, maxH));
1697 }
1698
calcReplacedHeightUsing(Length height) const1699 int RenderBox::calcReplacedHeightUsing(Length height) const
1700 {
1701 switch (height.type()) {
1702 case Fixed:
1703 return calcContentBoxHeight(height.value());
1704 case Percent:
1705 {
1706 RenderObject* cb = isPositioned() ? container() : containingBlock();
1707 while (cb->isAnonymous()) {
1708 cb = cb->containingBlock();
1709 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this));
1710 }
1711
1712 if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) {
1713 ASSERT(cb->isRenderBlock());
1714 RenderBlock* block = toRenderBlock(cb);
1715 int oldHeight = block->height();
1716 block->calcHeight();
1717 int newHeight = block->calcContentBoxHeight(block->contentHeight());
1718 block->setHeight(oldHeight);
1719 return calcContentBoxHeight(height.calcValue(newHeight));
1720 }
1721
1722 int availableHeight = isPositioned() ? containingBlockHeightForPositioned(toRenderBoxModelObject(cb)) : toRenderBox(cb)->availableHeight();
1723
1724 // It is necessary to use the border-box to match WinIE's broken
1725 // box model. This is essential for sizing inside
1726 // table cells using percentage heights.
1727 if (cb->isTableCell() && (cb->style()->height().isAuto() || cb->style()->height().isPercent())) {
1728 // Don't let table cells squeeze percent-height replaced elements
1729 // <http://bugs.webkit.org/show_bug.cgi?id=15359>
1730 availableHeight = max(availableHeight, intrinsicSize().height());
1731 return height.calcValue(availableHeight - (borderTop() + borderBottom()
1732 + paddingTop() + paddingBottom()));
1733 }
1734
1735 return calcContentBoxHeight(height.calcValue(availableHeight));
1736 }
1737 default:
1738 return intrinsicSize().height();
1739 }
1740 }
1741
availableHeight() const1742 int RenderBox::availableHeight() const
1743 {
1744 return availableHeightUsing(style()->height());
1745 }
1746
availableHeightUsing(const Length & h) const1747 int RenderBox::availableHeightUsing(const Length& h) const
1748 {
1749 if (h.isFixed())
1750 return calcContentBoxHeight(h.value());
1751
1752 if (isRenderView())
1753 return toRenderView(this)->frameView()->visibleHeight();
1754
1755 // We need to stop here, since we don't want to increase the height of the table
1756 // artificially. We're going to rely on this cell getting expanded to some new
1757 // height, and then when we lay out again we'll use the calculation below.
1758 if (isTableCell() && (h.isAuto() || h.isPercent()))
1759 return overrideSize() - (borderLeft() + borderRight() + paddingLeft() + paddingRight());
1760
1761 if (h.isPercent())
1762 return calcContentBoxHeight(h.calcValue(containingBlock()->availableHeight()));
1763
1764 if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) {
1765 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this));
1766 int oldHeight = block->height();
1767 block->calcHeight();
1768 int newHeight = block->calcContentBoxHeight(block->contentHeight());
1769 block->setHeight(oldHeight);
1770 return calcContentBoxHeight(newHeight);
1771 }
1772
1773 return containingBlock()->availableHeight();
1774 }
1775
calcVerticalMargins()1776 void RenderBox::calcVerticalMargins()
1777 {
1778 if (isTableCell()) {
1779 m_marginTop = 0;
1780 m_marginBottom = 0;
1781 return;
1782 }
1783
1784 // margins are calculated with respect to the _width_ of
1785 // the containing block (8.3)
1786 int cw = containingBlock()->contentWidth();
1787
1788 m_marginTop = style()->marginTop().calcMinValue(cw);
1789 m_marginBottom = style()->marginBottom().calcMinValue(cw);
1790 }
1791
containingBlockWidthForPositioned(const RenderBoxModelObject * containingBlock) const1792 int RenderBox::containingBlockWidthForPositioned(const RenderBoxModelObject* containingBlock) const
1793 {
1794 if (containingBlock->isBox()) {
1795 const RenderBox* containingBlockBox = toRenderBox(containingBlock);
1796 return containingBlockBox->width() - containingBlockBox->borderLeft() - containingBlockBox->borderRight() - containingBlockBox->verticalScrollbarWidth();
1797 }
1798
1799 ASSERT(containingBlock->isRenderInline() && containingBlock->isRelPositioned());
1800
1801 const RenderInline* flow = toRenderInline(containingBlock);
1802 InlineFlowBox* first = flow->firstLineBox();
1803 InlineFlowBox* last = flow->lastLineBox();
1804
1805 // If the containing block is empty, return a width of 0.
1806 if (!first || !last)
1807 return 0;
1808
1809 int fromLeft;
1810 int fromRight;
1811 if (containingBlock->style()->direction() == LTR) {
1812 fromLeft = first->x() + first->borderLeft();
1813 fromRight = last->x() + last->width() - last->borderRight();
1814 } else {
1815 fromRight = first->x() + first->width() - first->borderRight();
1816 fromLeft = last->x() + last->borderLeft();
1817 }
1818
1819 return max(0, (fromRight - fromLeft));
1820 }
1821
containingBlockHeightForPositioned(const RenderBoxModelObject * containingBlock) const1822 int RenderBox::containingBlockHeightForPositioned(const RenderBoxModelObject* containingBlock) const
1823 {
1824 int heightResult = 0;
1825 if (containingBlock->isBox())
1826 heightResult = toRenderBox(containingBlock)->height();
1827 else if (containingBlock->isRenderInline()) {
1828 ASSERT(containingBlock->isRelPositioned());
1829 heightResult = toRenderInline(containingBlock)->linesBoundingBox().height();
1830 }
1831 return heightResult - containingBlock->borderTop() - containingBlock->borderBottom();
1832 }
1833
calcAbsoluteHorizontal()1834 void RenderBox::calcAbsoluteHorizontal()
1835 {
1836 if (isReplaced()) {
1837 calcAbsoluteHorizontalReplaced();
1838 return;
1839 }
1840
1841 // QUESTIONS
1842 // FIXME 1: Which RenderObject's 'direction' property should used: the
1843 // containing block (cb) as the spec seems to imply, the parent (parent()) as
1844 // was previously done in calculating the static distances, or ourself, which
1845 // was also previously done for deciding what to override when you had
1846 // over-constrained margins? Also note that the container block is used
1847 // in similar situations in other parts of the RenderBox class (see calcWidth()
1848 // and calcHorizontalMargins()). For now we are using the parent for quirks
1849 // mode and the containing block for strict mode.
1850
1851 // FIXME 2: Should we still deal with these the cases of 'left' or 'right' having
1852 // the type 'static' in determining whether to calculate the static distance?
1853 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1.
1854
1855 // FIXME 3: Can perhaps optimize out cases when max-width/min-width are greater
1856 // than or less than the computed width(). Be careful of box-sizing and
1857 // percentage issues.
1858
1859 // The following is based off of the W3C Working Draft from April 11, 2006 of
1860 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
1861 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
1862 // (block-style-comments in this function and in calcAbsoluteHorizontalValues()
1863 // correspond to text from the spec)
1864
1865
1866 // We don't use containingBlock(), since we may be positioned by an enclosing
1867 // relative positioned inline.
1868 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
1869
1870 const int containerWidth = containingBlockWidthForPositioned(containerBlock);
1871
1872 // To match WinIE, in quirks mode use the parent's 'direction' property
1873 // instead of the the container block's.
1874 TextDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
1875
1876 const int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight();
1877 const Length marginLeft = style()->marginLeft();
1878 const Length marginRight = style()->marginRight();
1879 Length left = style()->left();
1880 Length right = style()->right();
1881
1882 /*---------------------------------------------------------------------------*\
1883 * For the purposes of this section and the next, the term "static position"
1884 * (of an element) refers, roughly, to the position an element would have had
1885 * in the normal flow. More precisely:
1886 *
1887 * * The static position for 'left' is the distance from the left edge of the
1888 * containing block to the left margin edge of a hypothetical box that would
1889 * have been the first box of the element if its 'position' property had
1890 * been 'static' and 'float' had been 'none'. The value is negative if the
1891 * hypothetical box is to the left of the containing block.
1892 * * The static position for 'right' is the distance from the right edge of the
1893 * containing block to the right margin edge of the same hypothetical box as
1894 * above. The value is positive if the hypothetical box is to the left of the
1895 * containing block's edge.
1896 *
1897 * But rather than actually calculating the dimensions of that hypothetical box,
1898 * user agents are free to make a guess at its probable position.
1899 *
1900 * For the purposes of calculating the static position, the containing block of
1901 * fixed positioned elements is the initial containing block instead of the
1902 * viewport, and all scrollable boxes should be assumed to be scrolled to their
1903 * origin.
1904 \*---------------------------------------------------------------------------*/
1905
1906 // see FIXME 2
1907 // Calculate the static distance if needed.
1908 if (left.isAuto() && right.isAuto()) {
1909 if (containerDirection == LTR) {
1910 // 'staticX' should already have been set through layout of the parent.
1911 int staticPosition = layer()->staticX() - containerBlock->borderLeft();
1912 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
1913 if (po->isBox())
1914 staticPosition += toRenderBox(po)->x();
1915 }
1916 left.setValue(Fixed, staticPosition);
1917 } else {
1918 RenderObject* po = parent();
1919 // 'staticX' should already have been set through layout of the parent.
1920 int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight();
1921 if (po->isBox())
1922 staticPosition -= toRenderBox(po)->width();
1923 for (; po && po != containerBlock; po = po->parent()) {
1924 if (po->isBox())
1925 staticPosition -= toRenderBox(po)->x();
1926 }
1927 right.setValue(Fixed, staticPosition);
1928 }
1929 }
1930
1931 // Calculate constraint equation values for 'width' case.
1932 int widthResult;
1933 int xResult;
1934 calcAbsoluteHorizontalValues(style()->width(), containerBlock, containerDirection,
1935 containerWidth, bordersPlusPadding,
1936 left, right, marginLeft, marginRight,
1937 widthResult, m_marginLeft, m_marginRight, xResult);
1938 setWidth(widthResult);
1939 setX(xResult);
1940
1941 // Calculate constraint equation values for 'max-width' case.
1942 if (!style()->maxWidth().isUndefined()) {
1943 int maxWidth;
1944 int maxMarginLeft;
1945 int maxMarginRight;
1946 int maxXPos;
1947
1948 calcAbsoluteHorizontalValues(style()->maxWidth(), containerBlock, containerDirection,
1949 containerWidth, bordersPlusPadding,
1950 left, right, marginLeft, marginRight,
1951 maxWidth, maxMarginLeft, maxMarginRight, maxXPos);
1952
1953 if (width() > maxWidth) {
1954 setWidth(maxWidth);
1955 m_marginLeft = maxMarginLeft;
1956 m_marginRight = maxMarginRight;
1957 m_frameRect.setX(maxXPos);
1958 }
1959 }
1960
1961 // Calculate constraint equation values for 'min-width' case.
1962 if (!style()->minWidth().isZero()) {
1963 int minWidth;
1964 int minMarginLeft;
1965 int minMarginRight;
1966 int minXPos;
1967
1968 calcAbsoluteHorizontalValues(style()->minWidth(), containerBlock, containerDirection,
1969 containerWidth, bordersPlusPadding,
1970 left, right, marginLeft, marginRight,
1971 minWidth, minMarginLeft, minMarginRight, minXPos);
1972
1973 if (width() < minWidth) {
1974 setWidth(minWidth);
1975 m_marginLeft = minMarginLeft;
1976 m_marginRight = minMarginRight;
1977 m_frameRect.setX(minXPos);
1978 }
1979 }
1980
1981 if (stretchesToMinIntrinsicWidth() && width() < minPrefWidth() - bordersPlusPadding) {
1982 calcAbsoluteHorizontalValues(Length(minPrefWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection,
1983 containerWidth, bordersPlusPadding,
1984 left, right, marginLeft, marginRight,
1985 widthResult, m_marginLeft, m_marginRight, xResult);
1986 setWidth(widthResult);
1987 setX(xResult);
1988 }
1989
1990 // Put width() into correct form.
1991 setWidth(width() + bordersPlusPadding);
1992 }
1993
calcAbsoluteHorizontalValues(Length width,const RenderBoxModelObject * containerBlock,TextDirection containerDirection,const int containerWidth,const int bordersPlusPadding,const Length left,const Length right,const Length marginLeft,const Length marginRight,int & widthValue,int & marginLeftValue,int & marginRightValue,int & xPos)1994 void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderBoxModelObject* containerBlock, TextDirection containerDirection,
1995 const int containerWidth, const int bordersPlusPadding,
1996 const Length left, const Length right, const Length marginLeft, const Length marginRight,
1997 int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos)
1998 {
1999 // 'left' and 'right' cannot both be 'auto' because one would of been
2000 // converted to the static position already
2001 ASSERT(!(left.isAuto() && right.isAuto()));
2002
2003 int leftValue = 0;
2004
2005 bool widthIsAuto = width.isIntrinsicOrAuto();
2006 bool leftIsAuto = left.isAuto();
2007 bool rightIsAuto = right.isAuto();
2008
2009 if (!leftIsAuto && !widthIsAuto && !rightIsAuto) {
2010 /*-----------------------------------------------------------------------*\
2011 * If none of the three is 'auto': If both 'margin-left' and 'margin-
2012 * right' are 'auto', solve the equation under the extra constraint that
2013 * the two margins get equal values, unless this would make them negative,
2014 * in which case when direction of the containing block is 'ltr' ('rtl'),
2015 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
2016 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
2017 * solve the equation for that value. If the values are over-constrained,
2018 * ignore the value for 'left' (in case the 'direction' property of the
2019 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
2020 * and solve for that value.
2021 \*-----------------------------------------------------------------------*/
2022 // NOTE: It is not necessary to solve for 'right' in the over constrained
2023 // case because the value is not used for any further calculations.
2024
2025 leftValue = left.calcValue(containerWidth);
2026 widthValue = calcContentBoxWidth(width.calcValue(containerWidth));
2027
2028 const int availableSpace = containerWidth - (leftValue + widthValue + right.calcValue(containerWidth) + bordersPlusPadding);
2029
2030 // Margins are now the only unknown
2031 if (marginLeft.isAuto() && marginRight.isAuto()) {
2032 // Both margins auto, solve for equality
2033 if (availableSpace >= 0) {
2034 marginLeftValue = availableSpace / 2; // split the difference
2035 marginRightValue = availableSpace - marginLeftValue; // account for odd valued differences
2036 } else {
2037 // see FIXME 1
2038 if (containerDirection == LTR) {
2039 marginLeftValue = 0;
2040 marginRightValue = availableSpace; // will be negative
2041 } else {
2042 marginLeftValue = availableSpace; // will be negative
2043 marginRightValue = 0;
2044 }
2045 }
2046 } else if (marginLeft.isAuto()) {
2047 // Solve for left margin
2048 marginRightValue = marginRight.calcValue(containerWidth);
2049 marginLeftValue = availableSpace - marginRightValue;
2050 } else if (marginRight.isAuto()) {
2051 // Solve for right margin
2052 marginLeftValue = marginLeft.calcValue(containerWidth);
2053 marginRightValue = availableSpace - marginLeftValue;
2054 } else {
2055 // Over-constrained, solve for left if direction is RTL
2056 marginLeftValue = marginLeft.calcValue(containerWidth);
2057 marginRightValue = marginRight.calcValue(containerWidth);
2058
2059 // see FIXME 1 -- used to be "this->style()->direction()"
2060 if (containerDirection == RTL)
2061 leftValue = (availableSpace + leftValue) - marginLeftValue - marginRightValue;
2062 }
2063 } else {
2064 /*--------------------------------------------------------------------*\
2065 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
2066 * to 0, and pick the one of the following six rules that applies.
2067 *
2068 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
2069 * width is shrink-to-fit. Then solve for 'left'
2070 *
2071 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
2072 * ------------------------------------------------------------------
2073 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
2074 * the 'direction' property of the containing block is 'ltr' set
2075 * 'left' to the static position, otherwise set 'right' to the
2076 * static position. Then solve for 'left' (if 'direction is 'rtl')
2077 * or 'right' (if 'direction' is 'ltr').
2078 * ------------------------------------------------------------------
2079 *
2080 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
2081 * width is shrink-to-fit . Then solve for 'right'
2082 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
2083 * for 'left'
2084 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
2085 * for 'width'
2086 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
2087 * for 'right'
2088 *
2089 * Calculation of the shrink-to-fit width is similar to calculating the
2090 * width of a table cell using the automatic table layout algorithm.
2091 * Roughly: calculate the preferred width by formatting the content
2092 * without breaking lines other than where explicit line breaks occur,
2093 * and also calculate the preferred minimum width, e.g., by trying all
2094 * possible line breaks. CSS 2.1 does not define the exact algorithm.
2095 * Thirdly, calculate the available width: this is found by solving
2096 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
2097 * to 0.
2098 *
2099 * Then the shrink-to-fit width is:
2100 * min(max(preferred minimum width, available width), preferred width).
2101 \*--------------------------------------------------------------------*/
2102 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
2103 // because the value is not used for any further calculations.
2104
2105 // Calculate margins, 'auto' margins are ignored.
2106 marginLeftValue = marginLeft.calcMinValue(containerWidth);
2107 marginRightValue = marginRight.calcMinValue(containerWidth);
2108
2109 const int availableSpace = containerWidth - (marginLeftValue + marginRightValue + bordersPlusPadding);
2110
2111 // FIXME: Is there a faster way to find the correct case?
2112 // Use rule/case that applies.
2113 if (leftIsAuto && widthIsAuto && !rightIsAuto) {
2114 // RULE 1: (use shrink-to-fit for width, and solve of left)
2115 int rightValue = right.calcValue(containerWidth);
2116
2117 // FIXME: would it be better to have shrink-to-fit in one step?
2118 int preferredWidth = maxPrefWidth() - bordersPlusPadding;
2119 int preferredMinWidth = minPrefWidth() - bordersPlusPadding;
2120 int availableWidth = availableSpace - rightValue;
2121 widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth);
2122 leftValue = availableSpace - (widthValue + rightValue);
2123 } else if (!leftIsAuto && widthIsAuto && rightIsAuto) {
2124 // RULE 3: (use shrink-to-fit for width, and no need solve of right)
2125 leftValue = left.calcValue(containerWidth);
2126
2127 // FIXME: would it be better to have shrink-to-fit in one step?
2128 int preferredWidth = maxPrefWidth() - bordersPlusPadding;
2129 int preferredMinWidth = minPrefWidth() - bordersPlusPadding;
2130 int availableWidth = availableSpace - leftValue;
2131 widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth);
2132 } else if (leftIsAuto && !width.isAuto() && !rightIsAuto) {
2133 // RULE 4: (solve for left)
2134 widthValue = calcContentBoxWidth(width.calcValue(containerWidth));
2135 leftValue = availableSpace - (widthValue + right.calcValue(containerWidth));
2136 } else if (!leftIsAuto && widthIsAuto && !rightIsAuto) {
2137 // RULE 5: (solve for width)
2138 leftValue = left.calcValue(containerWidth);
2139 widthValue = availableSpace - (leftValue + right.calcValue(containerWidth));
2140 } else if (!leftIsAuto&& !widthIsAuto && rightIsAuto) {
2141 // RULE 6: (no need solve for right)
2142 leftValue = left.calcValue(containerWidth);
2143 widthValue = calcContentBoxWidth(width.calcValue(containerWidth));
2144 }
2145 }
2146
2147 // Use computed values to calculate the horizontal position.
2148
2149 // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively
2150 // positioned, inline because right now, it is using the xPos
2151 // of the first line box when really it should use the last line box. When
2152 // this is fixed elsewhere, this block should be removed.
2153 if (containerBlock->isRenderInline() && containerBlock->style()->direction() == RTL) {
2154 const RenderInline* flow = toRenderInline(containerBlock);
2155 InlineFlowBox* firstLine = flow->firstLineBox();
2156 InlineFlowBox* lastLine = flow->lastLineBox();
2157 if (firstLine && lastLine && firstLine != lastLine) {
2158 xPos = leftValue + marginLeftValue + lastLine->borderLeft() + (lastLine->x() - firstLine->x());
2159 return;
2160 }
2161 }
2162
2163 xPos = leftValue + marginLeftValue + containerBlock->borderLeft();
2164 }
2165
calcAbsoluteVertical()2166 void RenderBox::calcAbsoluteVertical()
2167 {
2168 if (isReplaced()) {
2169 calcAbsoluteVerticalReplaced();
2170 return;
2171 }
2172
2173 // The following is based off of the W3C Working Draft from April 11, 2006 of
2174 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
2175 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
2176 // (block-style-comments in this function and in calcAbsoluteVerticalValues()
2177 // correspond to text from the spec)
2178
2179
2180 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2181 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2182
2183 const int containerHeight = containingBlockHeightForPositioned(containerBlock);
2184
2185 const int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom();
2186 const Length marginTop = style()->marginTop();
2187 const Length marginBottom = style()->marginBottom();
2188 Length top = style()->top();
2189 Length bottom = style()->bottom();
2190
2191 /*---------------------------------------------------------------------------*\
2192 * For the purposes of this section and the next, the term "static position"
2193 * (of an element) refers, roughly, to the position an element would have had
2194 * in the normal flow. More precisely, the static position for 'top' is the
2195 * distance from the top edge of the containing block to the top margin edge
2196 * of a hypothetical box that would have been the first box of the element if
2197 * its 'position' property had been 'static' and 'float' had been 'none'. The
2198 * value is negative if the hypothetical box is above the containing block.
2199 *
2200 * But rather than actually calculating the dimensions of that hypothetical
2201 * box, user agents are free to make a guess at its probable position.
2202 *
2203 * For the purposes of calculating the static position, the containing block
2204 * of fixed positioned elements is the initial containing block instead of
2205 * the viewport.
2206 \*---------------------------------------------------------------------------*/
2207
2208 // see FIXME 2
2209 // Calculate the static distance if needed.
2210 if (top.isAuto() && bottom.isAuto()) {
2211 // staticY should already have been set through layout of the parent()
2212 int staticTop = layer()->staticY() - containerBlock->borderTop();
2213 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
2214 if (po->isBox() && !po->isTableRow())
2215 staticTop += toRenderBox(po)->y();
2216 }
2217 top.setValue(Fixed, staticTop);
2218 }
2219
2220
2221 int h; // Needed to compute overflow.
2222 int y;
2223
2224 // Calculate constraint equation values for 'height' case.
2225 calcAbsoluteVerticalValues(style()->height(), containerBlock, containerHeight, bordersPlusPadding,
2226 top, bottom, marginTop, marginBottom,
2227 h, m_marginTop, m_marginBottom, y);
2228 setY(y);
2229
2230 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
2231 // see FIXME 3
2232
2233 // Calculate constraint equation values for 'max-height' case.
2234 if (!style()->maxHeight().isUndefined()) {
2235 int maxHeight;
2236 int maxMarginTop;
2237 int maxMarginBottom;
2238 int maxYPos;
2239
2240 calcAbsoluteVerticalValues(style()->maxHeight(), containerBlock, containerHeight, bordersPlusPadding,
2241 top, bottom, marginTop, marginBottom,
2242 maxHeight, maxMarginTop, maxMarginBottom, maxYPos);
2243
2244 if (h > maxHeight) {
2245 h = maxHeight;
2246 m_marginTop = maxMarginTop;
2247 m_marginBottom = maxMarginBottom;
2248 m_frameRect.setY(maxYPos);
2249 }
2250 }
2251
2252 // Calculate constraint equation values for 'min-height' case.
2253 if (!style()->minHeight().isZero()) {
2254 int minHeight;
2255 int minMarginTop;
2256 int minMarginBottom;
2257 int minYPos;
2258
2259 calcAbsoluteVerticalValues(style()->minHeight(), containerBlock, containerHeight, bordersPlusPadding,
2260 top, bottom, marginTop, marginBottom,
2261 minHeight, minMarginTop, minMarginBottom, minYPos);
2262
2263 if (h < minHeight) {
2264 h = minHeight;
2265 m_marginTop = minMarginTop;
2266 m_marginBottom = minMarginBottom;
2267 m_frameRect.setY(minYPos);
2268 }
2269 }
2270
2271 // Set final height value.
2272 setHeight(h + bordersPlusPadding);
2273 }
2274
calcAbsoluteVerticalValues(Length h,const RenderBoxModelObject * containerBlock,const int containerHeight,const int bordersPlusPadding,const Length top,const Length bottom,const Length marginTop,const Length marginBottom,int & heightValue,int & marginTopValue,int & marginBottomValue,int & yPos)2275 void RenderBox::calcAbsoluteVerticalValues(Length h, const RenderBoxModelObject* containerBlock,
2276 const int containerHeight, const int bordersPlusPadding,
2277 const Length top, const Length bottom, const Length marginTop, const Length marginBottom,
2278 int& heightValue, int& marginTopValue, int& marginBottomValue, int& yPos)
2279 {
2280 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
2281 // converted to the static position in calcAbsoluteVertical()
2282 ASSERT(!(top.isAuto() && bottom.isAuto()));
2283
2284 int contentHeight = height() - bordersPlusPadding;
2285
2286 int topValue = 0;
2287
2288 bool heightIsAuto = h.isAuto();
2289 bool topIsAuto = top.isAuto();
2290 bool bottomIsAuto = bottom.isAuto();
2291
2292 // Height is never unsolved for tables.
2293 if (isTable()) {
2294 h.setValue(Fixed, contentHeight);
2295 heightIsAuto = false;
2296 }
2297
2298 if (!topIsAuto && !heightIsAuto && !bottomIsAuto) {
2299 /*-----------------------------------------------------------------------*\
2300 * If none of the three are 'auto': If both 'margin-top' and 'margin-
2301 * bottom' are 'auto', solve the equation under the extra constraint that
2302 * the two margins get equal values. If one of 'margin-top' or 'margin-
2303 * bottom' is 'auto', solve the equation for that value. If the values
2304 * are over-constrained, ignore the value for 'bottom' and solve for that
2305 * value.
2306 \*-----------------------------------------------------------------------*/
2307 // NOTE: It is not necessary to solve for 'bottom' in the over constrained
2308 // case because the value is not used for any further calculations.
2309
2310 heightValue = calcContentBoxHeight(h.calcValue(containerHeight));
2311 topValue = top.calcValue(containerHeight);
2312
2313 const int availableSpace = containerHeight - (topValue + heightValue + bottom.calcValue(containerHeight) + bordersPlusPadding);
2314
2315 // Margins are now the only unknown
2316 if (marginTop.isAuto() && marginBottom.isAuto()) {
2317 // Both margins auto, solve for equality
2318 // NOTE: This may result in negative values.
2319 marginTopValue = availableSpace / 2; // split the difference
2320 marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences
2321 } else if (marginTop.isAuto()) {
2322 // Solve for top margin
2323 marginBottomValue = marginBottom.calcValue(containerHeight);
2324 marginTopValue = availableSpace - marginBottomValue;
2325 } else if (marginBottom.isAuto()) {
2326 // Solve for bottom margin
2327 marginTopValue = marginTop.calcValue(containerHeight);
2328 marginBottomValue = availableSpace - marginTopValue;
2329 } else {
2330 // Over-constrained, (no need solve for bottom)
2331 marginTopValue = marginTop.calcValue(containerHeight);
2332 marginBottomValue = marginBottom.calcValue(containerHeight);
2333 }
2334 } else {
2335 /*--------------------------------------------------------------------*\
2336 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
2337 * to 0, and pick the one of the following six rules that applies.
2338 *
2339 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
2340 * the height is based on the content, and solve for 'top'.
2341 *
2342 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
2343 * ------------------------------------------------------------------
2344 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
2345 * set 'top' to the static position, and solve for 'bottom'.
2346 * ------------------------------------------------------------------
2347 *
2348 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
2349 * the height is based on the content, and solve for 'bottom'.
2350 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
2351 * solve for 'top'.
2352 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
2353 * solve for 'height'.
2354 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
2355 * solve for 'bottom'.
2356 \*--------------------------------------------------------------------*/
2357 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
2358 // because the value is not used for any further calculations.
2359
2360 // Calculate margins, 'auto' margins are ignored.
2361 marginTopValue = marginTop.calcMinValue(containerHeight);
2362 marginBottomValue = marginBottom.calcMinValue(containerHeight);
2363
2364 const int availableSpace = containerHeight - (marginTopValue + marginBottomValue + bordersPlusPadding);
2365
2366 // Use rule/case that applies.
2367 if (topIsAuto && heightIsAuto && !bottomIsAuto) {
2368 // RULE 1: (height is content based, solve of top)
2369 heightValue = contentHeight;
2370 topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight));
2371 } else if (!topIsAuto && heightIsAuto && bottomIsAuto) {
2372 // RULE 3: (height is content based, no need solve of bottom)
2373 topValue = top.calcValue(containerHeight);
2374 heightValue = contentHeight;
2375 } else if (topIsAuto && !heightIsAuto && !bottomIsAuto) {
2376 // RULE 4: (solve of top)
2377 heightValue = calcContentBoxHeight(h.calcValue(containerHeight));
2378 topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight));
2379 } else if (!topIsAuto && heightIsAuto && !bottomIsAuto) {
2380 // RULE 5: (solve of height)
2381 topValue = top.calcValue(containerHeight);
2382 heightValue = max(0, availableSpace - (topValue + bottom.calcValue(containerHeight)));
2383 } else if (!topIsAuto && !heightIsAuto && bottomIsAuto) {
2384 // RULE 6: (no need solve of bottom)
2385 heightValue = calcContentBoxHeight(h.calcValue(containerHeight));
2386 topValue = top.calcValue(containerHeight);
2387 }
2388 }
2389
2390 // Use computed values to calculate the vertical position.
2391 yPos = topValue + marginTopValue + containerBlock->borderTop();
2392 }
2393
calcAbsoluteHorizontalReplaced()2394 void RenderBox::calcAbsoluteHorizontalReplaced()
2395 {
2396 // The following is based off of the W3C Working Draft from April 11, 2006 of
2397 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements"
2398 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
2399 // (block-style-comments in this function correspond to text from the spec and
2400 // the numbers correspond to numbers in spec)
2401
2402 // We don't use containingBlock(), since we may be positioned by an enclosing
2403 // relative positioned inline.
2404 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2405
2406 const int containerWidth = containingBlockWidthForPositioned(containerBlock);
2407
2408 // To match WinIE, in quirks mode use the parent's 'direction' property
2409 // instead of the the container block's.
2410 TextDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction();
2411
2412 // Variables to solve.
2413 Length left = style()->left();
2414 Length right = style()->right();
2415 Length marginLeft = style()->marginLeft();
2416 Length marginRight = style()->marginRight();
2417
2418
2419 /*-----------------------------------------------------------------------*\
2420 * 1. The used value of 'width' is determined as for inline replaced
2421 * elements.
2422 \*-----------------------------------------------------------------------*/
2423 // NOTE: This value of width is FINAL in that the min/max width calculations
2424 // are dealt with in calcReplacedWidth(). This means that the steps to produce
2425 // correct max/min in the non-replaced version, are not necessary.
2426 setWidth(calcReplacedWidth() + borderLeft() + borderRight() + paddingLeft() + paddingRight());
2427 const int availableSpace = containerWidth - width();
2428
2429 /*-----------------------------------------------------------------------*\
2430 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
2431 * of the containing block is 'ltr', set 'left' to the static position;
2432 * else if 'direction' is 'rtl', set 'right' to the static position.
2433 \*-----------------------------------------------------------------------*/
2434 // see FIXME 2
2435 if (left.isAuto() && right.isAuto()) {
2436 // see FIXME 1
2437 if (containerDirection == LTR) {
2438 // 'staticX' should already have been set through layout of the parent.
2439 int staticPosition = layer()->staticX() - containerBlock->borderLeft();
2440 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
2441 if (po->isBox())
2442 staticPosition += toRenderBox(po)->x();
2443 }
2444 left.setValue(Fixed, staticPosition);
2445 } else {
2446 RenderObject* po = parent();
2447 // 'staticX' should already have been set through layout of the parent.
2448 int staticPosition = layer()->staticX() + containerWidth + containerBlock->borderRight();
2449 for ( ; po && po != containerBlock; po = po->parent()) {
2450 if (po->isBox())
2451 staticPosition += toRenderBox(po)->x();
2452 }
2453 right.setValue(Fixed, staticPosition);
2454 }
2455 }
2456
2457 /*-----------------------------------------------------------------------*\
2458 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
2459 * or 'margin-right' with '0'.
2460 \*-----------------------------------------------------------------------*/
2461 if (left.isAuto() || right.isAuto()) {
2462 if (marginLeft.isAuto())
2463 marginLeft.setValue(Fixed, 0);
2464 if (marginRight.isAuto())
2465 marginRight.setValue(Fixed, 0);
2466 }
2467
2468 /*-----------------------------------------------------------------------*\
2469 * 4. If at this point both 'margin-left' and 'margin-right' are still
2470 * 'auto', solve the equation under the extra constraint that the two
2471 * margins must get equal values, unless this would make them negative,
2472 * in which case when the direction of the containing block is 'ltr'
2473 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
2474 * 'margin-right' ('margin-left').
2475 \*-----------------------------------------------------------------------*/
2476 int leftValue = 0;
2477 int rightValue = 0;
2478
2479 if (marginLeft.isAuto() && marginRight.isAuto()) {
2480 // 'left' and 'right' cannot be 'auto' due to step 3
2481 ASSERT(!(left.isAuto() && right.isAuto()));
2482
2483 leftValue = left.calcValue(containerWidth);
2484 rightValue = right.calcValue(containerWidth);
2485
2486 int difference = availableSpace - (leftValue + rightValue);
2487 if (difference > 0) {
2488 m_marginLeft = difference / 2; // split the difference
2489 m_marginRight = difference - m_marginLeft; // account for odd valued differences
2490 } else {
2491 // see FIXME 1
2492 if (containerDirection == LTR) {
2493 m_marginLeft = 0;
2494 m_marginRight = difference; // will be negative
2495 } else {
2496 m_marginLeft = difference; // will be negative
2497 m_marginRight = 0;
2498 }
2499 }
2500
2501 /*-----------------------------------------------------------------------*\
2502 * 5. If at this point there is an 'auto' left, solve the equation for
2503 * that value.
2504 \*-----------------------------------------------------------------------*/
2505 } else if (left.isAuto()) {
2506 m_marginLeft = marginLeft.calcValue(containerWidth);
2507 m_marginRight = marginRight.calcValue(containerWidth);
2508 rightValue = right.calcValue(containerWidth);
2509
2510 // Solve for 'left'
2511 leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight);
2512 } else if (right.isAuto()) {
2513 m_marginLeft = marginLeft.calcValue(containerWidth);
2514 m_marginRight = marginRight.calcValue(containerWidth);
2515 leftValue = left.calcValue(containerWidth);
2516
2517 // Solve for 'right'
2518 rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight);
2519 } else if (marginLeft.isAuto()) {
2520 m_marginRight = marginRight.calcValue(containerWidth);
2521 leftValue = left.calcValue(containerWidth);
2522 rightValue = right.calcValue(containerWidth);
2523
2524 // Solve for 'margin-left'
2525 m_marginLeft = availableSpace - (leftValue + rightValue + m_marginRight);
2526 } else if (marginRight.isAuto()) {
2527 m_marginLeft = marginLeft.calcValue(containerWidth);
2528 leftValue = left.calcValue(containerWidth);
2529 rightValue = right.calcValue(containerWidth);
2530
2531 // Solve for 'margin-right'
2532 m_marginRight = availableSpace - (leftValue + rightValue + m_marginLeft);
2533 } else {
2534 // Nothing is 'auto', just calculate the values.
2535 m_marginLeft = marginLeft.calcValue(containerWidth);
2536 m_marginRight = marginRight.calcValue(containerWidth);
2537 rightValue = right.calcValue(containerWidth);
2538 leftValue = left.calcValue(containerWidth);
2539 }
2540
2541 /*-----------------------------------------------------------------------*\
2542 * 6. If at this point the values are over-constrained, ignore the value
2543 * for either 'left' (in case the 'direction' property of the
2544 * containing block is 'rtl') or 'right' (in case 'direction' is
2545 * 'ltr') and solve for that value.
2546 \*-----------------------------------------------------------------------*/
2547 // NOTE: It is not necessary to solve for 'right' when the direction is
2548 // LTR because the value is not used.
2549 int totalWidth = width() + leftValue + rightValue + m_marginLeft + m_marginRight;
2550 if (totalWidth > containerWidth && (containerDirection == RTL))
2551 leftValue = containerWidth - (totalWidth - leftValue);
2552
2553 // Use computed values to calculate the horizontal position.
2554
2555 // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively
2556 // positioned, inline containing block because right now, it is using the xPos
2557 // of the first line box when really it should use the last line box. When
2558 // this is fixed elsewhere, this block should be removed.
2559 if (containerBlock->isRenderInline() && containerBlock->style()->direction() == RTL) {
2560 const RenderInline* flow = toRenderInline(containerBlock);
2561 InlineFlowBox* firstLine = flow->firstLineBox();
2562 InlineFlowBox* lastLine = flow->lastLineBox();
2563 if (firstLine && lastLine && firstLine != lastLine) {
2564 m_frameRect.setX(leftValue + m_marginLeft + lastLine->borderLeft() + (lastLine->x() - firstLine->x()));
2565 return;
2566 }
2567 }
2568
2569 m_frameRect.setX(leftValue + m_marginLeft + containerBlock->borderLeft());
2570 }
2571
calcAbsoluteVerticalReplaced()2572 void RenderBox::calcAbsoluteVerticalReplaced()
2573 {
2574 // The following is based off of the W3C Working Draft from April 11, 2006 of
2575 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements"
2576 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
2577 // (block-style-comments in this function correspond to text from the spec and
2578 // the numbers correspond to numbers in spec)
2579
2580 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
2581 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
2582
2583 const int containerHeight = containingBlockHeightForPositioned(containerBlock);
2584
2585 // Variables to solve.
2586 Length top = style()->top();
2587 Length bottom = style()->bottom();
2588 Length marginTop = style()->marginTop();
2589 Length marginBottom = style()->marginBottom();
2590
2591
2592 /*-----------------------------------------------------------------------*\
2593 * 1. The used value of 'height' is determined as for inline replaced
2594 * elements.
2595 \*-----------------------------------------------------------------------*/
2596 // NOTE: This value of height is FINAL in that the min/max height calculations
2597 // are dealt with in calcReplacedHeight(). This means that the steps to produce
2598 // correct max/min in the non-replaced version, are not necessary.
2599 setHeight(calcReplacedHeight() + borderTop() + borderBottom() + paddingTop() + paddingBottom());
2600 const int availableSpace = containerHeight - height();
2601
2602 /*-----------------------------------------------------------------------*\
2603 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
2604 * with the element's static position.
2605 \*-----------------------------------------------------------------------*/
2606 // see FIXME 2
2607 if (top.isAuto() && bottom.isAuto()) {
2608 // staticY should already have been set through layout of the parent().
2609 int staticTop = layer()->staticY() - containerBlock->borderTop();
2610 for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) {
2611 if (po->isBox() && !po->isTableRow())
2612 staticTop += toRenderBox(po)->y();
2613 }
2614 top.setValue(Fixed, staticTop);
2615 }
2616
2617 /*-----------------------------------------------------------------------*\
2618 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
2619 * 'margin-bottom' with '0'.
2620 \*-----------------------------------------------------------------------*/
2621 // FIXME: The spec. says that this step should only be taken when bottom is
2622 // auto, but if only top is auto, this makes step 4 impossible.
2623 if (top.isAuto() || bottom.isAuto()) {
2624 if (marginTop.isAuto())
2625 marginTop.setValue(Fixed, 0);
2626 if (marginBottom.isAuto())
2627 marginBottom.setValue(Fixed, 0);
2628 }
2629
2630 /*-----------------------------------------------------------------------*\
2631 * 4. If at this point both 'margin-top' and 'margin-bottom' are still
2632 * 'auto', solve the equation under the extra constraint that the two
2633 * margins must get equal values.
2634 \*-----------------------------------------------------------------------*/
2635 int topValue = 0;
2636 int bottomValue = 0;
2637
2638 if (marginTop.isAuto() && marginBottom.isAuto()) {
2639 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined.
2640 ASSERT(!(top.isAuto() || bottom.isAuto()));
2641
2642 topValue = top.calcValue(containerHeight);
2643 bottomValue = bottom.calcValue(containerHeight);
2644
2645 int difference = availableSpace - (topValue + bottomValue);
2646 // NOTE: This may result in negative values.
2647 m_marginTop = difference / 2; // split the difference
2648 m_marginBottom = difference - m_marginTop; // account for odd valued differences
2649
2650 /*-----------------------------------------------------------------------*\
2651 * 5. If at this point there is only one 'auto' left, solve the equation
2652 * for that value.
2653 \*-----------------------------------------------------------------------*/
2654 } else if (top.isAuto()) {
2655 m_marginTop = marginTop.calcValue(containerHeight);
2656 m_marginBottom = marginBottom.calcValue(containerHeight);
2657 bottomValue = bottom.calcValue(containerHeight);
2658
2659 // Solve for 'top'
2660 topValue = availableSpace - (bottomValue + m_marginTop + m_marginBottom);
2661 } else if (bottom.isAuto()) {
2662 m_marginTop = marginTop.calcValue(containerHeight);
2663 m_marginBottom = marginBottom.calcValue(containerHeight);
2664 topValue = top.calcValue(containerHeight);
2665
2666 // Solve for 'bottom'
2667 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
2668 // use the value.
2669 } else if (marginTop.isAuto()) {
2670 m_marginBottom = marginBottom.calcValue(containerHeight);
2671 topValue = top.calcValue(containerHeight);
2672 bottomValue = bottom.calcValue(containerHeight);
2673
2674 // Solve for 'margin-top'
2675 m_marginTop = availableSpace - (topValue + bottomValue + m_marginBottom);
2676 } else if (marginBottom.isAuto()) {
2677 m_marginTop = marginTop.calcValue(containerHeight);
2678 topValue = top.calcValue(containerHeight);
2679 bottomValue = bottom.calcValue(containerHeight);
2680
2681 // Solve for 'margin-bottom'
2682 m_marginBottom = availableSpace - (topValue + bottomValue + m_marginTop);
2683 } else {
2684 // Nothing is 'auto', just calculate the values.
2685 m_marginTop = marginTop.calcValue(containerHeight);
2686 m_marginBottom = marginBottom.calcValue(containerHeight);
2687 topValue = top.calcValue(containerHeight);
2688 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
2689 // use the value.
2690 }
2691
2692 /*-----------------------------------------------------------------------*\
2693 * 6. If at this point the values are over-constrained, ignore the value
2694 * for 'bottom' and solve for that value.
2695 \*-----------------------------------------------------------------------*/
2696 // NOTE: It is not necessary to do this step because we don't end up using
2697 // the value of 'bottom' regardless of whether the values are over-constrained
2698 // or not.
2699
2700 // Use computed values to calculate the vertical position.
2701 m_frameRect.setY(topValue + m_marginTop + containerBlock->borderTop());
2702 }
2703
localCaretRect(InlineBox * box,int caretOffset,int * extraWidthToEndOfLine)2704 IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine)
2705 {
2706 // VisiblePositions at offsets inside containers either a) refer to the positions before/after
2707 // those containers (tables and select elements) or b) refer to the position inside an empty block.
2708 // They never refer to children.
2709 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements.
2710
2711 // FIXME: What about border and padding?
2712 IntRect rect(x(), y(), caretWidth, height());
2713 TextDirection direction = box ? box->direction() : style()->direction();
2714
2715 if ((!caretOffset) ^ (direction == LTR))
2716 rect.move(IntSize(width() - caretWidth, 0));
2717
2718 if (box) {
2719 RootInlineBox* rootBox = box->root();
2720 int top = rootBox->lineTop();
2721 rect.setY(top);
2722 rect.setHeight(rootBox->lineBottom() - top);
2723 }
2724
2725 // If height of box is smaller than font height, use the latter one,
2726 // otherwise the caret might become invisible.
2727 //
2728 // Also, if the box is not a replaced element, always use the font height.
2729 // This prevents the "big caret" bug described in:
2730 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point
2731 //
2732 // FIXME: ignoring :first-line, missing good reason to take care of
2733 int fontHeight = style()->font().height();
2734 if (fontHeight > rect.height() || (!isReplaced() && !isTable()))
2735 rect.setHeight(fontHeight);
2736
2737 if (extraWidthToEndOfLine)
2738 *extraWidthToEndOfLine = x() + width() - rect.right();
2739
2740 // Move to local coords
2741 rect.move(-x(), -y());
2742 return rect;
2743 }
2744
lowestPosition(bool,bool includeSelf) const2745 int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2746 {
2747 if (!includeSelf || !width())
2748 return 0;
2749 int bottom = height();
2750 if (isRelPositioned())
2751 bottom += relativePositionOffsetY();
2752 return bottom;
2753 }
2754
rightmostPosition(bool,bool includeSelf) const2755 int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2756 {
2757 if (!includeSelf || !height())
2758 return 0;
2759 int right = width();
2760 if (isRelPositioned())
2761 right += relativePositionOffsetX();
2762 return right;
2763 }
2764
leftmostPosition(bool,bool includeSelf) const2765 int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const
2766 {
2767 if (!includeSelf || !height())
2768 return width();
2769 int left = 0;
2770 if (isRelPositioned())
2771 left += relativePositionOffsetX();
2772 return left;
2773 }
2774
positionForPoint(const IntPoint & point)2775 VisiblePosition RenderBox::positionForPoint(const IntPoint& point)
2776 {
2777 // no children...return this render object's element, if there is one, and offset 0
2778 if (!firstChild())
2779 return createVisiblePosition(node() ? firstDeepEditingPositionForNode(node()) : Position(0, 0));
2780
2781 int xPos = point.x();
2782 int yPos = point.y();
2783
2784 if (isTable() && node()) {
2785 int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft();
2786 int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom();
2787
2788 if (xPos < 0 || xPos > right || yPos < 0 || yPos > bottom) {
2789 if (xPos <= right / 2)
2790 return createVisiblePosition(firstDeepEditingPositionForNode(node()));
2791 return createVisiblePosition(lastDeepEditingPositionForNode(node()));
2792 }
2793 }
2794
2795 // Pass off to the closest child.
2796 int minDist = INT_MAX;
2797 RenderBox* closestRenderer = 0;
2798 int newX = xPos;
2799 int newY = yPos;
2800 if (isTableRow()) {
2801 newX += x();
2802 newY += y();
2803 }
2804 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) {
2805 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isBlockFlow() )
2806 || renderObject->style()->visibility() != VISIBLE)
2807 continue;
2808
2809 if (!renderObject->isBox())
2810 continue;
2811
2812 RenderBox* renderer = toRenderBox(renderObject);
2813
2814 int top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? 0 : renderer->y());
2815 int bottom = top + renderer->contentHeight();
2816 int left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? 0 : renderer->x());
2817 int right = left + renderer->contentWidth();
2818
2819 if (xPos <= right && xPos >= left && yPos <= top && yPos >= bottom) {
2820 if (renderer->isTableRow())
2821 return renderer->positionForCoordinates(xPos + newX - renderer->x(), yPos + newY - renderer->y());
2822 return renderer->positionForCoordinates(xPos - renderer->x(), yPos - renderer->y());
2823 }
2824
2825 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces
2826 // and use a different compare depending on which piece (x, y) is in.
2827 IntPoint cmp;
2828 if (xPos > right) {
2829 if (yPos < top)
2830 cmp = IntPoint(right, top);
2831 else if (yPos > bottom)
2832 cmp = IntPoint(right, bottom);
2833 else
2834 cmp = IntPoint(right, yPos);
2835 } else if (xPos < left) {
2836 if (yPos < top)
2837 cmp = IntPoint(left, top);
2838 else if (yPos > bottom)
2839 cmp = IntPoint(left, bottom);
2840 else
2841 cmp = IntPoint(left, yPos);
2842 } else {
2843 if (yPos < top)
2844 cmp = IntPoint(xPos, top);
2845 else
2846 cmp = IntPoint(xPos, bottom);
2847 }
2848
2849 int x1minusx2 = cmp.x() - xPos;
2850 int y1minusy2 = cmp.y() - yPos;
2851
2852 int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2;
2853 if (dist < minDist) {
2854 closestRenderer = renderer;
2855 minDist = dist;
2856 }
2857 }
2858
2859 if (closestRenderer)
2860 return closestRenderer->positionForCoordinates(newX - closestRenderer->x(), newY - closestRenderer->y());
2861
2862 return createVisiblePosition(firstDeepEditingPositionForNode(node()));
2863 }
2864
shrinkToAvoidFloats() const2865 bool RenderBox::shrinkToAvoidFloats() const
2866 {
2867 // FIXME: Technically we should be able to shrink replaced elements on a line, but this is difficult to accomplish, since this
2868 // involves doing a relayout during findNextLineBreak and somehow overriding the containingBlockWidth method to return the
2869 // current remaining width on a line.
2870 if ((isInline() && !isHTMLMarquee()) || !avoidsFloats())
2871 return false;
2872
2873 // All auto-width objects that avoid floats should always use lineWidth.
2874 return style()->width().isAuto();
2875 }
2876
avoidsFloats() const2877 bool RenderBox::avoidsFloats() const
2878 {
2879 return isReplaced() || hasOverflowClip() || isHR();
2880 }
2881
addShadowOverflow()2882 void RenderBox::addShadowOverflow()
2883 {
2884 int shadowLeft;
2885 int shadowRight;
2886 int shadowTop;
2887 int shadowBottom;
2888 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft);
2889 IntRect borderBox = borderBoxRect();
2890 int overflowLeft = borderBox.x() + shadowLeft;
2891 int overflowRight = borderBox.right() + shadowRight;
2892 int overflowTop = borderBox.y() + shadowTop;
2893 int overflowBottom = borderBox.bottom() + shadowBottom;
2894 addVisualOverflow(IntRect(overflowLeft, overflowTop, overflowRight - overflowLeft, overflowBottom - overflowTop));
2895 }
2896
addOverflowFromChild(RenderBox * child,const IntSize & delta)2897 void RenderBox::addOverflowFromChild(RenderBox* child, const IntSize& delta)
2898 {
2899 // Update our overflow in case the child spills out the block, but only if we were going to paint
2900 // the child block ourselves.
2901 if (child->hasSelfPaintingLayer())
2902 return;
2903
2904 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then
2905 // its overflow is internal to it, and we don't care about it.
2906 IntRect childLayoutOverflowRect = child->hasOverflowClip() ? child->borderBoxRect() : child->layoutOverflowRect();
2907 childLayoutOverflowRect.move(delta);
2908 addLayoutOverflow(childLayoutOverflowRect);
2909
2910 // Add in visual overflow from the child. Even if the child clips its overflow, it may still
2911 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this
2912 // overflow if we are clipping our own overflow.
2913 if (hasOverflowClip())
2914 return;
2915 IntRect childVisualOverflowRect = child->visualOverflowRect();
2916 childVisualOverflowRect.move(delta);
2917 addVisualOverflow(childVisualOverflowRect);
2918 }
2919
addLayoutOverflow(const IntRect & rect)2920 void RenderBox::addLayoutOverflow(const IntRect& rect)
2921 {
2922 IntRect borderBox = borderBoxRect();
2923 if (borderBox.contains(rect))
2924 return;
2925
2926 if (!m_overflow)
2927 m_overflow.set(new RenderOverflow(borderBox));
2928
2929 m_overflow->addLayoutOverflow(rect);
2930 }
2931
addVisualOverflow(const IntRect & rect)2932 void RenderBox::addVisualOverflow(const IntRect& rect)
2933 {
2934 IntRect borderBox = borderBoxRect();
2935 if (borderBox.contains(rect))
2936 return;
2937
2938 if (!m_overflow)
2939 m_overflow.set(new RenderOverflow(borderBox));
2940
2941 m_overflow->addVisualOverflow(rect);
2942 }
2943
clearLayoutOverflow()2944 void RenderBox::clearLayoutOverflow()
2945 {
2946 if (!m_overflow)
2947 return;
2948
2949 if (visualOverflowRect() == borderBoxRect()) {
2950 m_overflow.clear();
2951 return;
2952 }
2953
2954 m_overflow->resetLayoutOverflow(borderBoxRect());
2955 }
2956
2957 #if ENABLE(SVG)
2958
localTransform() const2959 AffineTransform RenderBox::localTransform() const
2960 {
2961 return AffineTransform(1, 0, 0, 1, x(), y());
2962 }
2963
2964 #endif
2965
2966 } // namespace WebCore
2967