2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
7 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
27 #include "core/rendering/RenderBox.h"
31 #include "HTMLNames.h"
32 #include "core/dom/Document.h"
33 #include "core/editing/htmlediting.h"
34 #include "core/html/HTMLElement.h"
35 #include "core/html/HTMLFrameElementBase.h"
36 #include "core/html/HTMLFrameOwnerElement.h"
37 #include "core/frame/Frame.h"
38 #include "core/frame/FrameView.h"
39 #include "core/page/AutoscrollController.h"
40 #include "core/page/EventHandler.h"
41 #include "core/page/Page.h"
42 #include "core/rendering/HitTestResult.h"
43 #include "core/rendering/LayoutRectRecorder.h"
44 #include "core/rendering/PaintInfo.h"
45 #include "core/rendering/RenderDeprecatedFlexibleBox.h"
46 #include "core/rendering/RenderFlexibleBox.h"
47 #include "core/rendering/RenderGeometryMap.h"
48 #include "core/rendering/RenderGrid.h"
49 #include "core/rendering/RenderInline.h"
50 #include "core/rendering/RenderLayer.h"
51 #include "core/rendering/RenderLayerCompositor.h"
52 #include "core/rendering/RenderListMarker.h"
53 #include "core/rendering/RenderTableCell.h"
54 #include "core/rendering/RenderTheme.h"
55 #include "core/rendering/RenderView.h"
56 #include "platform/LengthFunctions.h"
57 #include "platform/geometry/FloatQuad.h"
58 #include "platform/geometry/TransformState.h"
59 #include "platform/graphics/GraphicsContextStateSaver.h"
65 using namespace HTMLNames;
67 // Used by flexible boxes when flexing this element and by table cells.
68 typedef WTF::HashMap<const RenderBox*, LayoutUnit> OverrideSizeMap;
70 // Used by grid elements to properly size their grid items.
71 // FIXME: Move these into RenderBoxRareData.
72 static OverrideSizeMap* gOverrideContainingBlockLogicalHeightMap = 0;
73 static OverrideSizeMap* gOverrideContainingBlockLogicalWidthMap = 0;
76 // Size of border belt for autoscroll. When mouse pointer in border belt,
77 // autoscroll is started.
78 static const int autoscrollBeltSize = 20;
79 static const unsigned backgroundObscurationTestMaxDepth = 4;
81 static bool skipBodyBackground(const RenderBox* bodyElementRenderer)
83 ASSERT(bodyElementRenderer->isBody());
84 // The <body> only paints its background if the root element has defined a background independent of the body,
85 // or if the <body>'s parent is not the document element's renderer (e.g. inside SVG foreignObject).
86 RenderObject* documentElementRenderer = bodyElementRenderer->document().documentElement()->renderer();
87 return documentElementRenderer
88 && !documentElementRenderer->hasBackground()
89 && (documentElementRenderer == bodyElementRenderer->parent());
92 RenderBox::RenderBox(ContainerNode* node)
93 : RenderBoxModelObject(node)
94 , m_intrinsicContentLogicalHeight(-1)
95 , m_minPreferredLogicalWidth(-1)
96 , m_maxPreferredLogicalWidth(-1)
101 void RenderBox::willBeDestroyed()
104 clearContainingBlockOverrideSize();
106 RenderBlock::removePercentHeightDescendantIfNeeded(this);
108 ShapeOutsideInfo::removeInfo(this);
110 RenderBoxModelObject::willBeDestroyed();
113 void RenderBox::removeFloatingOrPositionedChildFromBlockLists()
115 ASSERT(isFloatingOrOutOfFlowPositioned());
117 if (documentBeingDestroyed())
121 RenderBlockFlow* parentBlockFlow = 0;
122 for (RenderObject* curr = parent(); curr && !curr->isRenderView(); curr = curr->parent()) {
123 if (curr->isRenderBlockFlow()) {
124 RenderBlockFlow* currBlockFlow = toRenderBlockFlow(curr);
125 if (!parentBlockFlow || currBlockFlow->containsFloat(this))
126 parentBlockFlow = currBlockFlow;
130 if (parentBlockFlow) {
131 parentBlockFlow->markSiblingsWithFloatsForLayout(this);
132 parentBlockFlow->markAllDescendantsWithFloatsForLayout(this, false);
136 if (isOutOfFlowPositioned())
137 RenderBlock::removePositionedObject(this);
140 void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
142 RenderStyle* oldStyle = style();
144 // The background of the root element or the body element could propagate up to
145 // the canvas. Just dirty the entire canvas when our style changes substantially.
146 if (diff >= StyleDifferenceRepaint && node() &&
147 (node()->hasTagName(htmlTag) || node()->hasTagName(bodyTag))) {
150 if (oldStyle->hasEntirelyFixedBackground() != newStyle->hasEntirelyFixedBackground())
151 view()->compositor()->rootFixedBackgroundsChanged();
154 // When a layout hint happens and an object's position style changes, we have to do a layout
155 // to dirty the render tree using the old position value now.
156 if (diff == StyleDifferenceLayout && parent() && oldStyle->position() != newStyle->position()) {
157 markContainingBlocksForLayout();
158 if (oldStyle->position() == StaticPosition)
160 else if (newStyle->hasOutOfFlowPosition())
161 parent()->setChildNeedsLayout();
162 if (isFloating() && !isOutOfFlowPositioned() && newStyle->hasOutOfFlowPosition())
163 removeFloatingOrPositionedChildFromBlockLists();
165 } else if (newStyle && isBody())
168 RenderBoxModelObject::styleWillChange(diff, newStyle);
171 void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
173 // Horizontal writing mode definition is updated in RenderBoxModelObject::updateFromStyle,
174 // (as part of the RenderBoxModelObject::styleDidChange call below). So, we can safely cache the horizontal
175 // writing mode value before style change here.
176 bool oldHorizontalWritingMode = isHorizontalWritingMode();
178 RenderBoxModelObject::styleDidChange(diff, oldStyle);
180 RenderStyle* newStyle = style();
181 if (needsLayout() && oldStyle) {
182 RenderBlock::removePercentHeightDescendantIfNeeded(this);
184 // Normally we can do optimized positioning layout for absolute/fixed positioned objects. There is one special case, however, which is
185 // when the positioned object's margin-before is changed. In this case the parent has to get a layout in order to run margin collapsing
186 // to determine the new static position.
187 if (isOutOfFlowPositioned() && newStyle->hasStaticBlockPosition(isHorizontalWritingMode()) && oldStyle->marginBefore() != newStyle->marginBefore()
188 && parent() && !parent()->normalChildNeedsLayout())
189 parent()->setChildNeedsLayout();
192 if (RenderBlock::hasPercentHeightContainerMap() && firstChild()
193 && oldHorizontalWritingMode != isHorizontalWritingMode())
194 RenderBlock::clearPercentHeightDescendantsFrom(this);
196 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the
197 // new zoomed coordinate space.
198 if (hasOverflowClip() && oldStyle && newStyle && oldStyle->effectiveZoom() != newStyle->effectiveZoom() && layer()) {
199 if (int left = layer()->scrollableArea()->scrollXOffset()) {
200 left = (left / oldStyle->effectiveZoom()) * newStyle->effectiveZoom();
201 layer()->scrollableArea()->scrollToXOffset(left);
203 if (int top = layer()->scrollableArea()->scrollYOffset()) {
204 top = (top / oldStyle->effectiveZoom()) * newStyle->effectiveZoom();
205 layer()->scrollableArea()->scrollToYOffset(top);
209 // Our opaqueness might have changed without triggering layout.
210 if (diff == StyleDifferenceRepaint || diff == StyleDifferenceRepaintIfTextOrColorChange || diff == StyleDifferenceRepaintLayer) {
211 RenderObject* parentToInvalidate = parent();
212 for (unsigned i = 0; i < backgroundObscurationTestMaxDepth && parentToInvalidate; ++i) {
213 parentToInvalidate->invalidateBackgroundObscurationStatus();
214 parentToInvalidate = parentToInvalidate->parent();
218 if (isRoot() || isBody())
219 document().view()->recalculateScrollbarOverlayStyle();
221 updateShapeOutsideInfoAfterStyleChange(*style(), oldStyle);
222 updateGridPositionAfterStyleChange(oldStyle);
225 void RenderBox::updateShapeOutsideInfoAfterStyleChange(const RenderStyle& style, const RenderStyle* oldStyle)
227 const ShapeValue* shapeOutside = style.shapeOutside();
228 const ShapeValue* oldShapeOutside = oldStyle ? oldStyle->shapeOutside() : RenderStyle::initialShapeOutside();
230 Length shapeMargin = style.shapeMargin();
231 Length oldShapeMargin = oldStyle ? oldStyle->shapeMargin() : RenderStyle::initialShapeMargin();
233 float shapeImageThreshold = style.shapeImageThreshold();
234 float oldShapeImageThreshold = oldStyle ? oldStyle->shapeImageThreshold() : RenderStyle::initialShapeImageThreshold();
236 // FIXME: A future optimization would do a deep comparison for equality. (bug 100811)
237 if (shapeOutside == oldShapeOutside && shapeMargin == oldShapeMargin && shapeImageThreshold == oldShapeImageThreshold)
241 ShapeOutsideInfo::removeInfo(this);
243 ShapeOutsideInfo::ensureInfo(this)->dirtyShapeSize();
245 if (shapeOutside || shapeOutside != oldShapeOutside)
246 markShapeOutsideDependentsForLayout();
249 void RenderBox::updateGridPositionAfterStyleChange(const RenderStyle* oldStyle)
251 if (!oldStyle || !parent() || !parent()->isRenderGrid())
254 if (oldStyle->gridColumnStart() == style()->gridColumnStart()
255 && oldStyle->gridColumnEnd() == style()->gridColumnEnd()
256 && oldStyle->gridRowStart() == style()->gridRowStart()
257 && oldStyle->gridRowEnd() == style()->gridRowEnd()
258 && oldStyle->order() == style()->order()
259 && oldStyle->hasOutOfFlowPosition() == style()->hasOutOfFlowPosition())
262 // It should be possible to not dirty the grid in some cases (like moving an explicitly placed grid item).
263 // For now, it's more simple to just always recompute the grid.
264 toRenderGrid(parent())->dirtyGrid();
267 void RenderBox::updateFromStyle()
269 RenderBoxModelObject::updateFromStyle();
271 RenderStyle* styleToUse = style();
272 bool isRootObject = isRoot();
273 bool isViewObject = isRenderView();
275 // The root and the RenderView always paint their backgrounds/borders.
276 if (isRootObject || isViewObject)
277 setHasBoxDecorations(true);
279 setFloating(!isOutOfFlowPositioned() && styleToUse->isFloating());
281 bool boxHasOverflowClip = false;
282 if (!styleToUse->isOverflowVisible() && isRenderBlock() && !isViewObject) {
283 // If overflow has been propagated to the viewport, it has no effect here.
284 if (node() != document().viewportDefiningElement()) {
285 boxHasOverflowClip = true;
286 if (!hasOverflowClip()) {
287 // If we are getting an overflow clip, preemptively erase any overflowing content.
288 // FIXME: This should probably consult RenderOverflow.
293 setHasOverflowClip(boxHasOverflowClip);
295 setHasTransform(styleToUse->hasTransformRelatedProperty());
296 setHasReflection(styleToUse->boxReflect());
299 void RenderBox::layout()
301 ASSERT(needsLayout());
303 LayoutRectRecorder recorder(*this);
305 RenderObject* child = firstChild();
311 LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode());
313 child->layoutIfNeeded();
314 ASSERT(!child->needsLayout());
315 child = child->nextSibling();
318 invalidateBackgroundObscurationStatus();
322 // More IE extensions. clientWidth and clientHeight represent the interior of an object
323 // excluding border and scrollbar.
324 LayoutUnit RenderBox::clientWidth() const
326 return width() - borderLeft() - borderRight() - verticalScrollbarWidth();
329 LayoutUnit RenderBox::clientHeight() const
331 return height() - borderTop() - borderBottom() - horizontalScrollbarHeight();
334 int RenderBox::pixelSnappedClientWidth() const
336 return snapSizeToPixel(clientWidth(), x() + clientLeft());
339 int RenderBox::pixelSnappedClientHeight() const
341 return snapSizeToPixel(clientHeight(), y() + clientTop());
344 int RenderBox::pixelSnappedOffsetWidth() const
346 return snapSizeToPixel(offsetWidth(), x() + clientLeft());
349 int RenderBox::pixelSnappedOffsetHeight() const
351 return snapSizeToPixel(offsetHeight(), y() + clientTop());
354 bool RenderBox::canDetermineWidthWithoutLayout() const
356 // FIXME: This optimization is incorrect as written.
357 // We need to be able to opt-in to this behavior only when
358 // it's guarentted correct.
359 // Until then disabling this optimization to be safe.
362 // FIXME: There are likely many subclasses of RenderBlockFlow which
363 // cannot determine their layout just from style!
364 // Perhaps we should create a "PlainRenderBlockFlow"
365 // and move this optimization there?
366 if (!isRenderBlockFlow()
367 // Flexbox items can be expanded beyond their width.
368 || isFlexItemIncludingDeprecated()
369 // Table Layout controls cell size and can expand beyond width.
373 RenderStyle* style = this->style();
374 return style->width().isFixed()
375 && style->minWidth().isFixed()
376 && (style->maxWidth().isUndefined() || style->maxWidth().isFixed())
377 && style->paddingLeft().isFixed()
378 && style->paddingRight().isFixed()
379 && style->boxSizing() == CONTENT_BOX;
382 LayoutUnit RenderBox::fixedOffsetWidth() const
384 ASSERT(canDetermineWidthWithoutLayout());
386 RenderStyle* style = this->style();
388 LayoutUnit width = std::max(LayoutUnit(style->minWidth().value()), LayoutUnit(style->width().value()));
389 if (style->maxWidth().isFixed())
390 width = std::min(LayoutUnit(style->maxWidth().value()), width);
392 LayoutUnit borderLeft = style->borderLeft().nonZero() ? style->borderLeft().width() : 0;
393 LayoutUnit borderRight = style->borderRight().nonZero() ? style->borderRight().width() : 0;
395 return width + borderLeft + borderRight + style->paddingLeft().value() + style->paddingRight().value();
398 int RenderBox::scrollWidth() const
400 if (hasOverflowClip())
401 return layer()->scrollableArea()->scrollWidth();
402 // For objects with visible overflow, this matches IE.
403 // FIXME: Need to work right with writing modes.
404 if (style()->isLeftToRightDirection())
405 return snapSizeToPixel(max(clientWidth(), layoutOverflowRect().maxX() - borderLeft()), x() + clientLeft());
406 return clientWidth() - min<LayoutUnit>(0, layoutOverflowRect().x() - borderLeft());
409 int RenderBox::scrollHeight() const
411 if (hasOverflowClip())
412 return layer()->scrollableArea()->scrollHeight();
413 // For objects with visible overflow, this matches IE.
414 // FIXME: Need to work right with writing modes.
415 return snapSizeToPixel(max(clientHeight(), layoutOverflowRect().maxY() - borderTop()), y() + clientTop());
418 int RenderBox::scrollLeft() const
420 return hasOverflowClip() ? layer()->scrollableArea()->scrollXOffset() : 0;
423 int RenderBox::scrollTop() const
425 return hasOverflowClip() ? layer()->scrollableArea()->scrollYOffset() : 0;
428 void RenderBox::setScrollLeft(int newLeft)
430 if (hasOverflowClip())
431 layer()->scrollableArea()->scrollToXOffset(newLeft, ScrollOffsetClamped);
434 void RenderBox::setScrollTop(int newTop)
436 if (hasOverflowClip())
437 layer()->scrollableArea()->scrollToYOffset(newTop, ScrollOffsetClamped);
440 void RenderBox::scrollToOffset(const IntSize& offset)
442 ASSERT(hasOverflowClip());
443 layer()->scrollableArea()->scrollToOffset(offset, ScrollOffsetClamped);
446 static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView* frameView)
448 // If scrollbars aren't explicitly forbidden, permit scrolling.
449 if (frameElementBase && frameElementBase->scrollingMode() != ScrollbarAlwaysOff)
452 // If scrollbars are forbidden, user initiated scrolls should obviously be ignored.
453 if (frameView->wasScrolledByUser())
456 // Forbid autoscrolls when scrollbars are off, but permits other programmatic scrolls,
457 // like navigation to an anchor.
458 Page* page = frameView->frame().page();
461 return !page->autoscrollController().autoscrollInProgress();
464 void RenderBox::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
466 RenderBox* parentBox = 0;
467 LayoutRect newRect = rect;
469 bool restrictedByLineClamp = false;
471 parentBox = parent()->enclosingBox();
472 restrictedByLineClamp = !parent()->style()->lineClamp().isNone();
475 if (hasOverflowClip() && !restrictedByLineClamp) {
476 // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property.
477 // This will prevent us from revealing text hidden by the slider in Safari RSS.
478 newRect = layer()->scrollableArea()->exposeRect(rect, alignX, alignY);
479 } else if (!parentBox && canBeProgramaticallyScrolled()) {
480 if (FrameView* frameView = this->frameView()) {
481 Element* ownerElement = document().ownerElement();
483 if (ownerElement && ownerElement->renderer()) {
484 HTMLFrameElementBase* frameElementBase = 0;
486 if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag))
487 frameElementBase = toHTMLFrameElementBase(ownerElement);
489 if (frameElementAndViewPermitScroll(frameElementBase, frameView)) {
490 LayoutRect viewRect = frameView->visibleContentRect();
491 LayoutRect exposeRect = ScrollAlignment::getRectToExpose(viewRect, rect, alignX, alignY);
493 int xOffset = roundToInt(exposeRect.x());
494 int yOffset = roundToInt(exposeRect.y());
495 // Adjust offsets if they're outside of the allowable range.
496 xOffset = max(0, min(frameView->contentsWidth(), xOffset));
497 yOffset = max(0, min(frameView->contentsHeight(), yOffset));
499 frameView->setScrollPosition(IntPoint(xOffset, yOffset));
500 if (frameView->safeToPropagateScrollToParent()) {
501 parentBox = ownerElement->renderer()->enclosingBox();
502 // FIXME: This doesn't correctly convert the rect to
503 // absolute coordinates in the parent.
504 newRect.setX(rect.x() - frameView->scrollX() + frameView->x());
505 newRect.setY(rect.y() - frameView->scrollY() + frameView->y());
511 LayoutRect viewRect = frameView->visibleContentRect();
512 LayoutRect r = ScrollAlignment::getRectToExpose(viewRect, rect, alignX, alignY);
513 frameView->setScrollPosition(roundedIntPoint(r.location()));
518 if (frame()->page()->autoscrollController().autoscrollInProgress())
519 parentBox = enclosingScrollableBox();
522 parentBox->scrollRectToVisible(newRect, alignX, alignY);
525 void RenderBox::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
527 rects.append(pixelSnappedIntRect(accumulatedOffset, size()));
530 void RenderBox::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
532 quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height()), 0 /* mode */, wasFixed));
535 void RenderBox::updateLayerTransform()
537 // Transform-origin depends on box size, so we need to update the layer transform after layout.
539 layer()->updateTransform();
542 LayoutUnit RenderBox::constrainLogicalWidthByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, RenderBlock* cb) const
544 RenderStyle* styleToUse = style();
545 if (!styleToUse->logicalMaxWidth().isUndefined())
546 logicalWidth = min(logicalWidth, computeLogicalWidthUsing(MaxSize, styleToUse->logicalMaxWidth(), availableWidth, cb));
547 return max(logicalWidth, computeLogicalWidthUsing(MinSize, styleToUse->logicalMinWidth(), availableWidth, cb));
550 LayoutUnit RenderBox::constrainLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const
552 RenderStyle* styleToUse = style();
553 if (!styleToUse->logicalMaxHeight().isUndefined()) {
554 LayoutUnit maxH = computeLogicalHeightUsing(styleToUse->logicalMaxHeight(), intrinsicContentHeight);
556 logicalHeight = min(logicalHeight, maxH);
558 return max(logicalHeight, computeLogicalHeightUsing(styleToUse->logicalMinHeight(), intrinsicContentHeight));
561 LayoutUnit RenderBox::constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const
563 RenderStyle* styleToUse = style();
564 if (!styleToUse->logicalMaxHeight().isUndefined()) {
565 LayoutUnit maxH = computeContentLogicalHeight(styleToUse->logicalMaxHeight(), intrinsicContentHeight);
567 logicalHeight = min(logicalHeight, maxH);
569 return max(logicalHeight, computeContentLogicalHeight(styleToUse->logicalMinHeight(), intrinsicContentHeight));
572 IntRect RenderBox::absoluteContentBox() const
574 // This is wrong with transforms and flipped writing modes.
575 IntRect rect = pixelSnappedIntRect(contentBoxRect());
576 FloatPoint absPos = localToAbsolute();
577 rect.move(absPos.x(), absPos.y());
581 FloatQuad RenderBox::absoluteContentQuad() const
583 LayoutRect rect = contentBoxRect();
584 return localToAbsoluteQuad(FloatRect(rect));
587 LayoutRect RenderBox::outlineBoundsForRepaint(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap) const
589 LayoutRect box = borderBoundingBox();
590 adjustRectForOutlineAndShadow(box);
592 if (repaintContainer != this) {
593 FloatQuad containerRelativeQuad;
595 containerRelativeQuad = geometryMap->mapToContainer(box, repaintContainer);
597 containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer);
599 box = containerRelativeQuad.enclosingBoundingBox();
602 if (!RuntimeEnabledFeatures::repaintAfterLayoutEnabled()) {
603 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
604 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
605 box.move(view()->layoutDelta());
611 void RenderBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*)
613 if (!size().isEmpty())
614 rects.append(pixelSnappedIntRect(additionalOffset, size()));
617 bool RenderBox::canResize() const
619 // We need a special case for <iframe> because they never have
620 // hasOverflowClip(). However, they do "implicitly" clip their contents, so
621 // we want to allow resizing them also.
622 return (hasOverflowClip() || isRenderIFrame()) && style()->resize() != RESIZE_NONE;
625 void RenderBox::addLayerHitTestRects(LayerHitTestRects& layerRects, const RenderLayer* currentLayer, const LayoutPoint& layerOffset, const LayoutRect& containerRect) const
627 LayoutPoint adjustedLayerOffset = layerOffset + locationOffset();
628 RenderBoxModelObject::addLayerHitTestRects(layerRects, currentLayer, adjustedLayerOffset, containerRect);
631 void RenderBox::computeSelfHitTestRects(Vector<LayoutRect>& rects, const LayoutPoint& layerOffset) const
633 if (!size().isEmpty())
634 rects.append(LayoutRect(layerOffset, size()));
637 LayoutRect RenderBox::reflectionBox() const
640 if (!style()->boxReflect())
642 LayoutRect box = borderBoxRect();
644 switch (style()->boxReflect()->direction()) {
645 case ReflectionBelow:
646 result.move(0, box.height() + reflectionOffset());
648 case ReflectionAbove:
649 result.move(0, -box.height() - reflectionOffset());
652 result.move(-box.width() - reflectionOffset(), 0);
654 case ReflectionRight:
655 result.move(box.width() + reflectionOffset(), 0);
661 int RenderBox::reflectionOffset() const
663 if (!style()->boxReflect())
665 if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight)
666 return valueForLength(style()->boxReflect()->offset(), borderBoxRect().width());
667 return valueForLength(style()->boxReflect()->offset(), borderBoxRect().height());
670 LayoutRect RenderBox::reflectedRect(const LayoutRect& r) const
672 if (!style()->boxReflect())
675 LayoutRect box = borderBoxRect();
676 LayoutRect result = r;
677 switch (style()->boxReflect()->direction()) {
678 case ReflectionBelow:
679 result.setY(box.maxY() + reflectionOffset() + (box.maxY() - r.maxY()));
681 case ReflectionAbove:
682 result.setY(box.y() - reflectionOffset() - box.height() + (box.maxY() - r.maxY()));
685 result.setX(box.x() - reflectionOffset() - box.width() + (box.maxX() - r.maxX()));
687 case ReflectionRight:
688 result.setX(box.maxX() + reflectionOffset() + (box.maxX() - r.maxX()));
694 int RenderBox::verticalScrollbarWidth() const
696 if (!hasOverflowClip() || style()->overflowY() == OOVERLAY)
699 return layer()->scrollableArea()->verticalScrollbarWidth();
702 int RenderBox::horizontalScrollbarHeight() const
704 if (!hasOverflowClip() || style()->overflowX() == OOVERLAY)
707 return layer()->scrollableArea()->horizontalScrollbarHeight();
710 int RenderBox::instrinsicScrollbarLogicalWidth() const
712 if (!hasOverflowClip())
715 if (isHorizontalWritingMode() && style()->overflowY() == OSCROLL) {
716 ASSERT(layer()->scrollableArea() && layer()->scrollableArea()->hasVerticalScrollbar());
717 return verticalScrollbarWidth();
720 if (!isHorizontalWritingMode() && style()->overflowX() == OSCROLL) {
721 ASSERT(layer()->scrollableArea() && layer()->scrollableArea()->hasHorizontalScrollbar());
722 return horizontalScrollbarHeight();
728 bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float delta)
730 // Logical scroll is a higher level concept, all directions by here must be physical
731 ASSERT(!isLogical(direction));
733 if (!layer() || !layer()->scrollableArea())
736 return layer()->scrollableArea()->scroll(direction, granularity, delta);
739 bool RenderBox::canBeScrolledAndHasScrollableArea() const
741 return canBeProgramaticallyScrolled() && (scrollHeight() != clientHeight() || scrollWidth() != clientWidth());
744 bool RenderBox::canBeProgramaticallyScrolled() const
746 Node* node = this->node();
747 if (node && node->isDocumentNode())
750 if (!hasOverflowClip())
753 bool hasScrollableOverflow = hasScrollableOverflowX() || hasScrollableOverflowY();
754 if (scrollsOverflow() && hasScrollableOverflow)
757 return node && node->rendererIsEditable();
760 bool RenderBox::usesCompositedScrolling() const
762 return hasOverflowClip() && hasLayer() && layer()->scrollableArea()->usesCompositedScrolling();
765 void RenderBox::autoscroll(const IntPoint& position)
767 Frame* frame = this->frame();
771 FrameView* frameView = frame->view();
775 IntPoint currentDocumentPosition = frameView->windowToContents(position);
776 scrollRectToVisible(LayoutRect(currentDocumentPosition, LayoutSize(1, 1)), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded);
779 bool RenderBox::autoscrollInProgress() const
781 return frame() && frame()->page() && frame()->page()->autoscrollController().autoscrollInProgress(this);
784 // There are two kinds of renderer that can autoscroll.
785 bool RenderBox::canAutoscroll() const
787 if (node() && node()->isDocumentNode())
788 return view()->frameView()->isScrollable();
790 // Check for a box that can be scrolled in its own right.
791 return canBeScrolledAndHasScrollableArea();
794 // If specified point is in border belt, returned offset denotes direction of
796 IntSize RenderBox::calculateAutoscrollDirection(const IntPoint& windowPoint) const
801 FrameView* frameView = frame()->view();
805 IntRect box(absoluteBoundingBoxRect());
806 box.move(view()->frameView()->scrollOffset());
807 IntRect windowBox = view()->frameView()->contentsToWindow(box);
809 IntPoint windowAutoscrollPoint = windowPoint;
811 if (windowAutoscrollPoint.x() < windowBox.x() + autoscrollBeltSize)
812 windowAutoscrollPoint.move(-autoscrollBeltSize, 0);
813 else if (windowAutoscrollPoint.x() > windowBox.maxX() - autoscrollBeltSize)
814 windowAutoscrollPoint.move(autoscrollBeltSize, 0);
816 if (windowAutoscrollPoint.y() < windowBox.y() + autoscrollBeltSize)
817 windowAutoscrollPoint.move(0, -autoscrollBeltSize);
818 else if (windowAutoscrollPoint.y() > windowBox.maxY() - autoscrollBeltSize)
819 windowAutoscrollPoint.move(0, autoscrollBeltSize);
821 return windowAutoscrollPoint - windowPoint;
824 RenderBox* RenderBox::findAutoscrollable(RenderObject* renderer)
826 while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll())) {
827 if (!renderer->parent() && renderer->node() == renderer->document() && renderer->document().ownerElement())
828 renderer = renderer->document().ownerElement()->renderer();
830 renderer = renderer->parent();
833 return renderer && renderer->isBox() ? toRenderBox(renderer) : 0;
836 static inline int adjustedScrollDelta(int beginningDelta)
838 // This implemention matches Firefox's.
839 // http://mxr.mozilla.org/firefox/source/toolkit/content/widgets/browser.xml#856.
840 const int speedReducer = 12;
842 int adjustedDelta = beginningDelta / speedReducer;
843 if (adjustedDelta > 1)
844 adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(adjustedDelta))) - 1;
845 else if (adjustedDelta < -1)
846 adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(-adjustedDelta))) + 1;
848 return adjustedDelta;
851 static inline IntSize adjustedScrollDelta(const IntSize& delta)
853 return IntSize(adjustedScrollDelta(delta.width()), adjustedScrollDelta(delta.height()));
856 void RenderBox::panScroll(const IntPoint& sourcePoint)
858 Frame* frame = this->frame();
862 IntPoint lastKnownMousePosition = frame->eventHandler().lastKnownMousePosition();
864 // We need to check if the last known mouse position is out of the window. When the mouse is out of the window, the position is incoherent
865 static IntPoint previousMousePosition;
866 if (lastKnownMousePosition.x() < 0 || lastKnownMousePosition.y() < 0)
867 lastKnownMousePosition = previousMousePosition;
869 previousMousePosition = lastKnownMousePosition;
871 IntSize delta = lastKnownMousePosition - sourcePoint;
873 if (abs(delta.width()) <= ScrollView::noPanScrollRadius) // at the center we let the space for the icon
875 if (abs(delta.height()) <= ScrollView::noPanScrollRadius)
878 scrollByRecursively(adjustedScrollDelta(delta), ScrollOffsetClamped);
881 void RenderBox::scrollByRecursively(const IntSize& delta, ScrollOffsetClamping clamp)
886 bool restrictedByLineClamp = false;
888 restrictedByLineClamp = !parent()->style()->lineClamp().isNone();
890 if (hasOverflowClip() && !restrictedByLineClamp) {
891 IntSize newScrollOffset = layer()->scrollableArea()->adjustedScrollOffset() + delta;
892 layer()->scrollableArea()->scrollToOffset(newScrollOffset, clamp);
894 // If this layer can't do the scroll we ask the next layer up that can scroll to try
895 IntSize remainingScrollOffset = newScrollOffset - layer()->scrollableArea()->adjustedScrollOffset();
896 if (!remainingScrollOffset.isZero() && parent()) {
897 if (RenderBox* scrollableBox = enclosingScrollableBox())
898 scrollableBox->scrollByRecursively(remainingScrollOffset, clamp);
900 Frame* frame = this->frame();
901 if (frame && frame->page())
902 frame->page()->autoscrollController().updateAutoscrollRenderer();
904 } else if (view()->frameView()) {
905 // If we are here, we were called on a renderer that can be programmatically scrolled, but doesn't
906 // have an overflow clip. Which means that it is a document node that can be scrolled.
907 view()->frameView()->scrollBy(delta);
909 // FIXME: If we didn't scroll the whole way, do we want to try looking at the frames ownerElement?
910 // https://bugs.webkit.org/show_bug.cgi?id=28237
914 bool RenderBox::needsPreferredWidthsRecalculation() const
916 return style()->paddingStart().isPercent() || style()->paddingEnd().isPercent();
919 IntSize RenderBox::scrolledContentOffset() const
921 ASSERT(hasOverflowClip());
923 return layer()->scrollableArea()->scrollOffset();
926 LayoutSize RenderBox::cachedSizeForOverflowClip() const
928 ASSERT(hasOverflowClip());
930 return layer()->size();
933 void RenderBox::applyCachedClipAndScrollOffsetForRepaint(LayoutRect& paintRect) const
935 flipForWritingMode(paintRect);
936 paintRect.move(-scrolledContentOffset()); // For overflow:auto/scroll/hidden.
938 // Do not clip scroll layer contents to reduce the number of repaints while scrolling.
939 if (usesCompositedScrolling()) {
940 flipForWritingMode(paintRect);
944 // height() is inaccurate if we're in the middle of a layout of this RenderBox, so use the
945 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
946 // anyway if its size does change.
947 LayoutRect clipRect(LayoutPoint(), cachedSizeForOverflowClip());
948 paintRect = intersection(paintRect, clipRect);
949 flipForWritingMode(paintRect);
952 void RenderBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
954 minLogicalWidth = minPreferredLogicalWidth() - borderAndPaddingLogicalWidth();
955 maxLogicalWidth = maxPreferredLogicalWidth() - borderAndPaddingLogicalWidth();
958 LayoutUnit RenderBox::minPreferredLogicalWidth() const
960 if (preferredLogicalWidthsDirty()) {
962 SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<RenderBox*>(this));
964 const_cast<RenderBox*>(this)->computePreferredLogicalWidths();
967 return m_minPreferredLogicalWidth;
970 LayoutUnit RenderBox::maxPreferredLogicalWidth() const
972 if (preferredLogicalWidthsDirty()) {
974 SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<RenderBox*>(this));
976 const_cast<RenderBox*>(this)->computePreferredLogicalWidths();
979 return m_maxPreferredLogicalWidth;
982 bool RenderBox::hasOverrideHeight() const
984 return m_rareData && m_rareData->m_overrideLogicalContentHeight != -1;
987 bool RenderBox::hasOverrideWidth() const
989 return m_rareData && m_rareData->m_overrideLogicalContentWidth != -1;
992 void RenderBox::setOverrideLogicalContentHeight(LayoutUnit height)
995 ensureRareData().m_overrideLogicalContentHeight = height;
998 void RenderBox::setOverrideLogicalContentWidth(LayoutUnit width)
1001 ensureRareData().m_overrideLogicalContentWidth = width;
1004 void RenderBox::clearOverrideLogicalContentHeight()
1007 m_rareData->m_overrideLogicalContentHeight = -1;
1010 void RenderBox::clearOverrideLogicalContentWidth()
1013 m_rareData->m_overrideLogicalContentWidth = -1;
1016 void RenderBox::clearOverrideSize()
1018 clearOverrideLogicalContentHeight();
1019 clearOverrideLogicalContentWidth();
1022 LayoutUnit RenderBox::overrideLogicalContentWidth() const
1024 ASSERT(hasOverrideWidth());
1025 return m_rareData->m_overrideLogicalContentWidth;
1028 LayoutUnit RenderBox::overrideLogicalContentHeight() const
1030 ASSERT(hasOverrideHeight());
1031 return m_rareData->m_overrideLogicalContentHeight;
1034 LayoutUnit RenderBox::overrideContainingBlockContentLogicalWidth() const
1036 ASSERT(hasOverrideContainingBlockLogicalWidth());
1037 return gOverrideContainingBlockLogicalWidthMap->get(this);
1040 LayoutUnit RenderBox::overrideContainingBlockContentLogicalHeight() const
1042 ASSERT(hasOverrideContainingBlockLogicalHeight());
1043 return gOverrideContainingBlockLogicalHeightMap->get(this);
1046 bool RenderBox::hasOverrideContainingBlockLogicalWidth() const
1048 return gOverrideContainingBlockLogicalWidthMap && gOverrideContainingBlockLogicalWidthMap->contains(this);
1051 bool RenderBox::hasOverrideContainingBlockLogicalHeight() const
1053 return gOverrideContainingBlockLogicalHeightMap && gOverrideContainingBlockLogicalHeightMap->contains(this);
1056 void RenderBox::setOverrideContainingBlockContentLogicalWidth(LayoutUnit logicalWidth)
1058 if (!gOverrideContainingBlockLogicalWidthMap)
1059 gOverrideContainingBlockLogicalWidthMap = new OverrideSizeMap;
1060 gOverrideContainingBlockLogicalWidthMap->set(this, logicalWidth);
1063 void RenderBox::setOverrideContainingBlockContentLogicalHeight(LayoutUnit logicalHeight)
1065 if (!gOverrideContainingBlockLogicalHeightMap)
1066 gOverrideContainingBlockLogicalHeightMap = new OverrideSizeMap;
1067 gOverrideContainingBlockLogicalHeightMap->set(this, logicalHeight);
1070 void RenderBox::clearContainingBlockOverrideSize()
1072 if (gOverrideContainingBlockLogicalWidthMap)
1073 gOverrideContainingBlockLogicalWidthMap->remove(this);
1074 clearOverrideContainingBlockContentLogicalHeight();
1077 void RenderBox::clearOverrideContainingBlockContentLogicalHeight()
1079 if (gOverrideContainingBlockLogicalHeightMap)
1080 gOverrideContainingBlockLogicalHeightMap->remove(this);
1083 LayoutUnit RenderBox::adjustBorderBoxLogicalWidthForBoxSizing(LayoutUnit width) const
1085 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth();
1086 if (style()->boxSizing() == CONTENT_BOX)
1087 return width + bordersPlusPadding;
1088 return max(width, bordersPlusPadding);
1091 LayoutUnit RenderBox::adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const
1093 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
1094 if (style()->boxSizing() == CONTENT_BOX)
1095 return height + bordersPlusPadding;
1096 return max(height, bordersPlusPadding);
1099 LayoutUnit RenderBox::adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const
1101 if (style()->boxSizing() == BORDER_BOX)
1102 width -= borderAndPaddingLogicalWidth();
1103 return max<LayoutUnit>(0, width);
1106 LayoutUnit RenderBox::adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit height) const
1108 if (style()->boxSizing() == BORDER_BOX)
1109 height -= borderAndPaddingLogicalHeight();
1110 return max<LayoutUnit>(0, height);
1114 bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1116 LayoutPoint adjustedLocation = accumulatedOffset + location();
1118 // Check kids first.
1119 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1120 if (!child->hasLayer() && child->nodeAtPoint(request, result, locationInContainer, adjustedLocation, action)) {
1121 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
1126 // Check our bounds next. For this purpose always assume that we can only be hit in the
1127 // foreground phase (which is true for replaced elements like images).
1128 LayoutRect boundsRect = borderBoxRect();
1129 boundsRect.moveBy(adjustedLocation);
1130 if (visibleToHitTestRequest(request) && action == HitTestForeground && locationInContainer.intersects(boundsRect)) {
1131 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
1132 if (!result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect))
1139 // --------------------- painting stuff -------------------------------
1141 void RenderBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1143 LayoutPoint adjustedPaintOffset = paintOffset + location();
1144 // default implementation. Just pass paint through to the children
1145 PaintInfo childInfo(paintInfo);
1146 childInfo.updatePaintingRootForChildren(this);
1147 for (RenderObject* child = firstChild(); child; child = child->nextSibling())
1148 child->paint(childInfo, adjustedPaintOffset);
1151 void RenderBox::paintRootBoxFillLayers(const PaintInfo& paintInfo)
1153 if (paintInfo.skipRootBackground())
1156 RenderObject* rootBackgroundRenderer = rendererForRootBackground();
1158 const FillLayer* bgLayer = rootBackgroundRenderer->style()->backgroundLayers();
1159 Color bgColor = rootBackgroundRenderer->resolveColor(CSSPropertyBackgroundColor);
1161 paintFillLayers(paintInfo, bgColor, bgLayer, view()->backgroundRect(this), BackgroundBleedNone, CompositeSourceOver, rootBackgroundRenderer);
1164 BackgroundBleedAvoidance RenderBox::determineBackgroundBleedAvoidance(GraphicsContext* context) const
1166 if (context->paintingDisabled())
1167 return BackgroundBleedNone;
1169 const RenderStyle* style = this->style();
1171 if (!style->hasBackground() || !style->hasBorder() || !style->hasBorderRadius() || borderImageIsLoadedAndCanBeRendered())
1172 return BackgroundBleedNone;
1174 AffineTransform ctm = context->getCTM();
1175 FloatSize contextScaling(static_cast<float>(ctm.xScale()), static_cast<float>(ctm.yScale()));
1177 // Because RoundedRect uses IntRect internally the inset applied by the
1178 // BackgroundBleedShrinkBackground strategy cannot be less than one integer
1179 // layout coordinate, even with subpixel layout enabled. To take that into
1180 // account, we clamp the contextScaling to 1.0 for the following test so
1181 // that borderObscuresBackgroundEdge can only return true if the border
1182 // widths are greater than 2 in both layout coordinates and screen
1184 // This precaution will become obsolete if RoundedRect is ever promoted to
1185 // a sub-pixel representation.
1186 if (contextScaling.width() > 1)
1187 contextScaling.setWidth(1);
1188 if (contextScaling.height() > 1)
1189 contextScaling.setHeight(1);
1191 if (borderObscuresBackgroundEdge(contextScaling))
1192 return BackgroundBleedShrinkBackground;
1193 if (!style->hasAppearance() && borderObscuresBackground() && backgroundHasOpaqueTopLayer())
1194 return BackgroundBleedBackgroundOverBorder;
1196 return BackgroundBleedUseTransparencyLayer;
1199 void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1201 if (!paintInfo.shouldPaintWithinRoot(this))
1204 LayoutRect paintRect = borderBoxRect();
1205 paintRect.moveBy(paintOffset);
1206 paintBoxDecorationsWithRect(paintInfo, paintOffset, paintRect);
1209 void RenderBox::paintBoxDecorationsWithRect(PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutRect& paintRect)
1211 BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context);
1213 // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
1214 // custom shadows of their own.
1215 if (!boxShadowShouldBeAppliedToBackground(bleedAvoidance))
1216 paintBoxShadow(paintInfo, paintRect, style(), Normal);
1218 GraphicsContextStateSaver stateSaver(*paintInfo.context, false);
1219 if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) {
1220 // To avoid the background color bleeding out behind the border, we'll render background and border
1221 // into a transparency layer, and then clip that in one go (which requires setting up the clip before
1222 // beginning the layer).
1223 RoundedRect border = style()->getRoundedBorderFor(paintRect);
1225 paintInfo.context->clipRoundedRect(border);
1226 paintInfo.context->beginTransparencyLayer(1);
1229 paintBackgroundWithBorderAndBoxShadow(paintInfo, paintRect, bleedAvoidance);
1231 if (bleedAvoidance == BackgroundBleedUseTransparencyLayer)
1232 paintInfo.context->endLayer();
1235 void RenderBox::paintBackgroundWithBorderAndBoxShadow(PaintInfo& paintInfo, const LayoutRect& paintRect, BackgroundBleedAvoidance bleedAvoidance)
1237 // If we have a native theme appearance, paint that before painting our background.
1238 // The theme will tell us whether or not we should also paint the CSS background.
1239 IntRect snappedPaintRect(pixelSnappedIntRect(paintRect));
1240 bool themePainted = style()->hasAppearance() && !RenderTheme::theme().paint(this, paintInfo, snappedPaintRect);
1241 if (!themePainted) {
1242 if (bleedAvoidance == BackgroundBleedBackgroundOverBorder)
1243 paintBorder(paintInfo, paintRect, style(), bleedAvoidance);
1245 paintBackground(paintInfo, paintRect, bleedAvoidance);
1247 if (style()->hasAppearance())
1248 RenderTheme::theme().paintDecorations(this, paintInfo, snappedPaintRect);
1250 paintBoxShadow(paintInfo, paintRect, style(), Inset);
1252 // The theme will tell us whether or not we should also paint the CSS border.
1253 if (bleedAvoidance != BackgroundBleedBackgroundOverBorder && (!style()->hasAppearance() || (!themePainted && RenderTheme::theme().paintBorderOnly(this, paintInfo, snappedPaintRect))) && style()->hasBorder() && !(isTable() && toRenderTable(this)->collapseBorders()))
1254 paintBorder(paintInfo, paintRect, style(), bleedAvoidance);
1257 void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, BackgroundBleedAvoidance bleedAvoidance)
1260 paintRootBoxFillLayers(paintInfo);
1263 if (isBody() && skipBodyBackground(this))
1265 if (backgroundIsKnownToBeObscured())
1267 paintFillLayers(paintInfo, resolveColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), paintRect, bleedAvoidance);
1270 LayoutRect RenderBox::backgroundPaintedExtent() const
1272 ASSERT(hasBackground());
1273 LayoutRect backgroundRect = pixelSnappedIntRect(borderBoxRect());
1275 Color backgroundColor = resolveColor(CSSPropertyBackgroundColor);
1276 if (backgroundColor.alpha())
1277 return backgroundRect;
1278 if (!style()->backgroundLayers()->image() || style()->backgroundLayers()->next())
1279 return backgroundRect;
1280 BackgroundImageGeometry geometry;
1281 const_cast<RenderBox*>(this)->calculateBackgroundImageGeometry(style()->backgroundLayers(), backgroundRect, geometry);
1282 return geometry.destRect();
1285 bool RenderBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const
1287 if (isBody() && skipBodyBackground(this))
1290 Color backgroundColor = resolveColor(CSSPropertyBackgroundColor);
1291 if (backgroundColor.hasAlpha())
1294 // If the element has appearance, it might be painted by theme.
1295 // We cannot be sure if theme paints the background opaque.
1296 // In this case it is safe to not assume opaqueness.
1297 // FIXME: May be ask theme if it paints opaque.
1298 if (style()->hasAppearance())
1300 // FIXME: Check the opaqueness of background images.
1302 // FIXME: Use rounded rect if border radius is present.
1303 if (style()->hasBorderRadius())
1305 // FIXME: The background color clip is defined by the last layer.
1306 if (style()->backgroundLayers()->next())
1308 LayoutRect backgroundRect;
1309 switch (style()->backgroundClip()) {
1311 backgroundRect = borderBoxRect();
1313 case PaddingFillBox:
1314 backgroundRect = paddingBoxRect();
1316 case ContentFillBox:
1317 backgroundRect = contentBoxRect();
1322 return backgroundRect.contains(localRect);
1325 static bool isCandidateForOpaquenessTest(RenderBox* childBox)
1327 RenderStyle* childStyle = childBox->style();
1328 if (childStyle->position() != StaticPosition && childBox->containingBlock() != childBox->parent())
1330 if (childStyle->visibility() != VISIBLE || childStyle->shapeOutside())
1332 if (!childBox->width() || !childBox->height())
1334 if (RenderLayer* childLayer = childBox->layer()) {
1335 // FIXME: perhaps this could be less conservative?
1336 if (childLayer->compositingState() != NotComposited)
1338 // FIXME: Deal with z-index.
1339 if (!childStyle->hasAutoZIndex())
1341 if (childLayer->hasTransform() || childLayer->isTransparent() || childLayer->hasFilter())
1343 if (childBox->hasOverflowClip() && childStyle->hasBorderRadius())
1349 bool RenderBox::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
1351 if (!maxDepthToTest)
1353 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
1354 if (!child->isBox())
1356 RenderBox* childBox = toRenderBox(child);
1357 if (!isCandidateForOpaquenessTest(childBox))
1359 LayoutPoint childLocation = childBox->location();
1360 if (childBox->isRelPositioned())
1361 childLocation.move(childBox->relativePositionOffset());
1362 LayoutRect childLocalRect = localRect;
1363 childLocalRect.moveBy(-childLocation);
1364 if (childLocalRect.y() < 0 || childLocalRect.x() < 0) {
1365 // If there is unobscured area above/left of a static positioned box then the rect is probably not covered.
1366 if (childBox->style()->position() == StaticPosition)
1370 if (childLocalRect.maxY() > childBox->height() || childLocalRect.maxX() > childBox->width())
1372 if (childBox->backgroundIsKnownToBeOpaqueInRect(childLocalRect))
1374 if (childBox->foregroundIsKnownToBeOpaqueInRect(childLocalRect, maxDepthToTest - 1))
1380 bool RenderBox::computeBackgroundIsKnownToBeObscured()
1382 // Test to see if the children trivially obscure the background.
1383 // FIXME: This test can be much more comprehensive.
1384 if (!hasBackground())
1386 // Table and root background painting is special.
1387 if (isTable() || isRoot())
1389 // FIXME: box-shadow is painted while background painting.
1390 if (style()->boxShadow())
1392 LayoutRect backgroundRect = backgroundPaintedExtent();
1393 return foregroundIsKnownToBeOpaqueInRect(backgroundRect, backgroundObscurationTestMaxDepth);
1396 bool RenderBox::backgroundHasOpaqueTopLayer() const
1398 const FillLayer* fillLayer = style()->backgroundLayers();
1399 if (!fillLayer || fillLayer->clip() != BorderFillBox)
1402 // Clipped with local scrolling
1403 if (hasOverflowClip() && fillLayer->attachment() == LocalBackgroundAttachment)
1406 if (fillLayer->hasOpaqueImage(this) && fillLayer->hasRepeatXY() && fillLayer->image()->canRender(this, style()->effectiveZoom()))
1409 // If there is only one layer and no image, check whether the background color is opaque
1410 if (!fillLayer->next() && !fillLayer->hasImage()) {
1411 Color bgColor = resolveColor(CSSPropertyBackgroundColor);
1412 if (bgColor.alpha() == 255)
1419 void RenderBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1421 if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask || paintInfo.context->paintingDisabled())
1424 LayoutRect paintRect = LayoutRect(paintOffset, size());
1425 paintMaskImages(paintInfo, paintRect);
1428 void RenderBox::paintClippingMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1430 if (!paintInfo.shouldPaintWithinRoot(this) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseClippingMask || paintInfo.context->paintingDisabled())
1433 if (!layer() || layer()->compositingState() != PaintsIntoOwnBacking)
1436 // We should never have this state in this function. A layer with a mask
1437 // should have always created its own backing if it became composited.
1438 ASSERT(layer()->compositingState() != HasOwnBackingButPaintsIntoAncestor);
1440 LayoutRect paintRect = LayoutRect(paintOffset, size());
1441 paintInfo.context->fillRect(pixelSnappedIntRect(paintRect), Color::black);
1444 void RenderBox::paintMaskImages(const PaintInfo& paintInfo, const LayoutRect& paintRect)
1446 // Figure out if we need to push a transparency layer to render our mask.
1447 bool pushTransparencyLayer = false;
1448 bool compositedMask = hasLayer() && layer()->hasCompositedMask();
1449 bool flattenCompositingLayers = view()->frameView() && view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers;
1450 CompositeOperator compositeOp = CompositeSourceOver;
1452 bool allMaskImagesLoaded = true;
1454 if (!compositedMask || flattenCompositingLayers) {
1455 pushTransparencyLayer = true;
1456 StyleImage* maskBoxImage = style()->maskBoxImage().image();
1457 const FillLayer* maskLayers = style()->maskLayers();
1459 // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content.
1461 allMaskImagesLoaded &= maskBoxImage->isLoaded();
1464 allMaskImagesLoaded &= maskLayers->imagesAreLoaded();
1466 paintInfo.context->setCompositeOperation(CompositeDestinationIn);
1467 paintInfo.context->beginTransparencyLayer(1);
1468 compositeOp = CompositeSourceOver;
1471 if (allMaskImagesLoaded) {
1472 paintFillLayers(paintInfo, Color::transparent, style()->maskLayers(), paintRect, BackgroundBleedNone, compositeOp);
1473 paintNinePieceImage(paintInfo.context, paintRect, style(), style()->maskBoxImage(), compositeOp);
1476 if (pushTransparencyLayer)
1477 paintInfo.context->endLayer();
1480 LayoutRect RenderBox::maskClipRect()
1482 const NinePieceImage& maskBoxImage = style()->maskBoxImage();
1483 if (maskBoxImage.image()) {
1484 LayoutRect borderImageRect = borderBoxRect();
1486 // Apply outsets to the border box.
1487 borderImageRect.expand(style()->maskBoxImageOutsets());
1488 return borderImageRect;
1492 LayoutRect borderBox = borderBoxRect();
1493 for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) {
1494 if (maskLayer->image()) {
1495 BackgroundImageGeometry geometry;
1496 calculateBackgroundImageGeometry(maskLayer, borderBox, geometry);
1497 result.unite(geometry.destRect());
1503 void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect,
1504 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject)
1506 Vector<const FillLayer*, 8> layers;
1507 const FillLayer* curLayer = fillLayer;
1508 bool shouldDrawBackgroundInSeparateBuffer = false;
1510 layers.append(curLayer);
1511 // Stop traversal when an opaque layer is encountered.
1512 // FIXME : It would be possible for the following occlusion culling test to be more aggressive
1513 // on layers with no repeat by testing whether the image covers the layout rect.
1514 // Testing that here would imply duplicating a lot of calculations that are currently done in
1515 // RenderBoxModelObject::paintFillLayerExtended. A more efficient solution might be to move
1516 // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here
1517 // and pass it down.
1519 if (!shouldDrawBackgroundInSeparateBuffer && curLayer->blendMode() != blink::WebBlendModeNormal)
1520 shouldDrawBackgroundInSeparateBuffer = true;
1522 // The clipOccludesNextLayers condition must be evaluated first to avoid short-circuiting.
1523 if (curLayer->clipOccludesNextLayers(curLayer == fillLayer) && curLayer->hasOpaqueImage(this) && curLayer->image()->canRender(this, style()->effectiveZoom()) && curLayer->hasRepeatXY() && curLayer->blendMode() == blink::WebBlendModeNormal && !boxShadowShouldBeAppliedToBackground(bleedAvoidance))
1525 curLayer = curLayer->next();
1528 GraphicsContext* context = paintInfo.context;
1530 shouldDrawBackgroundInSeparateBuffer = false;
1531 if (shouldDrawBackgroundInSeparateBuffer)
1532 context->beginTransparencyLayer(1);
1534 Vector<const FillLayer*>::const_reverse_iterator topLayer = layers.rend();
1535 for (Vector<const FillLayer*>::const_reverse_iterator it = layers.rbegin(); it != topLayer; ++it)
1536 paintFillLayer(paintInfo, c, *it, rect, bleedAvoidance, op, backgroundObject);
1538 if (shouldDrawBackgroundInSeparateBuffer)
1539 context->endLayer();
1542 void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect,
1543 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderObject* backgroundObject)
1545 paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoidance, 0, LayoutSize(), op, backgroundObject);
1548 static bool layersUseImage(WrappedImagePtr image, const FillLayer* layers)
1550 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
1551 if (curLayer->image() && image == curLayer->image()->data())
1558 void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*)
1563 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image) ||
1564 (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) {
1569 ShapeValue* shapeOutsideValue = style()->shapeOutside();
1570 if (!frameView()->isInPerformLayout() && isFloating() && shapeOutsideValue && shapeOutsideValue->image() && shapeOutsideValue->image()->data() == image) {
1571 ShapeOutsideInfo::ensureInfo(this)->dirtyShapeSize();
1572 markShapeOutsideDependentsForLayout();
1575 bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true);
1576 if (!didFullRepaint)
1577 repaintLayerRectsForImage(image, style()->maskLayers(), false);
1579 if (hasLayer() && layer()->hasCompositedMask() && layersUseImage(image, style()->maskLayers()))
1580 layer()->contentChanged(MaskImageChanged);
1583 bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground)
1585 LayoutRect rendererRect;
1586 RenderBox* layerRenderer = 0;
1588 for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) {
1589 if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(this, style()->effectiveZoom())) {
1590 // Now that we know this image is being used, compute the renderer and the rect
1591 // if we haven't already
1592 if (!layerRenderer) {
1593 bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document().documentElement()->renderer()->hasBackground()));
1594 if (drawingRootBackground) {
1595 layerRenderer = view();
1600 if (FrameView* frameView = toRenderView(layerRenderer)->frameView()) {
1601 rw = frameView->contentsWidth();
1602 rh = frameView->contentsHeight();
1604 rw = layerRenderer->width();
1605 rh = layerRenderer->height();
1607 rendererRect = LayoutRect(-layerRenderer->marginLeft(),
1608 -layerRenderer->marginTop(),
1609 max(layerRenderer->width() + layerRenderer->marginWidth() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw),
1610 max(layerRenderer->height() + layerRenderer->marginHeight() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh));
1612 layerRenderer = this;
1613 rendererRect = borderBoxRect();
1617 BackgroundImageGeometry geometry;
1618 layerRenderer->calculateBackgroundImageGeometry(curLayer, rendererRect, geometry);
1619 layerRenderer->repaintRectangle(geometry.destRect());
1620 if (geometry.destRect() == rendererRect)
1627 bool RenderBox::pushContentsClip(PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset, ContentsClipBehavior contentsClipBehavior)
1629 if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseSelfOutline || paintInfo.phase == PaintPhaseMask)
1632 bool isControlClip = hasControlClip();
1633 bool isOverflowClip = hasOverflowClip() && !layer()->isSelfPaintingLayer();
1635 if (!isControlClip && !isOverflowClip)
1638 LayoutRect clipRect = isControlClip ? controlClipRect(accumulatedOffset) : overflowClipRect(accumulatedOffset);
1639 RoundedRect clipRoundedRect(0, 0, 0, 0);
1640 bool hasBorderRadius = style()->hasBorderRadius();
1641 if (hasBorderRadius)
1642 clipRoundedRect = style()->getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, size()));
1644 if (contentsClipBehavior == SkipContentsClipIfPossible) {
1645 LayoutRect contentsVisualOverflow = contentsVisualOverflowRect();
1646 if (contentsVisualOverflow.isEmpty())
1649 // FIXME: Get rid of this slop from here and elsewhere.
1650 // Instead, properly include the outline in visual overflow.
1651 if (RenderView* view = this->view())
1652 contentsVisualOverflow.inflate(view->maximalOutlineSize());
1654 LayoutRect conservativeClipRect = clipRect;
1655 if (hasBorderRadius)
1656 conservativeClipRect.intersect(clipRoundedRect.radiusCenterRect());
1657 conservativeClipRect.moveBy(-accumulatedOffset);
1659 conservativeClipRect.move(scrolledContentOffset());
1660 if (conservativeClipRect.contains(contentsVisualOverflow))
1664 if (paintInfo.phase == PaintPhaseOutline)
1665 paintInfo.phase = PaintPhaseChildOutlines;
1666 else if (paintInfo.phase == PaintPhaseChildBlockBackground) {
1667 paintInfo.phase = PaintPhaseBlockBackground;
1668 paintObject(paintInfo, accumulatedOffset);
1669 paintInfo.phase = PaintPhaseChildBlockBackgrounds;
1671 paintInfo.context->save();
1672 if (hasBorderRadius)
1673 paintInfo.context->clipRoundedRect(clipRoundedRect);
1674 paintInfo.context->clip(pixelSnappedIntRect(clipRect));
1678 void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, const LayoutPoint& accumulatedOffset)
1680 ASSERT(hasControlClip() || (hasOverflowClip() && !layer()->isSelfPaintingLayer()));
1682 paintInfo.context->restore();
1683 if (originalPhase == PaintPhaseOutline) {
1684 paintInfo.phase = PaintPhaseSelfOutline;
1685 paintObject(paintInfo, accumulatedOffset);
1686 paintInfo.phase = originalPhase;
1687 } else if (originalPhase == PaintPhaseChildBlockBackground)
1688 paintInfo.phase = originalPhase;
1691 LayoutRect RenderBox::overflowClipRect(const LayoutPoint& location, OverlayScrollbarSizeRelevancy relevancy)
1693 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property
1695 LayoutRect clipRect = borderBoxRect();
1696 clipRect.setLocation(location + clipRect.location() + LayoutSize(borderLeft(), borderTop()));
1697 clipRect.setSize(clipRect.size() - LayoutSize(borderLeft() + borderRight(), borderTop() + borderBottom()));
1699 if (!hasOverflowClip())
1702 // Subtract out scrollbars if we have them.
1703 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1704 clipRect.move(layer()->scrollableArea()->verticalScrollbarWidth(relevancy), 0);
1705 clipRect.contract(layer()->scrollableArea()->verticalScrollbarWidth(relevancy), layer()->scrollableArea()->horizontalScrollbarHeight(relevancy));
1710 LayoutRect RenderBox::clipRect(const LayoutPoint& location)
1712 LayoutRect borderBoxRect = this->borderBoxRect();
1713 LayoutRect clipRect = LayoutRect(borderBoxRect.location() + location, borderBoxRect.size());
1715 if (!style()->clipLeft().isAuto()) {
1716 LayoutUnit c = valueForLength(style()->clipLeft(), borderBoxRect.width());
1717 clipRect.move(c, 0);
1718 clipRect.contract(c, 0);
1721 if (!style()->clipRight().isAuto())
1722 clipRect.contract(width() - valueForLength(style()->clipRight(), width()), 0);
1724 if (!style()->clipTop().isAuto()) {
1725 LayoutUnit c = valueForLength(style()->clipTop(), borderBoxRect.height());
1726 clipRect.move(0, c);
1727 clipRect.contract(0, c);
1730 if (!style()->clipBottom().isAuto())
1731 clipRect.contract(0, height() - valueForLength(style()->clipBottom(), height()));
1736 LayoutUnit RenderBox::shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart, LayoutUnit childMarginEnd, const RenderBlockFlow* cb) const
1738 LayoutUnit logicalTopPosition = logicalTop();
1739 LayoutUnit result = cb->availableLogicalWidthForLine(logicalTopPosition, false) - childMarginStart - childMarginEnd;
1741 // We need to see if margins on either the start side or the end side can contain the floats in question. If they can,
1742 // then just using the line width is inaccurate. In the case where a float completely fits, we don't need to use the line
1743 // offset at all, but can instead push all the way to the content edge of the containing block. In the case where the float
1744 // doesn't fit, we can use the line offset, but we need to grow it by the margin to reflect the fact that the margin was
1745 // "consumed" by the float. Negative margins aren't consumed by the float, and so we ignore them.
1746 if (childMarginStart > 0) {
1747 LayoutUnit startContentSide = cb->startOffsetForContent();
1748 LayoutUnit startContentSideWithMargin = startContentSide + childMarginStart;
1749 LayoutUnit startOffset = cb->startOffsetForLine(logicalTopPosition, false);
1750 if (startOffset > startContentSideWithMargin)
1751 result += childMarginStart;
1753 result += startOffset - startContentSide;
1756 if (childMarginEnd > 0) {
1757 LayoutUnit endContentSide = cb->endOffsetForContent();
1758 LayoutUnit endContentSideWithMargin = endContentSide + childMarginEnd;
1759 LayoutUnit endOffset = cb->endOffsetForLine(logicalTopPosition, false);
1760 if (endOffset > endContentSideWithMargin)
1761 result += childMarginEnd;
1763 result += endOffset - endContentSide;
1769 LayoutUnit RenderBox::containingBlockLogicalWidthForContent() const
1771 if (hasOverrideContainingBlockLogicalWidth())
1772 return overrideContainingBlockContentLogicalWidth();
1774 RenderBlock* cb = containingBlock();
1775 return cb->availableLogicalWidth();
1778 LayoutUnit RenderBox::containingBlockLogicalHeightForContent(AvailableLogicalHeightType heightType) const
1780 if (hasOverrideContainingBlockLogicalHeight())
1781 return overrideContainingBlockContentLogicalHeight();
1783 RenderBlock* cb = containingBlock();
1784 return cb->availableLogicalHeight(heightType);
1787 LayoutUnit RenderBox::containingBlockAvailableLineWidth() const
1789 RenderBlock* cb = containingBlock();
1790 if (cb->isRenderBlockFlow())
1791 return toRenderBlockFlow(cb)->availableLogicalWidthForLine(logicalTop(), false, availableLogicalHeight(IncludeMarginBorderPadding));
1795 LayoutUnit RenderBox::perpendicularContainingBlockLogicalHeight() const
1797 if (hasOverrideContainingBlockLogicalHeight())
1798 return overrideContainingBlockContentLogicalHeight();
1800 RenderBlock* cb = containingBlock();
1801 if (cb->hasOverrideHeight())
1802 return cb->overrideLogicalContentHeight();
1804 RenderStyle* containingBlockStyle = cb->style();
1805 Length logicalHeightLength = containingBlockStyle->logicalHeight();
1807 // FIXME: For now just support fixed heights. Eventually should support percentage heights as well.
1808 if (!logicalHeightLength.isFixed()) {
1809 LayoutUnit fillFallbackExtent = containingBlockStyle->isHorizontalWritingMode() ? view()->frameView()->visibleHeight() : view()->frameView()->visibleWidth();
1810 LayoutUnit fillAvailableExtent = containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding);
1811 return min(fillAvailableExtent, fillFallbackExtent);
1814 // Use the content box logical height as specified by the style.
1815 return cb->adjustContentBoxLogicalHeightForBoxSizing(logicalHeightLength.value());
1818 void RenderBox::mapLocalToContainer(const RenderLayerModelObject* repaintContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed) const
1820 if (repaintContainer == this)
1823 if (RenderView* v = view()) {
1824 if (v->layoutStateEnabled() && !repaintContainer) {
1825 LayoutState* layoutState = v->layoutState();
1826 LayoutSize offset = layoutState->m_paintOffset + locationOffset();
1827 if (style()->hasInFlowPosition() && layer())
1828 offset += layer()->offsetForInFlowPosition();
1829 transformState.move(offset);
1834 bool containerSkipped;
1835 RenderObject* o = container(repaintContainer, &containerSkipped);
1839 bool isFixedPos = style()->position() == FixedPosition;
1840 bool hasTransform = hasLayer() && layer()->transform();
1841 // If this box has a transform, it acts as a fixed position container for fixed descendants,
1842 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
1843 if (hasTransform && !isFixedPos)
1845 else if (isFixedPos)
1849 *wasFixed = mode & IsFixed;
1851 LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint()));
1853 bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D());
1854 if (mode & UseTransforms && shouldUseTransformFromContainer(o)) {
1855 TransformationMatrix t;
1856 getTransformFromContainer(o, containerOffset, t);
1857 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1859 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1861 if (containerSkipped) {
1862 // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
1863 // to just subtract the delta between the repaintContainer and o.
1864 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
1865 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1869 mode &= ~ApplyContainerFlip;
1871 o->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
1874 void RenderBox::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
1876 // We don't expect to be called during layout.
1877 ASSERT(!view() || !view()->layoutStateEnabled());
1879 bool isFixedPos = style()->position() == FixedPosition;
1880 bool hasTransform = hasLayer() && layer()->transform();
1881 if (hasTransform && !isFixedPos) {
1882 // If this box has a transform, it acts as a fixed position container for fixed descendants,
1883 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
1885 } else if (isFixedPos)
1888 RenderBoxModelObject::mapAbsoluteToLocalPoint(mode, transformState);
1891 LayoutSize RenderBox::offsetFromContainer(RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const
1893 ASSERT(o == container());
1896 if (isInFlowPositioned())
1897 offset += offsetForInFlowPosition();
1899 if (!isInline() || isReplaced()) {
1900 if (!style()->hasOutOfFlowPosition() && o->hasColumns()) {
1901 RenderBlock* block = toRenderBlock(o);
1902 LayoutRect columnRect(frameRect());
1903 block->adjustStartEdgeForWritingModeIncludingColumns(columnRect);
1904 offset += toSize(columnRect.location());
1905 LayoutPoint columnPoint = block->flipForWritingModeIncludingColumns(point + offset);
1906 offset = toLayoutSize(block->flipForWritingModeIncludingColumns(toLayoutPoint(offset)));
1907 o->adjustForColumns(offset, columnPoint);
1908 offset = block->flipForWritingMode(offset);
1910 if (offsetDependsOnPoint)
1911 *offsetDependsOnPoint = true;
1913 offset += topLeftLocationOffset();
1916 if (o->hasOverflowClip())
1917 offset -= toRenderBox(o)->scrolledContentOffset();
1919 if (style()->position() == AbsolutePosition && o->isInFlowPositioned() && o->isRenderInline())
1920 offset += toRenderInline(o)->offsetForInFlowPositionedInline(this);
1922 if (offsetDependsOnPoint)
1923 *offsetDependsOnPoint |= o->isRenderFlowThread();
1928 InlineBox* RenderBox::createInlineBox()
1930 return new InlineBox(this);
1933 void RenderBox::dirtyLineBoxes(bool fullLayout)
1935 if (inlineBoxWrapper()) {
1937 inlineBoxWrapper()->destroy();
1939 m_rareData->m_inlineBoxWrapper = 0;
1941 inlineBoxWrapper()->dirtyLineBoxes();
1946 void RenderBox::positionLineBox(InlineBox* box)
1948 if (isOutOfFlowPositioned()) {
1949 // Cache the x position only if we were an INLINE type originally.
1950 bool wasInline = style()->isOriginalDisplayInlineType();
1952 // The value is cached in the xPos of the box. We only need this value if
1953 // our object was inline originally, since otherwise it would have ended up underneath
1955 RootInlineBox* root = box->root();
1956 root->block()->setStaticInlinePositionForChild(this, root->lineTopWithLeading(), LayoutUnit::fromFloatRound(box->logicalLeft()));
1957 if (style()->hasStaticInlinePosition(box->isHorizontal()))
1958 setChildNeedsLayout(MarkOnlyThis); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
1960 // Our object was a block originally, so we make our normal flow position be
1961 // just below the line box (as though all the inlines that came before us got
1962 // wrapped in an anonymous block, which is what would have happened had we been
1963 // in flow). This value was cached in the y() of the box.
1964 layer()->setStaticBlockPosition(box->logicalTop());
1965 if (style()->hasStaticBlockPosition(box->isHorizontal()))
1966 setChildNeedsLayout(MarkOnlyThis); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly.
1972 } else if (isReplaced()) {
1973 setLocation(roundedLayoutPoint(box->topLeft()));
1974 setInlineBoxWrapper(box);
1978 void RenderBox::deleteLineBoxWrapper()
1980 if (inlineBoxWrapper()) {
1981 if (!documentBeingDestroyed())
1982 inlineBoxWrapper()->remove();
1983 inlineBoxWrapper()->destroy();
1985 m_rareData->m_inlineBoxWrapper = 0;
1989 LayoutRect RenderBox::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
1991 if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent())
1992 return LayoutRect();
1994 LayoutRect r = visualOverflowRect();
1996 RenderView* v = view();
1997 if (!RuntimeEnabledFeatures::repaintAfterLayoutEnabled() && v) {
1998 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
1999 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
2000 r.move(v->layoutDelta());
2004 // We have to use maximalOutlineSize() because a child might have an outline
2005 // that projects outside of our overflowRect.
2007 ASSERT(style()->outlineSize() <= v->maximalOutlineSize());
2008 r.inflate(v->maximalOutlineSize());
2012 computeRectForRepaint(repaintContainer, r);
2016 void RenderBox::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& rect, bool fixed) const
2018 // The rect we compute at each step is shifted by our x/y offset in the parent container's coordinate space.
2019 // Only when we cross a writing mode boundary will we have to possibly flipForWritingMode (to convert into a more appropriate
2020 // offset corner for the enclosing container). This allows for a fully RL or BT document to repaint
2021 // properly even during layout, since the rect remains flipped all the way until the end.
2023 // RenderView::computeRectForRepaint then converts the rect to physical coordinates. We also convert to
2024 // physical when we hit a repaintContainer boundary. Therefore the final rect returned is always in the
2025 // physical coordinate space of the repaintContainer.
2026 RenderStyle* styleToUse = style();
2027 if (RenderView* v = view()) {
2028 // LayoutState is only valid for root-relative, non-fixed position repainting
2029 if (v->layoutStateEnabled() && !repaintContainer && styleToUse->position() != FixedPosition) {
2030 LayoutState* layoutState = v->layoutState();
2032 if (layer() && layer()->transform())
2033 rect = layer()->transform()->mapRect(pixelSnappedIntRect(rect));
2035 // We can't trust the bits on RenderObject, because this might be called while re-resolving style.
2036 if (styleToUse->hasInFlowPosition() && layer())
2037 rect.move(layer()->offsetForInFlowPosition());
2039 rect.moveBy(location());
2040 rect.move(layoutState->m_paintOffset);
2041 if (layoutState->m_clipped)
2042 rect.intersect(layoutState->m_clipRect);
2047 if (hasReflection())
2048 rect.unite(reflectedRect(rect));
2050 if (repaintContainer == this) {
2051 if (repaintContainer->style()->isFlippedBlocksWritingMode())
2052 flipForWritingMode(rect);
2056 bool containerSkipped;
2057 RenderObject* o = container(repaintContainer, &containerSkipped);
2061 if (isWritingModeRoot() && !isOutOfFlowPositioned())
2062 flipForWritingMode(rect);
2064 LayoutPoint topLeft = rect.location();
2065 topLeft.move(locationOffset());
2067 EPosition position = styleToUse->position();
2069 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box
2070 // in the parent's coordinate space that encloses us.
2071 if (hasLayer() && layer()->transform()) {
2072 fixed = position == FixedPosition;
2073 rect = layer()->transform()->mapRect(pixelSnappedIntRect(rect));
2074 topLeft = rect.location();
2075 topLeft.move(locationOffset());
2076 } else if (position == FixedPosition)
2079 if (position == AbsolutePosition && o->isInFlowPositioned() && o->isRenderInline()) {
2080 topLeft += toRenderInline(o)->offsetForInFlowPositionedInline(this);
2081 } else if (styleToUse->hasInFlowPosition() && layer()) {
2082 // Apply the relative position offset when invalidating a rectangle. The layer
2083 // is translated, but the render box isn't, so we need to do this to get the
2084 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
2085 // flag on the RenderObject has been cleared, so use the one on the style().
2086 topLeft += layer()->offsetForInFlowPosition();
2089 if (position != AbsolutePosition && position != FixedPosition && o->hasColumns() && o->isRenderBlockFlow()) {
2090 LayoutRect repaintRect(topLeft, rect.size());
2091 toRenderBlock(o)->adjustRectForColumns(repaintRect);
2092 topLeft = repaintRect.location();
2096 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
2097 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
2098 rect.setLocation(topLeft);
2099 if (o->hasOverflowClip()) {
2100 RenderBox* containerBox = toRenderBox(o);
2101 containerBox->applyCachedClipAndScrollOffsetForRepaint(rect);
2106 if (containerSkipped) {
2107 // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
2108 LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(o);
2109 rect.move(-containerOffset);
2113 o->computeRectForRepaint(repaintContainer, rect, fixed);
2116 void RenderBox::repaintDuringLayoutIfMoved(const LayoutRect& oldRect)
2118 if (oldRect.location() != m_frameRect.location()) {
2119 LayoutRect newRect = m_frameRect;
2120 // The child moved. Invalidate the object's old and new positions. We have to do this
2121 // since the object may not have gotten a layout.
2122 m_frameRect = oldRect;
2124 repaintOverhangingFloats(true);
2125 m_frameRect = newRect;
2127 repaintOverhangingFloats(true);
2131 void RenderBox::repaintOverhangingFloats(bool)
2135 void RenderBox::updateLogicalWidth()
2137 LogicalExtentComputedValues computedValues;
2138 computeLogicalWidth(computedValues);
2140 setLogicalWidth(computedValues.m_extent);
2141 setLogicalLeft(computedValues.m_position);
2142 setMarginStart(computedValues.m_margins.m_start);
2143 setMarginEnd(computedValues.m_margins.m_end);
2146 static float getMaxWidthListMarker(const RenderBox* renderer)
2150 Node* parentNode = renderer->generatingNode();
2152 ASSERT(parentNode->hasTagName(olTag) || parentNode->hasTagName(ulTag));
2153 ASSERT(renderer->style()->textAutosizingMultiplier() != 1);
2156 for (RenderObject* child = renderer->firstChild(); child; child = child->nextSibling()) {
2157 if (!child->isListItem())
2160 RenderBox* listItem = toRenderBox(child);
2161 for (RenderObject* itemChild = listItem->firstChild(); itemChild; itemChild = itemChild->nextSibling()) {
2162 if (!itemChild->isListMarker())
2164 RenderBox* itemMarker = toRenderBox(itemChild);
2165 // FIXME: canDetermineWidthWithoutLayout expects us to use fixedOffsetWidth, which this code
2166 // does not do! This check is likely wrong.
2167 if (!itemMarker->canDetermineWidthWithoutLayout() && itemMarker->needsLayout()) {
2168 // Make sure to compute the autosized width.
2169 itemMarker->layout();
2171 maxWidth = max<float>(maxWidth, toRenderListMarker(itemMarker)->logicalWidth().toFloat());
2178 void RenderBox::computeLogicalWidth(LogicalExtentComputedValues& computedValues) const
2180 computedValues.m_extent = logicalWidth();
2181 computedValues.m_position = logicalLeft();
2182 computedValues.m_margins.m_start = marginStart();
2183 computedValues.m_margins.m_end = marginEnd();
2185 if (isOutOfFlowPositioned()) {
2186 // FIXME: This calculation is not patched for block-flow yet.
2187 // https://bugs.webkit.org/show_bug.cgi?id=46500
2188 computePositionedLogicalWidth(computedValues);
2192 // If layout is limited to a subtree, the subtree root's logical width does not change.
2193 if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this)
2196 // The parent box is flexing us, so it has increased or decreased our
2197 // width. Use the width from the style context.
2198 // FIXME: Account for block-flow in flexible boxes.
2199 // https://bugs.webkit.org/show_bug.cgi?id=46418
2200 if (hasOverrideWidth() && (style()->borderFit() == BorderFitLines || parent()->isFlexibleBoxIncludingDeprecated())) {
2201 computedValues.m_extent = overrideLogicalContentWidth() + borderAndPaddingLogicalWidth();
2205 // FIXME: Account for block-flow in flexible boxes.
2206 // https://bugs.webkit.org/show_bug.cgi?id=46418
2207 bool inVerticalBox = parent()->isDeprecatedFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL);
2208 bool stretching = (parent()->style()->boxAlign() == BSTRETCH);
2209 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching);
2211 RenderStyle* styleToUse = style();
2212 Length logicalWidthLength = treatAsReplaced ? Length(computeReplacedLogicalWidth(), Fixed) : styleToUse->logicalWidth();
2214 RenderBlock* cb = containingBlock();
2215 LayoutUnit containerLogicalWidth = max<LayoutUnit>(0, containingBlockLogicalWidthForContent());
2216 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode();
2218 if (isInline() && !isInlineBlockOrInlineTable()) {
2219 // just calculate margins
2220 computedValues.m_margins.m_start = minimumValueForLength(styleToUse->marginStart(), containerLogicalWidth);
2221 computedValues.m_margins.m_end = minimumValueForLength(styleToUse->marginEnd(), containerLogicalWidth);
2222 if (treatAsReplaced)
2223 computedValues.m_extent = max<LayoutUnit>(floatValueForLength(logicalWidthLength, 0) + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth());
2227 // Width calculations
2228 if (treatAsReplaced)
2229 computedValues.m_extent = logicalWidthLength.value() + borderAndPaddingLogicalWidth();
2231 LayoutUnit containerWidthInInlineDirection = containerLogicalWidth;
2232 if (hasPerpendicularContainingBlock)
2233 containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight();
2234 LayoutUnit preferredWidth = computeLogicalWidthUsing(MainOrPreferredSize, styleToUse->logicalWidth(), containerWidthInInlineDirection, cb);
2235 computedValues.m_extent = constrainLogicalWidthByMinMax(preferredWidth, containerWidthInInlineDirection, cb);
2238 // Margin calculations.
2239 if (hasPerpendicularContainingBlock || isFloating() || isInline()) {
2240 computedValues.m_margins.m_start = minimumValueForLength(styleToUse->marginStart(), containerLogicalWidth);
2241 computedValues.m_margins.m_end = minimumValueForLength(styleToUse->marginEnd(), containerLogicalWidth);
2243 LayoutUnit containerLogicalWidthForAutoMargins = containerLogicalWidth;
2244 if (avoidsFloats() && cb->containsFloats())
2245 containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidth();
2246 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection();
2247 computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, computedValues.m_extent,
2248 hasInvertedDirection ? computedValues.m_margins.m_end : computedValues.m_margins.m_start,
2249 hasInvertedDirection ? computedValues.m_margins.m_start : computedValues.m_margins.m_end);
2252 if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (computedValues.m_extent + computedValues.m_margins.m_start + computedValues.m_margins.m_end)
2253 && !isFloating() && !isInline() && !cb->isFlexibleBoxIncludingDeprecated() && !cb->isRenderGrid()) {
2254 LayoutUnit newMargin = containerLogicalWidth - computedValues.m_extent - cb->marginStartForChild(this);
2255 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection();
2256 if (hasInvertedDirection)
2257 computedValues.m_margins.m_start = newMargin;
2259 computedValues.m_margins.m_end = newMargin;
2262 if (styleToUse->textAutosizingMultiplier() != 1 && styleToUse->marginStart().type() == Fixed) {
2263 Node* parentNode = generatingNode();
2264 if (parentNode && (parentNode->hasTagName(olTag) || parentNode->hasTagName(ulTag))) {
2265 // Make sure the markers in a list are properly positioned (i.e. not chopped off) when autosized.
2266 const float adjustedMargin = (1 - 1.0 / styleToUse->textAutosizingMultiplier()) * getMaxWidthListMarker(this);
2267 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection();
2268 if (hasInvertedDirection)
2269 computedValues.m_margins.m_end += adjustedMargin;
2271 computedValues.m_margins.m_start += adjustedMargin;
2276 LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth) const
2278 LayoutUnit marginStart = 0;
2279 LayoutUnit marginEnd = 0;
2280 return fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd);
2283 LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const
2285 marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth);
2286 marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth);
2287 return availableLogicalWidth - marginStart - marginEnd;
2290 LayoutUnit RenderBox::computeIntrinsicLogicalWidthUsing(Length logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const
2292 if (logicalWidthLength.type() == FillAvailable)
2293 return fillAvailableMeasure(availableLogicalWidth);
2295 LayoutUnit minLogicalWidth = 0;
2296 LayoutUnit maxLogicalWidth = 0;
2297 computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth);
2299 if (logicalWidthLength.type() == MinContent)
2300 return minLogicalWidth + borderAndPadding;
2302 if (logicalWidthLength.type() == MaxContent)
2303 return maxLogicalWidth + borderAndPadding;
2305 if (logicalWidthLength.type() == FitContent) {
2306 minLogicalWidth += borderAndPadding;
2307 maxLogicalWidth += borderAndPadding;
2308 return max(minLogicalWidth, min(maxLogicalWidth, fillAvailableMeasure(availableLogicalWidth)));
2311 ASSERT_NOT_REACHED();
2315 LayoutUnit RenderBox::computeLogicalWidthUsing(SizeType widthType, Length logicalWidth, LayoutUnit availableLogicalWidth, const RenderBlock* cb) const
2317 if (!logicalWidth.isIntrinsicOrAuto()) {
2318 // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead.
2319 return adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, availableLogicalWidth));
2322 if (logicalWidth.isIntrinsic())
2323 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth());
2325 LayoutUnit marginStart = 0;
2326 LayoutUnit marginEnd = 0;
2327 LayoutUnit logicalWidthResult = fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd);
2329 if (shrinkToAvoidFloats() && cb->containsFloats())
2330 logicalWidthResult = min(logicalWidthResult, shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, toRenderBlockFlow(cb)));
2332 if (widthType == MainOrPreferredSize && sizesLogicalWidthToFitContent(widthType))
2333 return max(minPreferredLogicalWidth(), min(maxPreferredLogicalWidth(), logicalWidthResult));
2334 return logicalWidthResult;
2337 static bool columnFlexItemHasStretchAlignment(const RenderObject* flexitem)
2339 RenderObject* parent = flexitem->parent();
2340 // auto margins mean we don't stretch. Note that this function will only be used for
2341 // widths, so we don't have to check marginBefore/marginAfter.
2342 ASSERT(parent->style()->isColumnFlexDirection());
2343 if (flexitem->style()->marginStart().isAuto() || flexitem->style()->marginEnd().isAuto())
2345 return flexitem->style()->alignSelf() == ItemPositionStretch || (flexitem->style()->alignSelf() == ItemPositionAuto && parent->style()->alignItems() == ItemPositionStretch);
2348 static bool isStretchingColumnFlexItem(const RenderObject* flexitem)
2350 RenderObject* parent = flexitem->parent();
2351 if (parent->isDeprecatedFlexibleBox() && parent->style()->boxOrient() == VERTICAL && parent->style()->boxAlign() == BSTRETCH)
2354 // We don't stretch multiline flexboxes because they need to apply line spacing (align-content) first.
2355 if (parent->isFlexibleBox() && parent->style()->flexWrap() == FlexNoWrap && parent->style()->isColumnFlexDirection() && columnFlexItemHasStretchAlignment(flexitem))
2360 bool RenderBox::sizesLogicalWidthToFitContent(SizeType widthType) const
2362 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
2363 // but they allow text to sit on the same line as the marquee.
2364 if (isFloating() || (isInlineBlockOrInlineTable() && !isMarquee()))
2367 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both
2368 // min-width and width. max-width is only clamped if it is also intrinsic.
2369 Length logicalWidth = (widthType == MaxSize) ? style()->logicalMaxWidth() : style()->logicalWidth();
2370 if (logicalWidth.type() == Intrinsic)
2373 // Children of a horizontal marquee do not fill the container by default.
2374 // FIXME: Need to deal with MAUTO value properly. It could be vertical.
2375 // FIXME: Think about block-flow here. Need to find out how marquee direction relates to
2376 // block-flow (as well as how marquee overflow should relate to block flow).
2377 // https://bugs.webkit.org/show_bug.cgi?id=46472
2378 if (parent()->isMarquee()) {
2379 EMarqueeDirection dir = parent()->style()->marqueeDirection();
2380 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
2384 // Flexible box items should shrink wrap, so we lay them out at their intrinsic widths.
2385 // In the case of columns that have a stretch alignment, we go ahead and layout at the
2386 // stretched size to avoid an extra layout when applying alignment.
2387 if (parent()->isFlexibleBox()) {
2388 // For multiline columns, we need to apply align-content first, so we can't stretch now.
2389 if (!parent()->style()->isColumnFlexDirection() || parent()->style()->flexWrap() != FlexNoWrap)
2391 if (!columnFlexItemHasStretchAlignment(this))
2395 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes
2396 // that don't stretch their kids lay out their children at their intrinsic widths.
2397 // FIXME: Think about block-flow here.
2398 // https://bugs.webkit.org/show_bug.cgi?id=46473
2399 if (parent()->isDeprecatedFlexibleBox() && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
2402 // Button, input, select, textarea, and legend treat width value of 'auto' as 'intrinsic' unless it's in a
2403 // stretching column flexbox.
2404 // FIXME: Think about block-flow here.
2405 // https://bugs.webkit.org/show_bug.cgi?id=46473
2406 if (logicalWidth.type() == Auto && !isStretchingColumnFlexItem(this) && autoWidthShouldFitContent())
2409 if (isHorizontalWritingMode() != containingBlock()->isHorizontalWritingMode())
2415 bool RenderBox::autoWidthShouldFitContent() const
2417 return node() && (node()->hasTagName(inputTag) || node()->hasTagName(selectTag) || node()->hasTagName(buttonTag)
2418 || node()->hasTagName(textareaTag) || (node()->hasTagName(legendTag) && !style()->hasOutOfFlowPosition()));
2421 void RenderBox::computeInlineDirectionMargins(RenderBlock* containingBlock, LayoutUnit containerWidth, LayoutUnit childWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const
2423 const RenderStyle* containingBlockStyle = containingBlock->style();
2424 Length marginStartLength = style()->marginStartUsing(containingBlockStyle);
2425 Length marginEndLength = style()->marginEndUsing(containingBlockStyle);
2427 if (isFloating() || isInline()) {
2428 // Inline blocks/tables and floats don't have their margins increased.
2429 marginStart = minimumValueForLength(marginStartLength, containerWidth);
2430 marginEnd = minimumValueForLength(marginEndLength, containerWidth);
2434 if (containingBlock->isFlexibleBox()) {
2435 // We need to let flexbox handle the margin adjustment - otherwise, flexbox
2436 // will think we're wider than we actually are and calculate line sizes wrong.
2437 // See also http://dev.w3.org/csswg/css-flexbox/#auto-margins
2438 if (marginStartLength.isAuto())
2439 marginStartLength.setValue(0);
2440 if (marginEndLength.isAuto())
2441 marginEndLength.setValue(0);
2444 // Case One: The object is being centered in the containing block's available logical width.
2445 if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth)
2446 || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock->style()->textAlign() == WEBKIT_CENTER)) {
2447 // Other browsers center the margin box for align=center elements so we match them here.
2448 LayoutUnit marginStartWidth = minimumValueForLength(marginStartLength, containerWidth);
2449 LayoutUnit marginEndWidth = minimumValueForLength(marginEndLength, containerWidth);
2450 LayoutUnit centeredMarginBoxStart = max<LayoutUnit>(0, (containerWidth - childWidth - marginStartWidth - marginEndWidth) / 2);
2451 marginStart = centeredMarginBoxStart + marginStartWidth;
2452 marginEnd = containerWidth - childWidth - marginStart + marginEndWidth;
2456 // Case Two: The object is being pushed to the start of the containing block's available logical width.
2457 if (marginEndLength.isAuto() && childWidth < containerWidth) {
2458 marginStart = valueForLength(marginStartLength, containerWidth);
2459 marginEnd = containerWidth - childWidth - marginStart;
2463 // Case Three: The object is being pushed to the end of the containing block's available logical width.
2464 bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_LEFT)
2465 || (containingBlockStyle->isLeftToRightDirection() && containingBlockStyle->textAlign() == WEBKIT_RIGHT));
2466 if ((marginStartLength.isAuto() && childWidth < containerWidth) || pushToEndFromTextAlign) {
2467 marginEnd = valueForLength(marginEndLength, containerWidth);
2468 marginStart = containerWidth - childWidth - marginEnd;
2472 // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3). In that case
2473 // auto margins will just turn into 0.
2474 marginStart = minimumValueForLength(marginStartLength, containerWidth);
2475 marginEnd = minimumValueForLength(marginEndLength, containerWidth);
2478 static bool shouldFlipBeforeAfterMargins(const RenderStyle* containingBlockStyle, const RenderStyle* childStyle)
2480 ASSERT(containingBlockStyle->isHorizontalWritingMode() != childStyle->isHorizontalWritingMode());
2481 WritingMode childWritingMode = childStyle->writingMode();
2482 bool shouldFlip = false;
2483 switch (containingBlockStyle->writingMode()) {
2484 case TopToBottomWritingMode:
2485 shouldFlip = (childWritingMode == RightToLeftWritingMode);
2487 case BottomToTopWritingMode:
2488 shouldFlip = (childWritingMode == RightToLeftWritingMode);
2490 case RightToLeftWritingMode:
2491 shouldFlip = (childWritingMode == BottomToTopWritingMode);
2493 case LeftToRightWritingMode:
2494 shouldFlip = (childWritingMode == BottomToTopWritingMode);
2498 if (!containingBlockStyle->isLeftToRightDirection())
2499 shouldFlip = !shouldFlip;
2504 void RenderBox::updateLogicalHeight()
2506 m_intrinsicContentLogicalHeight = contentLogicalHeight();
2508 LogicalExtentComputedValues computedValues;
2509 computeLogicalHeight(logicalHeight(), logicalTop(), computedValues);
2511 setLogicalHeight(computedValues.m_extent);
2512 setLogicalTop(computedValues.m_position);
2513 setMarginBefore(computedValues.m_margins.m_before);
2514 setMarginAfter(computedValues.m_margins.m_after);
2517 void RenderBox::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
2519 computedValues.m_extent = logicalHeight;
2520 computedValues.m_position = logicalTop;
2522 // Cell height is managed by the table and inline non-replaced elements do not support a height property.
2523 if (isTableCell() || (isInline() && !isReplaced()))
2527 if (isOutOfFlowPositioned())
2528 computePositionedLogicalHeight(computedValues);
2530 RenderBlock* cb = containingBlock();
2531 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode();
2533 if (!hasPerpendicularContainingBlock) {
2534 bool shouldFlipBeforeAfter = cb->style()->writingMode() != style()->writingMode();
2535 computeBlockDirectionMargins(cb,
2536 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before,
2537 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after);
2540 // For tables, calculate margins only.
2542 if (hasPerpendicularContainingBlock) {
2543 bool shouldFlipBeforeAfter = shouldFlipBeforeAfterMargins(cb->style(), style());
2544 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), computedValues.m_extent,
2545 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before,
2546 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after);
2551 // FIXME: Account for block-flow in flexible boxes.
2552 // https://bugs.webkit.org/show_bug.cgi?id=46418
2553 bool inHorizontalBox = parent()->isDeprecatedFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL;
2554 bool stretching = parent()->style()->boxAlign() == BSTRETCH;
2555 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching);
2556 bool checkMinMaxHeight = false;
2558 // The parent box is flexing us, so it has increased or decreased our height. We have to
2559 // grab our cached flexible height.
2560 // FIXME: Account for block-flow in flexible boxes.
2561 // https://bugs.webkit.org/show_bug.cgi?id=46418
2562 if (hasOverrideHeight() && parent()->isFlexibleBoxIncludingDeprecated())
2563 h = Length(overrideLogicalContentHeight(), Fixed);
2564 else if (treatAsReplaced)
2565 h = Length(computeReplacedLogicalHeight(), Fixed);
2567 h = style()->logicalHeight();
2568 checkMinMaxHeight = true;
2571 // Block children of horizontal flexible boxes fill the height of the box.
2572 // FIXME: Account for block-flow in flexible boxes.
2573 // https://bugs.webkit.org/show_bug.cgi?id=46418
2574 if (h.isAuto() && inHorizontalBox && toRenderDeprecatedFlexibleBox(parent())->isStretchingChildren()) {
2575 h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter() - borderAndPaddingLogicalHeight(), Fixed);
2576 checkMinMaxHeight = false;
2579 LayoutUnit heightResult;
2580 if (checkMinMaxHeight) {
2581 heightResult = computeLogicalHeightUsing(style()->logicalHeight(), computedValues.m_extent - borderAndPaddingLogicalHeight());
2582 if (heightResult == -1)
2583 heightResult = computedValues.m_extent;
2584 heightResult = constrainLogicalHeightByMinMax(heightResult, computedValues.m_extent - borderAndPaddingLogicalHeight());
2586 // The only times we don't check min/max height are when a fixed length has
2587 // been given as an override. Just use that. The value has already been adjusted
2589 ASSERT(h.isFixed());
2590 heightResult = h.value() + borderAndPaddingLogicalHeight();
2593 computedValues.m_extent = heightResult;
2595 if (hasPerpendicularContainingBlock) {
2596 bool shouldFlipBeforeAfter = shouldFlipBeforeAfterMargins(cb->style(), style());
2597 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), heightResult,
2598 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before,
2599 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after);
2603 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the
2604 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height
2605 // is specified. When we're printing, we also need this quirk if the body or root has a percentage
2606 // height since we don't set a height in RenderView when we're printing. So without this quirk, the
2607 // height has nothing to be a percentage of, and it ends up being 0. That is bad.
2608 bool paginatedContentNeedsBaseHeight = document().printing() && h.isPercent()
2609 && (isRoot() || (isBody() && document().documentElement()->renderer()->style()->logicalHeight().isPercent())) && !isInline();
2610 if (stretchesToViewport() || paginatedContentNeedsBaseHeight) {
2611 LayoutUnit margins = collapsedMarginBefore() + collapsedMarginAfter();
2612 LayoutUnit visibleHeight = viewLogicalHeightForPercentages();
2614 computedValues.m_extent = max(computedValues.m_extent, visibleHeight - margins);
2616 LayoutUnit marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight();
2617 computedValues.m_extent = max(computedValues.m_extent, visibleHeight - marginsBordersPadding);
2622 LayoutUnit RenderBox::viewLogicalHeightForPercentages() const
2624 if (document().printing())
2625 return static_cast<LayoutUnit>(view()->pageLogicalHeight());
2626 return view()->viewLogicalHeight();
2629 LayoutUnit RenderBox::computeLogicalHeightUsing(const Length& height, LayoutUnit intrinsicContentHeight) const
2631 LayoutUnit logicalHeight = computeContentAndScrollbarLogicalHeightUsing(height, intrinsicContentHeight);
2632 if (logicalHeight != -1)
2633 logicalHeight = adjustBorderBoxLogicalHeightForBoxSizing(logicalHeight);
2634 return logicalHeight;
2637 LayoutUnit RenderBox::computeContentLogicalHeight(const Length& height, LayoutUnit intrinsicContentHeight) const
2639 LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(height, intrinsicContentHeight);
2640 if (heightIncludingScrollbar == -1)
2642 return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight());
2645 LayoutUnit RenderBox::computeIntrinsicLogicalContentHeightUsing(Length logicalHeightLength, LayoutUnit intrinsicContentHeight, LayoutUnit borderAndPadding) const
2647 // FIXME(cbiesinger): The css-sizing spec is considering changing what min-content/max-content should resolve to.
2648 // If that happens, this code will have to change.
2649 if (logicalHeightLength.isMinContent() || logicalHeightLength.isMaxContent() || logicalHeightLength.isFitContent()) {
2651 return intrinsicSize().height();
2652 if (m_intrinsicContentLogicalHeight != -1)
2653 return m_intrinsicContentLogicalHeight;
2654 return intrinsicContentHeight;
2656 if (logicalHeightLength.isFillAvailable())
2657 return containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding) - borderAndPadding;
2658 ASSERT_NOT_REACHED();
2662 LayoutUnit RenderBox::computeContentAndScrollbarLogicalHeightUsing(const Length& height, LayoutUnit intrinsicContentHeight) const
2664 // FIXME(cbiesinger): The css-sizing spec is considering changing what min-content/max-content should resolve to.
2665 // If that happens, this code will have to change.
2666 if (height.isIntrinsic()) {
2667 if (intrinsicContentHeight == -1)
2668 return -1; // Intrinsic height isn't available.
2669 return computeIntrinsicLogicalContentHeightUsing(height, intrinsicContentHeight, borderAndPaddingLogicalHeight());
2671 if (height.isFixed())
2672 return height.value();
2673 if (height.isPercent())
2674 return computePercentageLogicalHeight(height);
2678 bool RenderBox::skipContainingBlockForPercentHeightCalculation(const RenderBox* containingBlock) const
2680 // For quirks mode and anonymous blocks, we skip auto-height containingBlocks when computing percentages.
2681 // For standards mode, we treat the percentage as auto if it has an auto-height containing block.
2682 if (!document().inQuirksMode() && !containingBlock->isAnonymousBlock())
2684 return !containingBlock->isTableCell() && !containingBlock->isOutOfFlowPositioned() && containingBlock->style()->logicalHeight().isAuto() && isHorizontalWritingMode() == containingBlock->isHorizontalWritingMode();
2687 LayoutUnit RenderBox::computePercentageLogicalHeight(const Length& height) const
2689 LayoutUnit availableHeight = -1;
2691 bool skippedAutoHeightContainingBlock = false;
2692 RenderBlock* cb = containingBlock();
2693 const RenderBox* containingBlockChild = this;
2694 LayoutUnit rootMarginBorderPaddingHeight = 0;
2695 while (!cb->isRenderView() && skipContainingBlockForPercentHeightCalculation(cb)) {
2696 if (cb->isBody() || cb->isRoot())
2697 rootMarginBorderPaddingHeight += cb->marginBefore() + cb->marginAfter() + cb->borderAndPaddingLogicalHeight();
2698 skippedAutoHeightContainingBlock = true;
2699 containingBlockChild = cb;
2700 cb = cb->containingBlock();
2701 cb->addPercentHeightDescendant(const_cast<RenderBox*>(this));
2704 RenderStyle* cbstyle = cb->style();
2706 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height
2707 // explicitly specified that can be used for any percentage computations.
2708 bool isOutOfFlowPositionedWithSpecifiedHeight = cb->isOutOfFlowPositioned() && (!cbstyle->logicalHeight().isAuto() || (!cbstyle->logicalTop().isAuto() && !cbstyle->logicalBottom().isAuto()));
2710 bool includeBorderPadding = isTable();
2712 if (isHorizontalWritingMode() != cb->isHorizontalWritingMode())
2713 availableHeight = containingBlockChild->containingBlockLogicalWidthForContent();
2714 else if (hasOverrideContainingBlockLogicalHeight())
2715 availableHeight = overrideContainingBlockContentLogicalHeight();
2716 else if (cb->isTableCell()) {
2717 if (!skippedAutoHeightContainingBlock) {
2718 // Table cells violate what the CSS spec says to do with heights. Basically we
2719 // don't care if the cell specified a height or not. We just always make ourselves
2720 // be a percentage of the cell's current content height.
2721 if (!cb->hasOverrideHeight()) {
2722 // Normally we would let the cell size intrinsically, but scrolling overflow has to be
2723 // treated differently, since WinIE lets scrolled overflow regions shrink as needed.
2724 // While we can't get all cases right, we can at least detect when the cell has a specified
2725 // height or when the table has a specified height. In these cases we want to initially have
2726 // no size and allow the flexing of the table or the cell to its specified height to cause us
2727 // to grow to fill the space. This could end up being wrong in some cases, but it is
2728 // preferable to the alternative (sizing intrinsically and making the row end up too big).
2729 RenderTableCell* cell = toRenderTableCell(cb);
2730 if (scrollsOverflowY() && (!cell->style()->logicalHeight().isAuto() || !cell->table()->style()->logicalHeight().isAuto()))
2734 availableHeight = cb->overrideLogicalContentHeight();
2735 includeBorderPadding = true;
2737 } else if (cbstyle->logicalHeight().isFixed()) {
2738 LayoutUnit contentBoxHeight = cb->adjustContentBoxLogicalHeightForBoxSizing(cbstyle->logicalHeight().value());
2739 availableHeight = max<LayoutUnit>(0, cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeight - cb->scrollbarLogicalHeight(), -1));
2740 } else if (cbstyle->logicalHeight().isPercent() && !isOutOfFlowPositionedWithSpecifiedHeight) {
2741 // We need to recur and compute the percentage height for our containing block.
2742 LayoutUnit heightWithScrollbar = cb->computePercentageLogicalHeight(cbstyle->logicalHeight());
2743 if (heightWithScrollbar != -1) {
2744 LayoutUnit contentBoxHeightWithScrollbar = cb->adjustContentBoxLogicalHeightForBoxSizing(heightWithScrollbar);
2745 // We need to adjust for min/max height because this method does not
2746 // handle the min/max of the current block, its caller does. So the
2747 // return value from the recursive call will not have been adjusted
2749 LayoutUnit contentBoxHeight = cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeightWithScrollbar - cb->scrollbarLogicalHeight(), -1);
2750 availableHeight = max<LayoutUnit>(0, contentBoxHeight);
2752 } else if (isOutOfFlowPositionedWithSpecifiedHeight) {
2753 // Don't allow this to affect the block' height() member variable, since this
2754 // can get called while the block is still laying out its kids.
2755 LogicalExtentComputedValues computedValues;
2756 cb->computeLogicalHeight(cb->logicalHeight(), 0, computedValues);
2757 availableHeight = computedValues.m_extent - cb->borderAndPaddingLogicalHeight() - cb->scrollbarLogicalHeight();
2758 } else if (cb->isRenderView())
2759 availableHeight = viewLogicalHeightForPercentages();
2761 if (availableHeight == -1)
2762 return availableHeight;
2764 availableHeight -= rootMarginBorderPaddingHeight;
2766 LayoutUnit result = valueForLength(height, availableHeight);
2767 if (includeBorderPadding) {
2768 // FIXME: Table cells should default to box-sizing: border-box so we can avoid this hack.
2769 // It is necessary to use the border-box to match WinIE's broken
2770 // box model. This is essential for sizing inside
2771 // table cells using percentage heights.
2772 result -= borderAndPaddingLogicalHeight();
2773 return max<LayoutUnit>(0, result);
2778 LayoutUnit RenderBox::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
2780 return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(style()->logicalWidth()), shouldComputePreferred);
2783 LayoutUnit RenderBox::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred shouldComputePreferred) const
2785 LayoutUnit minLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMinWidth().isPercent()) || style()->logicalMinWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMinWidth());
2786 LayoutUnit maxLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMaxWidth().isPercent()) || style()->logicalMaxWidth().isUndefined() ? logicalWidth : computeReplacedLogicalWidthUsing(style()->logicalMaxWidth());
2787 return max(minLogicalWidth, min(logicalWidth, maxLogicalWidth));
2790 LayoutUnit RenderBox::computeReplacedLogicalWidthUsing(Length logicalWidth) const
2792 switch (logicalWidth.type()) {
2794 return adjustContentBoxLogicalWidthForBoxSizing(logicalWidth.value());
2797 // MinContent/MaxContent don't need the availableLogicalWidth argument.
2798 LayoutUnit availableLogicalWidth = 0;
2799 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
2805 // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's block-flow is perpendicular to the
2806 // containing block's block-flow.
2807 // https://bugs.webkit.org/show_bug.cgi?id=46496
2808 const LayoutUnit cw = isOutOfFlowPositioned() ? containingBlockLogicalWidthForPositioned(toRenderBoxModelObject(container())) : containingBlockLogicalWidthForContent();
2809 Length containerLogicalWidth = containingBlock()->style()->logicalWidth();
2810 // FIXME: Handle cases when containing block width is calculated or viewport percent.
2811 // https://bugs.webkit.org/show_bug.cgi?id=91071
2812 if (logicalWidth.isIntrinsic())
2813 return computeIntrinsicLogicalWidthUsing(logicalWidth, cw, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
2814 if (cw > 0 || (!cw && (containerLogicalWidth.isFixed() || containerLogicalWidth.isPercent())))
2815 return adjustContentBoxLogicalWidthForBoxSizing(minimumValueForLength(logicalWidth, cw));
2822 return intrinsicLogicalWidth();
2829 ASSERT_NOT_REACHED();
2833 LayoutUnit RenderBox::computeReplacedLogicalHeight() const
2835 return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(style()->logicalHeight()));
2838 LayoutUnit RenderBox::computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const
2840 LayoutUnit minLogicalHeight = computeReplacedLogicalHeightUsing(style()->logicalMinHeight());
2841 LayoutUnit maxLogicalHeight = style()->logicalMaxHeight().isUndefined() ? logicalHeight : computeReplacedLogicalHeightUsing(style()->logicalMaxHeight());
2842 return max(minLogicalHeight, min(logicalHeight, maxLogicalHeight));
2845 LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(Length logicalHeight) const
2847 switch (logicalHeight.type()) {
2849 return adjustContentBoxLogicalHeightForBoxSizing(logicalHeight.value());
2853 RenderObject* cb = isOutOfFlowPositioned() ? container() : containingBlock();
2854 while (cb->isAnonymous()) {
2855 cb = cb->containingBlock();
2856 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this));
2859 // FIXME: This calculation is not patched for block-flow yet.
2860 // https://bugs.webkit.org/show_bug.cgi?id=46500
2861 if (cb->isOutOfFlowPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) {
2862 ASSERT_WITH_SECURITY_IMPLICATION(cb->isRenderBlock());
2863 RenderBlock* block = toRenderBlock(cb);
2864 LogicalExtentComputedValues computedValues;
2865 block->computeLogicalHeight(block->logicalHeight(), 0, computedValues);
2866 LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight() - block->scrollbarLogicalHeight();
2867 LayoutUnit newHeight = block->adjustContentBoxLogicalHeightForBoxSizing(newContentHeight);
2868 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, newHeight));
2871 // FIXME: availableLogicalHeight() is wrong if the replaced element's block-flow is perpendicular to the
2872 // containing block's block-flow.
2873 // https://bugs.webkit.org/show_bug.cgi?id=46496
2874 LayoutUnit availableHeight;
2875 if (isOutOfFlowPositioned())
2876 availableHeight = containingBlockLogicalHeightForPositioned(toRenderBoxModelObject(cb));
2878 availableHeight = containingBlockLogicalHeightForContent(IncludeMarginBorderPadding);
2879 // It is necessary to use the border-box to match WinIE's broken
2880 // box model. This is essential for sizing inside
2881 // table cells using percentage heights.
2882 // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right.
2883 // https://bugs.webkit.org/show_bug.cgi?id=46997
2884 while (cb && !cb->isRenderView() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().isPercent())) {
2885 if (cb->isTableCell()) {
2886 // Don't let table cells squeeze percent-height replaced elements
2887 // <http://bugs.webkit.org/show_bug.cgi?id=15359>
2888 availableHeight = max(availableHeight, intrinsicLogicalHeight());
2889 return valueForLength(logicalHeight, availableHeight - borderAndPaddingLogicalHeight());
2891 toRenderBlock(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this));
2892 cb = cb->containingBlock();
2895 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, availableHeight));
2901 return adjustContentBoxLogicalHeightForBoxSizing(computeIntrinsicLogicalContentHeightUsing(logicalHeight, intrinsicLogicalHeight(), borderAndPaddingHeight()));
2903 return intrinsicLogicalHeight();
2907 LayoutUnit RenderBox::availableLogicalHeight(AvailableLogicalHeightType heightType) const
2909 return constrainLogicalHeightByMinMax(availableLogicalHeightUsing(style()->logicalHeight(), heightType), -1);
2912 LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h, AvailableLogicalHeightType heightType) const
2915 return isHorizontalWritingMode() ? toRenderView(this)->frameView()->visibleHeight() : toRenderView(this)->frameView()->visibleWidth();
2917 // We need to stop here, since we don't want to increase the height of the table
2918 // artificially. We're going to rely on this cell getting expanded to some new
2919 // height, and then when we lay out again we'll use the calculation below.
2920 if (isTableCell() && (h.isAuto() || h.isPercent())) {
2921 if (hasOverrideHeight())
2922 return overrideLogicalContentHeight();
2923 return logicalHeight() - borderAndPaddingLogicalHeight();
2926 if (h.isPercent() && isOutOfFlowPositioned() && !isRenderFlowThread()) {
2927 // FIXME: This is wrong if the containingBlock has a perpendicular writing mode.
2928 LayoutUnit availableHeight = containingBlockLogicalHeightForPositioned(containingBlock());
2929 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(h, availableHeight));
2932 LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(h, -1);
2933 if (heightIncludingScrollbar != -1)
2934 return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight());
2936 // FIXME: Check logicalTop/logicalBottom here to correctly handle vertical writing-mode.
2937 // https://bugs.webkit.org/show_bug.cgi?id=46500
2938 if (isRenderBlock() && isOutOfFlowPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) {
2939 RenderBlock* block = const_cast<RenderBlock*>(toRenderBlock(this));
2940 LogicalExtentComputedValues computedValues;
2941 block->computeLogicalHeight(block->logicalHeight(), 0, computedValues);
2942 LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight() - block->scrollbarLogicalHeight();
2943 return adjustContentBoxLogicalHeightForBoxSizing(newContentHeight);
2946 // FIXME: This is wrong if the containingBlock has a perpendicular writing mode.
2947 LayoutUnit availableHeight = containingBlockLogicalHeightForContent(heightType);
2948 if (heightType == ExcludeMarginBorderPadding) {
2949 // FIXME: Margin collapsing hasn't happened yet, so this incorrectly removes collapsed margins.
2950 availableHeight -= marginBefore() + marginAfter() + borderAndPaddingLogicalHeight();
2952 return availableHeight;
2955 void RenderBox::computeBlockDirectionMargins(const RenderBlock* containingBlock, LayoutUnit& marginBefore, LayoutUnit& marginAfter) const
2957 if (isTableCell()) {
2958 // FIXME: Not right if we allow cells to have different directionality than the table. If we do allow this, though,
2959 // we may just do it with an extra anonymous block inside the cell.
2965 // Margins are calculated with respect to the logical width of
2966 // the containing block (8.3)
2967 LayoutUnit cw = containingBlockLogicalWidthForContent();
2968 RenderStyle* containingBlockStyle = containingBlock->style();
2969 marginBefore = minimumValueForLength(style()->marginBeforeUsing(containingBlockStyle), cw);
2970 marginAfter = minimumValueForLength(style()->marginAfterUsing(containingBlockStyle), cw);
2973 void RenderBox::computeAndSetBlockDirectionMargins(const RenderBlock* containingBlock)
2975 LayoutUnit marginBefore;
2976 LayoutUnit marginAfter;
2977 computeBlockDirectionMargins(containingBlock, marginBefore, marginAfter);
2978 containingBlock->setMarginBeforeForChild(this, marginBefore);
2979 containingBlock->setMarginAfterForChild(this, marginAfter);
2982 LayoutUnit RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const
2984 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode())
2985 return containingBlockLogicalHeightForPositioned(containingBlock, false);
2987 // Use viewport as container for top-level fixed-position elements.
2988 if (style()->position() == FixedPosition && containingBlock->isRenderView()) {
2989 const RenderView* view = toRenderView(containingBlock);
2990 if (FrameView* frameView = view->frameView()) {
2991 LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect();
2992 return containingBlock->isHorizontalWritingMode() ? viewportRect.width() : viewportRect.height();
2996 if (containingBlock->isBox())
2997 return toRenderBox(containingBlock)->clientLogicalWidth();
2999 ASSERT(containingBlock->isRenderInline() && containingBlock->isInFlowPositioned());
3001 const RenderInline* flow = toRenderInline(containingBlock);
3002 InlineFlowBox* first = flow->firstLineBox();
3003 InlineFlowBox* last = flow->lastLineBox();
3005 // If the containing block is empty, return a width of 0.
3006 if (!first || !last)
3009 LayoutUnit fromLeft;
3010 LayoutUnit fromRight;
3011 if (containingBlock->style()->isLeftToRightDirection()) {
3012 fromLeft = first->logicalLeft() + first->borderLogicalLeft();
3013 fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight();
3015 fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight();
3016 fromLeft = last->logicalLeft() + last->borderLogicalLeft();
3019 return max<LayoutUnit>(0, fromRight - fromLeft);
3022 LayoutUnit RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const
3024 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode())
3025 return containingBlockLogicalWidthForPositioned(containingBlock, false);
3027 // Use viewport as container for top-level fixed-position elements.
3028 if (style()->position() == FixedPosition && containingBlock->isRenderView()) {
3029 const RenderView* view = toRenderView(containingBlock);
3030 if (FrameView* frameView = view->frameView()) {
3031 LayoutRect viewportRect = frameView->viewportConstrainedVisibleContentRect();
3032 return containingBlock->isHorizontalWritingMode() ? viewportRect.height() : viewportRect.width();
3036 if (containingBlock->isBox()) {
3037 const RenderBlock* cb = containingBlock->isRenderBlock() ?
3038 toRenderBlock(containingBlock) : containingBlock->containingBlock();
3039 return cb->clientLogicalHeight();
3042 ASSERT(containingBlock->isRenderInline() && containingBlock->isInFlowPositioned());
3044 const RenderInline* flow = toRenderInline(containingBlock);
3045 InlineFlowBox* first = flow->firstLineBox();
3046 InlineFlowBox* last = flow->lastLineBox();
3048 // If the containing block is empty, return a height of 0.
3049 if (!first || !last)
3052 LayoutUnit heightResult;
3053 LayoutRect boundingBox = flow->linesBoundingBox();
3054 if (containingBlock->isHorizontalWritingMode())
3055 heightResult = boundingBox.height();
3057 heightResult = boundingBox.width();
3058 heightResult -= (containingBlock->borderBefore() + containingBlock->borderAfter());
3059 return heightResult;
3062 static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRight, const RenderBox* child, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalWidth)
3064 if (!logicalLeft.isAuto() || !logicalRight.isAuto())
3067 // FIXME: The static distance computation has not been patched for mixed writing modes yet.
3068 if (child->parent()->style()->direction() == LTR) {
3069 LayoutUnit staticPosition = child->layer()->staticInlinePosition() - containerBlock->borderLogicalLeft();
3070 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) {
3071 if (curr->isBox()) {
3072 staticPosition += toRenderBox(curr)->logicalLeft();
3073 if (toRenderBox(curr)->isRelPositioned())
3074 staticPosition += toRenderBox(curr)->relativePositionOffset().width();
3075 } else if (curr->isInline()) {
3076 if (curr->isRelPositioned()) {
3077 if (!curr->style()->logicalLeft().isAuto())
3078 staticPosition += curr->style()->logicalLeft().value();
3080 staticPosition -= curr->style()->logicalRight().value();
3084 logicalLeft.setValue(Fixed, staticPosition);
3086 RenderBox* enclosingBox = child->parent()->enclosingBox();
3087 LayoutUnit staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock->borderLogicalLeft();
3088 for (RenderObject* curr = child->parent(); curr; curr = curr->container()) {
3089 if (curr->isBox()) {
3090 if (curr != containerBlock) {
3091 staticPosition -= toRenderBox(curr)->logicalLeft();
3092 if (toRenderBox(curr)->isRelPositioned())
3093 staticPosition -= toRenderBox(curr)->relativePositionOffset().width();
3095 if (curr == enclosingBox)
3096 staticPosition -= enclosingBox->logicalWidth();
3097 } else if (curr->isInline()) {
3098 if (curr->isRelPositioned()) {
3099 if (!curr->style()->logicalLeft().isAuto())
3100 staticPosition -= curr->style()->logicalLeft().value();
3102 staticPosition += curr->style()->logicalRight().value();
3105 if (curr == containerBlock)
3108 logicalRight.setValue(Fixed, staticPosition);
3112 void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& computedValues) const
3115 computePositionedLogicalWidthReplaced(computedValues);
3120 // FIXME 1: Should we still deal with these the cases of 'left' or 'right' having
3121 // the type 'static' in determining whether to calculate the static distance?
3122 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1.
3124 // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater
3125 // than or less than the computed width(). Be careful of box-sizing and
3126 // percentage issues.
3128 // The following is based off of the W3C Working Draft from April 11, 2006 of
3129 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
3130 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
3131 // (block-style-comments in this function and in computePositionedLogicalWidthUsing()
3132 // correspond to text from the spec)
3135 // We don't use containingBlock(), since we may be positioned by an enclosing
3136 // relative positioned inline.
3137 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
3139 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock);
3141 // Use the container block's direction except when calculating the static distance
3142 // This conforms with the reference results for abspos-replaced-width-margin-000.htm
3143 // of the CSS 2.1 test suite
3144 TextDirection containerDirection = containerBlock->style()->direction();
3146 bool isHorizontal = isHorizontalWritingMode();
3147 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth();
3148 const Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop();
3149 const Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom();
3151 Length logicalLeftLength = style()->logicalLeft();
3152 Length logicalRightLength = style()->logicalRight();
3154 /*---------------------------------------------------------------------------*\
3155 * For the purposes of this section and the next, the term "static position"
3156 * (of an element) refers, roughly, to the position an element would have had
3157 * in the normal flow. More precisely:
3159 * * The static position for 'left' is the distance from the left edge of the
3160 * containing block to the left margin edge of a hypothetical box that would
3161 * have been the first box of the element if its 'position' property had
3162 * been 'static' and 'float' had been 'none'. The value is negative if the
3163 * hypothetical box is to the left of the containing block.
3164 * * The static position for 'right' is the distance from the right edge of the
3165 * containing block to the right margin edge of the same hypothetical box as
3166 * above. The value is positive if the hypothetical box is to the left of the
3167 * containing block's edge.
3169 * But rather than actually calculating the dimensions of that hypothetical box,
3170 * user agents are free to make a guess at its probable position.
3172 * For the purposes of calculating the static position, the containing block of
3173 * fixed positioned elements is the initial containing block instead of the
3174 * viewport, and all scrollable boxes should be assumed to be scrolled to their
3176 \*---------------------------------------------------------------------------*/
3179 // Calculate the static distance if needed.
3180 computeInlineStaticDistance(logicalLeftLength, logicalRightLength, this, containerBlock, containerLogicalWidth);
3182 // Calculate constraint equation values for 'width' case.
3183 computePositionedLogicalWidthUsing(style()->logicalWidth(), containerBlock, containerDirection,
3184 containerLogicalWidth, bordersPlusPadding,
3185 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
3188 // Calculate constraint equation values for 'max-width' case.
3189 if (!style()->logicalMaxWidth().isUndefined()) {
3190 LogicalExtentComputedValues maxValues;
3192 computePositionedLogicalWidthUsing(style()->logicalMaxWidth(), containerBlock, containerDirection,
3193 containerLogicalWidth, bordersPlusPadding,
3194 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
3197 if (computedValues.m_extent > maxValues.m_extent) {
3198 computedValues.m_extent = maxValues.m_extent;
3199 computedValues.m_position = maxValues.m_position;
3200 computedValues.m_margins.m_start = maxValues.m_margins.m_start;
3201 computedValues.m_margins.m_end = maxValues.m_margins.m_end;
3205 // Calculate constraint equation values for 'min-width' case.
3206 if (!style()->logicalMinWidth().isZero() || style()->logicalMinWidth().isIntrinsic()) {
3207 LogicalExtentComputedValues minValues;
3209 computePositionedLogicalWidthUsing(style()->logicalMinWidth(), containerBlock, containerDirection,
3210 containerLogicalWidth, bordersPlusPadding,
3211 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
3214 if (computedValues.m_extent < minValues.m_extent) {
3215 computedValues.m_extent = minValues.m_extent;
3216 computedValues.m_position = minValues.m_position;
3217 computedValues.m_margins.m_start = minValues.m_margins.m_start;
3218 computedValues.m_margins.m_end = minValues.m_margins.m_end;
3222 computedValues.m_extent += bordersPlusPadding;
3225 static void computeLogicalLeftPositionedOffset(LayoutUnit& logicalLeftPos, const RenderBox* child, LayoutUnit logicalWidthValue, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalWidth)
3227 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
3228 // along this axis, then we need to flip the coordinate. This can only happen if the containing block is both a flipped mode and perpendicular to us.
3229 if (containerBlock->isHorizontalWritingMode() != child->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) {
3230 logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos;
3231 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom());
3233 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop());
3236 void RenderBox::shrinkToFitWidth(const LayoutUnit availableSpace, const LayoutUnit logicalLeftValue, const LayoutUnit bordersPlusPadding, LogicalExtentComputedValues& computedValues) const
3238 // FIXME: would it be better to have shrink-to-fit in one step?
3239 LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
3240 LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
3241 LayoutUnit availableWidth = availableSpace - logicalLeftValue;
3242 computedValues.m_extent = min(max(preferredMinWidth, availableWidth), preferredWidth);
3245 void RenderBox::computePositionedLogicalWidthUsing(Length logicalWidth, const RenderBoxModelObject* containerBlock, TextDirection containerDirection,
3246 LayoutUnit containerLogicalWidth, LayoutUnit bordersPlusPadding,
3247 Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight,
3248 LogicalExtentComputedValues& computedValues) const
3250 if (logicalWidth.isIntrinsic())
3251 logicalWidth = Length(computeIntrinsicLogicalWidthUsing(logicalWidth, containerLogicalWidth, bordersPlusPadding) - bordersPlusPadding, Fixed);
3253 // 'left' and 'right' cannot both be 'auto' because one would of been
3254 // converted to the static position already
3255 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
3257 LayoutUnit logicalLeftValue = 0;
3259 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false);
3261 bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto();
3262 bool logicalLeftIsAuto = logicalLeft.isAuto();
3263 bool logicalRightIsAuto = logicalRight.isAuto();
3264 LayoutUnit& marginLogicalLeftValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end;
3265 LayoutUnit& marginLogicalRightValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start;
3266 if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
3267 /*-----------------------------------------------------------------------*\
3268 * If none of the three is 'auto': If both 'margin-left' and 'margin-
3269 * right' are 'auto', solve the equation under the extra constraint that
3270 * the two margins get equal values, unless this would make them negative,
3271 * in which case when direction of the containing block is 'ltr' ('rtl'),
3272 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
3273 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
3274 * solve the equation for that value. If the values are over-constrained,
3275 * ignore the value for 'left' (in case the 'direction' property of the
3276 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
3277 * and solve for that value.
3278 \*-----------------------------------------------------------------------*/
3279 // NOTE: It is not necessary to solve for 'right' in the over constrained
3280 // case because the value is not used for any further calculations.
3282 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3283 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth));
3285 const LayoutUnit availableSpace = containerLogicalWidth - (logicalLeftValue + computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth) + bordersPlusPadding);
3287 // Margins are now the only unknown
3288 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
3289 // Both margins auto, solve for equality
3290 if (availableSpace >= 0) {
3291 marginLogicalLeftValue = availableSpace / 2; // split the difference
3292 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences
3294 // Use the containing block's direction rather than the parent block's
3295 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
3296 if (containerDirection == LTR) {
3297 marginLogicalLeftValue = 0;
3298 marginLogicalRightValue = availableSpace; // will be negative
3300 marginLogicalLeftValue = availableSpace; // will be negative
3301 marginLogicalRightValue = 0;
3304 } else if (marginLogicalLeft.isAuto()) {
3305 // Solve for left margin
3306 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3307 marginLogicalLeftValue = availableSpace - marginLogicalRightValue;
3308 } else if (marginLogicalRight.isAuto()) {
3309 // Solve for right margin
3310 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3311 marginLogicalRightValue = availableSpace - marginLogicalLeftValue;
3313 // Over-constrained, solve for left if direction is RTL
3314 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3315 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3317 // Use the containing block's direction rather than the parent block's
3318 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
3319 if (containerDirection == RTL)
3320 logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue;
3323 /*--------------------------------------------------------------------*\
3324 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
3325 * to 0, and pick the one of the following six rules that applies.
3327 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
3328 * width is shrink-to-fit. Then solve for 'left'
3330 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
3331 * ------------------------------------------------------------------
3332 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
3333 * the 'direction' property of the containing block is 'ltr' set
3334 * 'left' to the static position, otherwise set 'right' to the
3335 * static position. Then solve for 'left' (if 'direction is 'rtl')
3336 * or 'right' (if 'direction' is 'ltr').
3337 * ------------------------------------------------------------------
3339 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
3340 * width is shrink-to-fit . Then solve for 'right'
3341 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
3343 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
3345 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
3348 * Calculation of the shrink-to-fit width is similar to calculating the
3349 * width of a table cell using the automatic table layout algorithm.
3350 * Roughly: calculate the preferred width by formatting the content
3351 * without breaking lines other than where explicit line breaks occur,
3352 * and also calculate the preferred minimum width, e.g., by trying all
3353 * possible line breaks. CSS 2.1 does not define the exact algorithm.
3354 * Thirdly, calculate the available width: this is found by solving
3355 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
3358 * Then the shrink-to-fit width is:
3359 * min(max(preferred minimum width, available width), preferred width).
3360 \*--------------------------------------------------------------------*/
3361 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
3362 // because the value is not used for any further calculations.
3364 // Calculate margins, 'auto' margins are ignored.
3365 marginLogicalLeftValue = minimumValueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3366 marginLogicalRightValue = minimumValueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3368 const LayoutUnit availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding);
3370 // FIXME: Is there a faster way to find the correct case?
3371 // Use rule/case that applies.
3372 if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
3373 // RULE 1: (use shrink-to-fit for width, and solve of left)
3374 LayoutUnit logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3376 // FIXME: would it be better to have shrink-to-fit in one step?
3377 LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
3378 LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
3379 LayoutUnit availableWidth = availableSpace - logicalRightValue;
3380 computedValues.m_extent = min(max(preferredMinWidth, availableWidth), preferredWidth);
3381 logicalLeftValue = availableSpace - (computedValues.m_extent + logicalRightValue);
3382 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) {
3383 // RULE 3: (use shrink-to-fit for width, and no need solve of right)
3384 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3386 shrinkToFitWidth(availableSpace, logicalLeftValue, bordersPlusPadding, computedValues);
3387 } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
3388 // RULE 4: (solve for left)
3389 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth));
3390 logicalLeftValue = availableSpace - (computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth));
3391 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
3392 // RULE 5: (solve for width)
3393 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3394 if (autoWidthShouldFitContent())
3395 shrinkToFitWidth(availableSpace, logicalLeftValue, bordersPlusPadding, computedValues);
3397 computedValues.m_extent = availableSpace - (logicalLeftValue + valueForLength(logicalRight, containerLogicalWidth));
3398 } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) {
3399 // RULE 6: (no need solve for right)
3400 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3401 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth));
3405 // Use computed values to calculate the horizontal position.
3407 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
3408 // positioned, inline because right now, it is using the logical left position
3409 // of the first line box when really it should use the last line box. When
3410 // this is fixed elsewhere, this block should be removed.
3411 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) {
3412 const RenderInline* flow = toRenderInline(containerBlock);
3413 InlineFlowBox* firstLine = flow->firstLineBox();
3414 InlineFlowBox* lastLine = flow->lastLineBox();
3415 if (firstLine && lastLine && firstLine != lastLine) {
3416 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft());
3421 if (containerBlock->isBox() && toRenderBox(containerBlock)->scrollsOverflowY() && containerBlock->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
3422 logicalLeftValue = logicalLeftValue + toRenderBox(containerBlock)->verticalScrollbarWidth();
3425 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue;
3426 computeLogicalLeftPositionedOffset(computedValues.m_position, this, computedValues.m_extent, containerBlock, containerLogicalWidth);
3429 static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom, const RenderBox* child, const RenderBoxModelObject* containerBlock)
3431 if (!logicalTop.isAuto() || !logicalBottom.isAuto())
3434 // FIXME: The static distance computation has not been patched for mixed writing modes.
3435 LayoutUnit staticLogicalTop = child->layer()->staticBlockPosition() - containerBlock->borderBefore();
3436 for (RenderObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) {
3437 if (curr->isBox() && !curr->isTableRow())
3438 staticLogicalTop += toRenderBox(curr)->logicalTop();
3440 logicalTop.setValue(Fixed, staticLogicalTop);
3443 void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& computedValues) const
3446 computePositionedLogicalHeightReplaced(computedValues);
3450 // The following is based off of the W3C Working Draft from April 11, 2006 of
3451 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
3452 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
3453 // (block-style-comments in this function and in computePositionedLogicalHeightUsing()
3454 // correspond to text from the spec)
3457 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
3458 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
3460 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
3462 RenderStyle* styleToUse = style();
3463 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
3464 const Length marginBefore = styleToUse->marginBefore();
3465 const Length marginAfter = styleToUse->marginAfter();
3466 Length logicalTopLength = styleToUse->logicalTop();
3467 Length logicalBottomLength = styleToUse->logicalBottom();
3469 /*---------------------------------------------------------------------------*\
3470 * For the purposes of this section and the next, the term "static position"
3471 * (of an element) refers, roughly, to the position an element would have had
3472 * in the normal flow. More precisely, the static position for 'top' is the
3473 * distance from the top edge of the containing block to the top margin edge
3474 * of a hypothetical box that would have been the first box of the element if
3475 * its 'position' property had been 'static' and 'float' had been 'none'. The
3476 * value is negative if the hypothetical box is above the containing block.
3478 * But rather than actually calculating the dimensions of that hypothetical
3479 * box, user agents are free to make a guess at its probable position.
3481 * For the purposes of calculating the static position, the containing block
3482 * of fixed positioned elements is the initial containing block instead of
3484 \*---------------------------------------------------------------------------*/
3487 // Calculate the static distance if needed.
3488 computeBlockStaticDistance(logicalTopLength, logicalBottomLength, this, containerBlock);
3490 // Calculate constraint equation values for 'height' case.
3491 LayoutUnit logicalHeight = computedValues.m_extent;
3492 computePositionedLogicalHeightUsing(styleToUse->logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
3493 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
3496 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
3499 // Calculate constraint equation values for 'max-height' case.
3500 if (!styleToUse->logicalMaxHeight().isUndefined()) {
3501 LogicalExtentComputedValues maxValues;
3503 computePositionedLogicalHeightUsing(styleToUse->logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
3504 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
3507 if (computedValues.m_extent > maxValues.m_extent) {
3508 computedValues.m_extent = maxValues.m_extent;
3509 computedValues.m_position = maxValues.m_position;
3510 computedValues.m_margins.m_before = maxValues.m_margins.m_before;
3511 computedValues.m_margins.m_after = maxValues.m_margins.m_after;
3515 // Calculate constraint equation values for 'min-height' case.
3516 if (!styleToUse->logicalMinHeight().isZero() || styleToUse->logicalMinHeight().isIntrinsic()) {
3517 LogicalExtentComputedValues minValues;
3519 computePositionedLogicalHeightUsing(styleToUse->logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
3520 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
3523 if (computedValues.m_extent < minValues.m_extent) {
3524 computedValues.m_extent = minValues.m_extent;
3525 computedValues.m_position = minValues.m_position;
3526 computedValues.m_margins.m_before = minValues.m_margins.m_before;
3527 computedValues.m_margins.m_after = minValues.m_margins.m_after;
3531 // Set final height value.
3532 computedValues.m_extent += bordersPlusPadding;
3535 static void computeLogicalTopPositionedOffset(LayoutUnit& logicalTopPos, const RenderBox* child, LayoutUnit logicalHeightValue, const RenderBoxModelObject* containerBlock, LayoutUnit containerLogicalHeight)
3537 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
3538 // along this axis, then we need to flip the coordinate. This can only happen if the containing block is both a flipped mode and perpendicular to us.
3539 if ((child->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode())
3540 || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()))
3541 logicalTopPos = containerLogicalHeight - logicalHeightValue - logicalTopPos;
3543 // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt.
3544 if (containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) {
3545 if (child->isHorizontalWritingMode())
3546 logicalTopPos += containerBlock->borderBottom();
3548 logicalTopPos += containerBlock->borderRight();
3550 if (child->isHorizontalWritingMode())
3551 logicalTopPos += containerBlock->borderTop();
3553 logicalTopPos += containerBlock->borderLeft();
3557 void RenderBox::computePositionedLogicalHeightUsing(Length logicalHeightLength, const RenderBoxModelObject* containerBlock,
3558 LayoutUnit containerLogicalHeight, LayoutUnit bordersPlusPadding, LayoutUnit logicalHeight,
3559 Length logicalTop, Length logicalBottom, Length marginBefore, Length marginAfter,
3560 LogicalExtentComputedValues& computedValues) const
3562 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
3563 // converted to the static position in computePositionedLogicalHeight()
3564 ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto()));
3566 LayoutUnit logicalHeightValue;
3567 LayoutUnit contentLogicalHeight = logicalHeight - bordersPlusPadding;
3569 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false);
3571 LayoutUnit logicalTopValue = 0;
3573 bool logicalHeightIsAuto = logicalHeightLength.isAuto();
3574 bool logicalTopIsAuto = logicalTop.isAuto();
3575 bool logicalBottomIsAuto = logicalBottom.isAuto();
3577 LayoutUnit resolvedLogicalHeight;
3578 // Height is never unsolved for tables.
3580 resolvedLogicalHeight = contentLogicalHeight;
3581 logicalHeightIsAuto = false;
3583 if (logicalHeightLength.isIntrinsic())
3584 resolvedLogicalHeight = computeIntrinsicLogicalContentHeightUsing(logicalHeightLength, contentLogicalHeight, bordersPlusPadding);
3586 resolvedLogicalHeight = adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeightLength, containerLogicalHeight));
3589 if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
3590 /*-----------------------------------------------------------------------*\
3591 * If none of the three are 'auto': If both 'margin-top' and 'margin-
3592 * bottom' are 'auto', solve the equation under the extra constraint that
3593 * the two margins get equal values. If one of 'margin-top' or 'margin-
3594 * bottom' is 'auto', solve the equation for that value. If the values
3595 * are over-constrained, ignore the value for 'bottom' and solve for that
3597 \*-----------------------------------------------------------------------*/
3598 // NOTE: It is not necessary to solve for 'bottom' in the over constrained
3599 // case because the value is not used for any further calculations.
3601 logicalHeightValue = resolvedLogicalHeight;
3602 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3604 const LayoutUnit availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight) + bordersPlusPadding);
3606 // Margins are now the only unknown
3607 if (marginBefore.isAuto() && marginAfter.isAuto()) {
3608 // Both margins auto, solve for equality
3609 // NOTE: This may result in negative values.
3610 computedValues.m_margins.m_before = availableSpace / 2; // split the difference
3611 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; // account for odd valued differences
3612 } else if (marginBefore.isAuto()) {
3613 // Solve for top margin
3614 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth);
3615 computedValues.m_margins.m_before = availableSpace - computedValues.m_margins.m_after;
3616 } else if (marginAfter.isAuto()) {
3617 // Solve for bottom margin
3618 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth);
3619 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before;
3621 // Over-constrained, (no need solve for bottom)
3622 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth);
3623 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth);
3626 /*--------------------------------------------------------------------*\
3627 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
3628 * to 0, and pick the one of the following six rules that applies.
3630 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
3631 * the height is based on the content, and solve for 'top'.
3633 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
3634 * ------------------------------------------------------------------
3635 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
3636 * set 'top' to the static position, and solve for 'bottom'.
3637 * ------------------------------------------------------------------
3639 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
3640 * the height is based on the content, and solve for 'bottom'.
3641 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
3643 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
3644 * solve for 'height'.
3645 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
3646 * solve for 'bottom'.
3647 \*--------------------------------------------------------------------*/
3648 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
3649 // because the value is not used for any further calculations.
3651 // Calculate margins, 'auto' margins are ignored.
3652 computedValues.m_margins.m_before = minimumValueForLength(marginBefore, containerRelativeLogicalWidth);
3653 computedValues.m_margins.m_after = minimumValueForLength(marginAfter, containerRelativeLogicalWidth);
3655 const LayoutUnit availableSpace = containerLogicalHeight - (computedValues.m_margins.m_before + computedValues.m_margins.m_after + bordersPlusPadding);
3657 // Use rule/case that applies.
3658 if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
3659 // RULE 1: (height is content based, solve of top)
3660 logicalHeightValue = contentLogicalHeight;
3661 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight));
3662 } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) {
3663 // RULE 3: (height is content based, no need solve of bottom)
3664 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3665 logicalHeightValue = contentLogicalHeight;
3666 } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
3667 // RULE 4: (solve of top)
3668 logicalHeightValue = resolvedLogicalHeight;
3669 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight));
3670 } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
3671 // RULE 5: (solve of height)
3672 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3673 logicalHeightValue = max<LayoutUnit>(0, availableSpace - (logicalTopValue + valueForLength(logicalBottom, containerLogicalHeight)));
3674 } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) {
3675 // RULE 6: (no need solve of bottom)
3676 logicalHeightValue = resolvedLogicalHeight;
3677 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3680 computedValues.m_extent = logicalHeightValue;
3682 // Use computed values to calculate the vertical position.
3683 computedValues.m_position = logicalTopValue + computedValues.m_margins.m_before;
3684 computeLogicalTopPositionedOffset(computedValues.m_position, this, logicalHeightValue, containerBlock, containerLogicalHeight);
3687 void RenderBox::computePositionedLogicalWidthReplaced(LogicalExtentComputedValues& computedValues) const
3689 // The following is based off of the W3C Working Draft from April 11, 2006 of
3690 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements"
3691 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
3692 // (block-style-comments in this function correspond to text from the spec and
3693 // the numbers correspond to numbers in spec)
3695 // We don't use containingBlock(), since we may be positioned by an enclosing
3696 // relative positioned inline.
3697 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
3699 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock);
3700 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false);
3702 // To match WinIE, in quirks mode use the parent's 'direction' property
3703 // instead of the the container block's.
3704 TextDirection containerDirection = containerBlock->style()->direction();
3706 // Variables to solve.
3707 bool isHorizontal = isHorizontalWritingMode();
3708 Length logicalLeft = style()->logicalLeft();
3709 Length logicalRight = style()->logicalRight();
3710 Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop();
3711 Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom();
3712 LayoutUnit& marginLogicalLeftAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end;
3713 LayoutUnit& marginLogicalRightAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start;
3715 /*-----------------------------------------------------------------------*\
3716 * 1. The used value of 'width' is determined as for inline replaced
3718 \*-----------------------------------------------------------------------*/
3719 // NOTE: This value of width is FINAL in that the min/max width calculations
3720 // are dealt with in computeReplacedWidth(). This means that the steps to produce
3721 // correct max/min in the non-replaced version, are not necessary.
3722 computedValues.m_extent = computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth();
3724 const LayoutUnit availableSpace = containerLogicalWidth - computedValues.m_extent;
3726 /*-----------------------------------------------------------------------*\
3727 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
3728 * of the containing block is 'ltr', set 'left' to the static position;
3729 * else if 'direction' is 'rtl', set 'right' to the static position.
3730 \*-----------------------------------------------------------------------*/
3732 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth);
3734 /*-----------------------------------------------------------------------*\
3735 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
3736 * or 'margin-right' with '0'.
3737 \*-----------------------------------------------------------------------*/
3738 if (logicalLeft.isAuto() || logicalRight.isAuto()) {
3739 if (marginLogicalLeft.isAuto())
3740 marginLogicalLeft.setValue(Fixed, 0);
3741 if (marginLogicalRight.isAuto())
3742 marginLogicalRight.setValue(Fixed, 0);
3745 /*-----------------------------------------------------------------------*\
3746 * 4. If at this point both 'margin-left' and 'margin-right' are still
3747 * 'auto', solve the equation under the extra constraint that the two
3748 * margins must get equal values, unless this would make them negative,
3749 * in which case when the direction of the containing block is 'ltr'
3750 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
3751 * 'margin-right' ('margin-left').
3752 \*-----------------------------------------------------------------------*/
3753 LayoutUnit logicalLeftValue = 0;
3754 LayoutUnit logicalRightValue = 0;
3756 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
3757 // 'left' and 'right' cannot be 'auto' due to step 3
3758 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
3760 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3761 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3763 LayoutUnit difference = availableSpace - (logicalLeftValue + logicalRightValue);
3764 if (difference > 0) {
3765 marginLogicalLeftAlias = difference / 2; // split the difference
3766 marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences
3768 // Use the containing block's direction rather than the parent block's
3769 // per CSS 2.1 reference test abspos-replaced-width-margin-000.
3770 if (containerDirection == LTR) {
3771 marginLogicalLeftAlias = 0;
3772 marginLogicalRightAlias = difference; // will be negative
3774 marginLogicalLeftAlias = difference; // will be negative
3775 marginLogicalRightAlias = 0;
3779 /*-----------------------------------------------------------------------*\
3780 * 5. If at this point there is an 'auto' left, solve the equation for
3782 \*-----------------------------------------------------------------------*/
3783 } else if (logicalLeft.isAuto()) {
3784 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3785 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3786 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3789 logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias);
3790 } else if (logicalRight.isAuto()) {
3791 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3792 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3793 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3795 // Solve for 'right'
3796 logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias);
3797 } else if (marginLogicalLeft.isAuto()) {
3798 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3799 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3800 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3802 // Solve for 'margin-left'
3803 marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias);
3804 } else if (marginLogicalRight.isAuto()) {
3805 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3806 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3807 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3809 // Solve for 'margin-right'
3810 marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias);
3812 // Nothing is 'auto', just calculate the values.
3813 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3814 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3815 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3816 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3817 // If the containing block is right-to-left, then push the left position as far to the right as possible
3818 if (containerDirection == RTL) {
3819 int totalLogicalWidth = computedValues.m_extent + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias;
3820 logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue);
3824 /*-----------------------------------------------------------------------*\
3825 * 6. If at this point the values are over-constrained, ignore the value
3826 * for either 'left' (in case the 'direction' property of the
3827 * containing block is 'rtl') or 'right' (in case 'direction' is
3828 * 'ltr') and solve for that value.
3829 \*-----------------------------------------------------------------------*/
3830 // NOTE: Constraints imposed by the width of the containing block and its content have already been accounted for above.
3832 // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that
3833 // can make the result here rather complicated to compute.
3835 // Use computed values to calculate the horizontal position.
3837 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
3838 // positioned, inline containing block because right now, it is using the logical left position
3839 // of the first line box when really it should use the last line box. When
3840 // this is fixed elsewhere, this block should be removed.
3841 if (containerBlock->isRenderInline() && !containerBlock->style()->isLeftToRightDirection()) {
3842 const RenderInline* flow = toRenderInline(containerBlock);
3843 InlineFlowBox* firstLine = flow->firstLineBox();
3844 InlineFlowBox* lastLine = flow->lastLineBox();
3845 if (firstLine && lastLine && firstLine != lastLine) {
3846 computedValues.m_position = logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft());
3851 LayoutUnit logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias;
3852 computeLogicalLeftPositionedOffset(logicalLeftPos, this, computedValues.m_extent, containerBlock, containerLogicalWidth);
3853 computedValues.m_position = logicalLeftPos;
3856 void RenderBox::computePositionedLogicalHeightReplaced(LogicalExtentComputedValues& computedValues) const
3858 // The following is based off of the W3C Working Draft from April 11, 2006 of
3859 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements"
3860 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
3861 // (block-style-comments in this function correspond to text from the spec and
3862 // the numbers correspond to numbers in spec)
3864 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
3865 const RenderBoxModelObject* containerBlock = toRenderBoxModelObject(container());
3867 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
3868 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false);
3870 // Variables to solve.
3871 Length marginBefore = style()->marginBefore();
3872 Length marginAfter = style()->marginAfter();
3873 LayoutUnit& marginBeforeAlias = computedValues.m_margins.m_before;
3874 LayoutUnit& marginAfterAlias = computedValues.m_margins.m_after;
3876 Length logicalTop = style()->logicalTop();
3877 Length logicalBottom = style()->logicalBottom();
3879 /*-----------------------------------------------------------------------*\
3880 * 1. The used value of 'height' is determined as for inline replaced
3882 \*-----------------------------------------------------------------------*/
3883 // NOTE: This value of height is FINAL in that the min/max height calculations
3884 // are dealt with in computeReplacedHeight(). This means that the steps to produce
3885 // correct max/min in the non-replaced version, are not necessary.
3886 computedValues.m_extent = computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight();
3887 const LayoutUnit availableSpace = containerLogicalHeight - computedValues.m_extent;
3889 /*-----------------------------------------------------------------------*\
3890 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
3891 * with the element's static position.
3892 \*-----------------------------------------------------------------------*/
3894 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock);
3896 /*-----------------------------------------------------------------------*\
3897 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
3898 * 'margin-bottom' with '0'.
3899 \*-----------------------------------------------------------------------*/
3900 // FIXME: The spec. says that this step should only be taken when bottom is
3901 // auto, but if only top is auto, this makes step 4 impossible.
3902 if (logicalTop.isAuto() || logicalBottom.isAuto()) {
3903 if (marginBefore.isAuto())
3904 marginBefore.setValue(Fixed, 0);
3905 if (marginAfter.isAuto())
3906 marginAfter.setValue(Fixed, 0);
3909 /*-----------------------------------------------------------------------*\
3910 * 4. If at this point both 'margin-top' and 'margin-bottom' are still
3911 * 'auto', solve the equation under the extra constraint that the two
3912 * margins must get equal values.
3913 \*-----------------------------------------------------------------------*/
3914 LayoutUnit logicalTopValue = 0;
3915 LayoutUnit logicalBottomValue = 0;
3917 if (marginBefore.isAuto() && marginAfter.isAuto()) {
3918 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined.
3919 ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto()));
3921 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3922 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
3924 LayoutUnit difference = availableSpace - (logicalTopValue + logicalBottomValue);
3925 // NOTE: This may result in negative values.
3926 marginBeforeAlias = difference / 2; // split the difference
3927 marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences
3929 /*-----------------------------------------------------------------------*\
3930 * 5. If at this point there is only one 'auto' left, solve the equation
3932 \*-----------------------------------------------------------------------*/
3933 } else if (logicalTop.isAuto()) {
3934 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
3935 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
3936 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
3939 logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias);
3940 } else if (logicalBottom.isAuto()) {
3941 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
3942 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
3943 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3945 // Solve for 'bottom'
3946 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
3948 } else if (marginBefore.isAuto()) {
3949 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
3950 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3951 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
3953 // Solve for 'margin-top'
3954 marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias);
3955 } else if (marginAfter.isAuto()) {
3956 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
3957 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3958 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
3960 // Solve for 'margin-bottom'
3961 marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias);
3963 // Nothing is 'auto', just calculate the values.
3964 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
3965 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
3966 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3967 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
3971 /*-----------------------------------------------------------------------*\
3972 * 6. If at this point the values are over-constrained, ignore the value
3973 * for 'bottom' and solve for that value.
3974 \*-----------------------------------------------------------------------*/
3975 // NOTE: It is not necessary to do this step because we don't end up using
3976 // the value of 'bottom' regardless of whether the values are over-constrained
3979 // Use computed values to calculate the vertical position.
3980 LayoutUnit logicalTopPos = logicalTopValue + marginBeforeAlias;
3981 computeLogicalTopPositionedOffset(logicalTopPos, this, computedValues.m_extent, containerBlock, containerLogicalHeight);
3982 computedValues.m_position = logicalTopPos;
3985 LayoutRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, LayoutUnit* extraWidthToEndOfLine)
3987 // VisiblePositions at offsets inside containers either a) refer to the positions before/after
3988 // those containers (tables and select elements) or b) refer to the position inside an empty block.
3989 // They never refer to children.
3990 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements.
3992 LayoutRect rect(location(), LayoutSize(caretWidth, height()));
3993 bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection();
3995 if ((!caretOffset) ^ ltr)
3996 rect.move(LayoutSize(width() - caretWidth, 0));
3999 RootInlineBox* rootBox = box->root();
4000 LayoutUnit top = rootBox->lineTop();
4002 rect.setHeight(rootBox->lineBottom() - top);
4005 // If height of box is smaller than font height, use the latter one,
4006 // otherwise the caret might become invisible.
4008 // Also, if the box is not a replaced element, always use the font height.
4009 // This prevents the "big caret" bug described in:
4010 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point
4012 // FIXME: ignoring :first-line, missing good reason to take care of
4013 LayoutUnit fontHeight = style()->fontMetrics().height();
4014 if (fontHeight > rect.height() || (!isReplaced() && !isTable()))
4015 rect.setHeight(fontHeight);
4017 if (extraWidthToEndOfLine)
4018 *extraWidthToEndOfLine = x() + width() - rect.maxX();
4020 // Move to local coords
4021 rect.moveBy(-location());
4023 // FIXME: Border/padding should be added for all elements but this workaround
4024 // is needed because we use offsets inside an "atomic" element to represent
4025 // positions before and after the element in deprecated editing offsets.
4026 if (node() && !(editingIgnoresContent(node()) || isRenderedTable(node()))) {
4027 rect.setX(rect.x() + borderLeft() + paddingLeft());
4028 rect.setY(rect.y() + paddingTop() + borderTop());
4031 if (!isHorizontalWritingMode())
4032 return rect.transposedRect();
4037 PositionWithAffinity RenderBox::positionForPoint(const LayoutPoint& point)
4039 // no children...return this render object's element, if there is one, and offset 0
4041 return createPositionWithAffinity(nonPseudoNode() ? firstPositionInOrBeforeNode(nonPseudoNode()) : Position());
4043 if (isTable() && nonPseudoNode()) {
4044 LayoutUnit right = contentWidth() + borderAndPaddingWidth();
4045 LayoutUnit bottom = contentHeight() + borderAndPaddingHeight();
4047 if (point.x() < 0 || point.x() > right || point.y() < 0 || point.y() > bottom) {
4048 if (point.x() <= right / 2)
4049 return createPositionWithAffinity(firstPositionInOrBeforeNode(nonPseudoNode()));
4050 return createPositionWithAffinity(lastPositionInOrAfterNode(nonPseudoNode()));
4054 // Pass off to the closest child.
4055 LayoutUnit minDist = LayoutUnit::max();
4056 RenderBox* closestRenderer = 0;
4057 LayoutPoint adjustedPoint = point;
4059 adjustedPoint.moveBy(location());
4061 for (RenderObject* renderObject = firstChild(); renderObject; renderObject = renderObject->nextSibling()) {
4062 if ((!renderObject->firstChild() && !renderObject->isInline() && !renderObject->isRenderBlockFlow() )
4063 || renderObject->style()->visibility() != VISIBLE)
4066 if (!renderObject->isBox())
4069 RenderBox* renderer = toRenderBox(renderObject);
4071 LayoutUnit top = renderer->borderTop() + renderer->paddingTop() + (isTableRow() ? LayoutUnit() : renderer->y());
4072 LayoutUnit bottom = top + renderer->contentHeight();
4073 LayoutUnit left = renderer->borderLeft() + renderer->paddingLeft() + (isTableRow() ? LayoutUnit() : renderer->x());
4074 LayoutUnit right = left + renderer->contentWidth();
4076 if (point.x() <= right && point.x() >= left && point.y() <= top && point.y() >= bottom) {
4077 if (renderer->isTableRow())
4078 return renderer->positionForPoint(point + adjustedPoint - renderer->locationOffset());
4079 return renderer->positionForPoint(point - renderer->locationOffset());
4082 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces
4083 // and use a different compare depending on which piece (x, y) is in.
4085 if (point.x() > right) {
4086 if (point.y() < top)
4087 cmp = LayoutPoint(right, top);
4088 else if (point.y() > bottom)
4089 cmp = LayoutPoint(right, bottom);
4091 cmp = LayoutPoint(right, point.y());
4092 } else if (point.x() < left) {
4093 if (point.y() < top)
4094 cmp = LayoutPoint(left, top);
4095 else if (point.y() > bottom)
4096 cmp = LayoutPoint(left, bottom);
4098 cmp = LayoutPoint(left, point.y());
4100 if (point.y() < top)
4101 cmp = LayoutPoint(point.x(), top);
4103 cmp = LayoutPoint(point.x(), bottom);
4106 LayoutSize difference = cmp - point;
4108 LayoutUnit dist = difference.width() * difference.width() + difference.height() * difference.height();
4109 if (dist < minDist) {
4110 closestRenderer = renderer;
4115 if (closestRenderer)
4116 return closestRenderer->positionForPoint(adjustedPoint - closestRenderer->locationOffset());
4117 return createPositionWithAffinity(firstPositionInOrBeforeNode(nonPseudoNode()));
4120 bool RenderBox::shrinkToAvoidFloats() const
4122 // Floating objects don't shrink. Objects that don't avoid floats don't shrink. Marquees don't shrink.
4123 if ((isInline() && !isMarquee()) || !avoidsFloats() || isFloating())
4126 // Only auto width objects can possibly shrink to avoid floats.
4127 return style()->width().isAuto();
4130 bool RenderBox::avoidsFloats() const
4132 return isReplaced() || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot() || isFlexItemIncludingDeprecated();
4135 void RenderBox::addVisualEffectOverflow()
4137 if (!style()->boxShadow() && !style()->hasBorderImageOutsets())
4140 bool isFlipped = style()->isFlippedBlocksWritingMode();
4141 bool isHorizontal = isHorizontalWritingMode();
4143 LayoutRect borderBox = borderBoxRect();
4144 LayoutUnit overflowMinX = borderBox.x();
4145 LayoutUnit overflowMaxX = borderBox.maxX();
4146 LayoutUnit overflowMinY = borderBox.y();
4147 LayoutUnit overflowMaxY = borderBox.maxY();
4149 // Compute box-shadow overflow first.
4150 if (style()->boxShadow()) {
4151 LayoutUnit shadowLeft;
4152 LayoutUnit shadowRight;
4153 LayoutUnit shadowTop;
4154 LayoutUnit shadowBottom;
4155 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft);
4157 // In flipped blocks writing modes such as vertical-rl, the physical right shadow value is actually at the lower x-coordinate.
4158 overflowMinX = borderBox.x() + ((!isFlipped || isHorizontal) ? shadowLeft : -shadowRight);
4159 overflowMaxX = borderBox.maxX() + ((!isFlipped || isHorizontal) ? shadowRight : -shadowLeft);
4160 overflowMinY = borderBox.y() + ((!isFlipped || !isHorizontal) ? shadowTop : -shadowBottom);
4161 overflowMaxY = borderBox.maxY() + ((!isFlipped || !isHorizontal) ? shadowBottom : -shadowTop);
4164 // Now compute border-image-outset overflow.
4165 if (style()->hasBorderImageOutsets()) {
4166 LayoutBoxExtent borderOutsets = style()->borderImageOutsets();
4168 // In flipped blocks writing modes, the physical sides are inverted. For example in vertical-rl, the right
4169 // border is at the lower x coordinate value.
4170 overflowMinX = min(overflowMinX, borderBox.x() - ((!isFlipped || isHorizontal) ? borderOutsets.left() : borderOutsets.right()));
4171 overflowMaxX = max(overflowMaxX, borderBox.maxX() + ((!isFlipped || isHorizontal) ? borderOutsets.right() : borderOutsets.left()));
4172 overflowMinY = min(overflowMinY, borderBox.y() - ((!isFlipped || !isHorizontal) ? borderOutsets.top() : borderOutsets.bottom()));
4173 overflowMaxY = max(overflowMaxY, borderBox.maxY() + ((!isFlipped || !isHorizontal) ? borderOutsets.bottom() : borderOutsets.top()));
4176 // Add in the final overflow with shadows and outsets combined.
4177 LayoutRect visualEffectOverflow(overflowMinX, overflowMinY, overflowMaxX - overflowMinX, overflowMaxY - overflowMinY);
4178 addVisualOverflow(visualEffectOverflow);
4181 void RenderBox::addOverflowFromChild(RenderBox* child, const LayoutSize& delta)
4183 // Never allow flow threads to propagate overflow up to a parent.
4184 if (child->isRenderFlowThread())
4187 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then
4188 // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this
4189 // and just propagates the border box rect instead.
4190 LayoutRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(style());
4191 childLayoutOverflowRect.move(delta);
4192 addLayoutOverflow(childLayoutOverflowRect);
4194 // Add in visual overflow from the child. Even if the child clips its overflow, it may still
4195 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this
4196 // overflow if we are clipping our own overflow.
4197 if (child->hasSelfPaintingLayer())
4199 LayoutRect childVisualOverflowRect = child->visualOverflowRectForPropagation(style());
4200 childVisualOverflowRect.move(delta);
4201 addContentsVisualOverflow(childVisualOverflowRect);
4204 void RenderBox::addLayoutOverflow(const LayoutRect& rect)
4206 LayoutRect clientBox = noOverflowRect();
4207 if (clientBox.contains(rect) || rect.isEmpty())
4210 // For overflow clip objects, we don't want to propagate overflow into unreachable areas.
4211 LayoutRect overflowRect(rect);
4212 if (hasOverflowClip() || isRenderView()) {
4213 // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl
4214 // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same
4215 // and vertical-lr/rl as the same.
4216 bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode();
4217 bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode();
4218 if (isFlexibleBox() && style()->isReverseFlexDirection()) {
4219 RenderFlexibleBox* flexibleBox = toRenderFlexibleBox(this);
4220 if (flexibleBox->isHorizontalFlow())
4221 hasLeftOverflow = true;
4223 hasTopOverflow = true;
4226 if (hasColumns() && style()->columnProgression() == ReverseColumnProgression) {
4227 if (isHorizontalWritingMode() ^ !style()->hasInlineColumnAxis())
4228 hasLeftOverflow = !hasLeftOverflow;
4230 hasTopOverflow = !hasTopOverflow;
4233 if (!hasTopOverflow)
4234 overflowRect.shiftYEdgeTo(max(overflowRect.y(), clientBox.y()));
4236 overflowRect.shiftMaxYEdgeTo(min(overflowRect.maxY(), clientBox.maxY()));
4237 if (!hasLeftOverflow)
4238 overflowRect.shiftXEdgeTo(max(overflowRect.x(), clientBox.x()));
4240 overflowRect.shiftMaxXEdgeTo(min(overflowRect.maxX(), clientBox.maxX()));
4242 // Now re-test with the adjusted rectangle and see if it has become unreachable or fully
4244 if (clientBox.contains(overflowRect) || overflowRect.isEmpty())
4249 m_overflow = adoptPtr(new RenderOverflow(clientBox, borderBoxRect()));
4251 m_overflow->addLayoutOverflow(overflowRect);
4254 void RenderBox::addVisualOverflow(const LayoutRect& rect)
4256 LayoutRect borderBox = borderBoxRect();
4257 if (borderBox.contains(rect) || rect.isEmpty())
4261 m_overflow = adoptPtr(new RenderOverflow(noOverflowRect(), borderBox));
4263 m_overflow->addVisualOverflow(rect);
4266 void RenderBox::addContentsVisualOverflow(const LayoutRect& rect)
4268 if (!hasOverflowClip()) {
4269 addVisualOverflow(rect);
4274 m_overflow = adoptPtr(new RenderOverflow(clientBoxRect(), borderBoxRect()));
4275 m_overflow->addContentsVisualOverflow(rect);
4278 void RenderBox::clearLayoutOverflow()
4283 if (!hasVisualOverflow() && contentsVisualOverflowRect().isEmpty()) {
4288 m_overflow->setLayoutOverflow(noOverflowRect());
4291 inline static bool percentageLogicalHeightIsResolvable(const RenderBox* box)
4293 return RenderBox::percentageLogicalHeightIsResolvableFromBlock(box->containingBlock(), box->isOutOfFlowPositioned());
4296 bool RenderBox::percentageLogicalHeightIsResolvableFromBlock(const RenderBlock* containingBlock, bool isOutOfFlowPositioned)
4298 // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing
4299 // block that may have a specified height and then use it. In strict mode, this violates the
4300 // specification, which states that percentage heights just revert to auto if the containing
4301 // block has an auto height. We still skip anonymous containing blocks in both modes, though, and look
4302 // only at explicit containers.
4303 const RenderBlock* cb = containingBlock;
4304 bool inQuirksMode = cb->document().inQuirksMode();
4305 while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isOutOfFlowPositioned() && cb->style()->logicalHeight().isAuto()) {
4306 if (!inQuirksMode && !cb->isAnonymousBlock())
4308 cb = cb->containingBlock();
4311 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height
4312 // explicitly specified that can be used for any percentage computations.
4313 // FIXME: We can't just check top/bottom here.
4314 // https://bugs.webkit.org/show_bug.cgi?id=46500
4315 bool isOutOfFlowPositionedWithSpecifiedHeight = cb->isOutOfFlowPositioned() && (!cb->style()->logicalHeight().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto()));
4317 // Table cells violate what the CSS spec says to do with heights. Basically we
4318 // don't care if the cell specified a height or not. We just always make ourselves
4319 // be a percentage of the cell's current content height.
4320 if (cb->isTableCell())
4323 // Otherwise we only use our percentage height if our containing block had a specified
4325 if (cb->style()->logicalHeight().isFixed())
4327 if (cb->style()->logicalHeight().isPercent() && !isOutOfFlowPositionedWithSpecifiedHeight)
4328 return percentageLogicalHeightIsResolvableFromBlock(cb->containingBlock(), cb->isOutOfFlowPositioned());
4329 if (cb->isRenderView() || inQuirksMode || isOutOfFlowPositionedWithSpecifiedHeight)
4331 if (cb->isRoot() && isOutOfFlowPositioned) {
4332 // Match the positioned objects behavior, which is that positioned objects will fill their viewport
4333 // always. Note we could only hit this case by recurring into computePercentageLogicalHeight on a positioned containing block.
4340 bool RenderBox::hasUnsplittableScrollingOverflow() const
4342 // We will paginate as long as we don't scroll overflow in the pagination direction.
4343 bool isHorizontal = isHorizontalWritingMode();
4344 if ((isHorizontal && !scrollsOverflowY()) || (!isHorizontal && !scrollsOverflowX()))
4347 // We do have overflow. We'll still be willing to paginate as long as the block
4348 // has auto logical height, auto or undefined max-logical-height and a zero or auto min-logical-height.
4349 // Note this is just a heuristic, and it's still possible to have overflow under these
4350 // conditions, but it should work out to be good enough for common cases. Paginating overflow
4351 // with scrollbars present is not the end of the world and is what we used to do in the old model anyway.
4352 return !style()->logicalHeight().isIntrinsicOrAuto()
4353 || (!style()->logicalMaxHeight().isIntrinsicOrAuto() && !style()->logicalMaxHeight().isUndefined() && (!style()->logicalMaxHeight().isPercent() || percentageLogicalHeightIsResolvable(this)))
4354 || (!style()->logicalMinHeight().isIntrinsicOrAuto() && style()->logicalMinHeight().isPositive() && (!style()->logicalMinHeight().isPercent() || percentageLogicalHeightIsResolvable(this)));
4357 bool RenderBox::isUnsplittableForPagination() const
4359 return isReplaced() || hasUnsplittableScrollingOverflow() || (parent() && isWritingModeRoot());
4362 LayoutUnit RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const
4365 return direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left();
4369 int RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode linePositionMode) const
4371 ASSERT(linePositionMode == PositionOnContainingLine);
4373 int result = direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left();
4374 if (baselineType == AlphabeticBaseline)
4376 return result - result / 2;
4382 RenderLayer* RenderBox::enclosingFloatPaintingLayer() const
4384 const RenderObject* curr = this;
4386 RenderLayer* layer = curr->hasLayer() && curr->isBox() ? toRenderBox(curr)->layer() : 0;
4387 if (layer && layer->isSelfPaintingLayer())
4389 curr = curr->parent();
4394 LayoutRect RenderBox::logicalVisualOverflowRectForPropagation(RenderStyle* parentStyle) const
4396 LayoutRect rect = visualOverflowRectForPropagation(parentStyle);
4397 if (!parentStyle->isHorizontalWritingMode())
4398 return rect.transposedRect();
4402 LayoutRect RenderBox::visualOverflowRectForPropagation(RenderStyle* parentStyle) const
4404 // If the writing modes of the child and parent match, then we don't have to
4405 // do anything fancy. Just return the result.
4406 LayoutRect rect = visualOverflowRect();
4407 if (parentStyle->writingMode() == style()->writingMode())
4410 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
4411 // in a particular axis, then we have to flip the rect along that axis.
4412 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode)
4413 rect.setX(width() - rect.maxX());
4414 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode)
4415 rect.setY(height() - rect.maxY());
4420 LayoutRect RenderBox::logicalLayoutOverflowRectForPropagation(RenderStyle* parentStyle) const
4422 LayoutRect rect = layoutOverflowRectForPropagation(parentStyle);
4423 if (!parentStyle->isHorizontalWritingMode())
4424 return rect.transposedRect();
4428 LayoutRect RenderBox::layoutOverflowRectForPropagation(RenderStyle* parentStyle) const
4430 // Only propagate interior layout overflow if we don't clip it.
4431 LayoutRect rect = borderBoxRect();
4432 // We want to include the margin, but only when it adds height. Quirky margins don't contribute height
4433 // nor do the margins of self-collapsing blocks.
4434 if (!style()->hasMarginAfterQuirk() && !isSelfCollapsingBlock())
4435 rect.expand(isHorizontalWritingMode() ? LayoutSize(LayoutUnit(), marginAfter()) : LayoutSize(marginAfter(), LayoutUnit()));
4437 if (!hasOverflowClip())
4438 rect.unite(layoutOverflowRect());
4440 bool hasTransform = hasLayer() && layer()->transform();
4441 if (isInFlowPositioned() || hasTransform) {
4442 // If we are relatively positioned or if we have a transform, then we have to convert
4443 // this rectangle into physical coordinates, apply relative positioning and transforms
4444 // to it, and then convert it back.
4445 flipForWritingMode(rect);
4448 rect = layer()->currentTransform().mapRect(rect);
4450 if (isInFlowPositioned())
4451 rect.move(offsetForInFlowPosition());
4453 // Now we need to flip back.
4454 flipForWritingMode(rect);
4457 // If the writing modes of the child and parent match, then we don't have to
4458 // do anything fancy. Just return the result.
4459 if (parentStyle->writingMode() == style()->writingMode())
4462 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
4463 // in a particular axis, then we have to flip the rect along that axis.
4464 if (style()->writingMode() == RightToLeftWritingMode || parentStyle->writingMode() == RightToLeftWritingMode)
4465 rect.setX(width() - rect.maxX());
4466 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle->writingMode() == BottomToTopWritingMode)
4467 rect.setY(height() - rect.maxY());
4472 LayoutRect RenderBox::noOverflowRect() const
4474 // Because of the special coodinate system used for overflow rectangles and many other
4475 // rectangles (not quite logical, not quite physical), we need to flip the block progression
4476 // coordinate in vertical-rl and horizontal-bt writing modes. In other words, the rectangle
4477 // returned is physical, except for the block direction progression coordinate (y in horizontal
4478 // writing modes, x in vertical writing modes), which is always "logical top". Apart from the
4479 // flipping, this method does the same as clientBoxRect().
4481 LayoutUnit left = borderLeft() + (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft() ? verticalScrollbarWidth() : 0);
4482 LayoutUnit top = borderTop();
4483 LayoutUnit right = borderRight();
4484 LayoutUnit bottom = borderBottom();
4485 LayoutRect rect(left, top, width() - left - right, height() - top - bottom);
4486 flipForWritingMode(rect);
4487 // Subtract space occupied by scrollbars. Order is important here: first flip, then subtract
4488 // scrollbars. This may seem backwards and weird, since one would think that a horizontal
4489 // scrollbar at the physical bottom in horizontal-bt ought to be at the logical top (physical
4490 // bottom), between the logical top (physical bottom) border and the logical top (physical
4491 // bottom) padding. But this is how the rest of the code expects us to behave. This is highly
4492 // related to https://bugs.webkit.org/show_bug.cgi?id=76129
4493 // FIXME: when the above mentioned bug is fixed, it should hopefully be possible to call
4494 // clientBoxRect() or paddingBoxRect() in this method, rather than fiddling with the edges on
4496 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
4497 rect.contract(0, horizontalScrollbarHeight());
4499 rect.contract(verticalScrollbarWidth(), horizontalScrollbarHeight());
4503 LayoutRect RenderBox::overflowRectForPaintRejection() const
4505 LayoutRect overflowRect = visualOverflowRect();
4506 if (!m_overflow || !usesCompositedScrolling())
4507 return overflowRect;
4509 overflowRect.unite(layoutOverflowRect());
4510 overflowRect.move(-scrolledContentOffset());
4511 return overflowRect;
4514 LayoutUnit RenderBox::offsetLeft() const
4516 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).x();
4519 LayoutUnit RenderBox::offsetTop() const
4521 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).y();
4524 LayoutPoint RenderBox::flipForWritingModeForChild(const RenderBox* child, const LayoutPoint& point) const
4526 if (!style()->isFlippedBlocksWritingMode())
4529 // The child is going to add in its x() and y(), so we have to make sure it ends up in
4531 if (isHorizontalWritingMode())
4532 return LayoutPoint(point.x(), point.y() + height() - child->height() - (2 * child->y()));
4533 return LayoutPoint(point.x() + width() - child->width() - (2 * child->x()), point.y());
4536 void RenderBox::flipForWritingMode(LayoutRect& rect) const
4538 if (!style()->isFlippedBlocksWritingMode())
4541 if (isHorizontalWritingMode())
4542 rect.setY(height() - rect.maxY());
4544 rect.setX(width() - rect.maxX());
4547 LayoutUnit RenderBox::flipForWritingMode(LayoutUnit position) const
4549 if (!style()->isFlippedBlocksWritingMode())
4551 return logicalHeight() - position;
4554 LayoutPoint RenderBox::flipForWritingMode(const LayoutPoint& position) const
4556 if (!style()->isFlippedBlocksWritingMode())
4558 return isHorizontalWritingMode() ? LayoutPoint(position.x(), height() - position.y()) : LayoutPoint(width() - position.x(), position.y());
4561 LayoutPoint RenderBox::flipForWritingModeIncludingColumns(const LayoutPoint& point) const
4563 if (!hasColumns() || !style()->isFlippedBlocksWritingMode())
4564 return flipForWritingMode(point);
4565 return toRenderBlock(this)->flipForWritingModeIncludingColumns(point);
4568 LayoutSize RenderBox::flipForWritingMode(const LayoutSize& offset) const
4570 if (!style()->isFlippedBlocksWritingMode())
4572 return isHorizontalWritingMode() ? LayoutSize(offset.width(), height() - offset.height()) : LayoutSize(width() - offset.width(), offset.height());
4575 FloatPoint RenderBox::flipForWritingMode(const FloatPoint& position) const
4577 if (!style()->isFlippedBlocksWritingMode())
4579 return isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y());
4582 void RenderBox::flipForWritingMode(FloatRect& rect) const
4584 if (!style()->isFlippedBlocksWritingMode())
4587 if (isHorizontalWritingMode())
4588 rect.setY(height() - rect.maxY());
4590 rect.setX(width() - rect.maxX());
4593 LayoutPoint RenderBox::topLeftLocation() const
4595 RenderBlock* containerBlock = containingBlock();
4596 if (!containerBlock || containerBlock == this)
4598 return containerBlock->flipForWritingModeForChild(this, location());
4601 LayoutSize RenderBox::topLeftLocationOffset() const
4603 RenderBlock* containerBlock = containingBlock();
4604 if (!containerBlock || containerBlock == this)
4605 return locationOffset();
4607 LayoutRect rect(frameRect());
4608 containerBlock->flipForWritingMode(rect); // FIXME: This is wrong if we are an absolutely positioned object enclosed by a relative-positioned inline.
4609 return LayoutSize(rect.x(), rect.y());
4612 bool RenderBox::hasRelativeLogicalHeight() const
4614 return style()->logicalHeight().isPercent()
4615 || style()->logicalMinHeight().isPercent()
4616 || style()->logicalMaxHeight().isPercent();
4619 static void markBoxForRelayoutAfterSplit(RenderBox* box)
4621 // FIXME: The table code should handle that automatically. If not,
4622 // we should fix it and remove the table part checks.
4623 if (box->isTable()) {
4624 // Because we may have added some sections with already computed column structures, we need to
4625 // sync the table structure with them now. This avoids crashes when adding new cells to the table.
4626 toRenderTable(box)->forceSectionsRecalc();
4627 } else if (box->isTableSection())
4628 toRenderTableSection(box)->setNeedsCellRecalc();
4630 box->setNeedsLayoutAndPrefWidthsRecalc();
4633 RenderObject* RenderBox::splitAnonymousBoxesAroundChild(RenderObject* beforeChild)
4635 bool didSplitParentAnonymousBoxes = false;
4637 while (beforeChild->parent() != this) {
4638 RenderBox* boxToSplit = toRenderBox(beforeChild->parent());
4639 if (boxToSplit->firstChild() != beforeChild && boxToSplit->isAnonymous()) {
4640 didSplitParentAnonymousBoxes = true;
4642 // We have to split the parent box into two boxes and move children
4643 // from |beforeChild| to end into the new post box.
4644 RenderBox* postBox = boxToSplit->createAnonymousBoxWithSameTypeAs(this);
4645 postBox->setChildrenInline(boxToSplit->childrenInline());
4646 RenderBox* parentBox = toRenderBox(boxToSplit->parent());
4647 // We need to invalidate the |parentBox| before inserting the new node
4648 // so that the table repainting logic knows the structure is dirty.
4649 // See for example RenderTableCell:clippedOverflowRectForRepaint.
4650 markBoxForRelayoutAfterSplit(parentBox);
4651 parentBox->virtualChildren()->insertChildNode(parentBox, postBox, boxToSplit->nextSibling());
4652 boxToSplit->moveChildrenTo(postBox, beforeChild, 0, true);
4654 markBoxForRelayoutAfterSplit(boxToSplit);
4655 markBoxForRelayoutAfterSplit(postBox);
4657 beforeChild = postBox;
4659 beforeChild = boxToSplit;
4662 if (didSplitParentAnonymousBoxes)
4663 markBoxForRelayoutAfterSplit(this);
4665 ASSERT(beforeChild->parent() == this);
4669 LayoutUnit RenderBox::offsetFromLogicalTopOfFirstPage() const
4671 LayoutState* layoutState = view()->layoutState();
4672 if (layoutState && !layoutState->isPaginated())
4675 if (!layoutState && !flowThreadContainingBlock())
4678 RenderBlock* containerBlock = containingBlock();
4679 return containerBlock->offsetFromLogicalTopOfFirstPage() + logicalTop();
4682 } // namespace WebCore