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