2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
6 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
7 * Copyright (C) 2013 Adobe Systems Incorporated. All rights reserved.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
27 #include "core/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"
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)
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()
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())
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();
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
);
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();
260 LayoutFlowThread
* flowThread
= flowThreadContainingBlock();
261 if (flowThread
&& flowThread
!= this)
262 flowThread
->flowThreadDescendantStyleDidChange(this, diff
, *oldStyle
);
266 void LayoutBox::updateSlowRepaintStatusAfterStyleChange()
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
)
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
)
307 ShapeOutsideInfo::removeInfo(*this);
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())
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())
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();
378 LayoutState
state(*this, locationOffset());
380 child
->layoutIfNeeded();
381 ASSERT(!child
->needsLayout());
382 child
= child
->nextSibling();
384 invalidateBackgroundObscurationStatus();
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
;
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;
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());
545 // If we are fixed-position, it is useless to scroll the parent.
546 if (hasLayer() && layer()->scrollsWithViewport())
549 if (frame()->page()->autoscrollController().autoscrollInProgress())
550 parentBox
= enclosingScrollableBox();
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.
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
);
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
);
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();
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());
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())
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())
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()));
684 case ReflectionAbove
:
685 result
.setY(box
.y() - reflectionOffset() - box
.height() + (box
.maxY() - r
.maxY()));
688 result
.setX(box
.x() - reflectionOffset() - box
.width() + (box
.maxX() - r
.maxX()));
690 case ReflectionRight
:
691 result
.setX(box
.maxX() + reflectionOffset() + (box
.maxX() - r
.maxX()));
697 int LayoutBox::verticalScrollbarWidth() const
699 if (!hasOverflowClip() || style()->overflowY() == OOVERLAY
)
702 return layer()->scrollableArea()->verticalScrollbarWidth();
705 int LayoutBox::horizontalScrollbarHeight() const
707 if (!hasOverflowClip() || style()->overflowX() == OOVERLAY
)
710 return layer()->scrollableArea()->horizontalScrollbarHeight();
713 int LayoutBox::intrinsicScrollbarLogicalWidth() const
715 if (!hasOverflowClip())
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;
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())
755 if (!hasOverflowClip())
758 bool hasScrollableOverflow
= hasScrollableOverflowX() || hasScrollableOverflowY();
759 if (scrollsOverflow() && hasScrollableOverflow
)
762 return node
&& node
->hasEditableStyle();
765 void LayoutBox::autoscroll(const IntPoint
& positionInRootFrame
)
767 LocalFrame
* frame
= this->frame();
771 FrameView
* frameView
= frame
->view();
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
791 IntSize
LayoutBox::calculateAutoscrollDirection(const IntPoint
& pointInRootFrame
) const
796 FrameView
* frameView
= frame()->view();
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();
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();
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
;
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
870 if (abs(delta
.height()) <= AutoscrollController::noPanScrollRadius
)
872 scrollByRecursively(adjustedScrollDelta(delta
), ScrollOffsetClamped
);
875 void LayoutBox::scrollByRecursively(const DoubleSize
& delta
, ScrollOffsetClamping clamp
)
880 bool restrictedByLineClamp
= false;
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());
918 // FIXME: Return DoubleSize here. crbug.com/414283.
919 return flooredIntSize(layer()->scrollableArea()->scrollOffset());
922 void LayoutBox::applyCachedClipAndScrollOffsetForPaintInvalidation(LayoutRect
& paintRect
) const
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
);
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()) {
955 SetLayoutNeededForbiddenScope
layoutForbiddenScope(const_cast<LayoutBox
&>(*this));
957 const_cast<LayoutBox
*>(this)->computePreferredLogicalWidths();
960 return m_minPreferredLogicalWidth
;
963 LayoutUnit
LayoutBox::maxPreferredLogicalWidth() const
965 if (preferredLogicalWidthsDirty()) {
967 SetLayoutNeededForbiddenScope
layoutForbiddenScope(const_cast<LayoutBox
&>(*this));
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
)
988 ensureRareData().m_overrideLogicalContentHeight
= height
;
991 void LayoutBox::setOverrideLogicalContentWidth(LayoutUnit width
)
994 ensureRareData().m_overrideLogicalContentWidth
= width
;
997 void LayoutBox::clearOverrideLogicalContentHeight()
1000 m_rareData
->m_overrideLogicalContentHeight
= -1;
1003 void LayoutBox::clearOverrideLogicalContentWidth()
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
);
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
)) {
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
));
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
))
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
;
1199 if (!style()->backgroundLayers().image() || style()->backgroundLayers().next()) {
1200 paintedExtent
= backgroundRect
;
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())
1210 paintedExtent
= LayoutRect(geometry
.destRect());
1214 bool LayoutBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect
& localRect
) const
1216 if (isDocumentElement() || backgroundStolenForBeingBody())
1219 Color backgroundColor
= resolveColor(CSSPropertyBackgroundColor
);
1220 if (backgroundColor
.hasAlpha())
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())
1229 // FIXME: Check the opaqueness of background images.
1231 // FIXME: Use rounded rect if border radius is present.
1232 if (style()->hasBorderRadius())
1234 // FIXME: The background color clip is defined by the last layer.
1235 if (style()->backgroundLayers().next())
1237 LayoutRect backgroundRect
;
1238 switch (style()->backgroundClip()) {
1240 backgroundRect
= borderBoxRect();
1242 case PaddingFillBox
:
1243 backgroundRect
= paddingBoxRect();
1245 case ContentFillBox
:
1246 backgroundRect
= contentBoxRect();
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())
1259 if (childStyle
.visibility() != VISIBLE
|| childStyle
.shapeOutside())
1261 if (childBox
.size().isZero())
1263 if (DeprecatedPaintLayer
* childLayer
= childBox
.layer()) {
1264 // FIXME: perhaps this could be less conservative?
1265 if (childLayer
->compositingState() != NotComposited
)
1267 // FIXME: Deal with z-index.
1268 if (!childStyle
.hasAutoZIndex())
1270 if (childLayer
->hasTransformRelatedProperty() || childLayer
->isTransparent() || childLayer
->hasFilter())
1272 if (childBox
.hasOverflowClip() && childStyle
.hasBorderRadius())
1278 bool LayoutBox::foregroundIsKnownToBeOpaqueInRect(const LayoutRect
& localRect
, unsigned maxDepthToTest
) const
1280 if (!maxDepthToTest
)
1282 for (LayoutObject
* child
= slowFirstChild(); child
; child
= child
->nextSibling()) {
1283 if (!child
->isBox())
1285 LayoutBox
* childBox
= toLayoutBox(child
);
1286 if (!isCandidateForOpaquenessTest(*childBox
))
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
)
1299 if (childLocalRect
.maxY() > childBox
->size().height() || childLocalRect
.maxX() > childBox
->size().width())
1301 if (childBox
->backgroundIsKnownToBeOpaqueInRect(childLocalRect
))
1303 if (childBox
->foregroundIsKnownToBeOpaqueInRect(childLocalRect
, maxDepthToTest
- 1))
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())
1315 // Table and root background painting is special.
1316 if (isTable() || isLayoutView())
1318 // FIXME: box-shadow is painted while background painting.
1319 if (style()->boxShadow())
1321 LayoutRect backgroundRect
;
1322 if (!getBackgroundPaintedExtent(backgroundRect
))
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();
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()))
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
);
1364 setShouldDoFullPaintInvalidation();
1366 if (drawingBackground
)
1367 invalidateBackgroundObscurationStatus();
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();
1423 void LayoutBox::clearPaintInvalidationState(const PaintInvalidationState
& paintInvalidationState
)
1425 LayoutBoxModelObject::clearPaintInvalidationState(paintInvalidationState
);
1427 if (ScrollableArea
* area
= scrollableArea())
1428 area
->resetScrollbarDamage();
1432 bool LayoutBox::paintInvalidationStateIsDirty() const
1434 if (ScrollableArea
* area
= scrollableArea()) {
1435 if (area
->hasVerticalBarDamage() || area
->hasHorizontalBarDamage())
1438 return LayoutBoxModelObject::paintInvalidationStateIsDirty();
1442 LayoutRect
LayoutBox::overflowClipRect(const LayoutPoint
& location
, OverlayScrollbarSizeRelevancy relevancy
) const
1444 // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property
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
);
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()));
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
)
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
);
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();
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)
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
);
1600 bool containerSkipped
;
1601 LayoutObject
* o
= container(paintInvalidationContainer
, &containerSkipped
);
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
)
1611 else if (isFixedPos
)
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
);
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
);
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.
1649 } else if (isFixedPos
) {
1653 LayoutBoxModelObject::mapAbsoluteToLocalPoint(mode
, transformState
);
1656 LayoutSize
LayoutBox::offsetFromContainer(const LayoutObject
* o
, const LayoutPoint
& point
, bool* offsetDependsOnPoint
) const
1658 ASSERT(o
== container());
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);
1686 InlineBox
* LayoutBox::createInlineBox()
1688 return new InlineBox(*this);
1691 void LayoutBox::dirtyLineBoxes(bool fullLayout
)
1693 if (inlineBoxWrapper()) {
1695 inlineBoxWrapper()->destroy();
1697 m_rareData
->m_inlineBoxWrapper
= nullptr;
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
1713 RootInlineBox
& root
= box
->root();
1714 root
.block().setStaticInlinePositionForChild(*this, box
->logicalLeft());
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());
1727 box
->remove(DontMarkLineBoxes
);
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();
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()
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
);
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());
1820 if (paintInvalidationContainer
== this) {
1821 if (paintInvalidationContainer
->style()->isFlippedBlocksWritingMode())
1822 flipForWritingMode(rect
);
1826 bool containerSkipped
;
1827 LayoutObject
* o
= container(paintInvalidationContainer
, &containerSkipped
);
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
);
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
));
1875 if (o
->isLayoutView())
1876 toLayoutView(o
)->mapRectToPaintInvalidationBacking(paintInvalidationContainer
, rect
, LayoutView::viewportConstrainedPosition(position
), paintInvalidationState
);
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
)
1908 ASSERT(layoutObject
);
1909 Node
* parentNode
= layoutObject
->generatingNode();
1911 ASSERT(isHTMLOListElement(parentNode
) || isHTMLUListElement(parentNode
));
1912 ASSERT(layoutObject
->style()->textAutosizingMultiplier() != 1);
1915 for (LayoutObject
* child
= layoutObject
->slowFirstChild(); child
; child
= child
->nextSibling()) {
1916 if (!child
->isListItem())
1919 LayoutBox
* listItem
= toLayoutBox(child
);
1920 for (LayoutObject
* itemChild
= listItem
->slowFirstChild(); itemChild
; itemChild
= itemChild
->nextSibling()) {
1921 if (!itemChild
->isListMarker())
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());
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
);
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();
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
);
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());
1985 // Width calculations
1986 if (treatAsReplaced
) {
1987 computedValues
.m_extent
= logicalWidthLength
.value() + borderAndPaddingLogicalWidth();
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
;
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
;
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();
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())
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
)
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
))
2112 bool LayoutBox::sizesLogicalWidthToFitContent(const Length
& logicalWidth
) const
2114 if (isFloating() || isInlineBlockOrInlineTable())
2117 if (logicalWidth
.type() == Intrinsic
)
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
)
2127 if (!columnFlexItemHasStretchAlignment(this))
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
))
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())
2145 if (isHorizontalWritingMode() != containingBlock()->isHorizontalWritingMode())
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
);
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
;
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
);
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
;
2232 if (marginStartLength
.isAuto()) {
2233 marginEnd
= marginEndWidth
;
2234 marginStart
= availableWidth
- childWidth
- marginEnd
;
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()))
2267 if (isOutOfFlowPositioned()) {
2268 computePositionedLogicalHeight(computedValues
);
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.
2281 computeMarginsForDirection(flowDirection
, cb
, containingBlockLogicalWidthForContent(), computedValues
.m_extent
, computedValues
.m_margins
.m_before
,
2282 computedValues
.m_margins
.m_after
, style()->marginBefore(), style()->marginAfter());
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
);
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());
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
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
);
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)
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()) {
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();
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
);
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())
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();
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
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
);
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()) {
2554 return adjustContentBoxLogicalWidthForBoxSizing(logicalWidth
.value());
2557 // MinContent/MaxContent don't need the availableLogicalWidth argument.
2558 LayoutUnit availableLogicalWidth
= 0;
2559 return computeIntrinsicLogicalWidthUsing(logicalWidth
, availableLogicalWidth
, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
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();
2582 return intrinsicLogicalWidth();
2589 ASSERT_NOT_REACHED();
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
)
2607 if (LayoutBlock
* cb
= containingBlockForAutoHeightDetection(logicalHeight
))
2608 return cb
->hasAutoHeightOrContainingBlockWithAutoHeight();
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()) {
2633 return adjustContentBoxLogicalHeightForBoxSizing(logicalHeight
.value());
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
));
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
));
2683 return adjustContentBoxLogicalHeightForBoxSizing(computeIntrinsicLogicalContentHeightUsing(logicalHeight
, intrinsicLogicalHeight(), borderAndPaddingHeight()));
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
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();
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();
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())
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());
2857 staticPosition
-= valueForLength(curr
->style()->logicalRight(), curr
->containingBlock()->availableWidth());
2861 logicalLeft
.setValue(Fixed
, staticPosition
);
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());
2879 staticPosition
+= valueForLength(curr
->style()->logicalRight(), curr
->containingBlock()->availableWidth());
2882 if (curr
== containerBlock
)
2885 logicalRight
.setValue(Fixed
, staticPosition
);
2889 void LayoutBox::computePositionedLogicalWidth(LogicalExtentComputedValues
& computedValues
) const
2892 computePositionedLogicalWidthReplaced(computedValues
);
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
2953 \*---------------------------------------------------------------------------*/
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
,
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
,
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
,
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());
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
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
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
;
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
;
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
3126 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
3128 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
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)
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
);
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());
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())
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
3219 computePositionedLogicalHeightReplaced(computedValues
);
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
3257 \*---------------------------------------------------------------------------*/
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
,
3269 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
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
,
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
,
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();
3324 logicalTopPos
+= containerBlock
->borderRight();
3326 if (child
->isHorizontalWritingMode())
3327 logicalTopPos
+= containerBlock
->borderTop();
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.
3360 resolvedLogicalHeight
= contentLogicalHeight
;
3361 logicalHeightIsAuto
= false;
3363 if (logicalHeightLength
.isIntrinsic())
3364 resolvedLogicalHeight
= computeIntrinsicLogicalContentHeightUsing(logicalHeightLength
, contentLogicalHeight
, bordersPlusPadding
);
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
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
;
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
);
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
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
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 \*-----------------------------------------------------------------------*/
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
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
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
3562 \*-----------------------------------------------------------------------*/
3563 } else if (logicalLeft
.isAuto()) {
3564 marginLogicalLeftAlias
= valueForLength(marginLogicalLeft
, containerRelativeLogicalWidth
);
3565 marginLogicalRightAlias
= valueForLength(marginLogicalRight
, containerRelativeLogicalWidth
);
3566 logicalRightValue
= valueForLength(logicalRight
, containerLogicalWidth
);
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
);
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());
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
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 \*-----------------------------------------------------------------------*/
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
3712 \*-----------------------------------------------------------------------*/
3713 } else if (logicalTop
.isAuto()) {
3714 marginBeforeAlias
= valueForLength(marginBefore
, containerRelativeLogicalWidth
);
3715 marginAfterAlias
= valueForLength(marginAfter
, containerRelativeLogicalWidth
);
3716 logicalBottomValue
= valueForLength(logicalBottom
, containerLogicalHeight
);
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
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
);
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
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
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));
3779 RootInlineBox
& rootBox
= box
->root();
3780 LayoutUnit top
= rootBox
.lineTop();
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();
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();
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
;
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
)
3847 if (!layoutObject
->isBox())
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.
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
);
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
);
3879 cmp
= LayoutPoint(left
, point
.y());
3881 if (point
.y() < top
)
3882 cmp
= LayoutPoint(point
.x(), top
);
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
;
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())
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())
3928 if (scrollableArea
->hasVerticalScrollbar() && !scrollableArea
->layerForVerticalScrollbar())
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
)
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
)
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())
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());
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());
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
)
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
);
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())
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());
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())
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())
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())
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;
4187 hasTopOverflow
= true;
4190 if (!hasTopOverflow
)
4191 overflowRect
.shiftYEdgeTo(std::max(overflowRect
.y(), clientBox
.y()));
4193 overflowRect
.shiftMaxYEdgeTo(std::min(overflowRect
.maxY(), clientBox
.maxY()));
4194 if (!hasLeftOverflow
)
4195 overflowRect
.shiftXEdgeTo(std::max(overflowRect
.x(), clientBox
.x()));
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
4201 if (clientBox
.contains(overflowRect
) || overflowRect
.isEmpty())
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())
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
);
4231 m_overflow
= adoptPtr(new OverflowModel(noOverflowRect(), borderBoxRect()));
4232 m_overflow
->addContentsVisualOverflow(rect
);
4235 void LayoutBox::clearLayoutOverflow()
4240 if (!hasVisualOverflow() && contentsVisualOverflowRect().isEmpty()) {
4241 clearAllOverflows();
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())
4258 if (box
->isLayoutView())
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())
4264 if (box
->hasOverrideContainingBlockLogicalWidth())
4265 return box
->overrideContainingBlockContentLogicalWidth() != -1;
4266 if (box
->style()->logicalWidth().hasPercent())
4267 return logicalWidthIsResolvable(*box
->containingBlock());
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())
4288 if (logicalHeight
.isFixed())
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())
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()))
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
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
);
4333 int result
= direction
== HorizontalLine
? marginHeight() + size().height() : marginWidth() + size().width();
4334 if (baselineType
== AlphabeticBaseline
)
4336 return result
- result
/ 2;
4342 DeprecatedPaintLayer
* LayoutBox::enclosingFloatPaintingLayer() const
4344 const LayoutObject
* curr
= this;
4346 DeprecatedPaintLayer
* layer
= curr
->hasLayer() && curr
->isBox() ? toLayoutBox(curr
)->layer() : 0;
4347 if (layer
&& layer
->isSelfPaintingLayer())
4349 curr
= curr
->parent();
4354 LayoutRect
LayoutBox::logicalVisualOverflowRectForPropagation(const ComputedStyle
& parentStyle
) const
4356 LayoutRect rect
= visualOverflowRectForPropagation(parentStyle
);
4357 if (!parentStyle
.isHorizontalWritingMode())
4358 return rect
.transposedRect();
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())
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());
4380 LayoutRect
LayoutBox::logicalLayoutOverflowRectForPropagation(const ComputedStyle
& parentStyle
) const
4382 LayoutRect rect
= layoutOverflowRectForPropagation(parentStyle
);
4383 if (!parentStyle
.isHorizontalWritingMode())
4384 return rect
.transposedRect();
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
);
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())
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());
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
4458 if (style()->shouldPlaceBlockDirectionScrollbarOnLogicalLeft())
4459 rect
.contract(0, scrollBarHeight
);
4461 rect
.contract(scrollBarWidth
, scrollBarHeight
);
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())
4480 // The child is going to add in its x() and y(), so we have to make sure it ends up in
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)
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
;
4550 beforeChild
= boxToSplit
;
4554 if (didSplitParentAnonymousBoxes
)
4555 markBoxForRelayoutAfterSplit(this);
4557 ASSERT(beforeChild
->parent() == this);
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
)
4586 ensureRareData().m_pageLogicalOffset
= offset
;
4589 bool LayoutBox::needToSavePreviousBoxSizes()
4591 // If m_rareData is already created, always save.
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())
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())
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())
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())
4619 void LayoutBox::savePreviousBoxSizesIfNeeded()
4621 if (!needToSavePreviousBoxSizes())
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.
4672 // Make sure we have a valid image.
4673 StyleImage
* img
= layer
.image();
4674 if (!img
|| !img
->canRender(*this, style()->effectiveZoom()))
4677 if (layer
.repeatX() != RepeatFill
&& layer
.repeatX() != NoRepeatFill
)
4680 // TODO(alancutter): Make this work correctly for calc lengths.
4681 if (layer
.xPosition().hasPercent() && !layer
.xPosition().isZero())
4684 if (layer
.backgroundXOrigin() != LeftEdge
)
4687 EFillSizeType sizeType
= layer
.sizeType();
4689 if (sizeType
== Contain
|| sizeType
== Cover
)
4692 if (sizeType
== SizeLength
) {
4693 // TODO(alancutter): Make this work correctly for calc lengths.
4694 if (layer
.sizeLength().width().hasPercent() && !layer
.sizeLength().width().isZero())
4696 if (img
->isGeneratedImage() && layer
.sizeLength().width().isAuto())
4698 } else if (img
->usesImageContainerSize()) {
4705 bool LayoutBox::mustInvalidateBackgroundOrBorderPaintOnWidthChange() const
4707 if (hasMask() && mustInvalidateFillLayersPaintOnWidthChange(style()->maskLayers()))
4710 // If we don't have a background/border/mask, then nothing to do.
4711 if (!hasBoxDecorationBackground())
4714 if (mustInvalidateFillLayersPaintOnWidthChange(style()->backgroundLayers()))
4717 // Our fill layers are ok. Let's check border.
4718 if (style()->hasBorderDecoration() && canRenderBorderImage())
4724 bool LayoutBox::mustInvalidateBackgroundOrBorderPaintOnHeightChange() const
4726 if (hasMask() && mustInvalidateFillLayersPaintOnHeightChange(style()->maskLayers()))
4729 // If we don't have a background/border/mask, then nothing to do.
4730 if (!hasBoxDecorationBackground())
4733 if (mustInvalidateFillLayersPaintOnHeightChange(style()->backgroundLayers()))
4736 // Our fill layers are ok. Let's check border.
4737 if (style()->hasBorderDecoration() && canRenderBorderImage())
4743 bool LayoutBox::canRenderBorderImage() const
4745 if (!style()->hasBorderDecoration())
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