Remove unused variable movedSectionLogicalTop from table layout code.
[chromium-blink-merge.git] / third_party / WebKit / Source / core / layout / LayoutBox.cpp
blob0272e0ac8552e1f0090d5ad84d5e849afad17c0b
1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 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.
26 #include "config.h"
27 #include "core/layout/LayoutBox.h"
29 #include "core/HTMLNames.h"
30 #include "core/dom/Document.h"
31 #include "core/editing/EditingUtilities.h"
32 #include "core/frame/FrameHost.h"
33 #include "core/frame/FrameView.h"
34 #include "core/frame/LocalFrame.h"
35 #include "core/frame/Settings.h"
36 #include "core/html/HTMLElement.h"
37 #include "core/html/HTMLFrameElementBase.h"
38 #include "core/html/HTMLFrameOwnerElement.h"
39 #include "core/input/EventHandler.h"
40 #include "core/layout/HitTestResult.h"
41 #include "core/layout/LayoutAnalyzer.h"
42 #include "core/layout/LayoutDeprecatedFlexibleBox.h"
43 #include "core/layout/LayoutFlexibleBox.h"
44 #include "core/layout/LayoutGeometryMap.h"
45 #include "core/layout/LayoutGrid.h"
46 #include "core/layout/LayoutInline.h"
47 #include "core/layout/LayoutListBox.h"
48 #include "core/layout/LayoutListMarker.h"
49 #include "core/layout/LayoutMultiColumnSpannerPlaceholder.h"
50 #include "core/layout/LayoutPart.h"
51 #include "core/layout/LayoutScrollbarPart.h"
52 #include "core/layout/LayoutTableCell.h"
53 #include "core/layout/LayoutView.h"
54 #include "core/layout/compositing/DeprecatedPaintLayerCompositor.h"
55 #include "core/layout/shapes/ShapeOutsideInfo.h"
56 #include "core/page/AutoscrollController.h"
57 #include "core/page/Page.h"
58 #include "core/paint/BackgroundImageGeometry.h"
59 #include "core/paint/BoxPainter.h"
60 #include "core/paint/DeprecatedPaintLayer.h"
61 #include "core/style/ShadowList.h"
62 #include "platform/LengthFunctions.h"
63 #include "platform/geometry/DoubleRect.h"
64 #include "platform/geometry/FloatQuad.h"
65 #include "platform/geometry/FloatRoundedRect.h"
66 #include "platform/geometry/TransformState.h"
67 #include "platform/graphics/GraphicsContext.h"
68 #include "platform/graphics/paint/DisplayItemList.h"
69 #include <algorithm>
70 #include <math.h>
72 namespace blink {
74 using namespace HTMLNames;
76 // Used by flexible boxes when flexing this element and by table cells.
77 typedef WTF::HashMap<const LayoutBox*, LayoutUnit> OverrideSizeMap;
79 // Used by grid elements to properly size their grid items.
80 // FIXME: Move these into LayoutBoxRareData.
81 static OverrideSizeMap* gOverrideContainingBlockLogicalHeightMap = nullptr;
82 static OverrideSizeMap* gOverrideContainingBlockLogicalWidthMap = nullptr;
83 static OverrideSizeMap* gExtraInlineOffsetMap = nullptr;
84 static OverrideSizeMap* gExtraBlockOffsetMap = nullptr;
87 // Size of border belt for autoscroll. When mouse pointer in border belt,
88 // autoscroll is started.
89 static const int autoscrollBeltSize = 20;
90 static const unsigned backgroundObscurationTestMaxDepth = 4;
92 LayoutBox::LayoutBox(ContainerNode* node)
93 : LayoutBoxModelObject(node)
94 , m_intrinsicContentLogicalHeight(-1)
95 , m_minPreferredLogicalWidth(-1)
96 , m_maxPreferredLogicalWidth(-1)
98 setIsBox();
101 DeprecatedPaintLayerType LayoutBox::layerTypeRequired() const
103 // hasAutoZIndex only returns true if the element is positioned or a flex-item since
104 // position:static elements that are not flex-items get their z-index coerced to auto.
105 if (isPositioned() || createsGroup() || hasClipPath() || hasTransformRelatedProperty()
106 || style()->hasCompositorProxy() || hasHiddenBackface() || hasReflection() || style()->specifiesColumns()
107 || !style()->hasAutoZIndex() || style()->shouldCompositeForCurrentAnimations())
108 return NormalDeprecatedPaintLayer;
110 if (hasOverflowClip())
111 return OverflowClipDeprecatedPaintLayer;
113 return NoDeprecatedPaintLayer;
116 void LayoutBox::willBeDestroyed()
118 clearOverrideSize();
119 clearContainingBlockOverrideSize();
120 clearExtraInlineAndBlockOffests();
122 LayoutBlock::removePercentHeightDescendantIfNeeded(this);
124 ShapeOutsideInfo::removeInfo(*this);
126 LayoutBoxModelObject::willBeDestroyed();
129 void LayoutBox::removeFloatingOrPositionedChildFromBlockLists()
131 ASSERT(isFloatingOrOutOfFlowPositioned());
133 if (documentBeingDestroyed())
134 return;
136 if (isFloating()) {
137 LayoutBlockFlow* parentBlockFlow = nullptr;
138 for (LayoutObject* curr = parent(); curr && !curr->isLayoutView(); curr = curr->parent()) {
139 if (curr->isLayoutBlockFlow()) {
140 LayoutBlockFlow* currBlockFlow = toLayoutBlockFlow(curr);
141 if (!parentBlockFlow || currBlockFlow->containsFloat(this))
142 parentBlockFlow = currBlockFlow;
146 if (parentBlockFlow) {
147 parentBlockFlow->markSiblingsWithFloatsForLayout(this);
148 parentBlockFlow->markAllDescendantsWithFloatsForLayout(this, false);
152 if (isOutOfFlowPositioned())
153 LayoutBlock::removePositionedObject(this);
156 void LayoutBox::styleWillChange(StyleDifference diff, const ComputedStyle& newStyle)
158 const ComputedStyle* oldStyle = style();
159 if (oldStyle) {
160 LayoutFlowThread* flowThread = flowThreadContainingBlock();
161 if (flowThread && flowThread != this)
162 flowThread->flowThreadDescendantStyleWillChange(this, diff, newStyle);
164 // The background of the root element or the body element could propagate up to
165 // the canvas. Just dirty the entire canvas when our style changes substantially.
166 if ((diff.needsPaintInvalidation() || diff.needsLayout()) && node()
167 && (isHTMLHtmlElement(*node()) || isHTMLBodyElement(*node()))) {
168 view()->setShouldDoFullPaintInvalidation();
170 if (oldStyle->hasEntirelyFixedBackground() != newStyle.hasEntirelyFixedBackground())
171 view()->compositor()->setNeedsUpdateFixedBackground();
174 // When a layout hint happens and an object's position style changes, we have to do a layout
175 // to dirty the layout tree using the old position value now.
176 if (diff.needsFullLayout() && parent() && oldStyle->position() != newStyle.position()) {
177 if (!oldStyle->hasOutOfFlowPosition() && newStyle.hasOutOfFlowPosition()) {
178 // We're about to go out of flow. Before that takes place, we need to mark the
179 // current containing block chain for preferred widths recalculation.
180 setNeedsLayoutAndPrefWidthsRecalc(LayoutInvalidationReason::StyleChange);
181 } else {
182 markContainerChainForLayout();
184 if (oldStyle->position() == StaticPosition)
185 setShouldDoFullPaintInvalidation();
186 else if (newStyle.hasOutOfFlowPosition())
187 parent()->setChildNeedsLayout();
188 if (isFloating() && !isOutOfFlowPositioned() && newStyle.hasOutOfFlowPosition())
189 removeFloatingOrPositionedChildFromBlockLists();
191 // FIXME: This branch runs when !oldStyle, which means that layout was never called
192 // so what's the point in invalidating the whole view that we never painted?
193 } else if (isBody()) {
194 view()->setShouldDoFullPaintInvalidation();
197 LayoutBoxModelObject::styleWillChange(diff, newStyle);
200 void LayoutBox::styleDidChange(StyleDifference diff, const ComputedStyle* oldStyle)
202 // Horizontal writing mode definition is updated in LayoutBoxModelObject::updateFromStyle,
203 // (as part of the LayoutBoxModelObject::styleDidChange call below). So, we can safely cache the horizontal
204 // writing mode value before style change here.
205 bool oldHorizontalWritingMode = isHorizontalWritingMode();
207 LayoutBoxModelObject::styleDidChange(diff, oldStyle);
209 const ComputedStyle& newStyle = styleRef();
210 if (needsLayout() && oldStyle)
211 LayoutBlock::removePercentHeightDescendantIfNeeded(this);
213 if (LayoutBlock::hasPercentHeightContainerMap() && slowFirstChild()
214 && oldHorizontalWritingMode != isHorizontalWritingMode())
215 LayoutBlock::clearPercentHeightDescendantsFrom(this);
217 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the
218 // new zoomed coordinate space.
219 if (hasOverflowClip() && oldStyle && oldStyle->effectiveZoom() != newStyle.effectiveZoom() && layer()) {
220 if (int left = layer()->scrollableArea()->scrollXOffset()) {
221 left = (left / oldStyle->effectiveZoom()) * newStyle.effectiveZoom();
222 layer()->scrollableArea()->scrollToXOffset(left);
224 if (int top = layer()->scrollableArea()->scrollYOffset()) {
225 top = (top / oldStyle->effectiveZoom()) * newStyle.effectiveZoom();
226 layer()->scrollableArea()->scrollToYOffset(top);
230 // Our opaqueness might have changed without triggering layout.
231 if (diff.needsPaintInvalidation()) {
232 LayoutObject* parentToInvalidate = parent();
233 for (unsigned i = 0; i < backgroundObscurationTestMaxDepth && parentToInvalidate; ++i) {
234 parentToInvalidate->invalidateBackgroundObscurationStatus();
235 parentToInvalidate = parentToInvalidate->parent();
239 if (isDocumentElement() || isBody()) {
240 document().view()->recalculateScrollbarOverlayStyle();
241 document().view()->recalculateCustomScrollbarStyle();
242 if (LayoutView* layoutView = view()) {
243 if (DeprecatedPaintLayerScrollableArea* scrollableArea = layoutView->scrollableArea()) {
244 if (scrollableArea->horizontalScrollbar() && scrollableArea->horizontalScrollbar()->isCustomScrollbar())
245 scrollableArea->horizontalScrollbar()->styleChanged();
246 if (scrollableArea->verticalScrollbar() && scrollableArea->verticalScrollbar()->isCustomScrollbar())
247 scrollableArea->verticalScrollbar()->styleChanged();
251 updateShapeOutsideInfoAfterStyleChange(*style(), oldStyle);
252 updateGridPositionAfterStyleChange(oldStyle);
254 if (LayoutMultiColumnSpannerPlaceholder* placeholder = this->spannerPlaceholder())
255 placeholder->layoutObjectInFlowThreadStyleDidChange(oldStyle);
257 updateSlowRepaintStatusAfterStyleChange();
259 if (oldStyle) {
260 LayoutFlowThread* flowThread = flowThreadContainingBlock();
261 if (flowThread && flowThread != this)
262 flowThread->flowThreadDescendantStyleDidChange(this, diff, *oldStyle);
266 void LayoutBox::updateSlowRepaintStatusAfterStyleChange()
268 if (!frameView())
269 return;
271 // On low-powered/mobile devices, preventing blitting on a scroll can cause noticeable delays
272 // when scrolling a page with a fixed background image. As an optimization, assuming there are
273 // no fixed positoned elements on the page, we can acclerate scrolling (via blitting) if we
274 // ignore the CSS property "background-attachment: fixed".
275 bool ignoreFixedBackgroundAttachment = RuntimeEnabledFeatures::fastMobileScrollingEnabled();
276 if (ignoreFixedBackgroundAttachment)
277 return;
279 // An object needs to be repainted on frame scroll when it has background-attachment:fixed.
280 // LayoutObject is responsible for painting root background, thus the root element (and the
281 // body element if html element has no background) skips painting backgrounds.
282 bool isSlowRepaintObject = !isDocumentElement() && !backgroundStolenForBeingBody() && styleRef().hasFixedBackgroundImage();
283 if (isLayoutView() && view()->compositor()->supportsFixedRootBackgroundCompositing()) {
284 if (styleRef().hasEntirelyFixedBackground())
285 isSlowRepaintObject = false;
288 setIsSlowRepaintObject(isSlowRepaintObject);
291 void LayoutBox::updateShapeOutsideInfoAfterStyleChange(const ComputedStyle& style, const ComputedStyle* oldStyle)
293 const ShapeValue* shapeOutside = style.shapeOutside();
294 const ShapeValue* oldShapeOutside = oldStyle ? oldStyle->shapeOutside() : ComputedStyle::initialShapeOutside();
296 Length shapeMargin = style.shapeMargin();
297 Length oldShapeMargin = oldStyle ? oldStyle->shapeMargin() : ComputedStyle::initialShapeMargin();
299 float shapeImageThreshold = style.shapeImageThreshold();
300 float oldShapeImageThreshold = oldStyle ? oldStyle->shapeImageThreshold() : ComputedStyle::initialShapeImageThreshold();
302 // FIXME: A future optimization would do a deep comparison for equality. (bug 100811)
303 if (shapeOutside == oldShapeOutside && shapeMargin == oldShapeMargin && shapeImageThreshold == oldShapeImageThreshold)
304 return;
306 if (!shapeOutside)
307 ShapeOutsideInfo::removeInfo(*this);
308 else
309 ShapeOutsideInfo::ensureInfo(*this).markShapeAsDirty();
311 if (shapeOutside || shapeOutside != oldShapeOutside)
312 markShapeOutsideDependentsForLayout();
315 void LayoutBox::updateGridPositionAfterStyleChange(const ComputedStyle* oldStyle)
317 if (!oldStyle || !parent() || !parent()->isLayoutGrid())
318 return;
320 if (oldStyle->gridColumnStart() == style()->gridColumnStart()
321 && oldStyle->gridColumnEnd() == style()->gridColumnEnd()
322 && oldStyle->gridRowStart() == style()->gridRowStart()
323 && oldStyle->gridRowEnd() == style()->gridRowEnd()
324 && oldStyle->order() == style()->order()
325 && oldStyle->hasOutOfFlowPosition() == style()->hasOutOfFlowPosition())
326 return;
328 // It should be possible to not dirty the grid in some cases (like moving an explicitly placed grid item).
329 // For now, it's more simple to just always recompute the grid.
330 toLayoutGrid(parent())->dirtyGrid();
333 void LayoutBox::updateFromStyle()
335 LayoutBoxModelObject::updateFromStyle();
337 const ComputedStyle& styleToUse = styleRef();
338 bool isViewObject = isLayoutView();
339 bool rootLayerScrolls = document().settings() && document().settings()->rootLayerScrolls();
341 // LayoutView of the main frame is resposible from painting base background.
342 if (isViewObject && !document().ownerElement())
343 setHasBoxDecorationBackground(true);
345 setFloating(!isOutOfFlowPositioned() && styleToUse.isFloating());
347 bool boxHasOverflowClip = false;
348 if (!styleToUse.isOverflowVisible() && isLayoutBlock() && (rootLayerScrolls || !isViewObject)) {
349 // If overflow has been propagated to the viewport, it has no effect here.
350 if (node() != document().viewportDefiningElement())
351 boxHasOverflowClip = true;
354 if (boxHasOverflowClip != hasOverflowClip()) {
355 // FIXME: This shouldn't be required if we tracked the visual overflow
356 // generated by positioned children or self painting layers. crbug.com/345403
357 for (LayoutObject* child = slowFirstChild(); child; child = child->nextSibling())
358 child->setMayNeedPaintInvalidation();
361 setHasOverflowClip(boxHasOverflowClip);
363 setHasTransformRelatedProperty(styleToUse.hasTransformRelatedProperty());
364 setHasReflection(styleToUse.boxReflect());
367 void LayoutBox::layout()
369 ASSERT(needsLayout());
370 LayoutAnalyzer::Scope analyzer(*this);
372 LayoutObject* child = slowFirstChild();
373 if (!child) {
374 clearNeedsLayout();
375 return;
378 LayoutState state(*this, locationOffset());
379 while (child) {
380 child->layoutIfNeeded();
381 ASSERT(!child->needsLayout());
382 child = child->nextSibling();
384 invalidateBackgroundObscurationStatus();
385 clearNeedsLayout();
388 // More IE extensions. clientWidth and clientHeight represent the interior of an object
389 // excluding border and scrollbar.
390 LayoutUnit LayoutBox::clientWidth() const
392 return m_frameRect.width() - borderLeft() - borderRight() - verticalScrollbarWidth();
395 LayoutUnit LayoutBox::clientHeight() const
397 return m_frameRect.height() - borderTop() - borderBottom() - horizontalScrollbarHeight();
400 int LayoutBox::pixelSnappedClientWidth() const
402 return snapSizeToPixel(clientWidth(), location().x() + clientLeft());
405 int LayoutBox::pixelSnappedClientHeight() const
407 return snapSizeToPixel(clientHeight(), location().y() + clientTop());
410 int LayoutBox::pixelSnappedOffsetWidth() const
412 return snapSizeToPixel(offsetWidth(), location().x() + clientLeft());
415 int LayoutBox::pixelSnappedOffsetHeight() const
417 return snapSizeToPixel(offsetHeight(), location().y() + clientTop());
420 LayoutUnit LayoutBox::scrollWidth() const
422 if (hasOverflowClip())
423 return layer()->scrollableArea()->scrollWidth();
424 // For objects with visible overflow, this matches IE.
425 // FIXME: Need to work right with writing modes.
426 if (style()->isLeftToRightDirection())
427 return std::max(clientWidth(), layoutOverflowRect().maxX() - borderLeft());
428 return clientWidth() - std::min(LayoutUnit(), layoutOverflowRect().x() - borderLeft());
431 LayoutUnit LayoutBox::scrollHeight() const
433 if (hasOverflowClip())
434 return layer()->scrollableArea()->scrollHeight();
435 // For objects with visible overflow, this matches IE.
436 // FIXME: Need to work right with writing modes.
437 return std::max(clientHeight(), layoutOverflowRect().maxY() - borderTop());
440 LayoutUnit LayoutBox::scrollLeft() const
442 return hasOverflowClip() ? layer()->scrollableArea()->scrollXOffset() : 0;
445 LayoutUnit LayoutBox::scrollTop() const
447 return hasOverflowClip() ? layer()->scrollableArea()->scrollYOffset() : 0;
450 int LayoutBox::pixelSnappedScrollWidth() const
452 return snapSizeToPixel(scrollWidth(), location().x() + clientLeft());
455 int LayoutBox::pixelSnappedScrollHeight() const
457 if (hasOverflowClip())
458 return layer()->scrollableArea()->scrollHeight();
459 // For objects with visible overflow, this matches IE.
460 // FIXME: Need to work right with writing modes.
461 return snapSizeToPixel(scrollHeight(), location().y() + clientTop());
464 void LayoutBox::setScrollLeft(LayoutUnit newLeft)
466 // This doesn't hit in any tests, but since the equivalent code in setScrollTop
467 // does, presumably this code does as well.
468 DisableCompositingQueryAsserts disabler;
470 if (hasOverflowClip())
471 layer()->scrollableArea()->scrollToXOffset(newLeft, ScrollOffsetClamped, ScrollBehaviorAuto);
474 void LayoutBox::setScrollTop(LayoutUnit newTop)
476 // Hits in compositing/overflow/do-not-assert-on-invisible-composited-layers.html
477 DisableCompositingQueryAsserts disabler;
479 if (hasOverflowClip())
480 layer()->scrollableArea()->scrollToYOffset(newTop, ScrollOffsetClamped, ScrollBehaviorAuto);
483 void LayoutBox::scrollToOffset(const DoubleSize& offset, ScrollBehavior scrollBehavior)
485 // This doesn't hit in any tests, but since the equivalent code in setScrollTop
486 // does, presumably this code does as well.
487 DisableCompositingQueryAsserts disabler;
489 if (hasOverflowClip())
490 layer()->scrollableArea()->scrollToOffset(offset, ScrollOffsetClamped, scrollBehavior);
493 // Returns true iff we are attempting an autoscroll inside an iframe with scrolling="no".
494 static bool isDisallowedAutoscroll(HTMLFrameOwnerElement* ownerElement, FrameView* frameView)
496 if (ownerElement && isHTMLFrameElementBase(*ownerElement)) {
497 HTMLFrameElementBase* frameElementBase = toHTMLFrameElementBase(ownerElement);
498 if (Page* page = frameView->frame().page()) {
499 return page->autoscrollController().autoscrollInProgress()
500 && frameElementBase->scrollingMode() == ScrollbarAlwaysOff;
503 return false;
506 void LayoutBox::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
508 // Presumably the same issue as in setScrollTop. See crbug.com/343132.
509 DisableCompositingQueryAsserts disabler;
511 LayoutBox* parentBox = nullptr;
512 LayoutRect newRect = rect;
514 bool restrictedByLineClamp = false;
515 if (parent()) {
516 parentBox = parent()->enclosingBox();
517 restrictedByLineClamp = !parent()->style()->lineClamp().isNone();
520 if (hasOverflowClip() && !restrictedByLineClamp) {
521 // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property.
522 // This will prevent us from revealing text hidden by the slider in Safari RSS.
523 newRect = layer()->scrollableArea()->scrollIntoView(rect, alignX, alignY);
524 } else if (!parentBox && canBeProgramaticallyScrolled()) {
525 if (FrameView* frameView = this->frameView()) {
526 HTMLFrameOwnerElement* ownerElement = document().ownerElement();
527 if (!isDisallowedAutoscroll(ownerElement, frameView)) {
528 frameView->scrollableArea()->scrollIntoView(rect, alignX, alignY);
530 if (ownerElement && ownerElement->layoutObject()) {
531 if (frameView->safeToPropagateScrollToParent()) {
532 parentBox = ownerElement->layoutObject()->enclosingBox();
533 // FIXME: This doesn't correctly convert the rect to
534 // absolute coordinates in the parent.
535 newRect.setX(rect.x() - frameView->scrollX() + frameView->x());
536 newRect.setY(rect.y() - frameView->scrollY() + frameView->y());
537 } else {
538 parentBox = nullptr;
545 // If we are fixed-position, it is useless to scroll the parent.
546 if (hasLayer() && layer()->scrollsWithViewport())
547 return;
549 if (frame()->page()->autoscrollController().autoscrollInProgress())
550 parentBox = enclosingScrollableBox();
552 if (parentBox)
553 parentBox->scrollRectToVisible(newRect, alignX, alignY);
556 void LayoutBox::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
558 rects.append(pixelSnappedIntRect(accumulatedOffset, size()));
561 void LayoutBox::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
563 quads.append(localToAbsoluteQuad(FloatRect(0, 0, m_frameRect.width().toFloat(), m_frameRect.height().toFloat()), 0 /* mode */, wasFixed));
566 void LayoutBox::updateLayerTransformAfterLayout()
568 // Transform-origin depends on box size, so we need to update the layer transform after layout.
569 if (hasLayer())
570 layer()->updateTransformationMatrix();
573 LayoutUnit LayoutBox::constrainLogicalWidthByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, LayoutBlock* cb) const
575 const ComputedStyle& styleToUse = styleRef();
576 if (!styleToUse.logicalMaxWidth().isMaxSizeNone())
577 logicalWidth = std::min(logicalWidth, computeLogicalWidthUsing(MaxSize, styleToUse.logicalMaxWidth(), availableWidth, cb));
578 return std::max(logicalWidth, computeLogicalWidthUsing(MinSize, styleToUse.logicalMinWidth(), availableWidth, cb));
581 LayoutUnit LayoutBox::constrainLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const
583 const ComputedStyle& styleToUse = styleRef();
584 if (!styleToUse.logicalMaxHeight().isMaxSizeNone()) {
585 LayoutUnit maxH = computeLogicalHeightUsing(MaxSize, styleToUse.logicalMaxHeight(), intrinsicContentHeight);
586 if (maxH != -1)
587 logicalHeight = std::min(logicalHeight, maxH);
589 return std::max(logicalHeight, computeLogicalHeightUsing(MinSize, styleToUse.logicalMinHeight(), intrinsicContentHeight));
592 LayoutUnit LayoutBox::constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight, LayoutUnit intrinsicContentHeight) const
594 const ComputedStyle& styleToUse = styleRef();
595 if (!styleToUse.logicalMaxHeight().isMaxSizeNone()) {
596 LayoutUnit maxH = computeContentLogicalHeight(MaxSize, styleToUse.logicalMaxHeight(), intrinsicContentHeight);
597 if (maxH != -1)
598 logicalHeight = std::min(logicalHeight, maxH);
600 return std::max(logicalHeight, computeContentLogicalHeight(MinSize, styleToUse.logicalMinHeight(), intrinsicContentHeight));
603 void LayoutBox::setLocationAndUpdateOverflowControlsIfNeeded(const LayoutPoint& location)
605 if (hasOverflowClip()) {
606 IntSize oldPixelSnappedBorderRectSize = pixelSnappedBorderBoxRect().size();
607 setLocation(location);
608 if (pixelSnappedBorderBoxRect().size() != oldPixelSnappedBorderRectSize)
609 scrollableArea()->updateAfterLayout();
610 return;
613 setLocation(location);
616 IntRect LayoutBox::absoluteContentBox() const
618 // This is wrong with transforms and flipped writing modes.
619 IntRect rect = pixelSnappedIntRect(contentBoxRect());
620 FloatPoint absPos = localToAbsolute();
621 rect.move(absPos.x(), absPos.y());
622 return rect;
625 IntSize LayoutBox::absoluteContentBoxOffset() const
627 IntPoint offset = roundedIntPoint(contentBoxOffset());
628 FloatPoint absPos = localToAbsolute();
629 offset.move(absPos.x(), absPos.y());
630 return toIntSize(offset);
633 FloatQuad LayoutBox::absoluteContentQuad() const
635 LayoutRect rect = contentBoxRect();
636 return localToAbsoluteQuad(FloatRect(rect));
639 void LayoutBox::addOutlineRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, IncludeBlockVisualOverflowOrNot) const
641 rects.append(LayoutRect(additionalOffset, size()));
644 bool LayoutBox::canResize() const
646 // We need a special case for <iframe> because they never have
647 // hasOverflowClip(). However, they do "implicitly" clip their contents, so
648 // we want to allow resizing them also.
649 return (hasOverflowClip() || isLayoutIFrame()) && style()->resize() != RESIZE_NONE;
652 void LayoutBox::addLayerHitTestRects(LayerHitTestRects& layerRects, const DeprecatedPaintLayer* currentLayer, const LayoutPoint& layerOffset, const LayoutRect& containerRect) const
654 LayoutPoint adjustedLayerOffset = layerOffset + locationOffset();
655 LayoutBoxModelObject::addLayerHitTestRects(layerRects, currentLayer, adjustedLayerOffset, containerRect);
658 void LayoutBox::computeSelfHitTestRects(Vector<LayoutRect>& rects, const LayoutPoint& layerOffset) const
660 if (!size().isEmpty())
661 rects.append(LayoutRect(layerOffset, size()));
664 int LayoutBox::reflectionOffset() const
666 if (!style()->boxReflect())
667 return 0;
668 if (style()->boxReflect()->direction() == ReflectionLeft || style()->boxReflect()->direction() == ReflectionRight)
669 return valueForLength(style()->boxReflect()->offset(), borderBoxRect().width());
670 return valueForLength(style()->boxReflect()->offset(), borderBoxRect().height());
673 LayoutRect LayoutBox::reflectedRect(const LayoutRect& r) const
675 if (!style()->boxReflect())
676 return LayoutRect();
678 LayoutRect box = borderBoxRect();
679 LayoutRect result = r;
680 switch (style()->boxReflect()->direction()) {
681 case ReflectionBelow:
682 result.setY(box.maxY() + reflectionOffset() + (box.maxY() - r.maxY()));
683 break;
684 case ReflectionAbove:
685 result.setY(box.y() - reflectionOffset() - box.height() + (box.maxY() - r.maxY()));
686 break;
687 case ReflectionLeft:
688 result.setX(box.x() - reflectionOffset() - box.width() + (box.maxX() - r.maxX()));
689 break;
690 case ReflectionRight:
691 result.setX(box.maxX() + reflectionOffset() + (box.maxX() - r.maxX()));
692 break;
694 return result;
697 int LayoutBox::verticalScrollbarWidth() const
699 if (!hasOverflowClip() || style()->overflowY() == OOVERLAY)
700 return 0;
702 return layer()->scrollableArea()->verticalScrollbarWidth();
705 int LayoutBox::horizontalScrollbarHeight() const
707 if (!hasOverflowClip() || style()->overflowX() == OOVERLAY)
708 return 0;
710 return layer()->scrollableArea()->horizontalScrollbarHeight();
713 int LayoutBox::intrinsicScrollbarLogicalWidth() const
715 if (!hasOverflowClip())
716 return 0;
718 if (isHorizontalWritingMode() && style()->overflowY() == OSCROLL) {
719 ASSERT(layer()->scrollableArea());
720 // Even with OSCROLL, the scrollbar may not exist (crbug.com/415031).
721 return layer()->scrollableArea()->hasVerticalScrollbar() ? verticalScrollbarWidth() : 0;
724 if (!isHorizontalWritingMode() && style()->overflowX() == OSCROLL) {
725 ASSERT(layer()->scrollableArea());
726 // Even with OSCROLL, the scrollbar may not exist (crbug.com/415031).
727 return layer()->scrollableArea()->hasHorizontalScrollbar() ? horizontalScrollbarHeight() : 0;
730 return 0;
733 ScrollResultOneDimensional LayoutBox::scroll(ScrollDirectionPhysical direction, ScrollGranularity granularity, float delta)
735 // Presumably the same issue as in setScrollTop. See crbug.com/343132.
736 DisableCompositingQueryAsserts disabler;
738 if (!layer() || !layer()->scrollableArea())
739 return ScrollResultOneDimensional(false);
741 return layer()->scrollableArea()->userScroll(direction, granularity, delta);
744 bool LayoutBox::canBeScrolledAndHasScrollableArea() const
746 return canBeProgramaticallyScrolled() && (pixelSnappedScrollHeight() != pixelSnappedClientHeight() || pixelSnappedScrollWidth() != pixelSnappedClientWidth());
749 bool LayoutBox::canBeProgramaticallyScrolled() const
751 Node* node = this->node();
752 if (node && node->isDocumentNode())
753 return true;
755 if (!hasOverflowClip())
756 return false;
758 bool hasScrollableOverflow = hasScrollableOverflowX() || hasScrollableOverflowY();
759 if (scrollsOverflow() && hasScrollableOverflow)
760 return true;
762 return node && node->hasEditableStyle();
765 void LayoutBox::autoscroll(const IntPoint& positionInRootFrame)
767 LocalFrame* frame = this->frame();
768 if (!frame)
769 return;
771 FrameView* frameView = frame->view();
772 if (!frameView)
773 return;
775 IntPoint positionInContent = frameView->rootFrameToContents(positionInRootFrame);
776 scrollRectToVisible(LayoutRect(positionInContent, LayoutSize(1, 1)), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded);
779 // There are two kinds of layoutObject that can autoscroll.
780 bool LayoutBox::canAutoscroll() const
782 if (node() && node()->isDocumentNode())
783 return view()->frameView()->isScrollable();
785 // Check for a box that can be scrolled in its own right.
786 return canBeScrolledAndHasScrollableArea();
789 // If specified point is in border belt, returned offset denotes direction of
790 // scrolling.
791 IntSize LayoutBox::calculateAutoscrollDirection(const IntPoint& pointInRootFrame) const
793 if (!frame())
794 return IntSize();
796 FrameView* frameView = frame()->view();
797 if (!frameView)
798 return IntSize();
800 IntRect box(absoluteBoundingBoxRect());
801 box.move(view()->frameView()->scrollOffset());
802 IntRect windowBox = view()->frameView()->contentsToRootFrame(box);
804 IntPoint windowAutoscrollPoint = pointInRootFrame;
806 if (windowAutoscrollPoint.x() < windowBox.x() + autoscrollBeltSize)
807 windowAutoscrollPoint.move(-autoscrollBeltSize, 0);
808 else if (windowAutoscrollPoint.x() > windowBox.maxX() - autoscrollBeltSize)
809 windowAutoscrollPoint.move(autoscrollBeltSize, 0);
811 if (windowAutoscrollPoint.y() < windowBox.y() + autoscrollBeltSize)
812 windowAutoscrollPoint.move(0, -autoscrollBeltSize);
813 else if (windowAutoscrollPoint.y() > windowBox.maxY() - autoscrollBeltSize)
814 windowAutoscrollPoint.move(0, autoscrollBeltSize);
816 return windowAutoscrollPoint - pointInRootFrame;
819 LayoutBox* LayoutBox::findAutoscrollable(LayoutObject* layoutObject)
821 while (layoutObject && !(layoutObject->isBox() && toLayoutBox(layoutObject)->canAutoscroll())) {
822 if (!layoutObject->parent() && layoutObject->node() == layoutObject->document() && layoutObject->document().ownerElement())
823 layoutObject = layoutObject->document().ownerElement()->layoutObject();
824 else
825 layoutObject = layoutObject->parent();
828 return layoutObject && layoutObject->isBox() ? toLayoutBox(layoutObject) : 0;
831 static inline int adjustedScrollDelta(int beginningDelta)
833 // This implemention matches Firefox's.
834 // http://mxr.mozilla.org/firefox/source/toolkit/content/widgets/browser.xml#856.
835 const int speedReducer = 12;
837 int adjustedDelta = beginningDelta / speedReducer;
838 if (adjustedDelta > 1)
839 adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(adjustedDelta))) - 1;
840 else if (adjustedDelta < -1)
841 adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(-adjustedDelta))) + 1;
843 return adjustedDelta;
846 static inline IntSize adjustedScrollDelta(const IntSize& delta)
848 return IntSize(adjustedScrollDelta(delta.width()), adjustedScrollDelta(delta.height()));
851 void LayoutBox::panScroll(const IntPoint& sourcePoint)
853 LocalFrame* frame = this->frame();
854 if (!frame)
855 return;
857 IntPoint lastKnownMousePosition = frame->eventHandler().lastKnownMousePosition();
859 // 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
860 static IntPoint previousMousePosition;
861 if (lastKnownMousePosition.x() < 0 || lastKnownMousePosition.y() < 0)
862 lastKnownMousePosition = previousMousePosition;
863 else
864 previousMousePosition = lastKnownMousePosition;
866 IntSize delta = lastKnownMousePosition - sourcePoint;
868 if (abs(delta.width()) <= AutoscrollController::noPanScrollRadius) // at the center we let the space for the icon
869 delta.setWidth(0);
870 if (abs(delta.height()) <= AutoscrollController::noPanScrollRadius)
871 delta.setHeight(0);
872 scrollByRecursively(adjustedScrollDelta(delta), ScrollOffsetClamped);
875 void LayoutBox::scrollByRecursively(const DoubleSize& delta, ScrollOffsetClamping clamp)
877 if (delta.isZero())
878 return;
880 bool restrictedByLineClamp = false;
881 if (parent())
882 restrictedByLineClamp = !parent()->style()->lineClamp().isNone();
884 if (hasOverflowClip() && !restrictedByLineClamp) {
885 DoubleSize newScrollOffset = layer()->scrollableArea()->adjustedScrollOffset() + delta;
886 layer()->scrollableArea()->scrollToOffset(newScrollOffset, clamp);
888 // If this layer can't do the scroll we ask the next layer up that can scroll to try
889 DoubleSize remainingScrollOffset = newScrollOffset - layer()->scrollableArea()->adjustedScrollOffset();
890 if (!remainingScrollOffset.isZero() && parent()) {
891 if (LayoutBox* scrollableBox = enclosingScrollableBox())
892 scrollableBox->scrollByRecursively(remainingScrollOffset, clamp);
894 LocalFrame* frame = this->frame();
895 if (frame && frame->page())
896 frame->page()->autoscrollController().updateAutoscrollLayoutObject();
898 } else if (view()->frameView()) {
899 // If we are here, we were called on a layoutObject that can be programmatically scrolled, but doesn't
900 // have an overflow clip. Which means that it is a document node that can be scrolled.
901 // FIXME: Pass in DoubleSize. crbug.com/414283.
902 view()->frameView()->scrollBy(flooredIntSize(delta), UserScroll);
904 // FIXME: If we didn't scroll the whole way, do we want to try looking at the frames ownerElement?
905 // https://bugs.webkit.org/show_bug.cgi?id=28237
909 bool LayoutBox::needsPreferredWidthsRecalculation() const
911 return style()->paddingStart().hasPercent() || style()->paddingEnd().hasPercent();
914 IntSize LayoutBox::scrolledContentOffset() const
916 ASSERT(hasOverflowClip());
917 ASSERT(hasLayer());
918 // FIXME: Return DoubleSize here. crbug.com/414283.
919 return flooredIntSize(layer()->scrollableArea()->scrollOffset());
922 void LayoutBox::applyCachedClipAndScrollOffsetForPaintInvalidation(LayoutRect& paintRect) const
924 ASSERT(hasLayer());
925 ASSERT(hasOverflowClip());
927 flipForWritingMode(paintRect);
928 paintRect.move(-scrolledContentOffset()); // For overflow:auto/scroll/hidden.
930 // Do not clip scroll layer contents because the compositor expects the whole layer
931 // to be always invalidated in-time.
932 if (usesCompositedScrolling()) {
933 flipForWritingMode(paintRect);
934 return;
937 // size() is inaccurate if we're in the middle of a layout of this LayoutBox, so use the
938 // layer's size instead. Even if the layer's size is wrong, the layer itself will issue paint invalidations
939 // anyway if its size does change.
940 LayoutRect clipRect(LayoutPoint(), LayoutSize(layer()->size()));
941 paintRect = intersection(paintRect, clipRect);
942 flipForWritingMode(paintRect);
945 void LayoutBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
947 minLogicalWidth = minPreferredLogicalWidth() - borderAndPaddingLogicalWidth();
948 maxLogicalWidth = maxPreferredLogicalWidth() - borderAndPaddingLogicalWidth();
951 LayoutUnit LayoutBox::minPreferredLogicalWidth() const
953 if (preferredLogicalWidthsDirty()) {
954 #if ENABLE(ASSERT)
955 SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<LayoutBox&>(*this));
956 #endif
957 const_cast<LayoutBox*>(this)->computePreferredLogicalWidths();
960 return m_minPreferredLogicalWidth;
963 LayoutUnit LayoutBox::maxPreferredLogicalWidth() const
965 if (preferredLogicalWidthsDirty()) {
966 #if ENABLE(ASSERT)
967 SetLayoutNeededForbiddenScope layoutForbiddenScope(const_cast<LayoutBox&>(*this));
968 #endif
969 const_cast<LayoutBox*>(this)->computePreferredLogicalWidths();
972 return m_maxPreferredLogicalWidth;
975 bool LayoutBox::hasOverrideLogicalContentHeight() const
977 return m_rareData && m_rareData->m_overrideLogicalContentHeight != -1;
980 bool LayoutBox::hasOverrideLogicalContentWidth() const
982 return m_rareData && m_rareData->m_overrideLogicalContentWidth != -1;
985 void LayoutBox::setOverrideLogicalContentHeight(LayoutUnit height)
987 ASSERT(height >= 0);
988 ensureRareData().m_overrideLogicalContentHeight = height;
991 void LayoutBox::setOverrideLogicalContentWidth(LayoutUnit width)
993 ASSERT(width >= 0);
994 ensureRareData().m_overrideLogicalContentWidth = width;
997 void LayoutBox::clearOverrideLogicalContentHeight()
999 if (m_rareData)
1000 m_rareData->m_overrideLogicalContentHeight = -1;
1003 void LayoutBox::clearOverrideLogicalContentWidth()
1005 if (m_rareData)
1006 m_rareData->m_overrideLogicalContentWidth = -1;
1009 void LayoutBox::clearOverrideSize()
1011 clearOverrideLogicalContentHeight();
1012 clearOverrideLogicalContentWidth();
1015 LayoutUnit LayoutBox::overrideLogicalContentWidth() const
1017 ASSERT(hasOverrideLogicalContentWidth());
1018 return m_rareData->m_overrideLogicalContentWidth;
1021 LayoutUnit LayoutBox::overrideLogicalContentHeight() const
1023 ASSERT(hasOverrideLogicalContentHeight());
1024 return m_rareData->m_overrideLogicalContentHeight;
1027 LayoutUnit LayoutBox::overrideContainingBlockContentLogicalWidth() const
1029 ASSERT(hasOverrideContainingBlockLogicalWidth());
1030 return gOverrideContainingBlockLogicalWidthMap->get(this);
1033 LayoutUnit LayoutBox::overrideContainingBlockContentLogicalHeight() const
1035 ASSERT(hasOverrideContainingBlockLogicalHeight());
1036 return gOverrideContainingBlockLogicalHeightMap->get(this);
1039 bool LayoutBox::hasOverrideContainingBlockLogicalWidth() const
1041 return gOverrideContainingBlockLogicalWidthMap && gOverrideContainingBlockLogicalWidthMap->contains(this);
1044 bool LayoutBox::hasOverrideContainingBlockLogicalHeight() const
1046 return gOverrideContainingBlockLogicalHeightMap && gOverrideContainingBlockLogicalHeightMap->contains(this);
1049 void LayoutBox::setOverrideContainingBlockContentLogicalWidth(LayoutUnit logicalWidth)
1051 if (!gOverrideContainingBlockLogicalWidthMap)
1052 gOverrideContainingBlockLogicalWidthMap = new OverrideSizeMap;
1053 gOverrideContainingBlockLogicalWidthMap->set(this, logicalWidth);
1056 void LayoutBox::setOverrideContainingBlockContentLogicalHeight(LayoutUnit logicalHeight)
1058 if (!gOverrideContainingBlockLogicalHeightMap)
1059 gOverrideContainingBlockLogicalHeightMap = new OverrideSizeMap;
1060 gOverrideContainingBlockLogicalHeightMap->set(this, logicalHeight);
1063 void LayoutBox::clearContainingBlockOverrideSize()
1065 if (gOverrideContainingBlockLogicalWidthMap)
1066 gOverrideContainingBlockLogicalWidthMap->remove(this);
1067 clearOverrideContainingBlockContentLogicalHeight();
1070 void LayoutBox::clearOverrideContainingBlockContentLogicalHeight()
1072 if (gOverrideContainingBlockLogicalHeightMap)
1073 gOverrideContainingBlockLogicalHeightMap->remove(this);
1076 LayoutUnit LayoutBox::extraInlineOffset() const
1078 return gExtraInlineOffsetMap ? gExtraInlineOffsetMap->get(this) : LayoutUnit();
1081 LayoutUnit LayoutBox::extraBlockOffset() const
1083 return gExtraBlockOffsetMap ? gExtraBlockOffsetMap->get(this) : LayoutUnit();
1086 void LayoutBox::setExtraInlineOffset(LayoutUnit inlineOffest)
1088 if (!gExtraInlineOffsetMap)
1089 gExtraInlineOffsetMap = new OverrideSizeMap;
1090 gExtraInlineOffsetMap->set(this, inlineOffest);
1093 void LayoutBox::setExtraBlockOffset(LayoutUnit blockOffest)
1095 if (!gExtraBlockOffsetMap)
1096 gExtraBlockOffsetMap = new OverrideSizeMap;
1097 gExtraBlockOffsetMap->set(this, blockOffest);
1100 void LayoutBox::clearExtraInlineAndBlockOffests()
1102 if (gExtraInlineOffsetMap)
1103 gExtraInlineOffsetMap->remove(this);
1104 if (gExtraBlockOffsetMap)
1105 gExtraBlockOffsetMap->remove(this);
1108 LayoutUnit LayoutBox::adjustBorderBoxLogicalWidthForBoxSizing(LayoutUnit width) const
1110 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth();
1111 if (style()->boxSizing() == CONTENT_BOX)
1112 return width + bordersPlusPadding;
1113 return std::max(width, bordersPlusPadding);
1116 LayoutUnit LayoutBox::adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const
1118 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
1119 if (style()->boxSizing() == CONTENT_BOX)
1120 return height + bordersPlusPadding;
1121 return std::max(height, bordersPlusPadding);
1124 LayoutUnit LayoutBox::adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit width) const
1126 if (style()->boxSizing() == BORDER_BOX)
1127 width -= borderAndPaddingLogicalWidth();
1128 return std::max(LayoutUnit(), width);
1131 LayoutUnit LayoutBox::adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit height) const
1133 if (style()->boxSizing() == BORDER_BOX)
1134 height -= borderAndPaddingLogicalHeight();
1135 return std::max(LayoutUnit(), height);
1138 // Hit Testing
1139 bool LayoutBox::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1141 LayoutPoint adjustedLocation = accumulatedOffset + location();
1143 // Exit early if no children can be hit.
1144 LayoutRect overflowRect = visualOverflowRect();
1145 overflowRect.moveBy(adjustedLocation);
1146 if (!locationInContainer.intersects(overflowRect)) {
1147 return false;
1150 // Check kids first.
1151 for (LayoutObject* child = slowLastChild(); child; child = child->previousSibling()) {
1152 if ((!child->hasLayer() || !toLayoutBoxModelObject(child)->layer()->isSelfPaintingLayer()) && child->nodeAtPoint(result, locationInContainer, adjustedLocation, action)) {
1153 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
1154 return true;
1158 // Check our bounds next. For this purpose always assume that we can only be hit in the
1159 // foreground phase (which is true for replaced elements like images).
1160 LayoutRect boundsRect = borderBoxRect();
1161 boundsRect.moveBy(adjustedLocation);
1162 if (visibleToHitTestRequest(result.hitTestRequest()) && action == HitTestForeground && locationInContainer.intersects(boundsRect)) {
1163 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
1164 if (!result.addNodeToListBasedTestResult(node(), locationInContainer, boundsRect))
1165 return true;
1168 return false;
1171 void LayoutBox::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1173 BoxPainter(*this).paint(paintInfo, paintOffset);
1177 void LayoutBox::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1179 BoxPainter(*this).paintBoxDecorationBackground(paintInfo, paintOffset);
1183 bool LayoutBox::getBackgroundPaintedExtent(LayoutRect& paintedExtent)
1185 ASSERT(hasBackground());
1187 // LayoutView is special in the sense that it expands to the whole canvas,
1188 // thus can't be handled by this function.
1189 ASSERT(!isLayoutView());
1191 LayoutRect backgroundRect(enclosingIntRect(borderBoxRect()));
1193 Color backgroundColor = resolveColor(CSSPropertyBackgroundColor);
1194 if (backgroundColor.alpha()) {
1195 paintedExtent = backgroundRect;
1196 return true;
1199 if (!style()->backgroundLayers().image() || style()->backgroundLayers().next()) {
1200 paintedExtent = backgroundRect;
1201 return true;
1204 BackgroundImageGeometry geometry;
1205 // TODO(jchaffraix): This function should be rethought as it's called during and outside
1206 // of the paint phase. Potentially returning different results at different phases.
1207 geometry.calculate(*this, nullptr, GlobalPaintNormalPhase, style()->backgroundLayers(), backgroundRect);
1208 if (geometry.hasNonLocalGeometry())
1209 return false;
1210 paintedExtent = LayoutRect(geometry.destRect());
1211 return true;
1214 bool LayoutBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const
1216 if (isDocumentElement() || backgroundStolenForBeingBody())
1217 return false;
1219 Color backgroundColor = resolveColor(CSSPropertyBackgroundColor);
1220 if (backgroundColor.hasAlpha())
1221 return false;
1223 // If the element has appearance, it might be painted by theme.
1224 // We cannot be sure if theme paints the background opaque.
1225 // In this case it is safe to not assume opaqueness.
1226 // FIXME: May be ask theme if it paints opaque.
1227 if (style()->hasAppearance())
1228 return false;
1229 // FIXME: Check the opaqueness of background images.
1231 // FIXME: Use rounded rect if border radius is present.
1232 if (style()->hasBorderRadius())
1233 return false;
1234 // FIXME: The background color clip is defined by the last layer.
1235 if (style()->backgroundLayers().next())
1236 return false;
1237 LayoutRect backgroundRect;
1238 switch (style()->backgroundClip()) {
1239 case BorderFillBox:
1240 backgroundRect = borderBoxRect();
1241 break;
1242 case PaddingFillBox:
1243 backgroundRect = paddingBoxRect();
1244 break;
1245 case ContentFillBox:
1246 backgroundRect = contentBoxRect();
1247 break;
1248 default:
1249 break;
1251 return backgroundRect.contains(localRect);
1254 static bool isCandidateForOpaquenessTest(const LayoutBox& childBox)
1256 const ComputedStyle& childStyle = childBox.styleRef();
1257 if (childStyle.position() != StaticPosition && childBox.containingBlock() != childBox.parent())
1258 return false;
1259 if (childStyle.visibility() != VISIBLE || childStyle.shapeOutside())
1260 return false;
1261 if (childBox.size().isZero())
1262 return false;
1263 if (DeprecatedPaintLayer* childLayer = childBox.layer()) {
1264 // FIXME: perhaps this could be less conservative?
1265 if (childLayer->compositingState() != NotComposited)
1266 return false;
1267 // FIXME: Deal with z-index.
1268 if (!childStyle.hasAutoZIndex())
1269 return false;
1270 if (childLayer->hasTransformRelatedProperty() || childLayer->isTransparent() || childLayer->hasFilter())
1271 return false;
1272 if (childBox.hasOverflowClip() && childStyle.hasBorderRadius())
1273 return false;
1275 return true;
1278 bool LayoutBox::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
1280 if (!maxDepthToTest)
1281 return false;
1282 for (LayoutObject* child = slowFirstChild(); child; child = child->nextSibling()) {
1283 if (!child->isBox())
1284 continue;
1285 LayoutBox* childBox = toLayoutBox(child);
1286 if (!isCandidateForOpaquenessTest(*childBox))
1287 continue;
1288 LayoutPoint childLocation = childBox->location();
1289 if (childBox->isInFlowPositioned())
1290 childLocation.move(childBox->offsetForInFlowPosition());
1291 LayoutRect childLocalRect = localRect;
1292 childLocalRect.moveBy(-childLocation);
1293 if (childLocalRect.y() < 0 || childLocalRect.x() < 0) {
1294 // If there is unobscured area above/left of a static positioned box then the rect is probably not covered.
1295 if (childBox->style()->position() == StaticPosition)
1296 return false;
1297 continue;
1299 if (childLocalRect.maxY() > childBox->size().height() || childLocalRect.maxX() > childBox->size().width())
1300 continue;
1301 if (childBox->backgroundIsKnownToBeOpaqueInRect(childLocalRect))
1302 return true;
1303 if (childBox->foregroundIsKnownToBeOpaqueInRect(childLocalRect, maxDepthToTest - 1))
1304 return true;
1306 return false;
1309 bool LayoutBox::computeBackgroundIsKnownToBeObscured()
1311 // Test to see if the children trivially obscure the background.
1312 // FIXME: This test can be much more comprehensive.
1313 if (!hasBackground())
1314 return false;
1315 // Table and root background painting is special.
1316 if (isTable() || isLayoutView())
1317 return false;
1318 // FIXME: box-shadow is painted while background painting.
1319 if (style()->boxShadow())
1320 return false;
1321 LayoutRect backgroundRect;
1322 if (!getBackgroundPaintedExtent(backgroundRect))
1323 return false;
1324 return foregroundIsKnownToBeOpaqueInRect(backgroundRect, backgroundObscurationTestMaxDepth);
1327 void LayoutBox::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1329 BoxPainter(*this).paintMask(paintInfo, paintOffset);
1332 void LayoutBox::imageChanged(WrappedImagePtr image, const IntRect*)
1334 // TODO(chrishtr): support PaintInvalidationDelayedFull for animated border images.
1335 if ((style()->borderImage().image() && style()->borderImage().image()->data() == image)
1336 || (style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image)) {
1337 setShouldDoFullPaintInvalidation();
1338 return;
1341 ShapeValue* shapeOutsideValue = style()->shapeOutside();
1342 if (!frameView()->isInPerformLayout() && isFloating() && shapeOutsideValue && shapeOutsideValue->image() && shapeOutsideValue->image()->data() == image) {
1343 ShapeOutsideInfo& info = ShapeOutsideInfo::ensureInfo(*this);
1344 if (!info.isComputingShape()) {
1345 info.markShapeAsDirty();
1346 markShapeOutsideDependentsForLayout();
1350 if (!invalidatePaintOfLayerRectsForImage(image, style()->backgroundLayers(), true))
1351 invalidatePaintOfLayerRectsForImage(image, style()->maskLayers(), false);
1354 bool LayoutBox::invalidatePaintOfLayerRectsForImage(WrappedImagePtr image, const FillLayer& layers, bool drawingBackground)
1356 if (drawingBackground && (isDocumentElement() || backgroundStolenForBeingBody()))
1357 return false;
1358 for (const FillLayer* curLayer = &layers; curLayer; curLayer = curLayer->next()) {
1359 if (curLayer->image() && image == curLayer->image()->data()) {
1360 bool maybeAnimated = curLayer->image()->cachedImage() && curLayer->image()->cachedImage()->image() && curLayer->image()->cachedImage()->image()->maybeAnimated();
1361 if (maybeAnimated && drawingBackground)
1362 setShouldDoFullPaintInvalidation(PaintInvalidationDelayedFull);
1363 else
1364 setShouldDoFullPaintInvalidation();
1366 if (drawingBackground)
1367 invalidateBackgroundObscurationStatus();
1368 return true;
1371 return false;
1374 bool LayoutBox::intersectsVisibleViewport()
1376 LayoutRect rect = visualOverflowRect();
1377 LayoutView* layoutView = view();
1378 while (layoutView->frame()->ownerLayoutObject())
1379 layoutView = layoutView->frame()->ownerLayoutObject()->view();
1380 mapRectToPaintInvalidationBacking(layoutView, rect, 0);
1381 return rect.intersects(LayoutRect(layoutView->frameView()->scrollableArea()->visibleContentRectDouble()));
1384 PaintInvalidationReason LayoutBox::invalidatePaintIfNeeded(PaintInvalidationState& paintInvalidationState, const LayoutBoxModelObject& newPaintInvalidationContainer)
1386 PaintInvalidationReason fullInvalidationReason = fullPaintInvalidationReason();
1387 // If the current paint invalidation reason is PaintInvalidationDelayedFull, then this paint invalidation can delayed if the
1388 // LayoutBox in question is not on-screen. The logic to decide whether this is appropriate exists at the site of the original
1389 // paint invalidation that chose PaintInvalidationDelayedFull.
1390 if (fullInvalidationReason == PaintInvalidationDelayedFull) {
1391 if (!intersectsVisibleViewport())
1392 return PaintInvalidationDelayedFull;
1394 // Reset state back to regular full paint invalidation if the object is onscreen.
1395 setShouldDoFullPaintInvalidation(PaintInvalidationFull);
1398 PaintInvalidationReason reason = LayoutBoxModelObject::invalidatePaintIfNeeded(paintInvalidationState, newPaintInvalidationContainer);
1400 // If we are set to do a full paint invalidation that means the LayoutView will be
1401 // issue paint invalidations. We can then skip issuing of paint invalidations for the child
1402 // layoutObjects as they'll be covered by the LayoutView.
1403 if (!view()->doingFullPaintInvalidation() && !isFullPaintInvalidationReason(reason)) {
1404 invalidatePaintForOverflowIfNeeded();
1406 // Issue paint invalidations for any scrollbars if there is a scrollable area for this layoutObject.
1407 if (ScrollableArea* area = scrollableArea()) {
1408 // In slimming paint mode, we already invalidated the display item clients of the scrollbars
1409 // during DeprecatedPaintLayerScrollableArea::invalidateScrollbarRect(). However, for now we still need to
1410 // invalidate the rectangles to trigger repaints.
1411 if (area->hasVerticalBarDamage())
1412 invalidatePaintRectangleNotInvalidatingDisplayItemClients(LayoutRect(area->verticalBarDamage()));
1413 if (area->hasHorizontalBarDamage())
1414 invalidatePaintRectangleNotInvalidatingDisplayItemClients(LayoutRect(area->horizontalBarDamage()));
1418 // This is for the next invalidatePaintIfNeeded so must be at the end.
1419 savePreviousBoxSizesIfNeeded();
1420 return reason;
1423 void LayoutBox::clearPaintInvalidationState(const PaintInvalidationState& paintInvalidationState)
1425 LayoutBoxModelObject::clearPaintInvalidationState(paintInvalidationState);
1427 if (ScrollableArea* area = scrollableArea())
1428 area->resetScrollbarDamage();
1431 #if ENABLE(ASSERT)
1432 bool LayoutBox::paintInvalidationStateIsDirty() const
1434 if (ScrollableArea* area = scrollableArea()) {
1435 if (area->hasVerticalBarDamage() || area->hasHorizontalBarDamage())
1436 return true;
1438 return LayoutBoxModelObject::paintInvalidationStateIsDirty();
1440 #endif
1442 LayoutRect LayoutBox::overflowClipRect(const LayoutPoint& location, OverlayScrollbarSizeRelevancy relevancy) const
1444 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property
1445 // here.
1446 LayoutRect clipRect = borderBoxRect();
1447 clipRect.setLocation(location + clipRect.location() + LayoutSize(borderLeft(), borderTop()));
1448 clipRect.setSize(clipRect.size() - LayoutSize(borderLeft() + borderRight(), borderTop() + borderBottom()));
1450 if (hasOverflowClip())
1451 excludeScrollbars(clipRect, relevancy);
1452 return clipRect;
1455 void LayoutBox::excludeScrollbars(LayoutRect& rect, OverlayScrollbarSizeRelevancy relevancy) const
1457 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
1458 rect.move(layer()->scrollableArea()->verticalScrollbarWidth(relevancy), 0);
1459 rect.contract(layer()->scrollableArea()->verticalScrollbarWidth(relevancy), layer()->scrollableArea()->horizontalScrollbarHeight(relevancy));
1462 LayoutRect LayoutBox::clipRect(const LayoutPoint& location)
1464 LayoutRect borderBoxRect = this->borderBoxRect();
1465 LayoutRect clipRect = LayoutRect(borderBoxRect.location() + location, borderBoxRect.size());
1467 if (!style()->clipLeft().isAuto()) {
1468 LayoutUnit c = valueForLength(style()->clipLeft(), borderBoxRect.width());
1469 clipRect.move(c, 0);
1470 clipRect.contract(c, 0);
1473 if (!style()->clipRight().isAuto())
1474 clipRect.contract(size().width() - valueForLength(style()->clipRight(), size().width()), 0);
1476 if (!style()->clipTop().isAuto()) {
1477 LayoutUnit c = valueForLength(style()->clipTop(), borderBoxRect.height());
1478 clipRect.move(0, c);
1479 clipRect.contract(0, c);
1482 if (!style()->clipBottom().isAuto())
1483 clipRect.contract(0, size().height() - valueForLength(style()->clipBottom(), size().height()));
1485 return clipRect;
1488 static LayoutUnit portionOfMarginNotConsumedByFloat(LayoutUnit childMargin, LayoutUnit contentSide, LayoutUnit offset)
1490 if (childMargin <= 0)
1491 return LayoutUnit();
1492 LayoutUnit contentSideWithMargin = contentSide + childMargin;
1493 if (offset > contentSideWithMargin)
1494 return childMargin;
1495 return offset - contentSide;
1498 LayoutUnit LayoutBox::shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart, LayoutUnit childMarginEnd, const LayoutBlockFlow* cb) const
1500 LayoutUnit logicalTopPosition = logicalTop();
1501 LayoutUnit startOffsetForContent = cb->startOffsetForContent();
1502 LayoutUnit endOffsetForContent = cb->endOffsetForContent();
1503 LayoutUnit startOffsetForLine = cb->startOffsetForLine(logicalTopPosition, false);
1504 LayoutUnit endOffsetForLine = cb->endOffsetForLine(logicalTopPosition, false);
1506 // If there aren't any floats constraining us then allow the margins to shrink/expand the width as much as they want.
1507 if (startOffsetForContent == startOffsetForLine && endOffsetForContent == endOffsetForLine)
1508 return cb->availableLogicalWidthForLine(logicalTopPosition, false) - childMarginStart - childMarginEnd;
1510 LayoutUnit width = cb->availableLogicalWidthForLine(logicalTopPosition, false) - std::max(LayoutUnit(), childMarginStart) - std::max(LayoutUnit(), childMarginEnd);
1511 // We need to see if margins on either the start side or the end side can contain the floats in question. If they can,
1512 // then just using the line width is inaccurate. In the case where a float completely fits, we don't need to use the line
1513 // offset at all, but can instead push all the way to the content edge of the containing block. In the case where the float
1514 // 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
1515 // "consumed" by the float. Negative margins aren't consumed by the float, and so we ignore them.
1516 width += portionOfMarginNotConsumedByFloat(childMarginStart, startOffsetForContent, startOffsetForLine);
1517 width += portionOfMarginNotConsumedByFloat(childMarginEnd, endOffsetForContent, endOffsetForLine);
1518 return width;
1521 LayoutUnit LayoutBox::containingBlockLogicalHeightForGetComputedStyle() const
1523 if (hasOverrideContainingBlockLogicalHeight())
1524 return overrideContainingBlockContentLogicalHeight();
1526 if (!isPositioned())
1527 return containingBlockLogicalHeightForContent(ExcludeMarginBorderPadding);
1529 LayoutBoxModelObject* cb = toLayoutBoxModelObject(container());
1530 LayoutUnit height = containingBlockLogicalHeightForPositioned(cb);
1531 if (styleRef().position() != AbsolutePosition)
1532 height -= cb->paddingLogicalHeight();
1533 return height;
1536 LayoutUnit LayoutBox::containingBlockLogicalWidthForContent() const
1538 if (hasOverrideContainingBlockLogicalWidth())
1539 return overrideContainingBlockContentLogicalWidth();
1541 LayoutBlock* cb = containingBlock();
1542 return cb->availableLogicalWidth();
1545 LayoutUnit LayoutBox::containingBlockLogicalHeightForContent(AvailableLogicalHeightType heightType) const
1547 if (hasOverrideContainingBlockLogicalHeight())
1548 return overrideContainingBlockContentLogicalHeight();
1550 LayoutBlock* cb = containingBlock();
1551 return cb->availableLogicalHeight(heightType);
1554 LayoutUnit LayoutBox::containingBlockAvailableLineWidth() const
1556 LayoutBlock* cb = containingBlock();
1557 if (cb->isLayoutBlockFlow())
1558 return toLayoutBlockFlow(cb)->availableLogicalWidthForLine(logicalTop(), false, availableLogicalHeight(IncludeMarginBorderPadding));
1559 return LayoutUnit();
1562 LayoutUnit LayoutBox::perpendicularContainingBlockLogicalHeight() const
1564 if (hasOverrideContainingBlockLogicalHeight())
1565 return overrideContainingBlockContentLogicalHeight();
1567 LayoutBlock* cb = containingBlock();
1568 if (cb->hasOverrideLogicalContentHeight())
1569 return cb->overrideLogicalContentHeight();
1571 const ComputedStyle& containingBlockStyle = cb->styleRef();
1572 Length logicalHeightLength = containingBlockStyle.logicalHeight();
1574 // FIXME: For now just support fixed heights. Eventually should support percentage heights as well.
1575 if (!logicalHeightLength.isFixed()) {
1576 LayoutUnit fillFallbackExtent = containingBlockStyle.isHorizontalWritingMode()
1577 ? view()->frameView()->visibleContentSize().height()
1578 : view()->frameView()->visibleContentSize().width();
1579 LayoutUnit fillAvailableExtent = containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding);
1580 return std::min(fillAvailableExtent, fillFallbackExtent);
1583 // Use the content box logical height as specified by the style.
1584 return cb->adjustContentBoxLogicalHeightForBoxSizing(logicalHeightLength.value());
1587 void LayoutBox::mapLocalToContainer(const LayoutBoxModelObject* paintInvalidationContainer, TransformState& transformState, MapCoordinatesFlags mode, bool* wasFixed, const PaintInvalidationState* paintInvalidationState) const
1589 if (paintInvalidationContainer == this)
1590 return;
1592 if (paintInvalidationState && paintInvalidationState->canMapToContainer(paintInvalidationContainer)) {
1593 LayoutSize offset = paintInvalidationState->paintOffset() + locationOffset();
1594 if (style()->hasInFlowPosition() && layer())
1595 offset += layer()->offsetForInFlowPosition();
1596 transformState.move(offset);
1597 return;
1600 bool containerSkipped;
1601 LayoutObject* o = container(paintInvalidationContainer, &containerSkipped);
1602 if (!o)
1603 return;
1605 bool isFixedPos = style()->position() == FixedPosition;
1606 bool hasTransform = hasLayer() && layer()->transform();
1607 // If this box has a transform, it acts as a fixed position container for fixed descendants,
1608 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
1609 if (hasTransform && !isFixedPos)
1610 mode &= ~IsFixed;
1611 else if (isFixedPos)
1612 mode |= IsFixed;
1614 if (wasFixed)
1615 *wasFixed = mode & IsFixed;
1617 LayoutSize containerOffset = offsetFromContainer(o, roundedLayoutPoint(transformState.mappedPoint()));
1619 bool preserve3D = mode & UseTransforms && (o->style()->preserves3D() || style()->preserves3D());
1620 if (mode & UseTransforms && shouldUseTransformFromContainer(o)) {
1621 TransformationMatrix t;
1622 getTransformFromContainer(o, containerOffset, t);
1623 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1624 } else {
1625 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1628 if (containerSkipped) {
1629 // There can't be a transform between paintInvalidationContainer and o, because transforms create containers, so it should be safe
1630 // to just subtract the delta between the paintInvalidationContainer and o.
1631 LayoutSize containerOffset = paintInvalidationContainer->offsetFromAncestorContainer(o);
1632 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
1633 return;
1636 mode &= ~ApplyContainerFlip;
1638 o->mapLocalToContainer(paintInvalidationContainer, transformState, mode, wasFixed);
1641 void LayoutBox::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, TransformState& transformState) const
1643 bool isFixedPos = style()->position() == FixedPosition;
1644 bool hasTransform = hasLayer() && layer()->transform();
1645 if (hasTransform && !isFixedPos) {
1646 // If this box has a transform, it acts as a fixed position container for fixed descendants,
1647 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
1648 mode &= ~IsFixed;
1649 } else if (isFixedPos) {
1650 mode |= IsFixed;
1653 LayoutBoxModelObject::mapAbsoluteToLocalPoint(mode, transformState);
1656 LayoutSize LayoutBox::offsetFromContainer(const LayoutObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const
1658 ASSERT(o == container());
1660 LayoutSize offset;
1661 if (isInFlowPositioned())
1662 offset += offsetForInFlowPosition();
1664 if (!isInline() || isReplaced()) {
1665 offset += topLeftLocationOffset();
1666 if (o->isLayoutFlowThread()) {
1667 // So far the point has been in flow thread coordinates (i.e. as if everything in
1668 // the fragmentation context lived in one tall single column). Convert it to a
1669 // visual point now.
1670 LayoutPoint pointInContainer = point + offset;
1671 offset += o->columnOffset(pointInContainer);
1672 if (offsetDependsOnPoint)
1673 *offsetDependsOnPoint = true;
1677 if (o->hasOverflowClip())
1678 offset -= toLayoutBox(o)->scrolledContentOffset();
1680 if (style()->position() == AbsolutePosition && o->isInFlowPositioned() && o->isLayoutInline())
1681 offset += toLayoutInline(o)->offsetForInFlowPositionedInline(*this);
1683 return offset;
1686 InlineBox* LayoutBox::createInlineBox()
1688 return new InlineBox(*this);
1691 void LayoutBox::dirtyLineBoxes(bool fullLayout)
1693 if (inlineBoxWrapper()) {
1694 if (fullLayout) {
1695 inlineBoxWrapper()->destroy();
1696 ASSERT(m_rareData);
1697 m_rareData->m_inlineBoxWrapper = nullptr;
1698 } else {
1699 inlineBoxWrapper()->dirtyLineBoxes();
1704 void LayoutBox::positionLineBox(InlineBox* box)
1706 if (isOutOfFlowPositioned()) {
1707 // Cache the x position only if we were an INLINE type originally.
1708 bool originallyInline = style()->isOriginalDisplayInlineType();
1709 if (originallyInline) {
1710 // The value is cached in the xPos of the box. We only need this value if
1711 // our object was inline originally, since otherwise it would have ended up underneath
1712 // the inlines.
1713 RootInlineBox& root = box->root();
1714 root.block().setStaticInlinePositionForChild(*this, box->logicalLeft());
1715 } else {
1716 // Our object was a block originally, so we make our normal flow position be
1717 // just below the line box (as though all the inlines that came before us got
1718 // wrapped in an anonymous block, which is what would have happened had we been
1719 // in flow). This value was cached in the y() of the box.
1720 layer()->setStaticBlockPosition(box->logicalTop());
1723 if (container()->isLayoutInline())
1724 moveWithEdgeOfInlineContainerIfNecessary(box->isHorizontal());
1726 // Nuke the box.
1727 box->remove(DontMarkLineBoxes);
1728 box->destroy();
1729 } else if (isReplaced()) {
1730 // FIXME: the call to roundedLayoutPoint() below is temporary and should be removed once
1731 // the transition to LayoutUnit-based types is complete (crbug.com/321237)
1732 setLocationAndUpdateOverflowControlsIfNeeded(box->topLeft());
1733 setInlineBoxWrapper(box);
1737 void LayoutBox::moveWithEdgeOfInlineContainerIfNecessary(bool isHorizontal)
1739 ASSERT(isOutOfFlowPositioned() && container()->isLayoutInline() && container()->isInFlowPositioned());
1740 // If this object is inside a relative positioned inline and its inline position is an explicit offset from the edge of its container
1741 // then it will need to move if its inline container has changed width. We do not track if the width has changed
1742 // but if we are here then we are laying out lines inside it, so it probably has - mark our object for layout so that it can
1743 // move to the new offset created by the new width.
1744 if (!normalChildNeedsLayout() && !style()->hasStaticInlinePosition(isHorizontal))
1745 setChildNeedsLayout(MarkOnlyThis);
1748 void LayoutBox::deleteLineBoxWrapper()
1750 if (inlineBoxWrapper()) {
1751 if (!documentBeingDestroyed())
1752 inlineBoxWrapper()->remove();
1753 inlineBoxWrapper()->destroy();
1754 ASSERT(m_rareData);
1755 m_rareData->m_inlineBoxWrapper = nullptr;
1759 void LayoutBox::setSpannerPlaceholder(LayoutMultiColumnSpannerPlaceholder& placeholder)
1761 RELEASE_ASSERT(!m_rareData || !m_rareData->m_spannerPlaceholder); // not expected to change directly from one spanner to another.
1762 ensureRareData().m_spannerPlaceholder = &placeholder;
1765 void LayoutBox::clearSpannerPlaceholder()
1767 if (!m_rareData)
1768 return;
1769 m_rareData->m_spannerPlaceholder = nullptr;
1772 LayoutRect LayoutBox::clippedOverflowRectForPaintInvalidation(const LayoutBoxModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) const
1774 if (style()->visibility() != VISIBLE) {
1775 DeprecatedPaintLayer* layer = enclosingLayer();
1776 layer->updateDescendantDependentFlags();
1777 if (layer->subtreeIsInvisible())
1778 return LayoutRect();
1781 LayoutRect r = visualOverflowRect();
1782 mapRectToPaintInvalidationBacking(paintInvalidationContainer, r, paintInvalidationState);
1783 return r;
1786 void LayoutBox::mapRectToPaintInvalidationBacking(const LayoutBoxModelObject* paintInvalidationContainer, LayoutRect& rect, const PaintInvalidationState* paintInvalidationState) const
1788 // The rect we compute at each step is shifted by our x/y offset in the parent container's coordinate space.
1789 // Only when we cross a writing mode boundary will we have to possibly flipForWritingMode (to convert into a more appropriate
1790 // offset corner for the enclosing container). This allows for a fully RL or BT document to issue paint invalidations
1791 // properly even during layout, since the rect remains flipped all the way until the end.
1793 // LayoutView::computeRectForPaintInvalidation then converts the rect to physical coordinates. We also convert to
1794 // physical when we hit a paintInvalidationContainer boundary. Therefore the final rect returned is always in the
1795 // physical coordinate space of the paintInvalidationContainer.
1796 const ComputedStyle& styleToUse = styleRef();
1798 EPosition position = styleToUse.position();
1800 // We need to inflate the paint invalidation rect before we use paintInvalidationState,
1801 // else we would forget to inflate it for the current layoutObject. FIXME: If these were
1802 // included into the visual overflow for repaint, we wouldn't have this issue.
1803 inflatePaintInvalidationRectForReflectionAndFilter(rect);
1805 if (paintInvalidationState && paintInvalidationState->canMapToContainer(paintInvalidationContainer) && position != FixedPosition) {
1806 if (layer() && layer()->transform())
1807 rect = LayoutRect(layer()->transform()->mapRect(pixelSnappedIntRect(rect)));
1809 // We can't trust the bits on LayoutObject, because this might be called while re-resolving style.
1810 if (styleToUse.hasInFlowPosition() && layer())
1811 rect.move(layer()->offsetForInFlowPosition());
1813 rect.moveBy(location());
1814 rect.move(paintInvalidationState->paintOffset());
1815 if (paintInvalidationState->isClipped())
1816 rect.intersect(paintInvalidationState->clipRect());
1817 return;
1820 if (paintInvalidationContainer == this) {
1821 if (paintInvalidationContainer->style()->isFlippedBlocksWritingMode())
1822 flipForWritingMode(rect);
1823 return;
1826 bool containerSkipped;
1827 LayoutObject* o = container(paintInvalidationContainer, &containerSkipped);
1828 if (!o)
1829 return;
1831 if (isWritingModeRoot())
1832 flipForWritingMode(rect);
1834 LayoutPoint topLeft = rect.location();
1835 topLeft.move(locationOffset());
1837 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box
1838 // in the parent's coordinate space that encloses us.
1839 if (hasLayer() && layer()->transform()) {
1840 rect = LayoutRect(layer()->transform()->mapRect(pixelSnappedIntRect(rect)));
1841 topLeft = rect.location();
1842 topLeft.move(locationOffset());
1845 if (position == AbsolutePosition && o->isInFlowPositioned() && o->isLayoutInline()) {
1846 topLeft += toLayoutInline(o)->offsetForInFlowPositionedInline(*this);
1847 } else if (styleToUse.hasInFlowPosition() && layer()) {
1848 // Apply the relative position offset when invalidating a rectangle. The layer
1849 // is translated, but the layout box isn't, so we need to do this to get the
1850 // right dirty rect. Since this is called from LayoutObject::setStyle, the relative position
1851 // flag on the LayoutObject has been cleared, so use the one on the style().
1852 topLeft += layer()->offsetForInFlowPosition();
1855 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
1856 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
1857 rect.setLocation(topLeft);
1858 if (o->hasOverflowClip()) {
1859 LayoutBox* containerBox = toLayoutBox(o);
1860 containerBox->applyCachedClipAndScrollOffsetForPaintInvalidation(rect);
1861 if (rect.isEmpty())
1862 return;
1865 if (containerSkipped) {
1866 // If the paintInvalidationContainer is below o, then we need to map the rect into paintInvalidationContainer's coordinates.
1867 LayoutSize containerOffset = paintInvalidationContainer->offsetFromAncestorContainer(o);
1868 rect.move(-containerOffset);
1869 // If the paintInvalidationContainer is fixed, then the rect is already in its coordinates so doesn't need viewport-adjusting.
1870 if (paintInvalidationContainer->style()->position() != FixedPosition && o->isLayoutView())
1871 toLayoutView(o)->adjustViewportConstrainedOffset(rect, LayoutView::viewportConstrainedPosition(position));
1872 return;
1875 if (o->isLayoutView())
1876 toLayoutView(o)->mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, LayoutView::viewportConstrainedPosition(position), paintInvalidationState);
1877 else
1878 o->mapRectToPaintInvalidationBacking(paintInvalidationContainer, rect, paintInvalidationState);
1881 void LayoutBox::inflatePaintInvalidationRectForReflectionAndFilter(LayoutRect& paintInvalidationRect) const
1883 if (hasReflection())
1884 paintInvalidationRect.unite(reflectedRect(paintInvalidationRect));
1886 if (style()->hasFilter())
1887 paintInvalidationRect.expand(style()->filterOutsets());
1890 void LayoutBox::invalidatePaintForOverhangingFloats(bool)
1894 void LayoutBox::updateLogicalWidth()
1896 LogicalExtentComputedValues computedValues;
1897 computeLogicalWidth(computedValues);
1899 setLogicalWidth(computedValues.m_extent);
1900 setLogicalLeft(computedValues.m_position);
1901 setMarginStart(computedValues.m_margins.m_start);
1902 setMarginEnd(computedValues.m_margins.m_end);
1905 static float getMaxWidthListMarker(const LayoutBox* layoutObject)
1907 #if ENABLE(ASSERT)
1908 ASSERT(layoutObject);
1909 Node* parentNode = layoutObject->generatingNode();
1910 ASSERT(parentNode);
1911 ASSERT(isHTMLOListElement(parentNode) || isHTMLUListElement(parentNode));
1912 ASSERT(layoutObject->style()->textAutosizingMultiplier() != 1);
1913 #endif
1914 float maxWidth = 0;
1915 for (LayoutObject* child = layoutObject->slowFirstChild(); child; child = child->nextSibling()) {
1916 if (!child->isListItem())
1917 continue;
1919 LayoutBox* listItem = toLayoutBox(child);
1920 for (LayoutObject* itemChild = listItem->slowFirstChild(); itemChild; itemChild = itemChild->nextSibling()) {
1921 if (!itemChild->isListMarker())
1922 continue;
1923 LayoutBox* itemMarker = toLayoutBox(itemChild);
1924 // Make sure to compute the autosized width.
1925 if (itemMarker->needsLayout())
1926 itemMarker->layout();
1927 maxWidth = std::max<float>(maxWidth, toLayoutListMarker(itemMarker)->logicalWidth().toFloat());
1928 break;
1931 return maxWidth;
1934 void LayoutBox::computeLogicalWidth(LogicalExtentComputedValues& computedValues) const
1936 computedValues.m_extent = logicalWidth();
1937 computedValues.m_position = logicalLeft();
1938 computedValues.m_margins.m_start = marginStart();
1939 computedValues.m_margins.m_end = marginEnd();
1941 if (isOutOfFlowPositioned()) {
1942 computePositionedLogicalWidth(computedValues);
1943 return;
1946 // The parent box is flexing us, so it has increased or decreased our
1947 // width. Use the width from the style context.
1948 // FIXME: Account for writing-mode in flexible boxes.
1949 // https://bugs.webkit.org/show_bug.cgi?id=46418
1950 if (hasOverrideLogicalContentWidth() && (parent()->isFlexibleBoxIncludingDeprecated() || parent()->isLayoutGrid())) {
1951 computedValues.m_extent = overrideLogicalContentWidth() + borderAndPaddingLogicalWidth();
1952 return;
1955 // FIXME: Account for writing-mode in flexible boxes.
1956 // https://bugs.webkit.org/show_bug.cgi?id=46418
1957 bool inVerticalBox = parent()->isDeprecatedFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL);
1958 bool stretching = (parent()->style()->boxAlign() == BSTRETCH);
1959 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching);
1961 const ComputedStyle& styleToUse = styleRef();
1962 Length logicalWidthLength = treatAsReplaced ? Length(computeReplacedLogicalWidth(), Fixed) : styleToUse.logicalWidth();
1964 LayoutBlock* cb = containingBlock();
1965 LayoutUnit containerLogicalWidth = std::max(LayoutUnit(), containingBlockLogicalWidthForContent());
1966 bool hasPerpendicularContainingBlock = cb->isHorizontalWritingMode() != isHorizontalWritingMode();
1968 if (parent()->isLayoutGrid() && style()->logicalWidth().isAuto() && style()->minWidth().isAuto() && style()->overflowX() == OVISIBLE) {
1969 LayoutUnit minLogicalWidth = minPreferredLogicalWidth();
1970 if (containerLogicalWidth < minLogicalWidth) {
1971 computedValues.m_extent = constrainLogicalWidthByMinMax(minLogicalWidth, containerLogicalWidth, cb);
1972 return;
1976 if (isInline() && !isInlineBlockOrInlineTable()) {
1977 // just calculate margins
1978 computedValues.m_margins.m_start = minimumValueForLength(styleToUse.marginStart(), containerLogicalWidth);
1979 computedValues.m_margins.m_end = minimumValueForLength(styleToUse.marginEnd(), containerLogicalWidth);
1980 if (treatAsReplaced)
1981 computedValues.m_extent = std::max<LayoutUnit>(floatValueForLength(logicalWidthLength, 0) + borderAndPaddingLogicalWidth(), minPreferredLogicalWidth());
1982 return;
1985 // Width calculations
1986 if (treatAsReplaced) {
1987 computedValues.m_extent = logicalWidthLength.value() + borderAndPaddingLogicalWidth();
1988 } else {
1989 LayoutUnit containerWidthInInlineDirection = containerLogicalWidth;
1990 if (hasPerpendicularContainingBlock)
1991 containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight();
1992 LayoutUnit preferredWidth = computeLogicalWidthUsing(MainOrPreferredSize, styleToUse.logicalWidth(), containerWidthInInlineDirection, cb);
1993 computedValues.m_extent = constrainLogicalWidthByMinMax(preferredWidth, containerWidthInInlineDirection, cb);
1996 // Margin calculations.
1997 computeMarginsForDirection(InlineDirection, cb, containerLogicalWidth, computedValues.m_extent, computedValues.m_margins.m_start,
1998 computedValues.m_margins.m_end, style()->marginStart(), style()->marginEnd());
2000 if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (computedValues.m_extent + computedValues.m_margins.m_start + computedValues.m_margins.m_end)
2001 && !isFloating() && !isInline() && !cb->isFlexibleBoxIncludingDeprecated() && !cb->isLayoutGrid()) {
2002 LayoutUnit newMargin = containerLogicalWidth - computedValues.m_extent - cb->marginStartForChild(*this);
2003 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection();
2004 if (hasInvertedDirection)
2005 computedValues.m_margins.m_start = newMargin;
2006 else
2007 computedValues.m_margins.m_end = newMargin;
2010 if (styleToUse.textAutosizingMultiplier() != 1 && styleToUse.marginStart().type() == Fixed) {
2011 Node* parentNode = generatingNode();
2012 if (parentNode && (isHTMLOListElement(*parentNode) || isHTMLUListElement(*parentNode))) {
2013 // Make sure the markers in a list are properly positioned (i.e. not chopped off) when autosized.
2014 const float adjustedMargin = (1 - 1.0 / styleToUse.textAutosizingMultiplier()) * getMaxWidthListMarker(this);
2015 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() != style()->isLeftToRightDirection();
2016 if (hasInvertedDirection)
2017 computedValues.m_margins.m_end += adjustedMargin;
2018 else
2019 computedValues.m_margins.m_start += adjustedMargin;
2024 LayoutUnit LayoutBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth) const
2026 LayoutUnit marginStart = 0;
2027 LayoutUnit marginEnd = 0;
2028 return fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd);
2031 LayoutUnit LayoutBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const
2033 marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth);
2034 marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth);
2035 return availableLogicalWidth - marginStart - marginEnd;
2038 LayoutUnit LayoutBox::computeIntrinsicLogicalWidthUsing(const Length& logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const
2040 if (logicalWidthLength.type() == FillAvailable)
2041 return fillAvailableMeasure(availableLogicalWidth);
2043 LayoutUnit minLogicalWidth = 0;
2044 LayoutUnit maxLogicalWidth = 0;
2045 computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth);
2047 if (logicalWidthLength.type() == MinContent)
2048 return minLogicalWidth + borderAndPadding;
2050 if (logicalWidthLength.type() == MaxContent)
2051 return maxLogicalWidth + borderAndPadding;
2053 if (logicalWidthLength.type() == FitContent) {
2054 minLogicalWidth += borderAndPadding;
2055 maxLogicalWidth += borderAndPadding;
2056 return std::max(minLogicalWidth, std::min(maxLogicalWidth, fillAvailableMeasure(availableLogicalWidth)));
2059 ASSERT_NOT_REACHED();
2060 return 0;
2063 LayoutUnit LayoutBox::computeLogicalWidthUsing(SizeType widthType, const Length& logicalWidth, LayoutUnit availableLogicalWidth, const LayoutBlock* cb) const
2065 ASSERT(widthType == MinSize || widthType == MainOrPreferredSize || !logicalWidth.isAuto());
2066 if (widthType == MinSize && logicalWidth.isAuto())
2067 return adjustBorderBoxLogicalWidthForBoxSizing(0);
2069 if (!logicalWidth.isIntrinsicOrAuto()) {
2070 // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead.
2071 return adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, availableLogicalWidth));
2074 if (logicalWidth.isIntrinsic())
2075 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth());
2077 LayoutUnit marginStart = 0;
2078 LayoutUnit marginEnd = 0;
2079 LayoutUnit logicalWidthResult = fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd);
2081 if (shrinkToAvoidFloats() && cb->isLayoutBlockFlow() && toLayoutBlockFlow(cb)->containsFloats())
2082 logicalWidthResult = std::min(logicalWidthResult, shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, toLayoutBlockFlow(cb)));
2084 if (widthType == MainOrPreferredSize && sizesLogicalWidthToFitContent(logicalWidth))
2085 return std::max(minPreferredLogicalWidth(), std::min(maxPreferredLogicalWidth(), logicalWidthResult));
2086 return logicalWidthResult;
2089 static bool columnFlexItemHasStretchAlignment(const LayoutObject* flexitem)
2091 LayoutObject* parent = flexitem->parent();
2092 // auto margins mean we don't stretch. Note that this function will only be used for
2093 // widths, so we don't have to check marginBefore/marginAfter.
2094 ASSERT(parent->style()->isColumnFlexDirection());
2095 if (flexitem->style()->marginStart().isAuto() || flexitem->style()->marginEnd().isAuto())
2096 return false;
2097 return flexitem->style()->alignSelfPosition() == ItemPositionStretch || (flexitem->style()->alignSelfPosition() == ItemPositionAuto && parent->style()->alignItemsPosition() == ItemPositionStretch);
2100 static bool isStretchingColumnFlexItem(const LayoutObject* flexitem)
2102 LayoutObject* parent = flexitem->parent();
2103 if (parent->isDeprecatedFlexibleBox() && parent->style()->boxOrient() == VERTICAL && parent->style()->boxAlign() == BSTRETCH)
2104 return true;
2106 // We don't stretch multiline flexboxes because they need to apply line spacing (align-content) first.
2107 if (parent->isFlexibleBox() && parent->style()->flexWrap() == FlexNoWrap && parent->style()->isColumnFlexDirection() && columnFlexItemHasStretchAlignment(flexitem))
2108 return true;
2109 return false;
2112 bool LayoutBox::sizesLogicalWidthToFitContent(const Length& logicalWidth) const
2114 if (isFloating() || isInlineBlockOrInlineTable())
2115 return true;
2117 if (logicalWidth.type() == Intrinsic)
2118 return true;
2120 // Flexible box items should shrink wrap, so we lay them out at their intrinsic widths.
2121 // In the case of columns that have a stretch alignment, we go ahead and layout at the
2122 // stretched size to avoid an extra layout when applying alignment.
2123 if (parent()->isFlexibleBox()) {
2124 // For multiline columns, we need to apply align-content first, so we can't stretch now.
2125 if (!parent()->style()->isColumnFlexDirection() || parent()->style()->flexWrap() != FlexNoWrap)
2126 return true;
2127 if (!columnFlexItemHasStretchAlignment(this))
2128 return true;
2131 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes
2132 // that don't stretch their kids lay out their children at their intrinsic widths.
2133 // FIXME: Think about writing-mode here.
2134 // https://bugs.webkit.org/show_bug.cgi?id=46473
2135 if (parent()->isDeprecatedFlexibleBox() && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
2136 return true;
2138 // Button, input, select, textarea, and legend treat width value of 'auto' as 'intrinsic' unless it's in a
2139 // stretching column flexbox.
2140 // FIXME: Think about writing-mode here.
2141 // https://bugs.webkit.org/show_bug.cgi?id=46473
2142 if (logicalWidth.isAuto() && !isStretchingColumnFlexItem(this) && autoWidthShouldFitContent())
2143 return true;
2145 if (isHorizontalWritingMode() != containingBlock()->isHorizontalWritingMode())
2146 return true;
2148 return false;
2151 bool LayoutBox::autoWidthShouldFitContent() const
2153 return node() && (isHTMLInputElement(*node()) || isHTMLSelectElement(*node()) || isHTMLButtonElement(*node())
2154 || isHTMLTextAreaElement(*node()) || (isHTMLLegendElement(*node()) && !style()->hasOutOfFlowPosition()));
2157 void LayoutBox::computeMarginsForDirection(MarginDirection flowDirection, const LayoutBlock* containingBlock, LayoutUnit containerWidth, LayoutUnit childWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd, Length marginStartLength, Length marginEndLength) const
2159 // First assert that we're not calling this method on box types that don't support margins.
2160 ASSERT(!isTableCell());
2161 ASSERT(!isTableRow());
2162 ASSERT(!isTableSection());
2163 ASSERT(!isLayoutTableCol());
2164 if (flowDirection == BlockDirection || isFloating() || isInline()) {
2165 // Margins are calculated with respect to the logical width of
2166 // the containing block (8.3)
2167 // Inline blocks/tables and floats don't have their margins increased.
2168 marginStart = minimumValueForLength(marginStartLength, containerWidth);
2169 marginEnd = minimumValueForLength(marginEndLength, containerWidth);
2170 return;
2173 if (containingBlock->isFlexibleBox()) {
2174 // We need to let flexbox handle the margin adjustment - otherwise, flexbox
2175 // will think we're wider than we actually are and calculate line sizes wrong.
2176 // See also http://dev.w3.org/csswg/css-flexbox/#auto-margins
2177 if (marginStartLength.isAuto())
2178 marginStartLength.setValue(0);
2179 if (marginEndLength.isAuto())
2180 marginEndLength.setValue(0);
2183 LayoutUnit marginStartWidth = minimumValueForLength(marginStartLength, containerWidth);
2184 LayoutUnit marginEndWidth = minimumValueForLength(marginEndLength, containerWidth);
2186 LayoutUnit availableWidth = containerWidth;
2187 if (avoidsFloats() && containingBlock->isLayoutBlockFlow() && toLayoutBlockFlow(containingBlock)->containsFloats()) {
2188 availableWidth = containingBlockAvailableLineWidth();
2189 if (shrinkToAvoidFloats() && availableWidth < containerWidth) {
2190 marginStart = std::max(LayoutUnit(), marginStartWidth);
2191 marginEnd = std::max(LayoutUnit(), marginEndWidth);
2195 // CSS 2.1 (10.3.3): "If 'width' is not 'auto' and 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width'
2196 // (plus any of 'margin-left' or 'margin-right' that are not 'auto') is larger than the width of the containing block, then any 'auto'
2197 // values for 'margin-left' or 'margin-right' are, for the following rules, treated as zero.
2198 LayoutUnit marginBoxWidth = childWidth + (!style()->width().isAuto() ? marginStartWidth + marginEndWidth : LayoutUnit());
2200 if (marginBoxWidth < availableWidth) {
2201 // CSS 2.1: "If both 'margin-left' and 'margin-right' are 'auto', their used values are equal. This horizontally centers the element
2202 // with respect to the edges of the containing block."
2203 const ComputedStyle& containingBlockStyle = containingBlock->styleRef();
2204 if ((marginStartLength.isAuto() && marginEndLength.isAuto())
2205 || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlockStyle.textAlign() == WEBKIT_CENTER)) {
2206 // Other browsers center the margin box for align=center elements so we match them here.
2207 LayoutUnit centeredMarginBoxStart = std::max(LayoutUnit(), (availableWidth - childWidth - marginStartWidth - marginEndWidth) / 2);
2208 marginStart = centeredMarginBoxStart + marginStartWidth;
2209 marginEnd = availableWidth - childWidth - marginStart + marginEndWidth;
2210 return;
2213 // Adjust margins for the align attribute
2214 if ((!containingBlockStyle.isLeftToRightDirection() && containingBlockStyle.textAlign() == WEBKIT_LEFT)
2215 || (containingBlockStyle.isLeftToRightDirection() && containingBlockStyle.textAlign() == WEBKIT_RIGHT)) {
2216 if (containingBlockStyle.isLeftToRightDirection() != styleRef().isLeftToRightDirection()) {
2217 if (!marginStartLength.isAuto())
2218 marginEndLength = Length(Auto);
2219 } else {
2220 if (!marginEndLength.isAuto())
2221 marginStartLength = Length(Auto);
2225 // CSS 2.1: "If there is exactly one value specified as 'auto', its used value follows from the equality."
2226 if (marginEndLength.isAuto()) {
2227 marginStart = marginStartWidth;
2228 marginEnd = availableWidth - childWidth - marginStart;
2229 return;
2232 if (marginStartLength.isAuto()) {
2233 marginEnd = marginEndWidth;
2234 marginStart = availableWidth - childWidth - marginEnd;
2235 return;
2239 // Either no auto margins, or our margin box width is >= the container width, auto margins will just turn into 0.
2240 marginStart = marginStartWidth;
2241 marginEnd = marginEndWidth;
2244 void LayoutBox::updateLogicalHeight()
2246 m_intrinsicContentLogicalHeight = contentLogicalHeight();
2248 LogicalExtentComputedValues computedValues;
2249 computeLogicalHeight(logicalHeight(), logicalTop(), computedValues);
2251 setLogicalHeight(computedValues.m_extent);
2252 setLogicalTop(computedValues.m_position);
2253 setMarginBefore(computedValues.m_margins.m_before);
2254 setMarginAfter(computedValues.m_margins.m_after);
2257 void LayoutBox::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
2259 computedValues.m_extent = logicalHeight;
2260 computedValues.m_position = logicalTop;
2262 // Cell height is managed by the table and inline non-replaced elements do not support a height property.
2263 if (isTableCell() || (isInline() && !isReplaced()))
2264 return;
2266 Length h;
2267 if (isOutOfFlowPositioned()) {
2268 computePositionedLogicalHeight(computedValues);
2269 } else {
2270 LayoutBlock* cb = containingBlock();
2272 // If we are perpendicular to our containing block then we need to resolve our block-start and block-end margins so that if they
2273 // are 'auto' we are centred or aligned within the inline flow containing block: this is done by computing the margins as though they are inline.
2274 // Note that as this is the 'sizing phase' we are using our own writing mode rather than the containing block's. We use the containing block's
2275 // writing mode when figuring out the block-direction margins for positioning in |computeAndSetBlockDirectionMargins| (i.e. margin collapsing etc.).
2276 // See http://www.w3.org/TR/2014/CR-css-writing-modes-3-20140320/#orthogonal-flows
2277 MarginDirection flowDirection = isHorizontalWritingMode() != cb->isHorizontalWritingMode() ? InlineDirection : BlockDirection;
2279 // For tables, calculate margins only.
2280 if (isTable()) {
2281 computeMarginsForDirection(flowDirection, cb, containingBlockLogicalWidthForContent(), computedValues.m_extent, computedValues.m_margins.m_before,
2282 computedValues.m_margins.m_after, style()->marginBefore(), style()->marginAfter());
2283 return;
2286 // FIXME: Account for writing-mode in flexible boxes.
2287 // https://bugs.webkit.org/show_bug.cgi?id=46418
2288 bool inHorizontalBox = parent()->isDeprecatedFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL;
2289 bool stretching = parent()->style()->boxAlign() == BSTRETCH;
2290 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching);
2291 bool checkMinMaxHeight = false;
2293 // The parent box is flexing us, so it has increased or decreased our height. We have to
2294 // grab our cached flexible height.
2295 // FIXME: Account for writing-mode in flexible boxes.
2296 // https://bugs.webkit.org/show_bug.cgi?id=46418
2297 if (hasOverrideLogicalContentHeight() && (parent()->isFlexibleBoxIncludingDeprecated() || parent()->isLayoutGrid())) {
2298 LayoutUnit contentHeight = overrideLogicalContentHeight();
2299 if (parent()->isLayoutGrid() && style()->minHeight().isAuto() && style()->overflowY() == OVISIBLE) {
2300 ASSERT(style()->logicalHeight().isAuto());
2301 LayoutUnit minContentHeight = computeContentLogicalHeight(MinSize, Length(MinContent), computedValues.m_extent - borderAndPaddingLogicalHeight());
2302 contentHeight = std::max(contentHeight, constrainLogicalHeightByMinMax(minContentHeight, computedValues.m_extent - borderAndPaddingLogicalHeight()));
2304 h = Length(contentHeight, Fixed);
2305 } else if (treatAsReplaced) {
2306 h = Length(computeReplacedLogicalHeight(), Fixed);
2307 } else {
2308 h = style()->logicalHeight();
2309 checkMinMaxHeight = true;
2312 // Block children of horizontal flexible boxes fill the height of the box.
2313 // FIXME: Account for writing-mode in flexible boxes.
2314 // https://bugs.webkit.org/show_bug.cgi?id=46418
2315 if (h.isAuto() && inHorizontalBox && toLayoutDeprecatedFlexibleBox(parent())->isStretchingChildren()) {
2316 h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter() - borderAndPaddingLogicalHeight(), Fixed);
2317 checkMinMaxHeight = false;
2320 LayoutUnit heightResult;
2321 if (checkMinMaxHeight) {
2322 heightResult = computeLogicalHeightUsing(MainOrPreferredSize, style()->logicalHeight(), computedValues.m_extent - borderAndPaddingLogicalHeight());
2323 if (heightResult == -1)
2324 heightResult = computedValues.m_extent;
2325 heightResult = constrainLogicalHeightByMinMax(heightResult, computedValues.m_extent - borderAndPaddingLogicalHeight());
2326 } else {
2327 // The only times we don't check min/max height are when a fixed length has
2328 // been given as an override. Just use that. The value has already been adjusted
2329 // for box-sizing.
2330 ASSERT(h.isFixed());
2331 heightResult = h.value() + borderAndPaddingLogicalHeight();
2334 computedValues.m_extent = heightResult;
2335 computeMarginsForDirection(flowDirection, cb, containingBlockLogicalWidthForContent(), computedValues.m_extent, computedValues.m_margins.m_before,
2336 computedValues.m_margins.m_after, style()->marginBefore(), style()->marginAfter());
2339 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the
2340 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height
2341 // is specified. When we're printing, we also need this quirk if the body or root has a percentage
2342 // height since we don't set a height in LayoutView when we're printing. So without this quirk, the
2343 // height has nothing to be a percentage of, and it ends up being 0. That is bad.
2344 bool paginatedContentNeedsBaseHeight = document().printing() && h.hasPercent()
2345 && (isDocumentElement() || (isBody() && document().documentElement()->layoutObject()->style()->logicalHeight().hasPercent())) && !isInline();
2346 if (stretchesToViewport() || paginatedContentNeedsBaseHeight) {
2347 LayoutUnit margins = collapsedMarginBefore() + collapsedMarginAfter();
2348 LayoutUnit visibleHeight = view()->viewLogicalHeightForPercentages();
2349 if (isDocumentElement()) {
2350 computedValues.m_extent = std::max(computedValues.m_extent, visibleHeight - margins);
2351 } else {
2352 LayoutUnit marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight();
2353 computedValues.m_extent = std::max(computedValues.m_extent, visibleHeight - marginsBordersPadding);
2358 LayoutUnit LayoutBox::computeLogicalHeightWithoutLayout() const
2360 // TODO(cbiesinger): We should probably return something other than just border + padding, but for now
2361 // we have no good way to do anything else without layout, so we just use that.
2362 LogicalExtentComputedValues computedValues;
2363 computeLogicalHeight(borderAndPaddingLogicalHeight(), 0, computedValues);
2364 return computedValues.m_extent;
2367 LayoutUnit LayoutBox::computeLogicalHeightUsing(SizeType heightType, const Length& height, LayoutUnit intrinsicContentHeight) const
2369 LayoutUnit logicalHeight = computeContentAndScrollbarLogicalHeightUsing(heightType, height, intrinsicContentHeight);
2370 if (logicalHeight != -1)
2371 logicalHeight = adjustBorderBoxLogicalHeightForBoxSizing(logicalHeight);
2372 return logicalHeight;
2375 LayoutUnit LayoutBox::computeContentLogicalHeight(SizeType heightType, const Length& height, LayoutUnit intrinsicContentHeight) const
2377 LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(heightType, height, intrinsicContentHeight);
2378 if (heightIncludingScrollbar == -1)
2379 return -1;
2380 return std::max(LayoutUnit(), adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight());
2383 LayoutUnit LayoutBox::computeIntrinsicLogicalContentHeightUsing(const Length& logicalHeightLength, LayoutUnit intrinsicContentHeight, LayoutUnit borderAndPadding) const
2385 // FIXME(cbiesinger): The css-sizing spec is considering changing what min-content/max-content should resolve to.
2386 // If that happens, this code will have to change.
2387 if (logicalHeightLength.isMinContent() || logicalHeightLength.isMaxContent() || logicalHeightLength.isFitContent()) {
2388 if (isReplaced())
2389 return intrinsicSize().height();
2390 if (m_intrinsicContentLogicalHeight != -1)
2391 return m_intrinsicContentLogicalHeight;
2392 return intrinsicContentHeight;
2394 if (logicalHeightLength.isFillAvailable())
2395 return containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding) - borderAndPadding;
2396 ASSERT_NOT_REACHED();
2397 return 0;
2400 LayoutUnit LayoutBox::computeContentAndScrollbarLogicalHeightUsing(SizeType heightType, const Length& height, LayoutUnit intrinsicContentHeight) const
2402 if (height.isAuto())
2403 return heightType == MinSize ? 0 : -1;
2404 // FIXME(cbiesinger): The css-sizing spec is considering changing what min-content/max-content should resolve to.
2405 // If that happens, this code will have to change.
2406 if (height.isIntrinsic()) {
2407 if (intrinsicContentHeight == -1)
2408 return -1; // Intrinsic height isn't available.
2409 return computeIntrinsicLogicalContentHeightUsing(height, intrinsicContentHeight, borderAndPaddingLogicalHeight()) + scrollbarLogicalHeight();
2411 if (height.isFixed())
2412 return height.value();
2413 if (height.hasPercent())
2414 return computePercentageLogicalHeight(height);
2415 return -1;
2418 bool LayoutBox::skipContainingBlockForPercentHeightCalculation(const LayoutBox* containingBlock) const
2420 // If the writing mode of the containing block is orthogonal to ours, it means that we shouldn't
2421 // skip anything, since we're going to resolve the percentage height against a containing block *width*.
2422 if (isHorizontalWritingMode() != containingBlock->isHorizontalWritingMode())
2423 return false;
2425 // Anonymous blocks should not impede percentage resolution on a child. Examples of such
2426 // anonymous blocks are blocks wrapped around inlines that have block siblings (from the CSS
2427 // spec) and multicol flow threads (an implementation detail). Another implementation detail,
2428 // ruby runs, create anonymous inline-blocks, so skip those too. All other types of anonymous
2429 // objects, such as table-cells, will be treated just as if they were non-anonymous.
2430 if (containingBlock->isAnonymous()) {
2431 EDisplay display = containingBlock->styleRef().display();
2432 return display == BLOCK || display == INLINE_BLOCK;
2435 // For quirks mode, we skip most auto-height containing blocks when computing percentages.
2436 return document().inQuirksMode() && !containingBlock->isTableCell() && !containingBlock->isOutOfFlowPositioned() && containingBlock->style()->logicalHeight().isAuto();
2439 LayoutUnit LayoutBox::computePercentageLogicalHeight(const Length& height) const
2441 LayoutUnit availableHeight = -1;
2443 bool skippedAutoHeightContainingBlock = false;
2444 LayoutBlock* cb = containingBlock();
2445 const LayoutBox* containingBlockChild = this;
2446 LayoutUnit rootMarginBorderPaddingHeight = 0;
2447 while (!cb->isLayoutView() && skipContainingBlockForPercentHeightCalculation(cb)) {
2448 if (cb->isBody() || cb->isDocumentElement())
2449 rootMarginBorderPaddingHeight += cb->marginBefore() + cb->marginAfter() + cb->borderAndPaddingLogicalHeight();
2450 skippedAutoHeightContainingBlock = true;
2451 containingBlockChild = cb;
2452 cb = cb->containingBlock();
2454 cb->addPercentHeightDescendant(const_cast<LayoutBox*>(this));
2456 const ComputedStyle& cbstyle = cb->styleRef();
2458 // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height
2459 // explicitly specified that can be used for any percentage computations.
2460 bool isOutOfFlowPositionedWithSpecifiedHeight = cb->isOutOfFlowPositioned() && (!cbstyle.logicalHeight().isAuto() || (!cbstyle.logicalTop().isAuto() && !cbstyle.logicalBottom().isAuto()));
2462 bool includeBorderPadding = isTable();
2464 if (isHorizontalWritingMode() != cb->isHorizontalWritingMode()) {
2465 availableHeight = containingBlockChild->containingBlockLogicalWidthForContent();
2466 } else if (hasOverrideContainingBlockLogicalHeight()) {
2467 availableHeight = overrideContainingBlockContentLogicalHeight();
2468 } else if (cb->isTableCell()) {
2469 if (!skippedAutoHeightContainingBlock) {
2470 // Table cells violate what the CSS spec says to do with heights. Basically we
2471 // don't care if the cell specified a height or not. We just always make ourselves
2472 // be a percentage of the cell's current content height.
2473 if (!cb->hasOverrideLogicalContentHeight()) {
2474 // Normally we would let the cell size intrinsically, but scrolling overflow has to be
2475 // treated differently, since WinIE lets scrolled overflow regions shrink as needed.
2476 // While we can't get all cases right, we can at least detect when the cell has a specified
2477 // height or when the table has a specified height. In these cases we want to initially have
2478 // no size and allow the flexing of the table or the cell to its specified height to cause us
2479 // to grow to fill the space. This could end up being wrong in some cases, but it is
2480 // preferable to the alternative (sizing intrinsically and making the row end up too big).
2481 LayoutTableCell* cell = toLayoutTableCell(cb);
2482 if (scrollsOverflowY() && (!cell->style()->logicalHeight().isAuto() || !cell->table()->style()->logicalHeight().isAuto()))
2483 return LayoutUnit();
2484 return -1;
2486 availableHeight = cb->overrideLogicalContentHeight();
2487 includeBorderPadding = true;
2489 } else if (cbstyle.logicalHeight().isFixed()) {
2490 LayoutUnit contentBoxHeight = cb->adjustContentBoxLogicalHeightForBoxSizing(cbstyle.logicalHeight().value());
2491 availableHeight = std::max(LayoutUnit(), cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeight - cb->scrollbarLogicalHeight(), -1));
2492 } else if (cbstyle.logicalHeight().hasPercent() && !isOutOfFlowPositionedWithSpecifiedHeight) {
2493 // We need to recur and compute the percentage height for our containing block.
2494 LayoutUnit heightWithScrollbar = cb->computePercentageLogicalHeight(cbstyle.logicalHeight());
2495 if (heightWithScrollbar != -1) {
2496 LayoutUnit contentBoxHeightWithScrollbar = cb->adjustContentBoxLogicalHeightForBoxSizing(heightWithScrollbar);
2497 // We need to adjust for min/max height because this method does not
2498 // handle the min/max of the current block, its caller does. So the
2499 // return value from the recursive call will not have been adjusted
2500 // yet.
2501 LayoutUnit contentBoxHeight = cb->constrainContentBoxLogicalHeightByMinMax(contentBoxHeightWithScrollbar - cb->scrollbarLogicalHeight(), -1);
2502 availableHeight = std::max(LayoutUnit(), contentBoxHeight);
2504 } else if (isOutOfFlowPositionedWithSpecifiedHeight) {
2505 // Don't allow this to affect the block' size() member variable, since this
2506 // can get called while the block is still laying out its kids.
2507 LogicalExtentComputedValues computedValues;
2508 cb->computeLogicalHeight(cb->logicalHeight(), 0, computedValues);
2509 availableHeight = computedValues.m_extent - cb->borderAndPaddingLogicalHeight() - cb->scrollbarLogicalHeight();
2510 } else if (cb->isLayoutView()) {
2511 availableHeight = view()->viewLogicalHeightForPercentages();
2514 if (availableHeight == -1)
2515 return availableHeight;
2517 availableHeight -= rootMarginBorderPaddingHeight;
2519 if (isTable() && isOutOfFlowPositioned())
2520 availableHeight += cb->paddingLogicalHeight();
2522 LayoutUnit result = valueForLength(height, availableHeight);
2523 if (includeBorderPadding) {
2524 // FIXME: Table cells should default to box-sizing: border-box so we can avoid this hack.
2525 // It is necessary to use the border-box to match WinIE's broken
2526 // box model. This is essential for sizing inside
2527 // table cells using percentage heights.
2528 result -= borderAndPaddingLogicalHeight();
2529 return std::max(LayoutUnit(), result);
2531 return result;
2534 LayoutUnit LayoutBox::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
2536 return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(MainOrPreferredSize, style()->logicalWidth()), shouldComputePreferred);
2539 LayoutUnit LayoutBox::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred shouldComputePreferred) const
2541 LayoutUnit minLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMinWidth().hasPercent()) || style()->logicalMinWidth().isMaxSizeNone() ? logicalWidth : computeReplacedLogicalWidthUsing(MinSize, style()->logicalMinWidth());
2542 LayoutUnit maxLogicalWidth = (shouldComputePreferred == ComputePreferred && style()->logicalMaxWidth().hasPercent()) || style()->logicalMaxWidth().isMaxSizeNone() ? logicalWidth : computeReplacedLogicalWidthUsing(MaxSize, style()->logicalMaxWidth());
2543 return std::max(minLogicalWidth, std::min(logicalWidth, maxLogicalWidth));
2546 LayoutUnit LayoutBox::computeReplacedLogicalWidthUsing(SizeType sizeType, const Length& logicalWidth) const
2548 ASSERT(sizeType == MinSize || sizeType == MainOrPreferredSize || !logicalWidth.isAuto());
2549 if (sizeType == MinSize && logicalWidth.isAuto())
2550 return adjustContentBoxLogicalWidthForBoxSizing(0);
2552 switch (logicalWidth.type()) {
2553 case Fixed:
2554 return adjustContentBoxLogicalWidthForBoxSizing(logicalWidth.value());
2555 case MinContent:
2556 case MaxContent: {
2557 // MinContent/MaxContent don't need the availableLogicalWidth argument.
2558 LayoutUnit availableLogicalWidth = 0;
2559 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
2561 case FitContent:
2562 case FillAvailable:
2563 case Percent:
2564 case Calculated: {
2565 // FIXME: containingBlockLogicalWidthForContent() is wrong if the replaced element's writing-mode is perpendicular to the
2566 // containing block's writing-mode.
2567 // https://bugs.webkit.org/show_bug.cgi?id=46496
2568 const LayoutUnit cw = isOutOfFlowPositioned() ? containingBlockLogicalWidthForPositioned(toLayoutBoxModelObject(container())) : containingBlockLogicalWidthForContent();
2569 Length containerLogicalWidth = containingBlock()->style()->logicalWidth();
2570 // FIXME: Handle cases when containing block width is calculated or viewport percent.
2571 // https://bugs.webkit.org/show_bug.cgi?id=91071
2572 if (logicalWidth.isIntrinsic())
2573 return computeIntrinsicLogicalWidthUsing(logicalWidth, cw, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
2574 if (cw > 0 || (!cw && (containerLogicalWidth.isFixed() || containerLogicalWidth.hasPercent())))
2575 return adjustContentBoxLogicalWidthForBoxSizing(minimumValueForLength(logicalWidth, cw));
2576 return LayoutUnit();
2578 case Intrinsic:
2579 case MinIntrinsic:
2580 case Auto:
2581 case MaxSizeNone:
2582 return intrinsicLogicalWidth();
2583 case ExtendToZoom:
2584 case DeviceWidth:
2585 case DeviceHeight:
2586 break;
2589 ASSERT_NOT_REACHED();
2590 return 0;
2593 LayoutUnit LayoutBox::computeReplacedLogicalHeight() const
2595 return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(MainOrPreferredSize, style()->logicalHeight()));
2598 bool LayoutBox::logicalHeightComputesAsNone(SizeType sizeType) const
2600 ASSERT(sizeType == MinSize || sizeType == MaxSize);
2601 Length logicalHeight = sizeType == MinSize ? style()->logicalMinHeight() : style()->logicalMaxHeight();
2602 Length initialLogicalHeight = sizeType == MinSize ? ComputedStyle::initialMinSize() : ComputedStyle::initialMaxSize();
2604 if (logicalHeight == initialLogicalHeight)
2605 return true;
2607 if (LayoutBlock* cb = containingBlockForAutoHeightDetection(logicalHeight))
2608 return cb->hasAutoHeightOrContainingBlockWithAutoHeight();
2609 return false;
2612 LayoutUnit LayoutBox::computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const
2614 // If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned,
2615 // the percentage value is treated as '0' (for 'min-height') or 'none' (for 'max-height').
2616 LayoutUnit minLogicalHeight;
2617 if (!logicalHeightComputesAsNone(MinSize))
2618 minLogicalHeight = computeReplacedLogicalHeightUsing(MinSize, style()->logicalMinHeight());
2619 LayoutUnit maxLogicalHeight = logicalHeight;
2620 if (!logicalHeightComputesAsNone(MaxSize))
2621 maxLogicalHeight = computeReplacedLogicalHeightUsing(MaxSize, style()->logicalMaxHeight());
2622 return std::max(minLogicalHeight, std::min(logicalHeight, maxLogicalHeight));
2625 LayoutUnit LayoutBox::computeReplacedLogicalHeightUsing(SizeType sizeType, const Length& logicalHeight) const
2627 ASSERT(sizeType == MinSize || sizeType == MainOrPreferredSize || !logicalHeight.isAuto());
2628 if (sizeType == MinSize && logicalHeight.isAuto())
2629 return adjustContentBoxLogicalHeightForBoxSizing(0);
2631 switch (logicalHeight.type()) {
2632 case Fixed:
2633 return adjustContentBoxLogicalHeightForBoxSizing(logicalHeight.value());
2634 case Percent:
2635 case Calculated:
2637 LayoutObject* cb = isOutOfFlowPositioned() ? container() : containingBlock();
2638 while (cb->isAnonymous())
2639 cb = cb->containingBlock();
2640 if (cb->isLayoutBlock())
2641 toLayoutBlock(cb)->addPercentHeightDescendant(const_cast<LayoutBox*>(this));
2643 if (cb->isOutOfFlowPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) {
2644 ASSERT_WITH_SECURITY_IMPLICATION(cb->isLayoutBlock());
2645 LayoutBlock* block = toLayoutBlock(cb);
2646 LogicalExtentComputedValues computedValues;
2647 block->computeLogicalHeight(block->logicalHeight(), 0, computedValues);
2648 LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight() - block->scrollbarLogicalHeight();
2649 LayoutUnit newHeight = block->adjustContentBoxLogicalHeightForBoxSizing(newContentHeight);
2650 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, newHeight));
2653 // FIXME: availableLogicalHeight() is wrong if the replaced element's writing-mode is perpendicular to the
2654 // containing block's writing-mode.
2655 // https://bugs.webkit.org/show_bug.cgi?id=46496
2656 LayoutUnit availableHeight;
2657 if (isOutOfFlowPositioned()) {
2658 availableHeight = containingBlockLogicalHeightForPositioned(toLayoutBoxModelObject(cb));
2659 } else {
2660 availableHeight = containingBlockLogicalHeightForContent(IncludeMarginBorderPadding);
2661 // It is necessary to use the border-box to match WinIE's broken
2662 // box model. This is essential for sizing inside
2663 // table cells using percentage heights.
2664 // FIXME: This needs to be made writing-mode-aware. If the cell and image are perpendicular writing-modes, this isn't right.
2665 // https://bugs.webkit.org/show_bug.cgi?id=46997
2666 while (cb && !cb->isLayoutView() && (cb->style()->logicalHeight().isAuto() || cb->style()->logicalHeight().hasPercent())) {
2667 if (cb->isTableCell()) {
2668 // Don't let table cells squeeze percent-height replaced elements
2669 // <http://bugs.webkit.org/show_bug.cgi?id=15359>
2670 availableHeight = std::max(availableHeight, intrinsicLogicalHeight());
2671 return valueForLength(logicalHeight, availableHeight - borderAndPaddingLogicalHeight());
2673 toLayoutBlock(cb)->addPercentHeightDescendant(const_cast<LayoutBox*>(this));
2674 cb = cb->containingBlock();
2677 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, availableHeight));
2679 case MinContent:
2680 case MaxContent:
2681 case FitContent:
2682 case FillAvailable:
2683 return adjustContentBoxLogicalHeightForBoxSizing(computeIntrinsicLogicalContentHeightUsing(logicalHeight, intrinsicLogicalHeight(), borderAndPaddingHeight()));
2684 default:
2685 return intrinsicLogicalHeight();
2689 LayoutUnit LayoutBox::availableLogicalHeight(AvailableLogicalHeightType heightType) const
2691 // http://www.w3.org/TR/CSS2/visudet.html#propdef-height - We are interested in the content height.
2692 return constrainContentBoxLogicalHeightByMinMax(availableLogicalHeightUsing(style()->logicalHeight(), heightType), -1);
2695 LayoutUnit LayoutBox::availableLogicalHeightUsing(const Length& h, AvailableLogicalHeightType heightType) const
2697 if (isLayoutView())
2698 return isHorizontalWritingMode() ? toLayoutView(this)->frameView()->visibleContentSize().height() : toLayoutView(this)->frameView()->visibleContentSize().width();
2700 // We need to stop here, since we don't want to increase the height of the table
2701 // artificially. We're going to rely on this cell getting expanded to some new
2702 // height, and then when we lay out again we'll use the calculation below.
2703 if (isTableCell() && (h.isAuto() || h.hasPercent())) {
2704 if (hasOverrideLogicalContentHeight())
2705 return overrideLogicalContentHeight();
2706 return logicalHeight() - borderAndPaddingLogicalHeight();
2709 if (h.hasPercent() && isOutOfFlowPositioned()) {
2710 // FIXME: This is wrong if the containingBlock has a perpendicular writing mode.
2711 LayoutUnit availableHeight = containingBlockLogicalHeightForPositioned(containingBlock());
2712 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(h, availableHeight));
2715 LayoutUnit heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(MainOrPreferredSize, h, -1);
2716 if (heightIncludingScrollbar != -1)
2717 return std::max(LayoutUnit(), adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight());
2719 // FIXME: Check logicalTop/logicalBottom here to correctly handle vertical writing-mode.
2720 // https://bugs.webkit.org/show_bug.cgi?id=46500
2721 if (isLayoutBlock() && isOutOfFlowPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) {
2722 LayoutBlock* block = const_cast<LayoutBlock*>(toLayoutBlock(this));
2723 LogicalExtentComputedValues computedValues;
2724 block->computeLogicalHeight(block->logicalHeight(), 0, computedValues);
2725 LayoutUnit newContentHeight = computedValues.m_extent - block->borderAndPaddingLogicalHeight() - block->scrollbarLogicalHeight();
2726 return adjustContentBoxLogicalHeightForBoxSizing(newContentHeight);
2729 // FIXME: This is wrong if the containingBlock has a perpendicular writing mode.
2730 LayoutUnit availableHeight = containingBlockLogicalHeightForContent(heightType);
2731 if (heightType == ExcludeMarginBorderPadding) {
2732 // FIXME: Margin collapsing hasn't happened yet, so this incorrectly removes collapsed margins.
2733 availableHeight -= marginBefore() + marginAfter() + borderAndPaddingLogicalHeight();
2735 return availableHeight;
2738 void LayoutBox::computeAndSetBlockDirectionMargins(const LayoutBlock* containingBlock)
2740 LayoutUnit marginBefore;
2741 LayoutUnit marginAfter;
2742 computeMarginsForDirection(BlockDirection, containingBlock, containingBlockLogicalWidthForContent(), logicalHeight(), marginBefore, marginAfter,
2743 style()->marginBeforeUsing(containingBlock->style()),
2744 style()->marginAfterUsing(containingBlock->style()));
2745 // Note that in this 'positioning phase' of the layout we are using the containing block's writing mode rather than our own when calculating margins.
2746 // See http://www.w3.org/TR/2014/CR-css-writing-modes-3-20140320/#orthogonal-flows
2747 containingBlock->setMarginBeforeForChild(*this, marginBefore);
2748 containingBlock->setMarginAfterForChild(*this, marginAfter);
2751 LayoutUnit LayoutBox::containingBlockLogicalWidthForPositioned(const LayoutBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const
2753 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode())
2754 return containingBlockLogicalHeightForPositioned(containingBlock, false);
2756 // Use viewport as container for top-level fixed-position elements.
2757 if (style()->position() == FixedPosition && containingBlock->isLayoutView()) {
2758 const LayoutView* view = toLayoutView(containingBlock);
2759 if (FrameView* frameView = view->frameView()) {
2760 // Don't use visibleContentRect since the DeprecatedPaintLayer's size has not been set yet.
2761 IntSize viewportSize = frameView->layoutViewportScrollableArea()->excludeScrollbars(frameView->frameRect().size());
2762 return containingBlock->isHorizontalWritingMode() ? viewportSize.width() : viewportSize.height();
2766 if (hasOverrideContainingBlockLogicalWidth())
2767 return overrideContainingBlockContentLogicalWidth();
2769 if (containingBlock->isBox())
2770 return toLayoutBox(containingBlock)->clientLogicalWidth();
2772 ASSERT(containingBlock->isLayoutInline() && containingBlock->isInFlowPositioned());
2774 const LayoutInline* flow = toLayoutInline(containingBlock);
2775 InlineFlowBox* first = flow->firstLineBox();
2776 InlineFlowBox* last = flow->lastLineBox();
2778 // If the containing block is empty, return a width of 0.
2779 if (!first || !last)
2780 return LayoutUnit();
2782 LayoutUnit fromLeft;
2783 LayoutUnit fromRight;
2784 if (containingBlock->style()->isLeftToRightDirection()) {
2785 fromLeft = first->logicalLeft() + first->borderLogicalLeft();
2786 fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight();
2787 } else {
2788 fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight();
2789 fromLeft = last->logicalLeft() + last->borderLogicalLeft();
2792 return std::max(LayoutUnit(), fromRight - fromLeft);
2795 LayoutUnit LayoutBox::containingBlockLogicalHeightForPositioned(const LayoutBoxModelObject* containingBlock, bool checkForPerpendicularWritingMode) const
2797 if (checkForPerpendicularWritingMode && containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode())
2798 return containingBlockLogicalWidthForPositioned(containingBlock, false);
2800 // Use viewport as container for top-level fixed-position elements.
2801 if (style()->position() == FixedPosition && containingBlock->isLayoutView()) {
2802 const LayoutView* view = toLayoutView(containingBlock);
2803 if (FrameView* frameView = view->frameView()) {
2804 // Don't use visibleContentRect since the DeprecatedPaintLayer's size has not been set yet.
2805 IntSize viewportSize = frameView->layoutViewportScrollableArea()->excludeScrollbars(frameView->frameRect().size());
2806 return containingBlock->isHorizontalWritingMode() ? viewportSize.height() : viewportSize.width();
2810 if (hasOverrideContainingBlockLogicalHeight())
2811 return overrideContainingBlockContentLogicalHeight();
2813 if (containingBlock->isBox()) {
2814 const LayoutBlock* cb = containingBlock->isLayoutBlock() ?
2815 toLayoutBlock(containingBlock) : containingBlock->containingBlock();
2816 return cb->clientLogicalHeight();
2819 ASSERT(containingBlock->isLayoutInline() && containingBlock->isInFlowPositioned());
2821 const LayoutInline* flow = toLayoutInline(containingBlock);
2822 InlineFlowBox* first = flow->firstLineBox();
2823 InlineFlowBox* last = flow->lastLineBox();
2825 // If the containing block is empty, return a height of 0.
2826 if (!first || !last)
2827 return LayoutUnit();
2829 LayoutUnit heightResult;
2830 LayoutRect boundingBox(flow->linesBoundingBox());
2831 if (containingBlock->isHorizontalWritingMode())
2832 heightResult = boundingBox.height();
2833 else
2834 heightResult = boundingBox.width();
2835 heightResult -= (containingBlock->borderBefore() + containingBlock->borderAfter());
2836 return heightResult;
2839 static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRight, const LayoutBox* child, const LayoutBoxModelObject* containerBlock, LayoutUnit containerLogicalWidth)
2841 if (!logicalLeft.isAuto() || !logicalRight.isAuto())
2842 return;
2844 // FIXME: The static distance computation has not been patched for mixed writing modes yet.
2845 if (child->parent()->style()->direction() == LTR) {
2846 LayoutUnit staticPosition = child->layer()->staticInlinePosition() - containerBlock->borderLogicalLeft();
2847 for (LayoutObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) {
2848 if (curr->isBox()) {
2849 staticPosition += toLayoutBox(curr)->logicalLeft();
2850 if (toLayoutBox(curr)->isInFlowPositioned())
2851 staticPosition += toLayoutBox(curr)->offsetForInFlowPosition().width();
2852 } else if (curr->isInline()) {
2853 if (curr->isInFlowPositioned()) {
2854 if (!curr->style()->logicalLeft().isAuto())
2855 staticPosition += valueForLength(curr->style()->logicalLeft(), curr->containingBlock()->availableWidth());
2856 else
2857 staticPosition -= valueForLength(curr->style()->logicalRight(), curr->containingBlock()->availableWidth());
2861 logicalLeft.setValue(Fixed, staticPosition);
2862 } else {
2863 LayoutBox* enclosingBox = child->parent()->enclosingBox();
2864 LayoutUnit staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock->borderLogicalLeft();
2865 for (LayoutObject* curr = child->parent(); curr; curr = curr->container()) {
2866 if (curr->isBox()) {
2867 if (curr != containerBlock) {
2868 staticPosition -= toLayoutBox(curr)->logicalLeft();
2869 if (toLayoutBox(curr)->isInFlowPositioned())
2870 staticPosition -= toLayoutBox(curr)->offsetForInFlowPosition().width();
2872 if (curr == enclosingBox)
2873 staticPosition -= enclosingBox->logicalWidth();
2874 } else if (curr->isInline()) {
2875 if (curr->isInFlowPositioned()) {
2876 if (!curr->style()->logicalLeft().isAuto())
2877 staticPosition -= valueForLength(curr->style()->logicalLeft(), curr->containingBlock()->availableWidth());
2878 else
2879 staticPosition += valueForLength(curr->style()->logicalRight(), curr->containingBlock()->availableWidth());
2882 if (curr == containerBlock)
2883 break;
2885 logicalRight.setValue(Fixed, staticPosition);
2889 void LayoutBox::computePositionedLogicalWidth(LogicalExtentComputedValues& computedValues) const
2891 if (isReplaced()) {
2892 computePositionedLogicalWidthReplaced(computedValues);
2893 return;
2896 // QUESTIONS
2897 // FIXME 1: Should we still deal with these the cases of 'left' or 'right' having
2898 // the type 'static' in determining whether to calculate the static distance?
2899 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1.
2901 // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater
2902 // than or less than the computed width(). Be careful of box-sizing and
2903 // percentage issues.
2905 // The following is based off of the W3C Working Draft from April 11, 2006 of
2906 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
2907 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
2908 // (block-style-comments in this function and in computePositionedLogicalWidthUsing()
2909 // correspond to text from the spec)
2912 // We don't use containingBlock(), since we may be positioned by an enclosing
2913 // relative positioned inline.
2914 const LayoutBoxModelObject* containerBlock = toLayoutBoxModelObject(container());
2916 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock);
2918 // Use the container block's direction except when calculating the static distance
2919 // This conforms with the reference results for abspos-replaced-width-margin-000.htm
2920 // of the CSS 2.1 test suite
2921 TextDirection containerDirection = containerBlock->style()->direction();
2923 bool isHorizontal = isHorizontalWritingMode();
2924 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth();
2925 const Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop();
2926 const Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom();
2928 Length logicalLeftLength = style()->logicalLeft();
2929 Length logicalRightLength = style()->logicalRight();
2931 /*---------------------------------------------------------------------------*\
2932 * For the purposes of this section and the next, the term "static position"
2933 * (of an element) refers, roughly, to the position an element would have had
2934 * in the normal flow. More precisely:
2936 * * The static position for 'left' is the distance from the left edge of the
2937 * containing block to the left margin edge of a hypothetical box that would
2938 * have been the first box of the element if its 'position' property had
2939 * been 'static' and 'float' had been 'none'. The value is negative if the
2940 * hypothetical box is to the left of the containing block.
2941 * * The static position for 'right' is the distance from the right edge of the
2942 * containing block to the right margin edge of the same hypothetical box as
2943 * above. The value is positive if the hypothetical box is to the left of the
2944 * containing block's edge.
2946 * But rather than actually calculating the dimensions of that hypothetical box,
2947 * user agents are free to make a guess at its probable position.
2949 * For the purposes of calculating the static position, the containing block of
2950 * fixed positioned elements is the initial containing block instead of the
2951 * viewport, and all scrollable boxes should be assumed to be scrolled to their
2952 * origin.
2953 \*---------------------------------------------------------------------------*/
2955 // see FIXME 1
2956 // Calculate the static distance if needed.
2957 computeInlineStaticDistance(logicalLeftLength, logicalRightLength, this, containerBlock, containerLogicalWidth);
2959 // Calculate constraint equation values for 'width' case.
2960 computePositionedLogicalWidthUsing(MainOrPreferredSize, style()->logicalWidth(), containerBlock, containerDirection,
2961 containerLogicalWidth, bordersPlusPadding,
2962 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
2963 computedValues);
2965 // Calculate constraint equation values for 'max-width' case.
2966 if (!style()->logicalMaxWidth().isMaxSizeNone()) {
2967 LogicalExtentComputedValues maxValues;
2969 computePositionedLogicalWidthUsing(MaxSize, style()->logicalMaxWidth(), containerBlock, containerDirection,
2970 containerLogicalWidth, bordersPlusPadding,
2971 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
2972 maxValues);
2974 if (computedValues.m_extent > maxValues.m_extent) {
2975 computedValues.m_extent = maxValues.m_extent;
2976 computedValues.m_position = maxValues.m_position;
2977 computedValues.m_margins.m_start = maxValues.m_margins.m_start;
2978 computedValues.m_margins.m_end = maxValues.m_margins.m_end;
2982 // Calculate constraint equation values for 'min-width' case.
2983 if (!style()->logicalMinWidth().isZero() || style()->logicalMinWidth().isIntrinsic()) {
2984 LogicalExtentComputedValues minValues;
2986 computePositionedLogicalWidthUsing(MinSize, style()->logicalMinWidth(), containerBlock, containerDirection,
2987 containerLogicalWidth, bordersPlusPadding,
2988 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
2989 minValues);
2991 if (computedValues.m_extent < minValues.m_extent) {
2992 computedValues.m_extent = minValues.m_extent;
2993 computedValues.m_position = minValues.m_position;
2994 computedValues.m_margins.m_start = minValues.m_margins.m_start;
2995 computedValues.m_margins.m_end = minValues.m_margins.m_end;
2999 if (!style()->hasStaticInlinePosition(isHorizontal))
3000 computedValues.m_position += extraInlineOffset();
3002 computedValues.m_extent += bordersPlusPadding;
3005 static void computeLogicalLeftPositionedOffset(LayoutUnit& logicalLeftPos, const LayoutBox* child, LayoutUnit logicalWidthValue, const LayoutBoxModelObject* containerBlock, LayoutUnit containerLogicalWidth)
3007 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
3008 // 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.
3009 if (containerBlock->isHorizontalWritingMode() != child->isHorizontalWritingMode() && containerBlock->style()->isFlippedBlocksWritingMode()) {
3010 logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos;
3011 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderRight() : containerBlock->borderBottom());
3012 } else {
3013 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock->borderLeft() : containerBlock->borderTop());
3017 LayoutUnit LayoutBox::shrinkToFitLogicalWidth(LayoutUnit availableLogicalWidth, LayoutUnit bordersPlusPadding) const
3019 LayoutUnit preferredLogicalWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
3020 LayoutUnit preferredMinLogicalWidth = minPreferredLogicalWidth() - bordersPlusPadding;
3021 return std::min(std::max(preferredMinLogicalWidth, availableLogicalWidth), preferredLogicalWidth);
3024 void LayoutBox::computePositionedLogicalWidthUsing(SizeType widthSizeType, Length logicalWidth, const LayoutBoxModelObject* containerBlock, TextDirection containerDirection,
3025 LayoutUnit containerLogicalWidth, LayoutUnit bordersPlusPadding,
3026 const Length& logicalLeft, const Length& logicalRight, const Length& marginLogicalLeft,
3027 const Length& marginLogicalRight, LogicalExtentComputedValues& computedValues) const
3029 ASSERT(widthSizeType == MinSize || widthSizeType == MainOrPreferredSize || !logicalWidth.isAuto());
3030 if (widthSizeType == MinSize && logicalWidth.isAuto())
3031 logicalWidth = Length(0, Fixed);
3032 else if (logicalWidth.isIntrinsic())
3033 logicalWidth = Length(computeIntrinsicLogicalWidthUsing(logicalWidth, containerLogicalWidth, bordersPlusPadding) - bordersPlusPadding, Fixed);
3035 // 'left' and 'right' cannot both be 'auto' because one would of been
3036 // converted to the static position already
3037 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
3039 // minimumValueForLength will convert 'auto' to 0 so that it doesn't impact the available space computation below.
3040 LayoutUnit logicalLeftValue = minimumValueForLength(logicalLeft, containerLogicalWidth);
3041 LayoutUnit logicalRightValue = minimumValueForLength(logicalRight, containerLogicalWidth);
3043 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false);
3045 bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto();
3046 bool logicalLeftIsAuto = logicalLeft.isAuto();
3047 bool logicalRightIsAuto = logicalRight.isAuto();
3048 LayoutUnit& marginLogicalLeftValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end;
3049 LayoutUnit& marginLogicalRightValue = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start;
3050 if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
3051 /*-----------------------------------------------------------------------*\
3052 * If none of the three is 'auto': If both 'margin-left' and 'margin-
3053 * right' are 'auto', solve the equation under the extra constraint that
3054 * the two margins get equal values, unless this would make them negative,
3055 * in which case when direction of the containing block is 'ltr' ('rtl'),
3056 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
3057 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
3058 * solve the equation for that value. If the values are over-constrained,
3059 * ignore the value for 'left' (in case the 'direction' property of the
3060 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
3061 * and solve for that value.
3062 \*-----------------------------------------------------------------------*/
3063 // NOTE: It is not necessary to solve for 'right' in the over constrained
3064 // case because the value is not used for any further calculations.
3066 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth));
3068 const LayoutUnit availableSpace = containerLogicalWidth - (logicalLeftValue + computedValues.m_extent + logicalRightValue + bordersPlusPadding);
3070 // Margins are now the only unknown
3071 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
3072 // Both margins auto, solve for equality
3073 if (availableSpace >= 0) {
3074 marginLogicalLeftValue = availableSpace / 2; // split the difference
3075 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences
3076 } else {
3077 // Use the containing block's direction rather than the parent block's
3078 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
3079 if (containerDirection == LTR) {
3080 marginLogicalLeftValue = 0;
3081 marginLogicalRightValue = availableSpace; // will be negative
3082 } else {
3083 marginLogicalLeftValue = availableSpace; // will be negative
3084 marginLogicalRightValue = 0;
3087 } else if (marginLogicalLeft.isAuto()) {
3088 // Solve for left margin
3089 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3090 marginLogicalLeftValue = availableSpace - marginLogicalRightValue;
3091 } else if (marginLogicalRight.isAuto()) {
3092 // Solve for right margin
3093 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3094 marginLogicalRightValue = availableSpace - marginLogicalLeftValue;
3095 } else {
3096 // Over-constrained, solve for left if direction is RTL
3097 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3098 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3100 // Use the containing block's direction rather than the parent block's
3101 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
3102 if (containerDirection == RTL)
3103 logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue;
3105 } else {
3106 /*--------------------------------------------------------------------*\
3107 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
3108 * to 0, and pick the one of the following six rules that applies.
3110 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
3111 * width is shrink-to-fit. Then solve for 'left'
3113 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
3114 * ------------------------------------------------------------------
3115 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
3116 * the 'direction' property of the containing block is 'ltr' set
3117 * 'left' to the static position, otherwise set 'right' to the
3118 * static position. Then solve for 'left' (if 'direction is 'rtl')
3119 * or 'right' (if 'direction' is 'ltr').
3120 * ------------------------------------------------------------------
3122 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
3123 * width is shrink-to-fit . Then solve for 'right'
3124 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
3125 * for 'left'
3126 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
3127 * for 'width'
3128 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
3129 * for 'right'
3131 * Calculation of the shrink-to-fit width is similar to calculating the
3132 * width of a table cell using the automatic table layout algorithm.
3133 * Roughly: calculate the preferred width by formatting the content
3134 * without breaking lines other than where explicit line breaks occur,
3135 * and also calculate the preferred minimum width, e.g., by trying all
3136 * possible line breaks. CSS 2.1 does not define the exact algorithm.
3137 * Thirdly, calculate the available width: this is found by solving
3138 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
3139 * to 0.
3141 * Then the shrink-to-fit width is:
3142 * min(max(preferred minimum width, available width), preferred width).
3143 \*--------------------------------------------------------------------*/
3144 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
3145 // because the value is not used for any further calculations.
3147 // Calculate margins, 'auto' margins are ignored.
3148 marginLogicalLeftValue = minimumValueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3149 marginLogicalRightValue = minimumValueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3151 const LayoutUnit availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + logicalLeftValue + logicalRightValue + bordersPlusPadding);
3153 // FIXME: Is there a faster way to find the correct case?
3154 // Use rule/case that applies.
3155 if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
3156 // RULE 1: (use shrink-to-fit for width, and solve of left)
3157 computedValues.m_extent = shrinkToFitLogicalWidth(availableSpace, bordersPlusPadding);
3158 logicalLeftValue = availableSpace - computedValues.m_extent;
3159 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) {
3160 // RULE 3: (use shrink-to-fit for width, and no need solve of right)
3161 computedValues.m_extent = shrinkToFitLogicalWidth(availableSpace, bordersPlusPadding);
3162 } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
3163 // RULE 4: (solve for left)
3164 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth));
3165 logicalLeftValue = availableSpace - computedValues.m_extent;
3166 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
3167 // RULE 5: (solve for width)
3168 if (autoWidthShouldFitContent())
3169 computedValues.m_extent = shrinkToFitLogicalWidth(availableSpace, bordersPlusPadding);
3170 else
3171 computedValues.m_extent = std::max(LayoutUnit(), availableSpace);
3172 } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) {
3173 // RULE 6: (no need solve for right)
3174 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth));
3178 // Use computed values to calculate the horizontal position.
3180 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
3181 // positioned, inline because right now, it is using the logical left position
3182 // of the first line box when really it should use the last line box. When
3183 // this is fixed elsewhere, this block should be removed.
3184 if (containerBlock->isLayoutInline() && !containerBlock->style()->isLeftToRightDirection()) {
3185 const LayoutInline* flow = toLayoutInline(containerBlock);
3186 InlineFlowBox* firstLine = flow->firstLineBox();
3187 InlineFlowBox* lastLine = flow->lastLineBox();
3188 if (firstLine && lastLine && firstLine != lastLine) {
3189 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft());
3190 return;
3194 if (containerBlock->isBox() && toLayoutBox(containerBlock)->scrollsOverflowY() && containerBlock->style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) {
3195 logicalLeftValue = logicalLeftValue + toLayoutBox(containerBlock)->verticalScrollbarWidth();
3198 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue;
3199 computeLogicalLeftPositionedOffset(computedValues.m_position, this, computedValues.m_extent, containerBlock, containerLogicalWidth);
3202 static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom, const LayoutBox* child, const LayoutBoxModelObject* containerBlock)
3204 if (!logicalTop.isAuto() || !logicalBottom.isAuto())
3205 return;
3207 // FIXME: The static distance computation has not been patched for mixed writing modes.
3208 LayoutUnit staticLogicalTop = child->layer()->staticBlockPosition() - containerBlock->borderBefore();
3209 for (LayoutObject* curr = child->parent(); curr && curr != containerBlock; curr = curr->container()) {
3210 if (curr->isBox() && !curr->isTableRow())
3211 staticLogicalTop += toLayoutBox(curr)->logicalTop();
3213 logicalTop.setValue(Fixed, staticLogicalTop);
3216 void LayoutBox::computePositionedLogicalHeight(LogicalExtentComputedValues& computedValues) const
3218 if (isReplaced()) {
3219 computePositionedLogicalHeightReplaced(computedValues);
3220 return;
3223 // The following is based off of the W3C Working Draft from April 11, 2006 of
3224 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
3225 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
3226 // (block-style-comments in this function and in computePositionedLogicalHeightUsing()
3227 // correspond to text from the spec)
3230 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
3231 const LayoutBoxModelObject* containerBlock = toLayoutBoxModelObject(container());
3233 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
3235 const ComputedStyle& styleToUse = styleRef();
3236 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
3237 const Length marginBefore = styleToUse.marginBefore();
3238 const Length marginAfter = styleToUse.marginAfter();
3239 Length logicalTopLength = styleToUse.logicalTop();
3240 Length logicalBottomLength = styleToUse.logicalBottom();
3242 /*---------------------------------------------------------------------------*\
3243 * For the purposes of this section and the next, the term "static position"
3244 * (of an element) refers, roughly, to the position an element would have had
3245 * in the normal flow. More precisely, the static position for 'top' is the
3246 * distance from the top edge of the containing block to the top margin edge
3247 * of a hypothetical box that would have been the first box of the element if
3248 * its 'position' property had been 'static' and 'float' had been 'none'. The
3249 * value is negative if the hypothetical box is above the containing block.
3251 * But rather than actually calculating the dimensions of that hypothetical
3252 * box, user agents are free to make a guess at its probable position.
3254 * For the purposes of calculating the static position, the containing block
3255 * of fixed positioned elements is the initial containing block instead of
3256 * the viewport.
3257 \*---------------------------------------------------------------------------*/
3259 // see FIXME 1
3260 // Calculate the static distance if needed.
3261 computeBlockStaticDistance(logicalTopLength, logicalBottomLength, this, containerBlock);
3263 // Calculate constraint equation values for 'height' case.
3264 LayoutUnit logicalHeight = computedValues.m_extent;
3265 computePositionedLogicalHeightUsing(MainOrPreferredSize, styleToUse.logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
3266 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
3267 computedValues);
3269 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
3270 // see FIXME 2
3272 // Calculate constraint equation values for 'max-height' case.
3273 if (!styleToUse.logicalMaxHeight().isMaxSizeNone()) {
3274 LogicalExtentComputedValues maxValues;
3276 computePositionedLogicalHeightUsing(MaxSize, styleToUse.logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
3277 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
3278 maxValues);
3280 if (computedValues.m_extent > maxValues.m_extent) {
3281 computedValues.m_extent = maxValues.m_extent;
3282 computedValues.m_position = maxValues.m_position;
3283 computedValues.m_margins.m_before = maxValues.m_margins.m_before;
3284 computedValues.m_margins.m_after = maxValues.m_margins.m_after;
3288 // Calculate constraint equation values for 'min-height' case.
3289 if (!styleToUse.logicalMinHeight().isZero() || styleToUse.logicalMinHeight().isIntrinsic()) {
3290 LogicalExtentComputedValues minValues;
3292 computePositionedLogicalHeightUsing(MinSize, styleToUse.logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
3293 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
3294 minValues);
3296 if (computedValues.m_extent < minValues.m_extent) {
3297 computedValues.m_extent = minValues.m_extent;
3298 computedValues.m_position = minValues.m_position;
3299 computedValues.m_margins.m_before = minValues.m_margins.m_before;
3300 computedValues.m_margins.m_after = minValues.m_margins.m_after;
3304 if (!style()->hasStaticBlockPosition(isHorizontalWritingMode()))
3305 computedValues.m_position += extraBlockOffset();
3307 // Set final height value.
3308 computedValues.m_extent += bordersPlusPadding;
3311 static void computeLogicalTopPositionedOffset(LayoutUnit& logicalTopPos, const LayoutBox* child, LayoutUnit logicalHeightValue, const LayoutBoxModelObject* containerBlock, LayoutUnit containerLogicalHeight)
3313 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
3314 // 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.
3315 if ((child->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() != containerBlock->isHorizontalWritingMode())
3316 || (child->style()->isFlippedBlocksWritingMode() != containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()))
3317 logicalTopPos = containerLogicalHeight - logicalHeightValue - logicalTopPos;
3319 // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt.
3320 if (containerBlock->style()->isFlippedBlocksWritingMode() && child->isHorizontalWritingMode() == containerBlock->isHorizontalWritingMode()) {
3321 if (child->isHorizontalWritingMode())
3322 logicalTopPos += containerBlock->borderBottom();
3323 else
3324 logicalTopPos += containerBlock->borderRight();
3325 } else {
3326 if (child->isHorizontalWritingMode())
3327 logicalTopPos += containerBlock->borderTop();
3328 else
3329 logicalTopPos += containerBlock->borderLeft();
3333 void LayoutBox::computePositionedLogicalHeightUsing(SizeType heightSizeType, Length logicalHeightLength, const LayoutBoxModelObject* containerBlock,
3334 LayoutUnit containerLogicalHeight, LayoutUnit bordersPlusPadding, LayoutUnit logicalHeight,
3335 const Length& logicalTop, const Length& logicalBottom, const Length& marginBefore,
3336 const Length& marginAfter, LogicalExtentComputedValues& computedValues) const
3338 ASSERT(heightSizeType == MinSize || heightSizeType == MainOrPreferredSize || !logicalHeightLength.isAuto());
3339 if (heightSizeType == MinSize && logicalHeightLength.isAuto())
3340 logicalHeightLength = Length(0, Fixed);
3342 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
3343 // converted to the static position in computePositionedLogicalHeight()
3344 ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto()));
3346 LayoutUnit logicalHeightValue;
3347 LayoutUnit contentLogicalHeight = logicalHeight - bordersPlusPadding;
3349 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false);
3351 LayoutUnit logicalTopValue = 0;
3353 bool logicalHeightIsAuto = logicalHeightLength.isAuto();
3354 bool logicalTopIsAuto = logicalTop.isAuto();
3355 bool logicalBottomIsAuto = logicalBottom.isAuto();
3357 LayoutUnit resolvedLogicalHeight;
3358 // Height is never unsolved for tables.
3359 if (isTable()) {
3360 resolvedLogicalHeight = contentLogicalHeight;
3361 logicalHeightIsAuto = false;
3362 } else {
3363 if (logicalHeightLength.isIntrinsic())
3364 resolvedLogicalHeight = computeIntrinsicLogicalContentHeightUsing(logicalHeightLength, contentLogicalHeight, bordersPlusPadding);
3365 else
3366 resolvedLogicalHeight = adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeightLength, containerLogicalHeight));
3369 if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
3370 /*-----------------------------------------------------------------------*\
3371 * If none of the three are 'auto': If both 'margin-top' and 'margin-
3372 * bottom' are 'auto', solve the equation under the extra constraint that
3373 * the two margins get equal values. If one of 'margin-top' or 'margin-
3374 * bottom' is 'auto', solve the equation for that value. If the values
3375 * are over-constrained, ignore the value for 'bottom' and solve for that
3376 * value.
3377 \*-----------------------------------------------------------------------*/
3378 // NOTE: It is not necessary to solve for 'bottom' in the over constrained
3379 // case because the value is not used for any further calculations.
3381 logicalHeightValue = resolvedLogicalHeight;
3382 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3384 const LayoutUnit availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight) + bordersPlusPadding);
3386 // Margins are now the only unknown
3387 if (marginBefore.isAuto() && marginAfter.isAuto()) {
3388 // Both margins auto, solve for equality
3389 // NOTE: This may result in negative values.
3390 computedValues.m_margins.m_before = availableSpace / 2; // split the difference
3391 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; // account for odd valued differences
3392 } else if (marginBefore.isAuto()) {
3393 // Solve for top margin
3394 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth);
3395 computedValues.m_margins.m_before = availableSpace - computedValues.m_margins.m_after;
3396 } else if (marginAfter.isAuto()) {
3397 // Solve for bottom margin
3398 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth);
3399 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before;
3400 } else {
3401 // Over-constrained, (no need solve for bottom)
3402 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth);
3403 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth);
3405 } else {
3406 /*--------------------------------------------------------------------*\
3407 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
3408 * to 0, and pick the one of the following six rules that applies.
3410 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
3411 * the height is based on the content, and solve for 'top'.
3413 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
3414 * ------------------------------------------------------------------
3415 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
3416 * set 'top' to the static position, and solve for 'bottom'.
3417 * ------------------------------------------------------------------
3419 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
3420 * the height is based on the content, and solve for 'bottom'.
3421 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
3422 * solve for 'top'.
3423 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
3424 * solve for 'height'.
3425 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
3426 * solve for 'bottom'.
3427 \*--------------------------------------------------------------------*/
3428 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
3429 // because the value is not used for any further calculations.
3431 // Calculate margins, 'auto' margins are ignored.
3432 computedValues.m_margins.m_before = minimumValueForLength(marginBefore, containerRelativeLogicalWidth);
3433 computedValues.m_margins.m_after = minimumValueForLength(marginAfter, containerRelativeLogicalWidth);
3435 const LayoutUnit availableSpace = containerLogicalHeight - (computedValues.m_margins.m_before + computedValues.m_margins.m_after + bordersPlusPadding);
3437 // Use rule/case that applies.
3438 if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
3439 // RULE 1: (height is content based, solve of top)
3440 logicalHeightValue = contentLogicalHeight;
3441 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight));
3442 } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) {
3443 // RULE 3: (height is content based, no need solve of bottom)
3444 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3445 logicalHeightValue = contentLogicalHeight;
3446 } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
3447 // RULE 4: (solve of top)
3448 logicalHeightValue = resolvedLogicalHeight;
3449 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight));
3450 } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
3451 // RULE 5: (solve of height)
3452 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3453 logicalHeightValue = std::max(LayoutUnit(), availableSpace - (logicalTopValue + valueForLength(logicalBottom, containerLogicalHeight)));
3454 } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) {
3455 // RULE 6: (no need solve of bottom)
3456 logicalHeightValue = resolvedLogicalHeight;
3457 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3460 computedValues.m_extent = logicalHeightValue;
3462 // Use computed values to calculate the vertical position.
3463 computedValues.m_position = logicalTopValue + computedValues.m_margins.m_before;
3464 computeLogicalTopPositionedOffset(computedValues.m_position, this, logicalHeightValue, containerBlock, containerLogicalHeight);
3467 void LayoutBox::computePositionedLogicalWidthReplaced(LogicalExtentComputedValues& computedValues) const
3469 // The following is based off of the W3C Working Draft from April 11, 2006 of
3470 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements"
3471 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
3472 // (block-style-comments in this function correspond to text from the spec and
3473 // the numbers correspond to numbers in spec)
3475 // We don't use containingBlock(), since we may be positioned by an enclosing
3476 // relative positioned inline.
3477 const LayoutBoxModelObject* containerBlock = toLayoutBoxModelObject(container());
3479 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock);
3480 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false);
3482 // To match WinIE, in quirks mode use the parent's 'direction' property
3483 // instead of the the container block's.
3484 TextDirection containerDirection = containerBlock->style()->direction();
3486 // Variables to solve.
3487 bool isHorizontal = isHorizontalWritingMode();
3488 Length logicalLeft = style()->logicalLeft();
3489 Length logicalRight = style()->logicalRight();
3490 Length marginLogicalLeft = isHorizontal ? style()->marginLeft() : style()->marginTop();
3491 Length marginLogicalRight = isHorizontal ? style()->marginRight() : style()->marginBottom();
3492 LayoutUnit& marginLogicalLeftAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end;
3493 LayoutUnit& marginLogicalRightAlias = style()->isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start;
3495 /*-----------------------------------------------------------------------*\
3496 * 1. The used value of 'width' is determined as for inline replaced
3497 * elements.
3498 \*-----------------------------------------------------------------------*/
3499 // NOTE: This value of width is final in that the min/max width calculations
3500 // are dealt with in computeReplacedWidth(). This means that the steps to produce
3501 // correct max/min in the non-replaced version, are not necessary.
3502 computedValues.m_extent = computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth();
3504 const LayoutUnit availableSpace = containerLogicalWidth - computedValues.m_extent;
3506 /*-----------------------------------------------------------------------*\
3507 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
3508 * of the containing block is 'ltr', set 'left' to the static position;
3509 * else if 'direction' is 'rtl', set 'right' to the static position.
3510 \*-----------------------------------------------------------------------*/
3511 // see FIXME 1
3512 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth);
3514 /*-----------------------------------------------------------------------*\
3515 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
3516 * or 'margin-right' with '0'.
3517 \*-----------------------------------------------------------------------*/
3518 if (logicalLeft.isAuto() || logicalRight.isAuto()) {
3519 if (marginLogicalLeft.isAuto())
3520 marginLogicalLeft.setValue(Fixed, 0);
3521 if (marginLogicalRight.isAuto())
3522 marginLogicalRight.setValue(Fixed, 0);
3525 /*-----------------------------------------------------------------------*\
3526 * 4. If at this point both 'margin-left' and 'margin-right' are still
3527 * 'auto', solve the equation under the extra constraint that the two
3528 * margins must get equal values, unless this would make them negative,
3529 * in which case when the direction of the containing block is 'ltr'
3530 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
3531 * 'margin-right' ('margin-left').
3532 \*-----------------------------------------------------------------------*/
3533 LayoutUnit logicalLeftValue = 0;
3534 LayoutUnit logicalRightValue = 0;
3536 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
3537 // 'left' and 'right' cannot be 'auto' due to step 3
3538 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
3540 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3541 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3543 LayoutUnit difference = availableSpace - (logicalLeftValue + logicalRightValue);
3544 if (difference > 0) {
3545 marginLogicalLeftAlias = difference / 2; // split the difference
3546 marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences
3547 } else {
3548 // Use the containing block's direction rather than the parent block's
3549 // per CSS 2.1 reference test abspos-replaced-width-margin-000.
3550 if (containerDirection == LTR) {
3551 marginLogicalLeftAlias = 0;
3552 marginLogicalRightAlias = difference; // will be negative
3553 } else {
3554 marginLogicalLeftAlias = difference; // will be negative
3555 marginLogicalRightAlias = 0;
3559 /*-----------------------------------------------------------------------*\
3560 * 5. If at this point there is an 'auto' left, solve the equation for
3561 * that value.
3562 \*-----------------------------------------------------------------------*/
3563 } else if (logicalLeft.isAuto()) {
3564 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3565 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3566 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3568 // Solve for 'left'
3569 logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias);
3570 } else if (logicalRight.isAuto()) {
3571 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3572 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3573 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3575 // Solve for 'right'
3576 logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias);
3577 } else if (marginLogicalLeft.isAuto()) {
3578 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3579 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3580 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3582 // Solve for 'margin-left'
3583 marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias);
3584 } else if (marginLogicalRight.isAuto()) {
3585 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3586 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3587 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3589 // Solve for 'margin-right'
3590 marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias);
3591 } else {
3592 // Nothing is 'auto', just calculate the values.
3593 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
3594 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
3595 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
3596 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
3597 // If the containing block is right-to-left, then push the left position as far to the right as possible
3598 if (containerDirection == RTL) {
3599 int totalLogicalWidth = computedValues.m_extent + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias;
3600 logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue);
3604 /*-----------------------------------------------------------------------*\
3605 * 6. If at this point the values are over-constrained, ignore the value
3606 * for either 'left' (in case the 'direction' property of the
3607 * containing block is 'rtl') or 'right' (in case 'direction' is
3608 * 'ltr') and solve for that value.
3609 \*-----------------------------------------------------------------------*/
3610 // NOTE: Constraints imposed by the width of the containing block and its content have already been accounted for above.
3612 // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that
3613 // can make the result here rather complicated to compute.
3615 // Use computed values to calculate the horizontal position.
3617 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
3618 // positioned, inline containing block because right now, it is using the logical left position
3619 // of the first line box when really it should use the last line box. When
3620 // this is fixed elsewhere, this block should be removed.
3621 if (containerBlock->isLayoutInline() && !containerBlock->style()->isLeftToRightDirection()) {
3622 const LayoutInline* flow = toLayoutInline(containerBlock);
3623 InlineFlowBox* firstLine = flow->firstLineBox();
3624 InlineFlowBox* lastLine = flow->lastLineBox();
3625 if (firstLine && lastLine && firstLine != lastLine) {
3626 computedValues.m_position = logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft());
3627 return;
3631 LayoutUnit logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias;
3632 computeLogicalLeftPositionedOffset(logicalLeftPos, this, computedValues.m_extent, containerBlock, containerLogicalWidth);
3633 computedValues.m_position = logicalLeftPos;
3636 void LayoutBox::computePositionedLogicalHeightReplaced(LogicalExtentComputedValues& computedValues) const
3638 // The following is based off of the W3C Working Draft from April 11, 2006 of
3639 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements"
3640 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
3641 // (block-style-comments in this function correspond to text from the spec and
3642 // the numbers correspond to numbers in spec)
3644 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
3645 const LayoutBoxModelObject* containerBlock = toLayoutBoxModelObject(container());
3647 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
3648 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, false);
3650 // Variables to solve.
3651 Length marginBefore = style()->marginBefore();
3652 Length marginAfter = style()->marginAfter();
3653 LayoutUnit& marginBeforeAlias = computedValues.m_margins.m_before;
3654 LayoutUnit& marginAfterAlias = computedValues.m_margins.m_after;
3656 Length logicalTop = style()->logicalTop();
3657 Length logicalBottom = style()->logicalBottom();
3659 /*-----------------------------------------------------------------------*\
3660 * 1. The used value of 'height' is determined as for inline replaced
3661 * elements.
3662 \*-----------------------------------------------------------------------*/
3663 // NOTE: This value of height is final in that the min/max height calculations
3664 // are dealt with in computeReplacedHeight(). This means that the steps to produce
3665 // correct max/min in the non-replaced version, are not necessary.
3666 computedValues.m_extent = computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight();
3667 const LayoutUnit availableSpace = containerLogicalHeight - computedValues.m_extent;
3669 /*-----------------------------------------------------------------------*\
3670 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
3671 * with the element's static position.
3672 \*-----------------------------------------------------------------------*/
3673 // see FIXME 1
3674 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock);
3676 /*-----------------------------------------------------------------------*\
3677 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
3678 * 'margin-bottom' with '0'.
3679 \*-----------------------------------------------------------------------*/
3680 // FIXME: The spec. says that this step should only be taken when bottom is
3681 // auto, but if only top is auto, this makes step 4 impossible.
3682 if (logicalTop.isAuto() || logicalBottom.isAuto()) {
3683 if (marginBefore.isAuto())
3684 marginBefore.setValue(Fixed, 0);
3685 if (marginAfter.isAuto())
3686 marginAfter.setValue(Fixed, 0);
3689 /*-----------------------------------------------------------------------*\
3690 * 4. If at this point both 'margin-top' and 'margin-bottom' are still
3691 * 'auto', solve the equation under the extra constraint that the two
3692 * margins must get equal values.
3693 \*-----------------------------------------------------------------------*/
3694 LayoutUnit logicalTopValue = 0;
3695 LayoutUnit logicalBottomValue = 0;
3697 if (marginBefore.isAuto() && marginAfter.isAuto()) {
3698 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined.
3699 ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto()));
3701 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3702 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
3704 LayoutUnit difference = availableSpace - (logicalTopValue + logicalBottomValue);
3705 // NOTE: This may result in negative values.
3706 marginBeforeAlias = difference / 2; // split the difference
3707 marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences
3709 /*-----------------------------------------------------------------------*\
3710 * 5. If at this point there is only one 'auto' left, solve the equation
3711 * for that value.
3712 \*-----------------------------------------------------------------------*/
3713 } else if (logicalTop.isAuto()) {
3714 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
3715 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
3716 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
3718 // Solve for 'top'
3719 logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias);
3720 } else if (logicalBottom.isAuto()) {
3721 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
3722 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
3723 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3725 // Solve for 'bottom'
3726 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
3727 // use the value.
3728 } else if (marginBefore.isAuto()) {
3729 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
3730 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3731 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
3733 // Solve for 'margin-top'
3734 marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias);
3735 } else if (marginAfter.isAuto()) {
3736 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
3737 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3738 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
3740 // Solve for 'margin-bottom'
3741 marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias);
3742 } else {
3743 // Nothing is 'auto', just calculate the values.
3744 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
3745 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
3746 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
3747 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
3748 // use the value.
3751 /*-----------------------------------------------------------------------*\
3752 * 6. If at this point the values are over-constrained, ignore the value
3753 * for 'bottom' and solve for that value.
3754 \*-----------------------------------------------------------------------*/
3755 // NOTE: It is not necessary to do this step because we don't end up using
3756 // the value of 'bottom' regardless of whether the values are over-constrained
3757 // or not.
3759 // Use computed values to calculate the vertical position.
3760 LayoutUnit logicalTopPos = logicalTopValue + marginBeforeAlias;
3761 computeLogicalTopPositionedOffset(logicalTopPos, this, computedValues.m_extent, containerBlock, containerLogicalHeight);
3762 computedValues.m_position = logicalTopPos;
3765 LayoutRect LayoutBox::localCaretRect(InlineBox* box, int caretOffset, LayoutUnit* extraWidthToEndOfLine)
3767 // VisiblePositions at offsets inside containers either a) refer to the positions before/after
3768 // those containers (tables and select elements) or b) refer to the position inside an empty block.
3769 // They never refer to children.
3770 // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements.
3772 LayoutRect rect(location(), LayoutSize(caretWidth(), size().height()));
3773 bool ltr = box ? box->isLeftToRightDirection() : style()->isLeftToRightDirection();
3775 if ((!caretOffset) ^ ltr)
3776 rect.move(LayoutSize(size().width() - caretWidth(), 0));
3778 if (box) {
3779 RootInlineBox& rootBox = box->root();
3780 LayoutUnit top = rootBox.lineTop();
3781 rect.setY(top);
3782 rect.setHeight(rootBox.lineBottom() - top);
3785 // If height of box is smaller than font height, use the latter one,
3786 // otherwise the caret might become invisible.
3788 // Also, if the box is not a replaced element, always use the font height.
3789 // This prevents the "big caret" bug described in:
3790 // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point
3792 // FIXME: ignoring :first-line, missing good reason to take care of
3793 LayoutUnit fontHeight = style()->fontMetrics().height();
3794 if (fontHeight > rect.height() || (!isReplaced() && !isTable()))
3795 rect.setHeight(fontHeight);
3797 if (extraWidthToEndOfLine)
3798 *extraWidthToEndOfLine = location().x() + size().width() - rect.maxX();
3800 // Move to local coords
3801 rect.moveBy(-location());
3803 // FIXME: Border/padding should be added for all elements but this workaround
3804 // is needed because we use offsets inside an "atomic" element to represent
3805 // positions before and after the element in deprecated editing offsets.
3806 if (node() && !(editingIgnoresContent(node()) || isRenderedTableElement(node()))) {
3807 rect.setX(rect.x() + borderLeft() + paddingLeft());
3808 rect.setY(rect.y() + paddingTop() + borderTop());
3811 if (!isHorizontalWritingMode())
3812 return rect.transposedRect();
3814 return rect;
3817 PositionWithAffinity LayoutBox::positionForPoint(const LayoutPoint& point)
3819 // no children...return this layout object's element, if there is one, and offset 0
3820 LayoutObject* firstChild = slowFirstChild();
3821 if (!firstChild)
3822 return createPositionWithAffinity(nonPseudoNode() ? firstPositionInOrBeforeNode(nonPseudoNode()) : Position());
3824 if (isTable() && nonPseudoNode()) {
3825 LayoutUnit right = size().width() - verticalScrollbarWidth();
3826 LayoutUnit bottom = size().height() - horizontalScrollbarHeight();
3828 if (point.x() < 0 || point.x() > right || point.y() < 0 || point.y() > bottom) {
3829 if (point.x() <= right / 2)
3830 return createPositionWithAffinity(firstPositionInOrBeforeNode(nonPseudoNode()));
3831 return createPositionWithAffinity(lastPositionInOrAfterNode(nonPseudoNode()));
3835 // Pass off to the closest child.
3836 LayoutUnit minDist = LayoutUnit::max();
3837 LayoutBox* closestLayoutObject = nullptr;
3838 LayoutPoint adjustedPoint = point;
3839 if (isTableRow())
3840 adjustedPoint.moveBy(location());
3842 for (LayoutObject* layoutObject = firstChild; layoutObject; layoutObject = layoutObject->nextSibling()) {
3843 if ((!layoutObject->slowFirstChild() && !layoutObject->isInline() && !layoutObject->isLayoutBlockFlow() )
3844 || layoutObject->style()->visibility() != VISIBLE)
3845 continue;
3847 if (!layoutObject->isBox())
3848 continue;
3850 LayoutBox* layoutBox = toLayoutBox(layoutObject);
3852 LayoutUnit top = layoutBox->borderTop() + layoutBox->paddingTop() + (isTableRow() ? LayoutUnit() : layoutBox->location().y());
3853 LayoutUnit bottom = top + layoutBox->contentHeight();
3854 LayoutUnit left = layoutBox->borderLeft() + layoutBox->paddingLeft() + (isTableRow() ? LayoutUnit() : layoutBox->location().x());
3855 LayoutUnit right = left + layoutBox->contentWidth();
3857 if (point.x() <= right && point.x() >= left && point.y() <= top && point.y() >= bottom) {
3858 if (layoutBox->isTableRow())
3859 return layoutBox->positionForPoint(point + adjustedPoint - layoutBox->locationOffset());
3860 return layoutBox->positionForPoint(point - layoutBox->locationOffset());
3863 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces
3864 // and use a different compare depending on which piece (x, y) is in.
3865 LayoutPoint cmp;
3866 if (point.x() > right) {
3867 if (point.y() < top)
3868 cmp = LayoutPoint(right, top);
3869 else if (point.y() > bottom)
3870 cmp = LayoutPoint(right, bottom);
3871 else
3872 cmp = LayoutPoint(right, point.y());
3873 } else if (point.x() < left) {
3874 if (point.y() < top)
3875 cmp = LayoutPoint(left, top);
3876 else if (point.y() > bottom)
3877 cmp = LayoutPoint(left, bottom);
3878 else
3879 cmp = LayoutPoint(left, point.y());
3880 } else {
3881 if (point.y() < top)
3882 cmp = LayoutPoint(point.x(), top);
3883 else
3884 cmp = LayoutPoint(point.x(), bottom);
3887 LayoutSize difference = cmp - point;
3889 LayoutUnit dist = difference.width() * difference.width() + difference.height() * difference.height();
3890 if (dist < minDist) {
3891 closestLayoutObject = layoutBox;
3892 minDist = dist;
3896 if (closestLayoutObject)
3897 return closestLayoutObject->positionForPoint(adjustedPoint - closestLayoutObject->locationOffset());
3898 return createPositionWithAffinity(firstPositionInOrBeforeNode(nonPseudoNode()));
3901 bool LayoutBox::shrinkToAvoidFloats() const
3903 // Floating objects don't shrink. Objects that don't avoid floats don't shrink.
3904 if (isInline() || !avoidsFloats() || isFloating())
3905 return false;
3907 // Only auto width objects can possibly shrink to avoid floats.
3908 return style()->width().isAuto();
3911 static bool isReplacedElement(Node* node)
3913 // Checkboxes and radioboxes are not isReplaced() nor do they have their own layoutObject in which to override avoidFloats().
3914 return node && node->isElementNode() && (toElement(node)->isFormControlElement() || isHTMLImageElement(toElement(node)));
3917 bool LayoutBox::avoidsFloats() const
3919 return isReplaced() || isReplacedElement(node()) || hasOverflowClip() || isHR() || isLegend() || isWritingModeRoot() || isFlexItemIncludingDeprecated();
3922 bool LayoutBox::hasNonCompositedScrollbars() const
3924 if (DeprecatedPaintLayer* layer = this->layer()) {
3925 if (DeprecatedPaintLayerScrollableArea* scrollableArea = layer->scrollableArea()) {
3926 if (scrollableArea->hasHorizontalScrollbar() && !scrollableArea->layerForHorizontalScrollbar())
3927 return true;
3928 if (scrollableArea->hasVerticalScrollbar() && !scrollableArea->layerForVerticalScrollbar())
3929 return true;
3932 return false;
3935 PaintInvalidationReason LayoutBox::paintInvalidationReason(const LayoutBoxModelObject& paintInvalidationContainer,
3936 const LayoutRect& oldBounds, const LayoutPoint& oldLocation, const LayoutRect& newBounds, const LayoutPoint& newLocation) const
3938 PaintInvalidationReason invalidationReason = LayoutBoxModelObject::paintInvalidationReason(paintInvalidationContainer, oldBounds, oldLocation, newBounds, newLocation);
3939 if (isFullPaintInvalidationReason(invalidationReason))
3940 return invalidationReason;
3942 if (isLayoutView()) {
3943 const LayoutView* layoutView = toLayoutView(this);
3944 // In normal compositing mode, root background doesn't need to be invalidated for
3945 // box changes, because the background always covers the whole document rect
3946 // and clipping is done by compositor()->m_containerLayer. Also the scrollbars
3947 // are always composited. There are no other box decoration on the LayoutView thus
3948 // we can safely exit here.
3949 if (layoutView->usesCompositing() && (!document().settings() || !document().settings()->rootLayerScrolls()))
3950 return invalidationReason;
3953 // If the transform is not identity or translation, incremental invalidation is not applicable
3954 // because the difference between oldBounds and newBounds doesn't cover all area needing invalidation.
3955 // FIXME: Should also consider ancestor transforms since paintInvalidationContainer. crbug.com/426111.
3956 if (invalidationReason == PaintInvalidationIncremental
3957 && paintInvalidationContainer != this
3958 && hasLayer() && layer()->transform() && !layer()->transform()->isIdentityOrTranslation())
3959 return PaintInvalidationBoundsChange;
3961 if (!style()->hasBackground() && !style()->hasBoxDecorations()) {
3962 // We could let incremental invalidation cover non-composited scrollbars, but just
3963 // do a full invalidation because incremental invalidation will go away with slimming paint.
3964 if (invalidationReason == PaintInvalidationIncremental && hasNonCompositedScrollbars())
3965 return PaintInvalidationBorderBoxChange;
3966 return invalidationReason;
3969 if (style()->backgroundLayers().thisOrNextLayersUseContentBox() || style()->maskLayers().thisOrNextLayersUseContentBox()) {
3970 LayoutRect oldContentBoxRect = m_rareData ? m_rareData->m_previousContentBoxRect : LayoutRect();
3971 LayoutRect newContentBoxRect = contentBoxRect();
3972 if (oldContentBoxRect != newContentBoxRect)
3973 return PaintInvalidationContentBoxChange;
3976 if (style()->backgroundLayers().thisOrNextLayersHaveLocalAttachment()) {
3977 LayoutRect oldLayoutOverflowRect = m_rareData ? m_rareData->m_previousLayoutOverflowRect : LayoutRect();
3978 LayoutRect newLayoutOverflowRect = layoutOverflowRect();
3979 if (oldLayoutOverflowRect != newLayoutOverflowRect)
3980 return PaintInvalidationLayoutOverflowBoxChange;
3983 LayoutSize oldBorderBoxSize = computePreviousBorderBoxSize(oldBounds.size());
3984 LayoutSize newBorderBoxSize = size();
3986 if (oldBorderBoxSize == newBorderBoxSize)
3987 return invalidationReason;
3989 // See another hasNonCompositedScrollbars() callsite above.
3990 if (hasNonCompositedScrollbars())
3991 return PaintInvalidationBorderBoxChange;
3993 if (style()->hasVisualOverflowingEffect() || style()->hasAppearance() || style()->hasFilter() || style()->resize() != RESIZE_NONE)
3994 return PaintInvalidationBorderBoxChange;
3996 if (style()->hasBorderRadius()) {
3997 // If a border-radius exists and width/height is smaller than radius width/height,
3998 // we need to fully invalidate to cover the changed radius.
3999 FloatRoundedRect oldRoundedRect = style()->getRoundedBorderFor(LayoutRect(LayoutPoint(0, 0), oldBorderBoxSize));
4000 FloatRoundedRect newRoundedRect = style()->getRoundedBorderFor(LayoutRect(LayoutPoint(0, 0), newBorderBoxSize));
4001 if (oldRoundedRect.radii() != newRoundedRect.radii())
4002 return PaintInvalidationBorderBoxChange;
4005 if (oldBorderBoxSize.width() != newBorderBoxSize.width() && mustInvalidateBackgroundOrBorderPaintOnWidthChange())
4006 return PaintInvalidationBorderBoxChange;
4007 if (oldBorderBoxSize.height() != newBorderBoxSize.height() && mustInvalidateBackgroundOrBorderPaintOnHeightChange())
4008 return PaintInvalidationBorderBoxChange;
4010 return PaintInvalidationIncremental;
4013 void LayoutBox::incrementallyInvalidatePaint(const LayoutBoxModelObject& paintInvalidationContainer, const LayoutRect& oldBounds, const LayoutRect& newBounds, const LayoutPoint& positionFromPaintInvalidationBacking)
4015 LayoutObject::incrementallyInvalidatePaint(paintInvalidationContainer, oldBounds, newBounds, positionFromPaintInvalidationBacking);
4017 bool hasBoxDecorations = style()->hasBoxDecorations();
4018 if (!style()->hasBackground() && !hasBoxDecorations)
4019 return;
4021 LayoutSize oldBorderBoxSize = computePreviousBorderBoxSize(oldBounds.size());
4022 LayoutSize newBorderBoxSize = size();
4024 // If border box size didn't change, LayoutObject's incrementallyInvalidatePaint() is good.
4025 if (oldBorderBoxSize == newBorderBoxSize)
4026 return;
4028 // If size of the paint invalidation rect equals to size of border box, LayoutObject::incrementallyInvalidatePaint()
4029 // is good for boxes having background without box decorations.
4030 ASSERT(oldBounds.location() == newBounds.location()); // Otherwise we won't do incremental invalidation.
4031 if (!hasBoxDecorations
4032 && positionFromPaintInvalidationBacking == newBounds.location()
4033 && oldBorderBoxSize == oldBounds.size()
4034 && newBorderBoxSize == newBounds.size())
4035 return;
4037 // Invalidate the right delta part and the right border of the old or new box which has smaller width.
4038 LayoutUnit deltaWidth = absoluteValue(oldBorderBoxSize.width() - newBorderBoxSize.width());
4039 if (deltaWidth) {
4040 LayoutUnit smallerWidth = std::min(oldBorderBoxSize.width(), newBorderBoxSize.width());
4041 LayoutUnit borderTopRightRadiusWidth = valueForLength(style()->borderTopRightRadius().width(), smallerWidth);
4042 LayoutUnit borderBottomRightRadiusWidth = valueForLength(style()->borderBottomRightRadius().width(), smallerWidth);
4043 LayoutUnit borderWidth = std::max<LayoutUnit>(borderRight(), std::max(borderTopRightRadiusWidth, borderBottomRightRadiusWidth));
4044 LayoutRect rightDeltaRect(positionFromPaintInvalidationBacking.x() + smallerWidth - borderWidth,
4045 positionFromPaintInvalidationBacking.y(),
4046 deltaWidth + borderWidth,
4047 std::max(oldBorderBoxSize.height(), newBorderBoxSize.height()));
4048 invalidatePaintRectClippedByOldAndNewBounds(paintInvalidationContainer, rightDeltaRect, oldBounds, newBounds);
4051 // Invalidate the bottom delta part and the bottom border of the old or new box which has smaller height.
4052 LayoutUnit deltaHeight = absoluteValue(oldBorderBoxSize.height() - newBorderBoxSize.height());
4053 if (deltaHeight) {
4054 LayoutUnit smallerHeight = std::min(oldBorderBoxSize.height(), newBorderBoxSize.height());
4055 LayoutUnit borderBottomLeftRadiusHeight = valueForLength(style()->borderBottomLeftRadius().height(), smallerHeight);
4056 LayoutUnit borderBottomRightRadiusHeight = valueForLength(style()->borderBottomRightRadius().height(), smallerHeight);
4057 LayoutUnit borderHeight = std::max<LayoutUnit>(borderBottom(), std::max(borderBottomLeftRadiusHeight, borderBottomRightRadiusHeight));
4058 LayoutRect bottomDeltaRect(positionFromPaintInvalidationBacking.x(),
4059 positionFromPaintInvalidationBacking.y() + smallerHeight - borderHeight,
4060 std::max(oldBorderBoxSize.width(), newBorderBoxSize.width()),
4061 deltaHeight + borderHeight);
4062 invalidatePaintRectClippedByOldAndNewBounds(paintInvalidationContainer, bottomDeltaRect, oldBounds, newBounds);
4066 void LayoutBox::invalidatePaintRectClippedByOldAndNewBounds(const LayoutBoxModelObject& paintInvalidationContainer, const LayoutRect& rect, const LayoutRect& oldBounds, const LayoutRect& newBounds)
4068 if (rect.isEmpty())
4069 return;
4070 LayoutRect rectClippedByOldBounds = intersection(rect, oldBounds);
4071 LayoutRect rectClippedByNewBounds = intersection(rect, newBounds);
4072 // Invalidate only once if the clipped rects equal.
4073 if (rectClippedByOldBounds == rectClippedByNewBounds) {
4074 invalidatePaintUsingContainer(paintInvalidationContainer, rectClippedByOldBounds, PaintInvalidationIncremental);
4075 return;
4077 // Invalidate the bigger one if one contains another. Otherwise invalidate both.
4078 if (!rectClippedByNewBounds.contains(rectClippedByOldBounds))
4079 invalidatePaintUsingContainer(paintInvalidationContainer, rectClippedByOldBounds, PaintInvalidationIncremental);
4080 if (!rectClippedByOldBounds.contains(rectClippedByNewBounds))
4081 invalidatePaintUsingContainer(paintInvalidationContainer, rectClippedByNewBounds, PaintInvalidationIncremental);
4084 void LayoutBox::markForPaginationRelayoutIfNeeded(SubtreeLayoutScope& layoutScope)
4086 ASSERT(!needsLayout());
4087 // If fragmentation height has changed, we need to lay out. No need to enter the layoutObject if it
4088 // is childless, though.
4089 if (view()->layoutState()->pageLogicalHeightChanged() && slowFirstChild())
4090 layoutScope.setChildNeedsLayout(this);
4093 void LayoutBox::addVisualEffectOverflow()
4095 if (!style()->hasVisualOverflowingEffect())
4096 return;
4098 // Add in the final overflow with shadows, outsets and outline combined.
4099 LayoutRect visualEffectOverflow = borderBoxRect();
4100 visualEffectOverflow.expand(computeVisualEffectOverflowOutsets());
4101 addVisualOverflow(visualEffectOverflow);
4104 LayoutRectOutsets LayoutBox::computeVisualEffectOverflowOutsets() const
4106 ASSERT(style()->hasVisualOverflowingEffect());
4108 LayoutUnit top;
4109 LayoutUnit right;
4110 LayoutUnit bottom;
4111 LayoutUnit left;
4113 if (const ShadowList* boxShadow = style()->boxShadow()) {
4114 // FIXME: Use LayoutUnit edge outsets, and then simplify this.
4115 FloatRectOutsets outsets = boxShadow->rectOutsetsIncludingOriginal();
4116 top = outsets.top();
4117 right = outsets.right();
4118 bottom = outsets.bottom();
4119 left = outsets.left();
4122 if (style()->hasBorderImageOutsets()) {
4123 LayoutRectOutsets borderOutsets = style()->borderImageOutsets();
4124 top = std::max(top, borderOutsets.top());
4125 right = std::max(right, borderOutsets.right());
4126 bottom = std::max(bottom, borderOutsets.bottom());
4127 left = std::max(left, borderOutsets.left());
4130 if (style()->hasOutline()) {
4131 Vector<LayoutRect> outlineRects;
4132 // The result rects are in coordinates of this object's border box.
4133 addOutlineRects(outlineRects, LayoutPoint(), outlineRectsShouldIncludeBlockVisualOverflow());
4134 LayoutRect rect = unionRectEvenIfEmpty(outlineRects);
4135 int outlineOutset = style()->outlineOutsetExtent();
4136 top = std::max(top, -rect.y() + outlineOutset);
4137 right = std::max(right, rect.maxX() - size().width() + outlineOutset);
4138 bottom = std::max(bottom, rect.maxY() - size().height() + outlineOutset);
4139 left = std::max(left, -rect.x() + outlineOutset);
4142 return LayoutRectOutsets(top, right, bottom, left);
4145 void LayoutBox::addOverflowFromChild(LayoutBox* child, const LayoutSize& delta)
4147 // Never allow flow threads to propagate overflow up to a parent.
4148 if (child->isLayoutFlowThread())
4149 return;
4151 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then
4152 // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this
4153 // and just propagates the border box rect instead.
4154 LayoutRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(styleRef());
4155 childLayoutOverflowRect.move(delta);
4156 addLayoutOverflow(childLayoutOverflowRect);
4158 // Add in visual overflow from the child. Even if the child clips its overflow, it may still
4159 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this
4160 // overflow if we are clipping our own overflow.
4161 if (child->hasSelfPaintingLayer())
4162 return;
4163 LayoutRect childVisualOverflowRect = child->visualOverflowRectForPropagation(styleRef());
4164 childVisualOverflowRect.move(delta);
4165 addContentsVisualOverflow(childVisualOverflowRect);
4168 void LayoutBox::addLayoutOverflow(const LayoutRect& rect)
4170 LayoutRect clientBox = noOverflowRect();
4171 if (clientBox.contains(rect) || rect.isEmpty())
4172 return;
4174 // For overflow clip objects, we don't want to propagate overflow into unreachable areas.
4175 LayoutRect overflowRect(rect);
4176 if (hasOverflowClip() || isLayoutView()) {
4177 // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl
4178 // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same
4179 // and vertical-lr/rl as the same.
4180 bool hasTopOverflow = !style()->isLeftToRightDirection() && !isHorizontalWritingMode();
4181 bool hasLeftOverflow = !style()->isLeftToRightDirection() && isHorizontalWritingMode();
4182 if (isFlexibleBox() && style()->isReverseFlexDirection()) {
4183 LayoutFlexibleBox* flexibleBox = toLayoutFlexibleBox(this);
4184 if (flexibleBox->isHorizontalFlow())
4185 hasLeftOverflow = true;
4186 else
4187 hasTopOverflow = true;
4190 if (!hasTopOverflow)
4191 overflowRect.shiftYEdgeTo(std::max(overflowRect.y(), clientBox.y()));
4192 else
4193 overflowRect.shiftMaxYEdgeTo(std::min(overflowRect.maxY(), clientBox.maxY()));
4194 if (!hasLeftOverflow)
4195 overflowRect.shiftXEdgeTo(std::max(overflowRect.x(), clientBox.x()));
4196 else
4197 overflowRect.shiftMaxXEdgeTo(std::min(overflowRect.maxX(), clientBox.maxX()));
4199 // Now re-test with the adjusted rectangle and see if it has become unreachable or fully
4200 // contained.
4201 if (clientBox.contains(overflowRect) || overflowRect.isEmpty())
4202 return;
4205 if (!m_overflow)
4206 m_overflow = adoptPtr(new OverflowModel(clientBox, borderBoxRect()));
4208 m_overflow->addLayoutOverflow(overflowRect);
4211 void LayoutBox::addVisualOverflow(const LayoutRect& rect)
4213 LayoutRect borderBox = borderBoxRect();
4214 if (borderBox.contains(rect) || rect.isEmpty())
4215 return;
4217 if (!m_overflow)
4218 m_overflow = adoptPtr(new OverflowModel(noOverflowRect(), borderBox));
4220 m_overflow->addVisualOverflow(rect);
4223 void LayoutBox::addContentsVisualOverflow(const LayoutRect& rect)
4225 if (!hasOverflowClip()) {
4226 addVisualOverflow(rect);
4227 return;
4230 if (!m_overflow)
4231 m_overflow = adoptPtr(new OverflowModel(noOverflowRect(), borderBoxRect()));
4232 m_overflow->addContentsVisualOverflow(rect);
4235 void LayoutBox::clearLayoutOverflow()
4237 if (!m_overflow)
4238 return;
4240 if (!hasVisualOverflow() && contentsVisualOverflowRect().isEmpty()) {
4241 clearAllOverflows();
4242 return;
4245 m_overflow->setLayoutOverflow(noOverflowRect());
4248 static bool logicalWidthIsResolvable(const LayoutBox& layoutBox)
4250 const LayoutBox* box = &layoutBox;
4251 while (!box->isLayoutView() && !box->isOutOfFlowPositioned()
4252 && (box->style()->logicalWidth().isAuto() || box->isAnonymousBlock())
4253 && !box->hasOverrideContainingBlockLogicalWidth())
4254 box = box->containingBlock();
4256 if (box->style()->logicalWidth().isFixed())
4257 return true;
4258 if (box->isLayoutView())
4259 return true;
4260 // The size of the containing block of an absolutely positioned element is always definite with respect to that
4261 // element (http://dev.w3.org/csswg/css-sizing-3/#definite).
4262 if (box->isOutOfFlowPositioned())
4263 return true;
4264 if (box->hasOverrideContainingBlockLogicalWidth())
4265 return box->overrideContainingBlockContentLogicalWidth() != -1;
4266 if (box->style()->logicalWidth().hasPercent())
4267 return logicalWidthIsResolvable(*box->containingBlock());
4269 return false;
4272 bool LayoutBox::hasDefiniteLogicalWidth() const
4274 return logicalWidthIsResolvable(*this);
4277 bool LayoutBox::percentageLogicalHeightIsResolvable() const
4279 Length fakeLength(100, Percent);
4280 return computePercentageLogicalHeight(fakeLength) != -1;
4283 bool LayoutBox::hasDefiniteLogicalHeight() const
4285 const Length& logicalHeight = style()->logicalHeight();
4286 if (logicalHeight.isIntrinsicOrAuto())
4287 return false;
4288 if (logicalHeight.isFixed())
4289 return true;
4290 // The size of the containing block of an absolutely positioned element is always definite with respect to that
4291 // element (http://dev.w3.org/csswg/css-sizing-3/#definite).
4292 if (isOutOfFlowPositioned())
4293 return true;
4294 if (hasOverrideContainingBlockLogicalHeight())
4295 return overrideContainingBlockContentLogicalHeight() != -1;
4297 return percentageLogicalHeightIsResolvable();
4300 bool LayoutBox::hasUnsplittableScrollingOverflow() const
4302 // We will paginate as long as we don't scroll overflow in the pagination direction.
4303 bool isHorizontal = isHorizontalWritingMode();
4304 if ((isHorizontal && !scrollsOverflowY()) || (!isHorizontal && !scrollsOverflowX()))
4305 return false;
4307 // We do have overflow. We'll still be willing to paginate as long as the block
4308 // has auto logical height, auto or undefined max-logical-height and a zero or auto min-logical-height.
4309 // Note this is just a heuristic, and it's still possible to have overflow under these
4310 // conditions, but it should work out to be good enough for common cases. Paginating overflow
4311 // with scrollbars present is not the end of the world and is what we used to do in the old model anyway.
4312 return !style()->logicalHeight().isIntrinsicOrAuto()
4313 || (!style()->logicalMaxHeight().isIntrinsicOrAuto() && !style()->logicalMaxHeight().isMaxSizeNone() && (!style()->logicalMaxHeight().hasPercent() || percentageLogicalHeightIsResolvable()))
4314 || (!style()->logicalMinHeight().isIntrinsicOrAuto() && style()->logicalMinHeight().isPositive() && (!style()->logicalMinHeight().hasPercent() || percentageLogicalHeightIsResolvable()));
4317 bool LayoutBox::isUnsplittableForPagination() const
4319 return isReplaced() || hasUnsplittableScrollingOverflow() || (parent() && isWritingModeRoot());
4322 LayoutUnit LayoutBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const
4324 if (isReplaced())
4325 return direction == HorizontalLine ? marginHeight() + size().height() : marginWidth() + size().width();
4326 return LayoutUnit();
4329 int LayoutBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode linePositionMode) const
4331 ASSERT(linePositionMode == PositionOnContainingLine);
4332 if (isReplaced()) {
4333 int result = direction == HorizontalLine ? marginHeight() + size().height() : marginWidth() + size().width();
4334 if (baselineType == AlphabeticBaseline)
4335 return result;
4336 return result - result / 2;
4338 return 0;
4342 DeprecatedPaintLayer* LayoutBox::enclosingFloatPaintingLayer() const
4344 const LayoutObject* curr = this;
4345 while (curr) {
4346 DeprecatedPaintLayer* layer = curr->hasLayer() && curr->isBox() ? toLayoutBox(curr)->layer() : 0;
4347 if (layer && layer->isSelfPaintingLayer())
4348 return layer;
4349 curr = curr->parent();
4351 return nullptr;
4354 LayoutRect LayoutBox::logicalVisualOverflowRectForPropagation(const ComputedStyle& parentStyle) const
4356 LayoutRect rect = visualOverflowRectForPropagation(parentStyle);
4357 if (!parentStyle.isHorizontalWritingMode())
4358 return rect.transposedRect();
4359 return rect;
4362 LayoutRect LayoutBox::visualOverflowRectForPropagation(const ComputedStyle& parentStyle) const
4364 // If the writing modes of the child and parent match, then we don't have to
4365 // do anything fancy. Just return the result.
4366 LayoutRect rect = visualOverflowRect();
4367 if (parentStyle.writingMode() == style()->writingMode())
4368 return rect;
4370 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
4371 // in a particular axis, then we have to flip the rect along that axis.
4372 if (style()->writingMode() == RightToLeftWritingMode || parentStyle.writingMode() == RightToLeftWritingMode)
4373 rect.setX(size().width() - rect.maxX());
4374 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle.writingMode() == BottomToTopWritingMode)
4375 rect.setY(size().height() - rect.maxY());
4377 return rect;
4380 LayoutRect LayoutBox::logicalLayoutOverflowRectForPropagation(const ComputedStyle& parentStyle) const
4382 LayoutRect rect = layoutOverflowRectForPropagation(parentStyle);
4383 if (!parentStyle.isHorizontalWritingMode())
4384 return rect.transposedRect();
4385 return rect;
4388 LayoutRect LayoutBox::layoutOverflowRectForPropagation(const ComputedStyle& parentStyle) const
4390 // Only propagate interior layout overflow if we don't clip it.
4391 LayoutRect rect = borderBoxRect();
4392 // We want to include the margin, but only when it adds height. Quirky margins don't contribute height
4393 // nor do the margins of self-collapsing blocks.
4394 if (!styleRef().hasMarginAfterQuirk() && !isSelfCollapsingBlock())
4395 rect.expand(isHorizontalWritingMode() ? LayoutSize(LayoutUnit(), marginAfter()) : LayoutSize(marginAfter(), LayoutUnit()));
4397 if (!hasOverflowClip())
4398 rect.unite(layoutOverflowRect());
4400 bool hasTransform = hasLayer() && layer()->transform();
4401 if (isInFlowPositioned() || hasTransform) {
4402 // If we are relatively positioned or if we have a transform, then we have to convert
4403 // this rectangle into physical coordinates, apply relative positioning and transforms
4404 // to it, and then convert it back.
4405 flipForWritingMode(rect);
4407 if (hasTransform)
4408 rect = layer()->currentTransform().mapRect(rect);
4410 if (isInFlowPositioned())
4411 rect.move(offsetForInFlowPosition());
4413 // Now we need to flip back.
4414 flipForWritingMode(rect);
4417 // If the writing modes of the child and parent match, then we don't have to
4418 // do anything fancy. Just return the result.
4419 if (parentStyle.writingMode() == style()->writingMode())
4420 return rect;
4422 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
4423 // in a particular axis, then we have to flip the rect along that axis.
4424 if (style()->writingMode() == RightToLeftWritingMode || parentStyle.writingMode() == RightToLeftWritingMode)
4425 rect.setX(size().width() - rect.maxX());
4426 else if (style()->writingMode() == BottomToTopWritingMode || parentStyle.writingMode() == BottomToTopWritingMode)
4427 rect.setY(size().height() - rect.maxY());
4429 return rect;
4432 LayoutRect LayoutBox::noOverflowRect() const
4434 // Because of the special coordinate system used for overflow rectangles and many other
4435 // rectangles (not quite logical, not quite physical), we need to flip the block progression
4436 // coordinate in vertical-rl and horizontal-bt writing modes. In other words, the rectangle
4437 // returned is physical, except for the block direction progression coordinate (y in horizontal
4438 // writing modes, x in vertical writing modes), which is always "logical top". Apart from the
4439 // flipping, this method does the same as clientBoxRect().
4441 const int scrollBarWidth = verticalScrollbarWidth();
4442 const int scrollBarHeight = horizontalScrollbarHeight();
4443 LayoutUnit left = borderLeft() + (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft() ? scrollBarWidth : 0);
4444 LayoutUnit top = borderTop();
4445 LayoutUnit right = borderRight();
4446 LayoutUnit bottom = borderBottom();
4447 LayoutRect rect(left, top, size().width() - left - right, size().height() - top - bottom);
4448 flipForWritingMode(rect);
4449 // Subtract space occupied by scrollbars. Order is important here: first flip, then subtract
4450 // scrollbars. This may seem backwards and weird, since one would think that a horizontal
4451 // scrollbar at the physical bottom in horizontal-bt ought to be at the logical top (physical
4452 // bottom), between the logical top (physical bottom) border and the logical top (physical
4453 // bottom) padding. But this is how the rest of the code expects us to behave. This is highly
4454 // related to https://bugs.webkit.org/show_bug.cgi?id=76129
4455 // FIXME: when the above mentioned bug is fixed, it should hopefully be possible to call
4456 // clientBoxRect() or paddingBoxRect() in this method, rather than fiddling with the edges on
4457 // our own.
4458 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
4459 rect.contract(0, scrollBarHeight);
4460 else
4461 rect.contract(scrollBarWidth, scrollBarHeight);
4462 return rect;
4465 LayoutUnit LayoutBox::offsetLeft() const
4467 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).x();
4470 LayoutUnit LayoutBox::offsetTop() const
4472 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).y();
4475 LayoutPoint LayoutBox::flipForWritingModeForChild(const LayoutBox* child, const LayoutPoint& point) const
4477 if (!style()->isFlippedBlocksWritingMode())
4478 return point;
4480 // The child is going to add in its x() and y(), so we have to make sure it ends up in
4481 // the right place.
4482 if (isHorizontalWritingMode())
4483 return LayoutPoint(point.x(), point.y() + size().height() - child->size().height() - (2 * child->location().y()));
4484 return LayoutPoint(point.x() + size().width() - child->size().width() - (2 * child->location().x()), point.y());
4487 LayoutPoint LayoutBox::topLeftLocation() const
4489 LayoutBlock* containerBlock = containingBlock();
4490 if (!containerBlock || containerBlock == this)
4491 return location();
4492 return containerBlock->flipForWritingModeForChild(this, location());
4495 bool LayoutBox::hasRelativeLogicalWidth() const
4497 return style()->logicalWidth().hasPercent()
4498 || style()->logicalMinWidth().hasPercent()
4499 || style()->logicalMaxWidth().hasPercent();
4502 bool LayoutBox::hasRelativeLogicalHeight() const
4504 return style()->logicalHeight().hasPercent()
4505 || style()->logicalMinHeight().hasPercent()
4506 || style()->logicalMaxHeight().hasPercent();
4509 static void markBoxForRelayoutAfterSplit(LayoutBox* box)
4511 // FIXME: The table code should handle that automatically. If not,
4512 // we should fix it and remove the table part checks.
4513 if (box->isTable()) {
4514 // Because we may have added some sections with already computed column structures, we need to
4515 // sync the table structure with them now. This avoids crashes when adding new cells to the table.
4516 toLayoutTable(box)->forceSectionsRecalc();
4517 } else if (box->isTableSection()) {
4518 toLayoutTableSection(box)->setNeedsCellRecalc();
4521 box->setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(LayoutInvalidationReason::AnonymousBlockChange);
4524 LayoutObject* LayoutBox::splitAnonymousBoxesAroundChild(LayoutObject* beforeChild)
4526 bool didSplitParentAnonymousBoxes = false;
4528 while (beforeChild->parent() != this) {
4529 LayoutBox* boxToSplit = toLayoutBox(beforeChild->parent());
4530 if (boxToSplit->slowFirstChild() != beforeChild && boxToSplit->isAnonymous()) {
4531 didSplitParentAnonymousBoxes = true;
4533 // We have to split the parent box into two boxes and move children
4534 // from |beforeChild| to end into the new post box.
4535 LayoutBox* postBox = boxToSplit->createAnonymousBoxWithSameTypeAs(this);
4536 postBox->setChildrenInline(boxToSplit->childrenInline());
4537 LayoutBox* parentBox = toLayoutBox(boxToSplit->parent());
4538 // We need to invalidate the |parentBox| before inserting the new node
4539 // so that the table paint invalidation logic knows the structure is dirty.
4540 // See for example LayoutTableCell:clippedOverflowRectForPaintInvalidation.
4541 markBoxForRelayoutAfterSplit(parentBox);
4542 parentBox->virtualChildren()->insertChildNode(parentBox, postBox, boxToSplit->nextSibling());
4543 boxToSplit->moveChildrenTo(postBox, beforeChild, 0, true);
4545 markBoxForRelayoutAfterSplit(boxToSplit);
4546 markBoxForRelayoutAfterSplit(postBox);
4548 beforeChild = postBox;
4549 } else {
4550 beforeChild = boxToSplit;
4554 if (didSplitParentAnonymousBoxes)
4555 markBoxForRelayoutAfterSplit(this);
4557 ASSERT(beforeChild->parent() == this);
4558 return beforeChild;
4561 LayoutUnit LayoutBox::offsetFromLogicalTopOfFirstPage() const
4563 LayoutState* layoutState = view()->layoutState();
4564 if (!layoutState || !layoutState->isPaginated())
4565 return LayoutUnit();
4567 if (layoutState->layoutObject() == this) {
4568 LayoutSize offsetDelta = layoutState->layoutOffset() - layoutState->pageOffset();
4569 return isHorizontalWritingMode() ? offsetDelta.height() : offsetDelta.width();
4572 // A LayoutBlock always establishes a layout state, and this method is only meant to be called
4573 // on the object currently being laid out.
4574 ASSERT(!isLayoutBlock());
4576 // In case this box doesn't establish a layout state, try the containing block.
4577 LayoutBlock* containerBlock = containingBlock();
4578 ASSERT(layoutState->layoutObject() == containerBlock);
4579 return containerBlock->offsetFromLogicalTopOfFirstPage() + logicalTop();
4582 void LayoutBox::setPageLogicalOffset(LayoutUnit offset)
4584 if (!m_rareData && !offset)
4585 return;
4586 ensureRareData().m_pageLogicalOffset = offset;
4589 bool LayoutBox::needToSavePreviousBoxSizes()
4591 // If m_rareData is already created, always save.
4592 if (m_rareData)
4593 return true;
4595 LayoutSize paintInvalidationSize = previousPaintInvalidationRectSize();
4596 // Don't save old box sizes if the paint rect is empty because we'll
4597 // full invalidate once the paint rect becomes non-empty.
4598 if (paintInvalidationSize.isEmpty())
4599 return false;
4601 // We need the old box sizes only when the box has background, decorations, or masks.
4602 // Main LayoutView paints base background, thus interested in box size.
4603 if (!isLayoutView() && !style()->hasBackground() && !style()->hasBoxDecorations() && !style()->hasMask())
4604 return false;
4606 // No need to save old border box size if we can use size of the old paint
4607 // rect as the old border box size in the next invalidation.
4608 if (paintInvalidationSize != size())
4609 return true;
4611 // Background and mask layers can depend on other boxes than border box. See crbug.com/490533
4612 if (style()->backgroundLayers().thisOrNextLayersUseContentBox() || style()->backgroundLayers().thisOrNextLayersHaveLocalAttachment()
4613 || style()->maskLayers().thisOrNextLayersUseContentBox())
4614 return true;
4616 return false;
4619 void LayoutBox::savePreviousBoxSizesIfNeeded()
4621 if (!needToSavePreviousBoxSizes())
4622 return;
4624 LayoutBoxRareData& rareData = ensureRareData();
4625 rareData.m_previousBorderBoxSize = size();
4626 rareData.m_previousContentBoxRect = contentBoxRect();
4627 rareData.m_previousLayoutOverflowRect = layoutOverflowRect();
4630 LayoutSize LayoutBox::computePreviousBorderBoxSize(const LayoutSize& previousBoundsSize) const
4632 // PreviousBorderBoxSize is only valid when there is background or box decorations.
4633 ASSERT(style()->hasBackground() || style()->hasBoxDecorations());
4635 if (m_rareData && m_rareData->m_previousBorderBoxSize.width() != -1)
4636 return m_rareData->m_previousBorderBoxSize;
4638 // We didn't save the old border box size because it was the same as the size of oldBounds.
4639 return previousBoundsSize;
4642 void LayoutBox::logicalExtentAfterUpdatingLogicalWidth(const LayoutUnit& newLogicalTop, LayoutBox::LogicalExtentComputedValues& computedValues)
4644 // FIXME: None of this is right for perpendicular writing-mode children.
4645 LayoutUnit oldLogicalWidth = logicalWidth();
4646 LayoutUnit oldLogicalLeft = logicalLeft();
4647 LayoutUnit oldMarginLeft = marginLeft();
4648 LayoutUnit oldMarginRight = marginRight();
4649 LayoutUnit oldLogicalTop = logicalTop();
4651 setLogicalTop(newLogicalTop);
4652 updateLogicalWidth();
4654 computedValues.m_extent = logicalWidth();
4655 computedValues.m_position = logicalLeft();
4656 computedValues.m_margins.m_start = marginStart();
4657 computedValues.m_margins.m_end = marginEnd();
4659 setLogicalTop(oldLogicalTop);
4660 setLogicalWidth(oldLogicalWidth);
4661 setLogicalLeft(oldLogicalLeft);
4662 setMarginLeft(oldMarginLeft);
4663 setMarginRight(oldMarginRight);
4666 inline bool LayoutBox::mustInvalidateFillLayersPaintOnWidthChange(const FillLayer& layer) const
4668 // Nobody will use multiple layers without wanting fancy positioning.
4669 if (layer.next())
4670 return true;
4672 // Make sure we have a valid image.
4673 StyleImage* img = layer.image();
4674 if (!img || !img->canRender(*this, style()->effectiveZoom()))
4675 return false;
4677 if (layer.repeatX() != RepeatFill && layer.repeatX() != NoRepeatFill)
4678 return true;
4680 // TODO(alancutter): Make this work correctly for calc lengths.
4681 if (layer.xPosition().hasPercent() && !layer.xPosition().isZero())
4682 return true;
4684 if (layer.backgroundXOrigin() != LeftEdge)
4685 return true;
4687 EFillSizeType sizeType = layer.sizeType();
4689 if (sizeType == Contain || sizeType == Cover)
4690 return true;
4692 if (sizeType == SizeLength) {
4693 // TODO(alancutter): Make this work correctly for calc lengths.
4694 if (layer.sizeLength().width().hasPercent() && !layer.sizeLength().width().isZero())
4695 return true;
4696 if (img->isGeneratedImage() && layer.sizeLength().width().isAuto())
4697 return true;
4698 } else if (img->usesImageContainerSize()) {
4699 return true;
4702 return false;
4705 bool LayoutBox::mustInvalidateBackgroundOrBorderPaintOnWidthChange() const
4707 if (hasMask() && mustInvalidateFillLayersPaintOnWidthChange(style()->maskLayers()))
4708 return true;
4710 // If we don't have a background/border/mask, then nothing to do.
4711 if (!hasBoxDecorationBackground())
4712 return false;
4714 if (mustInvalidateFillLayersPaintOnWidthChange(style()->backgroundLayers()))
4715 return true;
4717 // Our fill layers are ok. Let's check border.
4718 if (style()->hasBorderDecoration() && canRenderBorderImage())
4719 return true;
4721 return false;
4724 bool LayoutBox::mustInvalidateBackgroundOrBorderPaintOnHeightChange() const
4726 if (hasMask() && mustInvalidateFillLayersPaintOnHeightChange(style()->maskLayers()))
4727 return true;
4729 // If we don't have a background/border/mask, then nothing to do.
4730 if (!hasBoxDecorationBackground())
4731 return false;
4733 if (mustInvalidateFillLayersPaintOnHeightChange(style()->backgroundLayers()))
4734 return true;
4736 // Our fill layers are ok. Let's check border.
4737 if (style()->hasBorderDecoration() && canRenderBorderImage())
4738 return true;
4740 return false;
4743 bool LayoutBox::canRenderBorderImage() const
4745 if (!style()->hasBorderDecoration())
4746 return false;
4748 StyleImage* borderImage = style()->borderImage().image();
4749 return borderImage && borderImage->canRender(*this, style()->effectiveZoom()) && borderImage->isLoaded();
4752 ShapeOutsideInfo* LayoutBox::shapeOutsideInfo() const
4754 return ShapeOutsideInfo::isEnabledFor(*this) ? ShapeOutsideInfo::info(*this) : nullptr;
4757 } // namespace blink