source: webkit/trunk/Source/WebCore/rendering/RenderBox.cpp

Last change on this file was 295633, checked in by Alan Bujtas, 3 years ago

Remove redundant logical right computation for grid items in RenderBlock::computeOverflow
https://bugs.webkit.org/show_bug.cgi?id=241689

Reviewed by Simon Fraser.

If the grid content produces layout overflow, we should not need to re-compute it again by looping through the grid items.

  1. Decouple "include padding end" and "include child's margin end" logic
  2. Decouple "include padding after" and "include padding end" logic.
  3. Restore RenderFlexibleBox and RenderGrid computeOverflow calls to pre-r282463 (when clientLogicalRightAndBottomAfterRepositioning was introduced)
  • LayoutTests/fast/overflow/grid-horizontal-overflow-with-padding-end-expected.html: Added.
  • LayoutTests/fast/overflow/grid-horizontal-overflow-with-padding-end.html: Added.
  • Source/WebCore/rendering/RenderBlock.cpp:

(WebCore::RenderBlock::computeOverflow):
(WebCore::RenderBlock::layoutOverflowLogicalBottom):
(WebCore::RenderBlock::clientLogicalRightAndBottomAfterRepositioning const): Deleted.

  • Source/WebCore/rendering/RenderBlock.h:
  • Source/WebCore/rendering/RenderBox.cpp:

(WebCore::RenderBox::layoutOverflowRectForPropagation const):

  • Source/WebCore/rendering/RenderFlexibleBox.cpp:

(WebCore::RenderFlexibleBox::layoutBlock):

  • Source/WebCore/rendering/RenderGrid.cpp:

(WebCore::RenderGrid::layoutBlock):

Canonical link: https://commits.webkit.org/251638@main

  • Property svn:eol-style set to native
File size: 265.0 KB
Line 
1/*
2 * Copyright (C) 1999 Lars Knoll ([email protected])
3 * (C) 1999 Antti Koivisto ([email protected])
4 * (C) 2005 Allan Sandfeld Jensen ([email protected])
5 * (C) 2005, 2006 Samuel Weinig ([email protected])
6 * Copyright (C) 2005-2010, 2015 Apple Inc. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "RenderBox.h"
27
28#include "CSSFontSelector.h"
29#include "ControlStates.h"
30#include "Document.h"
31#include "Editing.h"
32#include "EventHandler.h"
33#include "FloatQuad.h"
34#include "FloatRoundedRect.h"
35#include "Frame.h"
36#include "FrameView.h"
37#include "GraphicsContext.h"
38#include "HTMLBodyElement.h"
39#include "HTMLButtonElement.h"
40#include "HTMLFrameOwnerElement.h"
41#include "HTMLHtmlElement.h"
42#include "HTMLImageElement.h"
43#include "HTMLInputElement.h"
44#include "HTMLLegendElement.h"
45#include "HTMLNames.h"
46#include "HTMLSelectElement.h"
47#include "HTMLTextAreaElement.h"
48#include "HitTestResult.h"
49#include "InlineIteratorInlineBox.h"
50#include "InlineIteratorLineBox.h"
51#include "InlineRunAndOffset.h"
52#include "LayoutIntegrationLineLayout.h"
53#include "LegacyInlineElementBox.h"
54#include "Page.h"
55#include "PaintInfo.h"
56#include "PathOperation.h"
57#include "RenderBoxFragmentInfo.h"
58#include "RenderChildIterator.h"
59#include "RenderDeprecatedFlexibleBox.h"
60#include "RenderFlexibleBox.h"
61#include "RenderFragmentContainer.h"
62#include "RenderGeometryMap.h"
63#include "RenderGrid.h"
64#include "RenderInline.h"
65#include "RenderIterator.h"
66#include "RenderLayer.h"
67#include "RenderLayerCompositor.h"
68#include "RenderLayerScrollableArea.h"
69#include "RenderLayoutState.h"
70#include "RenderMultiColumnFlow.h"
71#include "RenderSVGResourceClipper.h"
72#include "RenderTableCell.h"
73#include "RenderTheme.h"
74#include "RenderView.h"
75#include "RuntimeApplicationChecks.h"
76#include "SVGClipPathElement.h"
77#include "SVGElementTypeHelpers.h"
78#include "ScrollAnimator.h"
79#include "ScrollbarTheme.h"
80#include "Settings.h"
81#include "StyleScrollSnapPoints.h"
82#include "TextDirection.h"
83#include "TransformState.h"
84#include <algorithm>
85#include <math.h>
86#include <wtf/IsoMallocInlines.h>
87#include <wtf/StackStats.h>
88
89namespace WebCore {
90
91WTF_MAKE_ISO_ALLOCATED_IMPL(RenderBox);
92
93struct SameSizeAsRenderBox : public RenderBoxModelObject {
94 virtual ~SameSizeAsRenderBox() = default;
95 LayoutRect frameRect;
96 LayoutBoxExtent marginBox;
97 LayoutUnit preferredLogicalWidths[2];
98 void* pointers[2];
99};
100
101static_assert(sizeof(RenderBox) == sizeof(SameSizeAsRenderBox), "RenderBox should stay small");
102
103using namespace HTMLNames;
104
105typedef HashMap<const RenderBox*, LayoutUnit> OverrideSizeMap;
106static OverrideSizeMap* gOverridingLogicalHeightMap = nullptr;
107static OverrideSizeMap* gOverridingLogicalWidthMap = nullptr;
108
109typedef HashMap<const RenderBox*, Length> OverridingLengthMap;
110static OverridingLengthMap* gOverridingLogicalHeightLengthMap = nullptr;
111static OverridingLengthMap* gOverridingLogicalWidthLengthMap = nullptr;
112
113// FIXME: We should store these based on physical direction.
114typedef HashMap<const RenderBox*, std::optional<LayoutUnit>> OverrideOptionalSizeMap;
115static OverrideOptionalSizeMap* gOverridingContainingBlockContentLogicalHeightMap = nullptr;
116static OverrideOptionalSizeMap* gOverridingContainingBlockContentLogicalWidthMap = nullptr;
117
118// Size of border belt for autoscroll. When mouse pointer in border belt,
119// autoscroll is started.
120static const int autoscrollBeltSize = 20;
121static const unsigned backgroundObscurationTestMaxDepth = 4;
122
123using ControlStatesRendererMap = HashMap<const RenderObject*, std::unique_ptr<ControlStates>>;
124static ControlStatesRendererMap& controlStatesRendererMap()
125{
126 static NeverDestroyed<ControlStatesRendererMap> map;
127 return map;
128}
129
130static ControlStates* controlStatesForRenderer(const RenderBox& renderer)
131{
132 return controlStatesRendererMap().ensure(&renderer, [] {
133 return makeUnique<ControlStates>();
134 }).iterator->value.get();
135}
136
137static void removeControlStatesForRenderer(const RenderBox& renderer)
138{
139 controlStatesRendererMap().remove(&renderer);
140}
141
142bool RenderBox::s_hadNonVisibleOverflow = false;
143
144RenderBox::RenderBox(Element& element, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
145 : RenderBoxModelObject(element, WTFMove(style), baseTypeFlags)
146{
147 setIsBox();
148}
149
150RenderBox::RenderBox(Document& document, RenderStyle&& style, BaseTypeFlags baseTypeFlags)
151 : RenderBoxModelObject(document, WTFMove(style), baseTypeFlags)
152{
153 setIsBox();
154}
155
156RenderBox::~RenderBox()
157{
158 // Do not add any code here. Add it to willBeDestroyed() instead.
159}
160
161void RenderBox::willBeDestroyed()
162{
163 if (frame().eventHandler().autoscrollRenderer() == this)
164 frame().eventHandler().stopAutoscrollTimer(true);
165
166 clearOverridingContentSize();
167 clearOverridingContainingBlockContentSize();
168
169 RenderBlock::removePercentHeightDescendantIfNeeded(*this);
170
171 ShapeOutsideInfo::removeInfo(*this);
172
173 view().unscheduleLazyRepaint(*this);
174 removeControlStatesForRenderer(*this);
175
176 if (hasInitializedStyle()) {
177 if (style().hasSnapPosition())
178 view().unregisterBoxWithScrollSnapPositions(*this);
179 if (style().containerType() != ContainerType::None)
180 view().unregisterContainerQueryBox(*this);
181 }
182
183 RenderBoxModelObject::willBeDestroyed();
184}
185
186RenderFragmentContainer* RenderBox::clampToStartAndEndFragments(RenderFragmentContainer* fragment) const
187{
188 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
189
190 ASSERT(isRenderView() || (fragment && fragmentedFlow));
191 if (isRenderView())
192 return fragment;
193
194 // We need to clamp to the block, since we want any lines or blocks that overflow out of the
195 // logical top or logical bottom of the block to size as though the border box in the first and
196 // last fragments extended infinitely. Otherwise the lines are going to size according to the fragments
197 // they overflow into, which makes no sense when this block doesn't exist in |fragment| at all.
198 RenderFragmentContainer* startFragment = nullptr;
199 RenderFragmentContainer* endFragment = nullptr;
200 if (!fragmentedFlow->getFragmentRangeForBox(this, startFragment, endFragment))
201 return fragment;
202
203 if (fragment->logicalTopForFragmentedFlowContent() < startFragment->logicalTopForFragmentedFlowContent())
204 return startFragment;
205 if (fragment->logicalTopForFragmentedFlowContent() > endFragment->logicalTopForFragmentedFlowContent())
206 return endFragment;
207
208 return fragment;
209}
210
211bool RenderBox::hasFragmentRangeInFragmentedFlow() const
212{
213 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
214 if (!fragmentedFlow || !fragmentedFlow->hasValidFragmentInfo())
215 return false;
216
217 return fragmentedFlow->hasCachedFragmentRangeForBox(*this);
218}
219
220LayoutRect RenderBox::clientBoxRectInFragment(RenderFragmentContainer* fragment) const
221{
222 if (!fragment)
223 return clientBoxRect();
224
225 LayoutRect clientBox = borderBoxRectInFragment(fragment);
226 clientBox.setLocation(clientBox.location() + LayoutSize(borderLeft(), borderTop()));
227 clientBox.setSize(clientBox.size() - LayoutSize(borderLeft() + borderRight() + verticalScrollbarWidth(), borderTop() + borderBottom() + horizontalScrollbarHeight()));
228
229 return clientBox;
230}
231
232LayoutRect RenderBox::borderBoxRectInFragment(RenderFragmentContainer*, RenderBoxFragmentInfoFlags) const
233{
234 return borderBoxRect();
235}
236
237static RenderBlockFlow* outermostBlockContainingFloatingObject(RenderBox& box)
238{
239 ASSERT(box.isFloating());
240 RenderBlockFlow* parentBlock = nullptr;
241 for (auto& ancestor : ancestorsOfType<RenderBlockFlow>(box)) {
242 if (!parentBlock || ancestor.containsFloat(box))
243 parentBlock = &ancestor;
244 }
245 return parentBlock;
246}
247
248void RenderBox::removeFloatingOrPositionedChildFromBlockLists()
249{
250 ASSERT(isFloatingOrOutOfFlowPositioned());
251
252 if (renderTreeBeingDestroyed())
253 return;
254
255 if (isFloating()) {
256 if (RenderBlockFlow* parentBlock = outermostBlockContainingFloatingObject(*this)) {
257 parentBlock->markSiblingsWithFloatsForLayout(this);
258 parentBlock->markAllDescendantsWithFloatsForLayout(this, false);
259 }
260 }
261
262 if (isOutOfFlowPositioned())
263 RenderBlock::removePositionedObject(*this);
264}
265
266void RenderBox::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
267{
268 s_hadNonVisibleOverflow = hasNonVisibleOverflow();
269
270 const RenderStyle* oldStyle = hasInitializedStyle() ? &style() : nullptr;
271 if (oldStyle) {
272 // The background of the root element or the body element could propagate up to
273 // the canvas. Issue full repaint, when our style changes substantially.
274 if (diff >= StyleDifference::Repaint && (isDocumentElementRenderer() || isBody())) {
275 view().repaintRootContents();
276 if (oldStyle->hasEntirelyFixedBackground() != newStyle.hasEntirelyFixedBackground())
277 view().compositor().rootLayerConfigurationChanged();
278 }
279
280 // When a layout hint happens and an object's position style changes, we have to do a layout
281 // to dirty the render tree using the old position value now.
282 if (diff == StyleDifference::Layout && parent() && oldStyle->position() != newStyle.position()) {
283 markContainingBlocksForLayout();
284 if (oldStyle->position() != PositionType::Static && newStyle.hasOutOfFlowPosition())
285 parent()->setChildNeedsLayout();
286 if (isFloating() && !isOutOfFlowPositioned() && newStyle.hasOutOfFlowPosition())
287 removeFloatingOrPositionedChildFromBlockLists();
288 }
289 } else if (isBody())
290 view().repaintRootContents();
291
292 bool boxContributesSnapPositions = newStyle.hasSnapPosition();
293 if (boxContributesSnapPositions || (oldStyle && oldStyle->hasSnapPosition())) {
294 if (boxContributesSnapPositions)
295 view().registerBoxWithScrollSnapPositions(*this);
296 else
297 view().unregisterBoxWithScrollSnapPositions(*this);
298 }
299
300 if (newStyle.containerType() != ContainerType::None)
301 view().registerContainerQueryBox(*this);
302 else if (oldStyle && oldStyle->containerType() != ContainerType::None)
303 view().unregisterContainerQueryBox(*this);
304
305 RenderBoxModelObject::styleWillChange(diff, newStyle);
306}
307
308void RenderBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
309{
310 // Horizontal writing mode definition is updated in RenderBoxModelObject::updateFromStyle,
311 // (as part of the RenderBoxModelObject::styleDidChange call below). So, we can safely cache the horizontal
312 // writing mode value before style change here.
313 bool oldHorizontalWritingMode = isHorizontalWritingMode();
314
315 RenderBoxModelObject::styleDidChange(diff, oldStyle);
316
317 const RenderStyle& newStyle = style();
318 if (needsLayout() && oldStyle) {
319 RenderBlock::removePercentHeightDescendantIfNeeded(*this);
320
321 // Normally we can do optimized positioning layout for absolute/fixed positioned objects. There is one special case, however, which is
322 // when the positioned object's margin-before is changed. In this case the parent has to get a layout in order to run margin collapsing
323 // to determine the new static position.
324 if (isOutOfFlowPositioned() && newStyle.hasStaticBlockPosition(isHorizontalWritingMode()) && oldStyle->marginBefore() != newStyle.marginBefore()
325 && parent() && !parent()->normalChildNeedsLayout())
326 parent()->setChildNeedsLayout();
327 }
328
329 if (RenderBlock::hasPercentHeightContainerMap() && firstChild()
330 && oldHorizontalWritingMode != isHorizontalWritingMode())
331 RenderBlock::clearPercentHeightDescendantsFrom(*this);
332
333 // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the
334 // new zoomed coordinate space.
335 if (hasNonVisibleOverflow() && layer() && oldStyle && oldStyle->effectiveZoom() != newStyle.effectiveZoom()) {
336 if (auto* scrollableArea = layer()->scrollableArea()) {
337 ScrollPosition scrollPosition = scrollableArea->scrollPosition();
338 float zoomScaleFactor = newStyle.effectiveZoom() / oldStyle->effectiveZoom();
339 scrollPosition.scale(zoomScaleFactor);
340 scrollableArea->setPostLayoutScrollPosition(scrollPosition);
341 }
342 }
343
344 // Our opaqueness might have changed without triggering layout.
345 if (diff >= StyleDifference::Repaint && diff <= StyleDifference::RepaintLayer) {
346 auto parentToInvalidate = parent();
347 for (unsigned i = 0; i < backgroundObscurationTestMaxDepth && parentToInvalidate; ++i) {
348 parentToInvalidate->invalidateBackgroundObscurationStatus();
349 parentToInvalidate = parentToInvalidate->parent();
350 }
351 }
352
353 bool isBodyRenderer = isBody();
354 bool isDocElementRenderer = isDocumentElementRenderer();
355
356 if (isDocElementRenderer || isBodyRenderer) {
357 auto* documentElementRenderer = document().documentElement()->renderer();
358 auto& viewStyle = view().mutableStyle();
359 bool rootStyleChanged = false;
360 bool viewDirectionOrWritingModeChanged = false;
361 auto* rootRenderer = isBodyRenderer ? documentElementRenderer : nullptr;
362
363 auto propagateWritingModeToRenderViewIfApplicable = [&] {
364 // Propagate the new writing mode and direction up to the RenderView.
365 if (!documentElementRenderer)
366 return;
367 if (!isBodyRenderer || !(shouldApplyAnyContainment() || documentElementRenderer->shouldApplyAnyContainment())) {
368 if (viewStyle.direction() != newStyle.direction() && (isDocElementRenderer || !documentElementRenderer->style().hasExplicitlySetDirection())) {
369 viewStyle.setDirection(newStyle.direction());
370 viewDirectionOrWritingModeChanged = true;
371 if (isBodyRenderer) {
372 rootRenderer->mutableStyle().setDirection(newStyle.direction());
373 rootStyleChanged = true;
374 }
375 setNeedsLayoutAndPrefWidthsRecalc();
376
377 view().frameView().topContentDirectionDidChange();
378 }
379
380 if (viewStyle.writingMode() != newStyle.writingMode() && (isDocElementRenderer || !documentElementRenderer->style().hasExplicitlySetWritingMode())) {
381 viewStyle.setWritingMode(newStyle.writingMode());
382 viewDirectionOrWritingModeChanged = true;
383 view().setHorizontalWritingMode(newStyle.isHorizontalWritingMode());
384 view().markAllDescendantsWithFloatsForLayout();
385 if (isBodyRenderer) {
386 rootStyleChanged = true;
387 rootRenderer->mutableStyle().setWritingMode(newStyle.writingMode());
388 rootRenderer->setHorizontalWritingMode(newStyle.isHorizontalWritingMode());
389 }
390 setNeedsLayoutAndPrefWidthsRecalc();
391 }
392 }
393 };
394 propagateWritingModeToRenderViewIfApplicable();
395
396#if ENABLE(DARK_MODE_CSS)
397 view().frameView().recalculateBaseBackgroundColor();
398#endif
399
400 view().frameView().recalculateScrollbarOverlayStyle();
401
402 const Pagination& pagination = view().frameView().pagination();
403 if (viewDirectionOrWritingModeChanged && pagination.mode != Pagination::Unpaginated) {
404 viewStyle.setColumnStylesFromPaginationMode(pagination.mode);
405 if (view().multiColumnFlow())
406 view().updateColumnProgressionFromStyle(viewStyle);
407 }
408
409 if (viewDirectionOrWritingModeChanged && view().multiColumnFlow())
410 view().updateStylesForColumnChildren();
411
412 if (rootStyleChanged && is<RenderBlockFlow>(rootRenderer) && downcast<RenderBlockFlow>(*rootRenderer).multiColumnFlow())
413 downcast<RenderBlockFlow>(*rootRenderer).updateStylesForColumnChildren();
414
415 if (isBodyRenderer && pagination.mode != Pagination::Unpaginated && page().paginationLineGridEnabled()) {
416 // Propagate the body font back up to the RenderView and use it as
417 // the basis of the grid.
418 if (newStyle.fontDescription() != view().style().fontDescription()) {
419 view().mutableStyle().setFontDescription(FontCascadeDescription { newStyle.fontDescription() });
420 view().mutableStyle().fontCascade().update(&document().fontSelector());
421 }
422 }
423
424 if (diff != StyleDifference::Equal)
425 view().compositor().rootOrBodyStyleChanged(*this, oldStyle);
426 }
427
428 if ((oldStyle && oldStyle->shapeOutside()) || style().shapeOutside())
429 updateShapeOutsideInfoAfterStyleChange(style(), oldStyle);
430 updateGridPositionAfterStyleChange(style(), oldStyle);
431
432 // Changing the position from/to absolute can potentially create/remove flex/grid items, as absolutely positioned
433 // children of a flex/grid box are out-of-flow, and thus, not flex/grid items. This means that we need to clear
434 // any override content size set by our container, because it would likely be incorrect after the style change.
435 if (isOutOfFlowPositioned() && parent() && parent()->style().isDisplayFlexibleBoxIncludingDeprecatedOrGridBox())
436 clearOverridingContentSize();
437
438#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
439 if (auto* lineLayout = LayoutIntegration::LineLayout::containing(*this))
440 lineLayout->updateStyle(*this, *oldStyle);
441#endif
442}
443
444void RenderBox::updateGridPositionAfterStyleChange(const RenderStyle& style, const RenderStyle* oldStyle)
445{
446 if (!oldStyle || !is<RenderGrid>(parent()))
447 return;
448
449 if (oldStyle->gridItemColumnStart() == style.gridItemColumnStart()
450 && oldStyle->gridItemColumnEnd() == style.gridItemColumnEnd()
451 && oldStyle->gridItemRowStart() == style.gridItemRowStart()
452 && oldStyle->gridItemRowEnd() == style.gridItemRowEnd()
453 && oldStyle->order() == style.order()
454 && oldStyle->hasOutOfFlowPosition() == style.hasOutOfFlowPosition())
455 return;
456
457 // Positioned items don't participate on the layout of the grid,
458 // so we don't need to mark the grid as dirty if they change positions.
459 if (oldStyle->hasOutOfFlowPosition() && style.hasOutOfFlowPosition())
460 return;
461
462 // It should be possible to not dirty the grid in some cases (like moving an
463 // explicitly placed grid item).
464 // For now, it's more simple to just always recompute the grid.
465 downcast<RenderGrid>(*parent()).dirtyGrid();
466}
467
468void RenderBox::updateShapeOutsideInfoAfterStyleChange(const RenderStyle& style, const RenderStyle* oldStyle)
469{
470 const ShapeValue* shapeOutside = style.shapeOutside();
471 const ShapeValue* oldShapeOutside = oldStyle ? oldStyle->shapeOutside() : nullptr;
472
473 Length shapeMargin = style.shapeMargin();
474 Length oldShapeMargin = oldStyle ? oldStyle->shapeMargin() : RenderStyle::initialShapeMargin();
475
476 float shapeImageThreshold = style.shapeImageThreshold();
477 float oldShapeImageThreshold = oldStyle ? oldStyle->shapeImageThreshold() : RenderStyle::initialShapeImageThreshold();
478
479 // FIXME: A future optimization would do a deep comparison for equality. (bug 100811)
480 if (shapeOutside == oldShapeOutside && shapeMargin == oldShapeMargin && shapeImageThreshold == oldShapeImageThreshold)
481 return;
482
483 if (!shapeOutside)
484 ShapeOutsideInfo::removeInfo(*this);
485 else
486 ShapeOutsideInfo::ensureInfo(*this).markShapeAsDirty();
487
488 if (shapeOutside || shapeOutside != oldShapeOutside)
489 markShapeOutsideDependentsForLayout();
490}
491
492void RenderBox::updateFromStyle()
493{
494 RenderBoxModelObject::updateFromStyle();
495
496 const RenderStyle& styleToUse = style();
497 bool isDocElementRenderer = isDocumentElementRenderer();
498 bool isViewObject = isRenderView();
499
500 // The root and the RenderView always paint their backgrounds/borders.
501 if (isDocElementRenderer || isViewObject)
502 setHasVisibleBoxDecorations(true);
503
504 setFloating(!isOutOfFlowPositioned() && styleToUse.isFloating());
505
506 // We also handle <body> and <html>, whose overflow applies to the viewport.
507 if (!(effectiveOverflowX() == Overflow::Visible && effectiveOverflowY() == Overflow::Visible) && !isDocElementRenderer && isRenderBlock()) {
508 bool boxHasNonVisibleOverflow = true;
509 if (isBody()) {
510 // Overflow on the body can propagate to the viewport under the following conditions.
511 // (1) The root element is <html>.
512 // (2) We are the primary <body> (can be checked by looking at document.body).
513 // (3) The root element has visible overflow.
514 // (4) No containment is set either on the body or on the html document element.
515 auto& documentElement = *document().documentElement();
516 auto& documentElementRenderer = *documentElement.renderer();
517 if (is<HTMLHtmlElement>(documentElement)
518 && document().body() == element()
519 && documentElementRenderer.effectiveOverflowX() == Overflow::Visible
520 && !styleToUse.effectiveContainment()
521 && !documentElementRenderer.style().effectiveContainment()) {
522 boxHasNonVisibleOverflow = false;
523 }
524 }
525 // Check for overflow clip.
526 // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value.
527 if (boxHasNonVisibleOverflow) {
528 if (!s_hadNonVisibleOverflow && hasRenderOverflow()) {
529 // Erase the overflow.
530 // Overflow changes have to result in immediate repaints of the entire layout overflow area because
531 // repaints issued by removal of descendants get clipped using the updated style when they shouldn't.
532 repaintRectangle(visualOverflowRect());
533 repaintRectangle(layoutOverflowRect());
534 }
535 setHasNonVisibleOverflow();
536 }
537 }
538 setHasTransformRelatedProperty(styleToUse.hasTransformRelatedProperty());
539 setHasReflection(styleToUse.boxReflect());
540}
541
542void RenderBox::layout()
543{
544 StackStats::LayoutCheckPoint layoutCheckPoint;
545 ASSERT(needsLayout());
546
547 RenderObject* child = firstChild();
548 if (!child) {
549 clearNeedsLayout();
550 return;
551 }
552
553 LayoutStateMaintainer statePusher(*this, locationOffset(), style().isFlippedBlocksWritingMode());
554 while (child) {
555 if (child->needsLayout())
556 downcast<RenderElement>(*child).layout();
557 ASSERT(!child->needsLayout());
558 child = child->nextSibling();
559 }
560 invalidateBackgroundObscurationStatus();
561 clearNeedsLayout();
562}
563
564// More IE extensions. clientWidth and clientHeight represent the interior of an object
565// excluding border and scrollbar.
566LayoutUnit RenderBox::clientWidth() const
567{
568 return paddingBoxWidth();
569}
570
571LayoutUnit RenderBox::clientHeight() const
572{
573 return paddingBoxHeight();
574}
575
576int RenderBox::scrollWidth() const
577{
578 if (hasPotentiallyScrollableOverflow() && layer())
579 return layer()->scrollWidth();
580 // For objects with visible overflow, this matches IE.
581 // FIXME: Need to work right with writing modes.
582 if (style().isLeftToRightDirection()) {
583 // FIXME: This should use snappedIntSize() instead with absolute coordinates.
584 return roundToInt(std::max(clientWidth(), layoutOverflowRect().maxX() - borderLeft()));
585 }
586 return roundToInt(clientWidth() - std::min<LayoutUnit>(0, layoutOverflowRect().x() - borderLeft()));
587}
588
589int RenderBox::scrollHeight() const
590{
591 if (hasPotentiallyScrollableOverflow() && layer())
592 return layer()->scrollHeight();
593 // For objects with visible overflow, this matches IE.
594 // FIXME: Need to work right with writing modes.
595 // FIXME: This should use snappedIntSize() instead with absolute coordinates.
596 return roundToInt(std::max(clientHeight(), layoutOverflowRect().maxY() - borderTop()));
597}
598
599int RenderBox::scrollLeft() const
600{
601 auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr;
602 return (hasNonVisibleOverflow() && scrollableArea) ? scrollableArea->scrollPosition().x() : 0;
603}
604
605int RenderBox::scrollTop() const
606{
607 auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr;
608 return (hasNonVisibleOverflow() && scrollableArea) ? scrollableArea->scrollPosition().y() : 0;
609}
610
611void RenderBox::resetLogicalHeightBeforeLayoutIfNeeded()
612{
613 if (shouldResetLogicalHeightBeforeLayout() || (is<RenderBlock>(parent()) && downcast<RenderBlock>(*parent()).shouldResetChildLogicalHeightBeforeLayout(*this)))
614 setLogicalHeight(0_lu);
615}
616
617static void setupWheelEventMonitor(RenderLayerScrollableArea& scrollableArea)
618{
619 Page& page = scrollableArea.layer().renderer().page();
620 if (!page.isMonitoringWheelEvents())
621 return;
622 scrollableArea.scrollAnimator().setWheelEventTestMonitor(page.wheelEventTestMonitor());
623}
624
625void RenderBox::setScrollLeft(int newLeft, const ScrollPositionChangeOptions& options)
626{
627 if (!hasPotentiallyScrollableOverflow() || !layer())
628 return;
629 auto* scrollableArea = layer()->scrollableArea();
630 ASSERT(scrollableArea);
631 setupWheelEventMonitor(*scrollableArea);
632 scrollableArea->scrollToXPosition(newLeft, options);
633}
634
635void RenderBox::setScrollTop(int newTop, const ScrollPositionChangeOptions& options)
636{
637 if (!hasPotentiallyScrollableOverflow() || !layer())
638 return;
639 auto* scrollableArea = layer()->scrollableArea();
640 ASSERT(scrollableArea);
641 setupWheelEventMonitor(*scrollableArea);
642 scrollableArea->scrollToYPosition(newTop, options);
643}
644
645void RenderBox::setScrollPosition(const ScrollPosition& position, const ScrollPositionChangeOptions& options)
646{
647 if (!hasPotentiallyScrollableOverflow() || !layer())
648 return;
649 auto* scrollableArea = layer()->scrollableArea();
650 ASSERT(scrollableArea);
651 setupWheelEventMonitor(*scrollableArea);
652 scrollableArea->setScrollPosition(position, options);
653}
654
655void RenderBox::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
656{
657 rects.append(snappedIntRect(accumulatedOffset, size()));
658}
659
660void RenderBox::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
661{
662 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
663 if (fragmentedFlow && fragmentedFlow->absoluteQuadsForBox(quads, wasFixed, this))
664 return;
665
666 auto localRect = FloatRect { 0, 0, width(), height() };
667 quads.append(localToAbsoluteQuad(localRect, UseTransforms, wasFixed));
668}
669
670void RenderBox::applyTransform(TransformationMatrix& t, const RenderStyle& style, const FloatRect& boundingBox, OptionSet<RenderStyle::TransformOperationOption> options) const
671{
672 style.applyTransform(t, boundingBox, options);
673}
674
675LayoutUnit RenderBox::constrainLogicalWidthInFragmentByMinMax(LayoutUnit logicalWidth, LayoutUnit availableWidth, RenderBlock& cb, RenderFragmentContainer* fragment, AllowIntrinsic allowIntrinsic) const
676{
677 const RenderStyle& styleToUse = style();
678
679 if (shouldComputeLogicalHeightFromAspectRatio()) {
680 auto [logicalMinWidth, logicalMaxWidth] = computeMinMaxLogicalWidthFromAspectRatio();
681 logicalWidth = std::clamp(logicalWidth, logicalMinWidth, logicalMaxWidth);
682 }
683
684 if (!styleToUse.logicalMaxWidth().isUndefined() && (allowIntrinsic == AllowIntrinsic::Yes || !styleToUse.logicalMaxWidth().isIntrinsic()))
685 logicalWidth = std::min(logicalWidth, computeLogicalWidthInFragmentUsing(MaxSize, styleToUse.logicalMaxWidth(), availableWidth, cb, fragment));
686 if (allowIntrinsic == AllowIntrinsic::No && styleToUse.logicalMinWidth().isIntrinsic())
687 return logicalWidth;
688 auto logicalMinWidth = styleToUse.logicalMinWidth();
689 if (logicalMinWidth.isAuto() && shouldComputeLogicalWidthFromAspectRatio() && (styleToUse.logicalWidth().isAuto() || styleToUse.logicalWidth().isMinContent() || styleToUse.logicalWidth().isMaxContent()) && !is<RenderReplaced>(*this) && effectiveOverflowInlineDirection() == Overflow::Visible) {
690 // Make sure we actually used the aspect ratio.
691 logicalMinWidth = Length(LengthType::MinContent);
692 }
693 return std::max(logicalWidth, computeLogicalWidthInFragmentUsing(MinSize, logicalMinWidth, availableWidth, cb, fragment));
694}
695
696LayoutUnit RenderBox::constrainLogicalHeightByMinMax(LayoutUnit logicalHeight, std::optional<LayoutUnit> intrinsicContentHeight) const
697{
698 const RenderStyle& styleToUse = style();
699 if (!styleToUse.logicalMaxHeight().isUndefined()) {
700 if (std::optional<LayoutUnit> maxH = computeLogicalHeightUsing(MaxSize, styleToUse.logicalMaxHeight(), intrinsicContentHeight))
701 logicalHeight = std::min(logicalHeight, maxH.value());
702 }
703 auto logicalMinHeight = styleToUse.logicalMinHeight();
704 if (logicalMinHeight.isAuto() && shouldComputeLogicalHeightFromAspectRatio() && intrinsicContentHeight && !is<RenderReplaced>(*this) && effectiveOverflowBlockDirection() == Overflow::Visible) {
705 auto heightFromAspectRatio = blockSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), logicalWidth()) - borderAndPaddingLogicalHeight();
706 if (firstChild())
707 heightFromAspectRatio = std::max(heightFromAspectRatio, *intrinsicContentHeight);
708 logicalMinHeight = Length(heightFromAspectRatio, LengthType::Fixed);
709 }
710 if (logicalMinHeight.isMinContent() || logicalMinHeight.isMaxContent())
711 logicalMinHeight = Length();
712 if (std::optional<LayoutUnit> computedLogicalHeight = computeLogicalHeightUsing(MinSize, logicalMinHeight, intrinsicContentHeight))
713 return std::max(logicalHeight, computedLogicalHeight.value());
714 return logicalHeight;
715}
716
717LayoutUnit RenderBox::constrainContentBoxLogicalHeightByMinMax(LayoutUnit logicalHeight, std::optional<LayoutUnit> intrinsicContentHeight) const
718{
719 const RenderStyle& styleToUse = style();
720 if (!styleToUse.logicalMaxHeight().isUndefined()) {
721 if (std::optional<LayoutUnit> maxH = computeContentLogicalHeight(MaxSize, styleToUse.logicalMaxHeight(), intrinsicContentHeight))
722 logicalHeight = std::min(logicalHeight, maxH.value());
723 }
724 if (std::optional<LayoutUnit> computedContentLogicalHeight = computeContentLogicalHeight(MinSize, styleToUse.logicalMinHeight(), intrinsicContentHeight))
725 return std::max(logicalHeight, computedContentLogicalHeight.value());
726 return logicalHeight;
727}
728
729RoundedRect::Radii RenderBox::borderRadii() const
730{
731 auto& style = this->style();
732 LayoutRect bounds = frameRect();
733
734 unsigned borderLeft = style.borderLeftWidth();
735 unsigned borderTop = style.borderTopWidth();
736 bounds.moveBy(LayoutPoint(borderLeft, borderTop));
737 bounds.contract(borderLeft + style.borderRightWidth(), borderTop + style.borderBottomWidth());
738 return style.getRoundedBorderFor(bounds).radii();
739}
740
741RoundedRect RenderBox::roundedBorderBoxRect() const
742{
743 return style().getRoundedInnerBorderFor(borderBoxRect());
744}
745
746LayoutRect RenderBox::paddingBoxRect() const
747{
748 auto verticalScrollbarWidth = this->verticalScrollbarWidth();
749 LayoutUnit offsetForScrollbar = shouldPlaceVerticalScrollbarOnLeft() ? verticalScrollbarWidth : 0;
750
751 return LayoutRect(borderLeft() + offsetForScrollbar, borderTop(),
752 width() - borderLeft() - borderRight() - verticalScrollbarWidth,
753 height() - borderTop() - borderBottom() - horizontalScrollbarHeight());
754}
755
756LayoutRect RenderBox::contentBoxRect() const
757{
758 return { contentBoxLocation(), contentSize() };
759}
760
761LayoutPoint RenderBox::contentBoxLocation() const
762{
763 LayoutUnit scrollbarSpace = shouldPlaceVerticalScrollbarOnLeft() ? verticalScrollbarWidth() : 0;
764 return { borderLeft() + paddingLeft() + scrollbarSpace, borderTop() + paddingTop() };
765}
766
767FloatRect RenderBox::referenceBoxRect(CSSBoxType boxType) const
768{
769 switch (boxType) {
770 case CSSBoxType::ContentBox:
771 case CSSBoxType::FillBox:
772 return contentBoxRect();
773 case CSSBoxType::PaddingBox:
774 return paddingBoxRect();
775 case CSSBoxType::MarginBox:
776 return marginBoxRect();
777 // stroke-box, view-box compute to border-box for HTML elements.
778 case CSSBoxType::StrokeBox:
779 case CSSBoxType::ViewBox:
780 case CSSBoxType::BorderBox:
781 case CSSBoxType::BoxMissing:
782 return borderBoxRect();
783 }
784
785 ASSERT_NOT_REACHED();
786 return { };
787}
788
789IntRect RenderBox::absoluteContentBox() const
790{
791 // This is wrong with transforms and flipped writing modes.
792 IntRect rect = snappedIntRect(contentBoxRect());
793 FloatPoint absPos = localToAbsolute();
794 rect.move(absPos.x(), absPos.y());
795 return rect;
796}
797
798FloatQuad RenderBox::absoluteContentQuad() const
799{
800 LayoutRect rect = contentBoxRect();
801 return localToAbsoluteQuad(FloatRect(rect));
802}
803
804LayoutRect RenderBox::outlineBoundsForRepaint(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap) const
805{
806 LayoutRect box = borderBoundingBox();
807 adjustRectForOutlineAndShadow(box);
808
809 if (repaintContainer != this) {
810 FloatQuad containerRelativeQuad;
811 if (geometryMap)
812 containerRelativeQuad = geometryMap->mapToContainer(box, repaintContainer);
813 else
814 containerRelativeQuad = localToContainerQuad(FloatRect(box), repaintContainer);
815
816 box = LayoutRect(containerRelativeQuad.boundingBox());
817 }
818
819 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
820 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
821 box.move(view().frameView().layoutContext().layoutDelta());
822
823 return LayoutRect(snapRectToDevicePixels(box, document().deviceScaleFactor()));
824}
825
826void RenderBox::addFocusRingRects(Vector<LayoutRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*)
827{
828 if (!size().isEmpty())
829 rects.append(LayoutRect(additionalOffset, size()));
830}
831
832int RenderBox::reflectionOffset() const
833{
834 if (!style().boxReflect())
835 return 0;
836 if (style().boxReflect()->direction() == ReflectionDirection::Left || style().boxReflect()->direction() == ReflectionDirection::Right)
837 return valueForLength(style().boxReflect()->offset(), borderBoxRect().width());
838 return valueForLength(style().boxReflect()->offset(), borderBoxRect().height());
839}
840
841LayoutRect RenderBox::reflectedRect(const LayoutRect& r) const
842{
843 if (!style().boxReflect())
844 return LayoutRect();
845
846 LayoutRect box = borderBoxRect();
847 LayoutRect result = r;
848 switch (style().boxReflect()->direction()) {
849 case ReflectionDirection::Below:
850 result.setY(box.maxY() + reflectionOffset() + (box.maxY() - r.maxY()));
851 break;
852 case ReflectionDirection::Above:
853 result.setY(box.y() - reflectionOffset() - box.height() + (box.maxY() - r.maxY()));
854 break;
855 case ReflectionDirection::Left:
856 result.setX(box.x() - reflectionOffset() - box.width() + (box.maxX() - r.maxX()));
857 break;
858 case ReflectionDirection::Right:
859 result.setX(box.maxX() + reflectionOffset() + (box.maxX() - r.maxX()));
860 break;
861 }
862 return result;
863}
864
865bool RenderBox::fixedElementLaysOutRelativeToFrame(const FrameView& frameView) const
866{
867 return isFixedPositioned() && container()->isRenderView() && frameView.fixedElementsLayoutRelativeToFrame();
868}
869
870bool RenderBox::includeVerticalScrollbarSize() const
871{
872 return hasNonVisibleOverflow() && layer() && !layer()->hasOverlayScrollbars()
873 && (style().overflowY() == Overflow::Scroll || style().overflowY() == Overflow::Auto);
874}
875
876bool RenderBox::includeHorizontalScrollbarSize() const
877{
878 return hasNonVisibleOverflow() && layer() && !layer()->hasOverlayScrollbars()
879 && (style().overflowX() == Overflow::Scroll || style().overflowX() == Overflow::Auto);
880}
881
882int RenderBox::verticalScrollbarWidth() const
883{
884 auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr;
885 if (!scrollableArea)
886 return 0;
887 return includeVerticalScrollbarSize() ? scrollableArea->verticalScrollbarWidth() : 0;
888}
889
890int RenderBox::horizontalScrollbarHeight() const
891{
892 auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr;
893 if (!scrollableArea)
894 return 0;
895 return includeHorizontalScrollbarSize() ? scrollableArea->horizontalScrollbarHeight() : 0;
896}
897
898int RenderBox::intrinsicScrollbarLogicalWidth() const
899{
900 if (!hasNonVisibleOverflow())
901 return 0;
902
903 if (isHorizontalWritingMode() && (style().overflowY() == Overflow::Scroll && !canUseOverlayScrollbars())) {
904 ASSERT(layer() && layer()->scrollableArea() && layer()->scrollableArea()->hasVerticalScrollbar());
905 return verticalScrollbarWidth();
906 }
907
908 if (!isHorizontalWritingMode() && (style().overflowX() == Overflow::Scroll && !canUseOverlayScrollbars())) {
909 ASSERT(layer() && layer()->scrollableArea() && layer()->scrollableArea()->hasHorizontalScrollbar());
910 return horizontalScrollbarHeight();
911 }
912
913 return 0;
914}
915
916bool RenderBox::scrollLayer(ScrollDirection direction, ScrollGranularity granularity, unsigned stepCount, Element** stopElement)
917{
918 auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr;
919 if (scrollableArea && scrollableArea->scroll(direction, granularity, stepCount)) {
920 if (stopElement)
921 *stopElement = element();
922
923 return true;
924 }
925
926 return false;
927}
928
929bool RenderBox::scroll(ScrollDirection direction, ScrollGranularity granularity, unsigned stepCount, Element** stopElement, RenderBox* startBox, const IntPoint& wheelEventAbsolutePoint)
930{
931 if (scrollLayer(direction, granularity, stepCount, stopElement))
932 return true;
933
934 if (stopElement && *stopElement && *stopElement == element())
935 return true;
936
937 RenderBlock* nextScrollBlock = containingBlock();
938
939 if (nextScrollBlock && !nextScrollBlock->isRenderView())
940 return nextScrollBlock->scroll(direction, granularity, stepCount, stopElement, startBox, wheelEventAbsolutePoint);
941
942 return false;
943}
944
945bool RenderBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, unsigned stepCount, Element** stopElement)
946{
947 bool scrolled = false;
948
949 if (auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr) {
950#if PLATFORM(COCOA)
951 // On Mac only we reset the inline direction position when doing a document scroll (e.g., hitting Home/End).
952 if (granularity == ScrollGranularity::Document)
953 scrolled = scrollableArea->scroll(logicalToPhysical(ScrollInlineDirectionBackward, isHorizontalWritingMode(), style().isFlippedBlocksWritingMode()), ScrollGranularity::Document, stepCount);
954#endif
955 if (scrollableArea->scroll(logicalToPhysical(direction, isHorizontalWritingMode(), style().isFlippedBlocksWritingMode()), granularity, stepCount))
956 scrolled = true;
957
958 if (scrolled) {
959 if (stopElement)
960 *stopElement = element();
961 return true;
962 }
963 }
964
965 if (stopElement && *stopElement && *stopElement == element())
966 return true;
967
968 RenderBlock* b = containingBlock();
969 if (b && !b->isRenderView())
970 return b->logicalScroll(direction, granularity, stepCount, stopElement);
971 return false;
972}
973
974bool RenderBox::canBeScrolledAndHasScrollableArea() const
975{
976 return canBeProgramaticallyScrolled() && (hasHorizontalOverflow() || hasVerticalOverflow());
977}
978
979bool RenderBox::isScrollableOrRubberbandableBox() const
980{
981 return canBeScrolledAndHasScrollableArea();
982}
983
984bool RenderBox::requiresLayerWithScrollableArea() const
985{
986 // FIXME: This is wrong; these boxes' layers should not need ScrollableAreas via RenderLayer.
987 if (isRenderView() || isDocumentElementRenderer())
988 return true;
989
990 if (hasPotentiallyScrollableOverflow())
991 return true;
992
993 if (style().resize() != Resize::None)
994 return true;
995
996 if (isHTMLMarquee() && style().marqueeBehavior() != MarqueeBehavior::None)
997 return true;
998
999 return false;
1000}
1001
1002// FIXME: This is badly named. overflow:hidden can be programmatically scrolled, yet this returns false in that case.
1003bool RenderBox::canBeProgramaticallyScrolled() const
1004{
1005 if (isRenderView())
1006 return true;
1007
1008 if (!hasPotentiallyScrollableOverflow())
1009 return false;
1010
1011 if (hasScrollableOverflowX() || hasScrollableOverflowY())
1012 return true;
1013
1014 return element() && element()->hasEditableStyle();
1015}
1016
1017bool RenderBox::usesCompositedScrolling() const
1018{
1019 return hasNonVisibleOverflow() && hasLayer() && layer()->usesCompositedScrolling();
1020}
1021
1022void RenderBox::autoscroll(const IntPoint& position)
1023{
1024 if (layer())
1025 layer()->autoscroll(position);
1026}
1027
1028// There are two kinds of renderer that can autoscroll.
1029bool RenderBox::canAutoscroll() const
1030{
1031 if (isRenderView())
1032 return view().frameView().isScrollable();
1033
1034 // Check for a box that can be scrolled in its own right.
1035 if (canBeScrolledAndHasScrollableArea())
1036 return true;
1037
1038 return false;
1039}
1040
1041// If specified point is in border belt, returned offset denotes direction of
1042// scrolling.
1043IntSize RenderBox::calculateAutoscrollDirection(const IntPoint& windowPoint) const
1044{
1045 IntRect box(absoluteBoundingBoxRect());
1046 box.moveBy(view().frameView().scrollPosition());
1047 IntRect windowBox = view().frameView().contentsToWindow(box);
1048
1049 IntPoint windowAutoscrollPoint = windowPoint;
1050
1051 if (windowAutoscrollPoint.x() < windowBox.x() + autoscrollBeltSize)
1052 windowAutoscrollPoint.move(-autoscrollBeltSize, 0);
1053 else if (windowAutoscrollPoint.x() > windowBox.maxX() - autoscrollBeltSize)
1054 windowAutoscrollPoint.move(autoscrollBeltSize, 0);
1055
1056 if (windowAutoscrollPoint.y() < windowBox.y() + autoscrollBeltSize)
1057 windowAutoscrollPoint.move(0, -autoscrollBeltSize);
1058 else if (windowAutoscrollPoint.y() > windowBox.maxY() - autoscrollBeltSize)
1059 windowAutoscrollPoint.move(0, autoscrollBeltSize);
1060
1061 return windowAutoscrollPoint - windowPoint;
1062}
1063
1064RenderBox* RenderBox::findAutoscrollable(RenderObject* renderer)
1065{
1066 while (renderer && !(is<RenderBox>(*renderer) && downcast<RenderBox>(*renderer).canAutoscroll())) {
1067 if (is<RenderView>(*renderer) && renderer->document().ownerElement())
1068 renderer = renderer->document().ownerElement()->renderer();
1069 else
1070 renderer = renderer->parent();
1071 }
1072
1073 return dynamicDowncast<RenderBox>(renderer);
1074}
1075
1076void RenderBox::panScroll(const IntPoint& source)
1077{
1078 if (auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr)
1079 scrollableArea->panScrollFromPoint(source);
1080}
1081
1082bool RenderBox::canUseOverlayScrollbars() const
1083{
1084 return !style().hasPseudoStyle(PseudoId::Scrollbar) && ScrollbarTheme::theme().usesOverlayScrollbars();
1085}
1086
1087bool RenderBox::hasAutoScrollbar(ScrollbarOrientation orientation) const
1088{
1089 if (!hasNonVisibleOverflow())
1090 return false;
1091
1092 auto isAutoOrScrollWithOverlayScrollbar = [&](Overflow overflow) {
1093 return overflow == Overflow::Auto || (overflow == Overflow::Scroll && canUseOverlayScrollbars());
1094 };
1095
1096 switch (orientation) {
1097 case ScrollbarOrientation::Horizontal:
1098 return isAutoOrScrollWithOverlayScrollbar(style().overflowX());
1099 case ScrollbarOrientation::Vertical:
1100 return isAutoOrScrollWithOverlayScrollbar(style().overflowY());
1101 }
1102 return false;
1103}
1104
1105bool RenderBox::hasAlwaysPresentScrollbar(ScrollbarOrientation orientation) const
1106{
1107 if (!hasNonVisibleOverflow())
1108 return false;
1109
1110 auto isAlwaysVisibleScrollbar = [&](Overflow overflow) {
1111 return overflow == Overflow::Scroll && !canUseOverlayScrollbars();
1112 };
1113
1114 switch (orientation) {
1115 case ScrollbarOrientation::Horizontal:
1116 return isAlwaysVisibleScrollbar(style().overflowX());
1117 case ScrollbarOrientation::Vertical:
1118 return isAlwaysVisibleScrollbar(style().overflowY());
1119 }
1120 return false;
1121}
1122
1123bool RenderBox::needsPreferredWidthsRecalculation() const
1124{
1125 return style().paddingStart().isPercentOrCalculated() || style().paddingEnd().isPercentOrCalculated() || (style().hasAspectRatio() && (hasRelativeLogicalHeight() || (isFlexItem() && hasStretchedLogicalHeight())));
1126}
1127
1128ScrollPosition RenderBox::scrollPosition() const
1129{
1130 if (!hasPotentiallyScrollableOverflow())
1131 return { 0, 0 };
1132
1133 ASSERT(hasLayer());
1134 auto* scrollableArea = layer()->scrollableArea();
1135 if (!scrollableArea)
1136 return { 0, 0 };
1137
1138 return scrollableArea->scrollPosition();
1139}
1140
1141LayoutSize RenderBox::cachedSizeForOverflowClip() const
1142{
1143 ASSERT(hasNonVisibleOverflow());
1144 ASSERT(hasLayer());
1145 return layer()->size();
1146}
1147
1148bool RenderBox::applyCachedClipAndScrollPosition(LayoutRect& rect, const RenderLayerModelObject* container, VisibleRectContext context) const
1149{
1150 flipForWritingMode(rect);
1151
1152 if (context.options.contains(VisibleRectContextOption::ApplyCompositedContainerScrolls) || this != container || !usesCompositedScrolling())
1153 rect.moveBy(-scrollPosition()); // For overflow:auto/scroll/hidden.
1154
1155 // Do not clip scroll layer contents to reduce the number of repaints while scrolling.
1156 if ((!context.options.contains(VisibleRectContextOption::ApplyCompositedClips) && usesCompositedScrolling())
1157 || (!context.options.contains(VisibleRectContextOption::ApplyContainerClip) && this == container)) {
1158 flipForWritingMode(rect);
1159 return true;
1160 }
1161
1162 // height() is inaccurate if we're in the middle of a layout of this RenderBox, so use the
1163 // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint
1164 // anyway if its size does change.
1165 LayoutRect clipRect(LayoutPoint(), cachedSizeForOverflowClip());
1166 bool intersects;
1167 if (context.options.contains(VisibleRectContextOption::UseEdgeInclusiveIntersection))
1168 intersects = rect.edgeInclusiveIntersect(clipRect);
1169 else {
1170 rect.intersect(clipRect);
1171 intersects = !rect.isEmpty();
1172 }
1173 flipForWritingMode(rect);
1174 return intersects;
1175}
1176
1177void RenderBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
1178{
1179 minLogicalWidth = minPreferredLogicalWidth() - borderAndPaddingLogicalWidth();
1180 maxLogicalWidth = maxPreferredLogicalWidth() - borderAndPaddingLogicalWidth();
1181}
1182
1183LayoutUnit RenderBox::minPreferredLogicalWidth() const
1184{
1185 if (preferredLogicalWidthsDirty()) {
1186 SetLayoutNeededForbiddenScope layoutForbiddenScope(*this);
1187 const_cast<RenderBox&>(*this).computePreferredLogicalWidths();
1188 }
1189 return m_minPreferredLogicalWidth;
1190}
1191
1192LayoutUnit RenderBox::maxPreferredLogicalWidth() const
1193{
1194 if (preferredLogicalWidthsDirty()) {
1195 SetLayoutNeededForbiddenScope layoutForbiddenScope(*this);
1196 const_cast<RenderBox&>(*this).computePreferredLogicalWidths();
1197 }
1198 return m_maxPreferredLogicalWidth;
1199}
1200
1201bool RenderBox::hasOverridingLogicalHeight() const
1202{
1203 return gOverridingLogicalHeightMap && gOverridingLogicalHeightMap->contains(this);
1204}
1205
1206bool RenderBox::hasOverridingLogicalWidth() const
1207{
1208 return gOverridingLogicalWidthMap && gOverridingLogicalWidthMap->contains(this);
1209}
1210
1211void RenderBox::setOverridingLogicalHeight(LayoutUnit height)
1212{
1213 if (!gOverridingLogicalHeightMap)
1214 gOverridingLogicalHeightMap = new OverrideSizeMap();
1215 gOverridingLogicalHeightMap->set(this, height);
1216}
1217
1218void RenderBox::setOverridingLogicalWidth(LayoutUnit width)
1219{
1220 if (!gOverridingLogicalWidthMap)
1221 gOverridingLogicalWidthMap = new OverrideSizeMap();
1222 gOverridingLogicalWidthMap->set(this, width);
1223}
1224
1225void RenderBox::clearOverridingLogicalHeight()
1226{
1227 if (gOverridingLogicalHeightMap)
1228 gOverridingLogicalHeightMap->remove(this);
1229}
1230
1231void RenderBox::clearOverridingLogicalWidth()
1232{
1233 if (gOverridingLogicalWidthMap)
1234 gOverridingLogicalWidthMap->remove(this);
1235}
1236
1237void RenderBox::clearOverridingContentSize()
1238{
1239 clearOverridingLogicalHeight();
1240 clearOverridingLogicalWidth();
1241}
1242
1243LayoutUnit RenderBox::overridingLogicalWidth() const
1244{
1245 ASSERT(hasOverridingLogicalWidth());
1246 return gOverridingLogicalWidthMap->get(this);
1247}
1248
1249LayoutUnit RenderBox::overridingLogicalHeight() const
1250{
1251 ASSERT(hasOverridingLogicalHeight());
1252 return gOverridingLogicalHeightMap->get(this);
1253}
1254
1255std::optional<LayoutUnit> RenderBox::overridingContainingBlockContentWidth() const
1256{
1257 ASSERT(hasOverridingContainingBlockContentWidth());
1258 return containingBlock()->style().isHorizontalWritingMode()
1259 ? gOverridingContainingBlockContentLogicalWidthMap->get(this)
1260 : gOverridingContainingBlockContentLogicalHeightMap->get(this);
1261}
1262
1263std::optional<LayoutUnit> RenderBox::overridingContainingBlockContentHeight() const
1264{
1265 ASSERT(hasOverridingContainingBlockContentHeight());
1266 return containingBlock()->style().isHorizontalWritingMode()
1267 ? gOverridingContainingBlockContentLogicalHeightMap->get(this)
1268 : gOverridingContainingBlockContentLogicalWidthMap->get(this);
1269}
1270
1271bool RenderBox::hasOverridingContainingBlockContentWidth() const
1272{
1273 RenderBlock* cb = containingBlock();
1274 if (!cb)
1275 return false;
1276
1277 return cb->style().isHorizontalWritingMode()
1278 ? gOverridingContainingBlockContentLogicalWidthMap && gOverridingContainingBlockContentLogicalWidthMap->contains(this)
1279 : gOverridingContainingBlockContentLogicalHeightMap && gOverridingContainingBlockContentLogicalHeightMap->contains(this);
1280}
1281
1282bool RenderBox::hasOverridingContainingBlockContentHeight() const
1283{
1284 RenderBlock* cb = containingBlock();
1285 if (!cb)
1286 return false;
1287
1288 return cb->style().isHorizontalWritingMode()
1289 ? gOverridingContainingBlockContentLogicalHeightMap && gOverridingContainingBlockContentLogicalHeightMap->contains(this)
1290 : gOverridingContainingBlockContentLogicalWidthMap && gOverridingContainingBlockContentLogicalWidthMap->contains(this);
1291}
1292
1293std::optional<LayoutUnit> RenderBox::overridingContainingBlockContentLogicalWidth() const
1294{
1295 ASSERT(hasOverridingContainingBlockContentLogicalWidth());
1296 return gOverridingContainingBlockContentLogicalWidthMap->get(this);
1297}
1298
1299std::optional<LayoutUnit> RenderBox::overridingContainingBlockContentLogicalHeight() const
1300{
1301 ASSERT(hasOverridingContainingBlockContentLogicalHeight());
1302 return gOverridingContainingBlockContentLogicalHeightMap->get(this);
1303}
1304
1305bool RenderBox::hasOverridingContainingBlockContentLogicalWidth() const
1306{
1307 return gOverridingContainingBlockContentLogicalWidthMap && gOverridingContainingBlockContentLogicalWidthMap->contains(this);
1308}
1309
1310bool RenderBox::hasOverridingContainingBlockContentLogicalHeight() const
1311{
1312 return gOverridingContainingBlockContentLogicalHeightMap && gOverridingContainingBlockContentLogicalHeightMap->contains(this);
1313}
1314
1315void RenderBox::setOverridingContainingBlockContentLogicalWidth(std::optional<LayoutUnit> logicalWidth)
1316{
1317 if (!gOverridingContainingBlockContentLogicalWidthMap)
1318 gOverridingContainingBlockContentLogicalWidthMap = new OverrideOptionalSizeMap;
1319 gOverridingContainingBlockContentLogicalWidthMap->set(this, logicalWidth);
1320}
1321
1322void RenderBox::setOverridingContainingBlockContentLogicalHeight(std::optional<LayoutUnit> logicalHeight)
1323{
1324 if (!gOverridingContainingBlockContentLogicalHeightMap)
1325 gOverridingContainingBlockContentLogicalHeightMap = new OverrideOptionalSizeMap;
1326 gOverridingContainingBlockContentLogicalHeightMap->set(this, logicalHeight);
1327}
1328
1329void RenderBox::clearOverridingContainingBlockContentSize()
1330{
1331 if (gOverridingContainingBlockContentLogicalWidthMap)
1332 gOverridingContainingBlockContentLogicalWidthMap->remove(this);
1333 clearOverridingContainingBlockContentLogicalHeight();
1334}
1335
1336void RenderBox::clearOverridingContainingBlockContentLogicalHeight()
1337{
1338 if (gOverridingContainingBlockContentLogicalHeightMap)
1339 gOverridingContainingBlockContentLogicalHeightMap->remove(this);
1340}
1341
1342Length RenderBox::overridingLogicalHeightLength() const
1343{
1344 ASSERT(hasOverridingLogicalHeightLength());
1345 return gOverridingLogicalHeightLengthMap->get(this);
1346}
1347
1348void RenderBox::setOverridingLogicalHeightLength(const Length& height)
1349{
1350 if (!gOverridingLogicalHeightLengthMap)
1351 gOverridingLogicalHeightLengthMap = new OverridingLengthMap();
1352 gOverridingLogicalHeightLengthMap->set(this, height);
1353}
1354
1355bool RenderBox::hasOverridingLogicalHeightLength() const
1356{
1357 return gOverridingLogicalHeightLengthMap && gOverridingLogicalHeightLengthMap->contains(this);
1358}
1359
1360void RenderBox::clearOverridingLogicalHeightLength()
1361{
1362 if (gOverridingLogicalHeightLengthMap)
1363 gOverridingLogicalHeightLengthMap->remove(this);
1364}
1365
1366Length RenderBox::overridingLogicalWidthLength() const
1367{
1368 ASSERT(hasOverridingLogicalWidthLength());
1369 return gOverridingLogicalWidthLengthMap->get(this);
1370}
1371
1372void RenderBox::setOverridingLogicalWidthLength(const Length& height)
1373{
1374 if (!gOverridingLogicalWidthLengthMap)
1375 gOverridingLogicalWidthLengthMap = new OverridingLengthMap();
1376 gOverridingLogicalWidthLengthMap->set(this, height);
1377}
1378
1379bool RenderBox::hasOverridingLogicalWidthLength() const
1380{
1381 return gOverridingLogicalWidthLengthMap && gOverridingLogicalWidthLengthMap->contains(this);
1382}
1383
1384void RenderBox::clearOverridingLogicalWidthLength()
1385{
1386 if (gOverridingLogicalWidthLengthMap)
1387 gOverridingLogicalWidthLengthMap->remove(this);
1388}
1389
1390LayoutUnit RenderBox::adjustBorderBoxLogicalWidthForBoxSizing(const Length& logicalWidth) const
1391{
1392 auto width = LayoutUnit { logicalWidth.value() };
1393 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth();
1394 if (style().boxSizing() == BoxSizing::ContentBox || logicalWidth.isIntrinsicOrAuto())
1395 return width + bordersPlusPadding;
1396 return std::max(width, bordersPlusPadding);
1397}
1398
1399LayoutUnit RenderBox::adjustBorderBoxLogicalWidthForBoxSizing(LayoutUnit computedLogicalWidth, LengthType originalType) const
1400{
1401 if (originalType == LengthType::Calculated)
1402 return adjustBorderBoxLogicalWidthForBoxSizing({ computedLogicalWidth, LengthType::Fixed, false });
1403 return adjustBorderBoxLogicalWidthForBoxSizing({ computedLogicalWidth, originalType, false });
1404}
1405
1406LayoutUnit RenderBox::adjustBorderBoxLogicalHeightForBoxSizing(LayoutUnit height) const
1407{
1408 LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
1409 if (style().boxSizing() == BoxSizing::ContentBox)
1410 return height + bordersPlusPadding;
1411 return std::max(height, bordersPlusPadding);
1412}
1413
1414LayoutUnit RenderBox::adjustContentBoxLogicalWidthForBoxSizing(const Length& logicalWidth) const
1415{
1416 auto width = LayoutUnit { logicalWidth.value() };
1417 if (style().boxSizing() == BoxSizing::ContentBox || logicalWidth.isIntrinsicOrAuto())
1418 return std::max(0_lu, width);
1419 return std::max(0_lu, width - borderAndPaddingLogicalWidth());
1420}
1421
1422LayoutUnit RenderBox::adjustContentBoxLogicalWidthForBoxSizing(LayoutUnit computedLogicalWidth, LengthType originalType) const
1423{
1424 if (originalType == LengthType::Calculated)
1425 return adjustContentBoxLogicalWidthForBoxSizing({ computedLogicalWidth, LengthType::Fixed, false });
1426 return adjustContentBoxLogicalWidthForBoxSizing({ computedLogicalWidth, originalType, false });
1427}
1428
1429LayoutUnit RenderBox::adjustContentBoxLogicalHeightForBoxSizing(std::optional<LayoutUnit> height) const
1430{
1431 if (!height)
1432 return 0;
1433 LayoutUnit result = height.value();
1434 if (style().boxSizing() == BoxSizing::BorderBox)
1435 result -= borderAndPaddingLogicalHeight();
1436 return std::max(0_lu, result);
1437}
1438
1439LayoutUnit RenderBox::adjustIntrinsicLogicalHeightForBoxSizing(LayoutUnit height) const
1440{
1441 if (style().boxSizing() == BoxSizing::BorderBox)
1442 return height + borderAndPaddingLogicalHeight();
1443 return height;
1444}
1445
1446// Hit Testing
1447bool RenderBox::hitTestVisualOverflow(const HitTestLocation& hitTestLocation, const LayoutPoint& accumulatedOffset) const
1448{
1449 if (isRenderView())
1450 return true;
1451
1452 LayoutPoint adjustedLocation = accumulatedOffset + location();
1453 LayoutRect overflowBox = visualOverflowRect();
1454 flipForWritingMode(overflowBox);
1455 overflowBox.moveBy(adjustedLocation);
1456 return hitTestLocation.intersects(overflowBox);
1457}
1458
1459bool RenderBox::hitTestClipPath(const HitTestLocation& hitTestLocation, const LayoutPoint& accumulatedOffset) const
1460{
1461 if (!style().clipPath())
1462 return true;
1463
1464 auto offsetFromHitTestRoot = toLayoutSize(accumulatedOffset + location());
1465 auto hitTestLocationInLocalCoordinates = hitTestLocation.point() - offsetFromHitTestRoot;
1466 switch (style().clipPath()->type()) {
1467 case PathOperation::Shape: {
1468 auto& clipPath = downcast<ShapePathOperation>(*style().clipPath());
1469 auto referenceBoxRect = this->referenceBoxRect(clipPath.referenceBox());
1470 if (!clipPath.pathForReferenceRect(referenceBoxRect).contains(hitTestLocationInLocalCoordinates, clipPath.windRule()))
1471 return false;
1472 break;
1473 }
1474 case PathOperation::Reference: {
1475 const auto& referencePathOperation = downcast<ReferencePathOperation>(*style().clipPath());
1476 auto* element = document().getElementById(referencePathOperation.fragment());
1477 if (!element || !element->renderer())
1478 break;
1479 if (!is<SVGClipPathElement>(*element))
1480 break;
1481 auto& clipper = downcast<RenderSVGResourceClipper>(*element->renderer());
1482 if (!clipper.hitTestClipContent(FloatRect(borderBoxRect()), FloatPoint { hitTestLocationInLocalCoordinates }))
1483 return false;
1484 break;
1485 }
1486 case PathOperation::Box:
1487 break;
1488 case PathOperation::Ray:
1489 ASSERT_NOT_REACHED("clip-path does not support Ray shape");
1490 break;
1491 }
1492
1493 return true;
1494}
1495
1496bool RenderBox::hitTestBorderRadius(const HitTestLocation& hitTestLocation, const LayoutPoint& accumulatedOffset) const
1497{
1498 if (isRenderView() || !style().hasBorderRadius())
1499 return true;
1500
1501 LayoutPoint adjustedLocation = accumulatedOffset + location();
1502 LayoutRect borderRect = borderBoxRect();
1503 borderRect.moveBy(adjustedLocation);
1504 RoundedRect border = style().getRoundedBorderFor(borderRect);
1505 return hitTestLocation.intersects(border);
1506}
1507
1508bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1509{
1510 LayoutPoint adjustedLocation = accumulatedOffset + location();
1511
1512 // Check kids first.
1513 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1514 if (!child->hasLayer() && child->nodeAtPoint(request, result, locationInContainer, adjustedLocation, action)) {
1515 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
1516 return true;
1517 }
1518 }
1519
1520 // Check our bounds next. For this purpose always assume that we can only be hit in the
1521 // foreground phase (which is true for replaced elements like images).
1522 LayoutRect boundsRect = borderBoxRectInFragment(nullptr);
1523 boundsRect.moveBy(adjustedLocation);
1524 if (visibleToHitTesting(request) && action == HitTestForeground && locationInContainer.intersects(boundsRect)) {
1525 if (!hitTestVisualOverflow(locationInContainer, accumulatedOffset))
1526 return false;
1527
1528 if (!hitTestClipPath(locationInContainer, accumulatedOffset))
1529 return false;
1530
1531 if (!hitTestBorderRadius(locationInContainer, accumulatedOffset))
1532 return false;
1533
1534 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
1535 if (result.addNodeToListBasedTestResult(nodeForHitTest(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
1536 return true;
1537 }
1538
1539 return RenderBoxModelObject::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, action);
1540}
1541
1542// --------------------- painting stuff -------------------------------
1543
1544void RenderBox::paintRootBoxFillLayers(const PaintInfo& paintInfo)
1545{
1546 ASSERT(isDocumentElementRenderer());
1547 if (paintInfo.skipRootBackground())
1548 return;
1549
1550 auto* rootBackgroundRenderer = view().rendererForRootBackground();
1551 if (!rootBackgroundRenderer)
1552 return;
1553
1554 auto& style = rootBackgroundRenderer->style();
1555 auto color = style.visitedDependentColor(CSSPropertyBackgroundColor);
1556 auto compositeOp = document().compositeOperatorForBackgroundColor(color, *this);
1557
1558 paintFillLayers(paintInfo, style.colorByApplyingColorFilter(color), style.backgroundLayers(), view().backgroundRect(), BackgroundBleedNone, compositeOp, rootBackgroundRenderer);
1559}
1560
1561BackgroundBleedAvoidance RenderBox::determineBackgroundBleedAvoidance(GraphicsContext& context) const
1562{
1563 if (context.paintingDisabled())
1564 return BackgroundBleedNone;
1565
1566 const RenderStyle& style = this->style();
1567
1568 if (!style.hasBackground() || !style.hasBorder() || !style.hasBorderRadius() || borderImageIsLoadedAndCanBeRendered())
1569 return BackgroundBleedNone;
1570
1571 AffineTransform ctm = context.getCTM();
1572 FloatSize contextScaling(static_cast<float>(ctm.xScale()), static_cast<float>(ctm.yScale()));
1573
1574 // Because RoundedRect uses IntRect internally the inset applied by the
1575 // BackgroundBleedShrinkBackground strategy cannot be less than one integer
1576 // layout coordinate, even with subpixel layout enabled. To take that into
1577 // account, we clamp the contextScaling to 1.0 for the following test so
1578 // that borderObscuresBackgroundEdge can only return true if the border
1579 // widths are greater than 2 in both layout coordinates and screen
1580 // coordinates.
1581 // This precaution will become obsolete if RoundedRect is ever promoted to
1582 // a sub-pixel representation.
1583 if (contextScaling.width() > 1)
1584 contextScaling.setWidth(1);
1585 if (contextScaling.height() > 1)
1586 contextScaling.setHeight(1);
1587
1588 if (borderObscuresBackgroundEdge(contextScaling))
1589 return BackgroundBleedShrinkBackground;
1590 if (!style.hasEffectiveAppearance() && borderObscuresBackground() && backgroundHasOpaqueTopLayer())
1591 return BackgroundBleedBackgroundOverBorder;
1592
1593 return BackgroundBleedUseTransparencyLayer;
1594}
1595
1596void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1597{
1598 if (!paintInfo.shouldPaintWithinRoot(*this))
1599 return;
1600
1601 LayoutRect paintRect = borderBoxRectInFragment(nullptr);
1602 paintRect.moveBy(paintOffset);
1603 adjustBorderBoxRectForPainting(paintRect);
1604
1605 paintRect = theme().adjustedPaintRect(*this, paintRect);
1606 BackgroundBleedAvoidance bleedAvoidance = determineBackgroundBleedAvoidance(paintInfo.context());
1607
1608 // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have
1609 // custom shadows of their own.
1610 if (!boxShadowShouldBeAppliedToBackground(paintRect.location(), bleedAvoidance, { }))
1611 paintBoxShadow(paintInfo, paintRect, style(), ShadowStyle::Normal);
1612
1613 GraphicsContextStateSaver stateSaver(paintInfo.context(), false);
1614 if (bleedAvoidance == BackgroundBleedUseTransparencyLayer) {
1615 // To avoid the background color bleeding out behind the border, we'll render background and border
1616 // into a transparency layer, and then clip that in one go (which requires setting up the clip before
1617 // beginning the layer).
1618 stateSaver.save();
1619 paintInfo.context().clipRoundedRect(style().getRoundedBorderFor(paintRect).pixelSnappedRoundedRectForPainting(document().deviceScaleFactor()));
1620 paintInfo.context().beginTransparencyLayer(1);
1621 }
1622
1623 // If we have a native theme appearance, paint that before painting our background.
1624 // The theme will tell us whether or not we should also paint the CSS background.
1625 bool borderOrBackgroundPaintingIsNeeded = true;
1626 if (style().hasEffectiveAppearance()) {
1627 ControlStates* controlStates = controlStatesForRenderer(*this);
1628 borderOrBackgroundPaintingIsNeeded = theme().paint(*this, *controlStates, paintInfo, paintRect);
1629 if (controlStates->needsRepaint())
1630 view().scheduleLazyRepaint(*this);
1631 }
1632
1633 if (borderOrBackgroundPaintingIsNeeded) {
1634 if (bleedAvoidance == BackgroundBleedBackgroundOverBorder)
1635 paintBorder(paintInfo, paintRect, style(), bleedAvoidance);
1636
1637 paintBackground(paintInfo, paintRect, bleedAvoidance);
1638
1639 if (style().hasEffectiveAppearance())
1640 theme().paintDecorations(*this, paintInfo, paintRect);
1641 }
1642 paintBoxShadow(paintInfo, paintRect, style(), ShadowStyle::Inset);
1643
1644 // The theme will tell us whether or not we should also paint the CSS border.
1645 if (bleedAvoidance != BackgroundBleedBackgroundOverBorder && (!style().hasEffectiveAppearance() || (borderOrBackgroundPaintingIsNeeded && theme().paintBorderOnly(*this, paintInfo, paintRect))) && style().hasVisibleBorderDecoration())
1646 paintBorder(paintInfo, paintRect, style(), bleedAvoidance);
1647
1648 if (bleedAvoidance == BackgroundBleedUseTransparencyLayer)
1649 paintInfo.context().endTransparencyLayer();
1650}
1651
1652bool RenderBox::paintsOwnBackground() const
1653{
1654 if (isBody()) {
1655 // The <body> only paints its background if the root element has defined a background independent of the body,
1656 // or if the <body>'s parent is not the document element's renderer (e.g. inside SVG foreignObject).
1657 auto documentElementRenderer = document().documentElement()->renderer();
1658 return !documentElementRenderer
1659 || documentElementRenderer->hasBackground()
1660 || (documentElementRenderer != parent());
1661 }
1662
1663 return true;
1664}
1665
1666void RenderBox::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, BackgroundBleedAvoidance bleedAvoidance)
1667{
1668 if (isDocumentElementRenderer()) {
1669 paintRootBoxFillLayers(paintInfo);
1670 return;
1671 }
1672
1673 if (!paintsOwnBackground())
1674 return;
1675
1676 if (backgroundIsKnownToBeObscured(paintRect.location()) && !boxShadowShouldBeAppliedToBackground(paintRect.location(), bleedAvoidance, { }))
1677 return;
1678
1679 auto backgroundColor = style().visitedDependentColor(CSSPropertyBackgroundColor);
1680 auto compositeOp = document().compositeOperatorForBackgroundColor(backgroundColor, *this);
1681
1682 paintFillLayers(paintInfo, style().colorByApplyingColorFilter(backgroundColor), style().backgroundLayers(), paintRect, bleedAvoidance, compositeOp);
1683}
1684
1685bool RenderBox::getBackgroundPaintedExtent(const LayoutPoint& paintOffset, LayoutRect& paintedExtent) const
1686{
1687 ASSERT(hasBackground());
1688 LayoutRect backgroundRect = snappedIntRect(borderBoxRect());
1689
1690 Color backgroundColor = style().visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor);
1691 if (backgroundColor.isVisible()) {
1692 paintedExtent = backgroundRect;
1693 return true;
1694 }
1695
1696 auto& layers = style().backgroundLayers();
1697 if (!layers.image() || layers.next()) {
1698 paintedExtent = backgroundRect;
1699 return true;
1700 }
1701
1702 auto geometry = calculateBackgroundImageGeometry(nullptr, layers, paintOffset, backgroundRect);
1703 paintedExtent = geometry.destRect();
1704 return !geometry.hasNonLocalGeometry();
1705}
1706
1707bool RenderBox::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const
1708{
1709 if (!paintsOwnBackground())
1710 return false;
1711
1712 Color backgroundColor = style().visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor);
1713 if (!backgroundColor.isOpaque())
1714 return false;
1715
1716 // If the element has appearance, it might be painted by theme.
1717 // We cannot be sure if theme paints the background opaque.
1718 // In this case it is safe to not assume opaqueness.
1719 // FIXME: May be ask theme if it paints opaque.
1720 if (style().hasEffectiveAppearance())
1721 return false;
1722 // FIXME: Check the opaqueness of background images.
1723
1724 if (hasClip() || hasClipPath())
1725 return false;
1726
1727 // FIXME: Use rounded rect if border radius is present.
1728 if (style().hasBorderRadius())
1729 return false;
1730
1731 // FIXME: The background color clip is defined by the last layer.
1732 if (style().backgroundLayers().next())
1733 return false;
1734 LayoutRect backgroundRect;
1735 switch (style().backgroundClip()) {
1736 case FillBox::Border:
1737 backgroundRect = borderBoxRect();
1738 break;
1739 case FillBox::Padding:
1740 backgroundRect = paddingBoxRect();
1741 break;
1742 case FillBox::Content:
1743 backgroundRect = contentBoxRect();
1744 break;
1745 default:
1746 break;
1747 }
1748 return backgroundRect.contains(localRect);
1749}
1750
1751static bool isCandidateForOpaquenessTest(const RenderBox& childBox)
1752{
1753 const RenderStyle& childStyle = childBox.style();
1754 if (childStyle.position() != PositionType::Static && childBox.containingBlock() != childBox.parent())
1755 return false;
1756 if (childStyle.visibility() != Visibility::Visible)
1757 return false;
1758 if (childStyle.shapeOutside())
1759 return false;
1760 if (!childBox.width() || !childBox.height())
1761 return false;
1762 if (RenderLayer* childLayer = childBox.layer()) {
1763 if (childLayer->isComposited())
1764 return false;
1765 // FIXME: Deal with z-index.
1766 if (!childStyle.hasAutoUsedZIndex())
1767 return false;
1768 if (childLayer->hasTransform() || childLayer->isTransparent() || childLayer->hasFilter())
1769 return false;
1770 if (!childBox.scrollPosition().isZero())
1771 return false;
1772 }
1773 return true;
1774}
1775
1776bool RenderBox::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
1777{
1778 if (!maxDepthToTest)
1779 return false;
1780
1781 for (auto& childBox : childrenOfType<RenderBox>(*this)) {
1782 if (!isCandidateForOpaquenessTest(childBox))
1783 continue;
1784 LayoutPoint childLocation = childBox.location();
1785 if (childBox.isRelativelyPositioned())
1786 childLocation.move(childBox.relativePositionOffset());
1787 LayoutRect childLocalRect = localRect;
1788 childLocalRect.moveBy(-childLocation);
1789 if (childLocalRect.y() < 0 || childLocalRect.x() < 0) {
1790 // If there is unobscured area above/left of a static positioned box then the rect is probably not covered.
1791 if (childBox.style().position() == PositionType::Static)
1792 return false;
1793 continue;
1794 }
1795 if (childLocalRect.maxY() > childBox.height() || childLocalRect.maxX() > childBox.width())
1796 continue;
1797 if (childBox.backgroundIsKnownToBeOpaqueInRect(childLocalRect))
1798 return true;
1799 if (childBox.foregroundIsKnownToBeOpaqueInRect(childLocalRect, maxDepthToTest - 1))
1800 return true;
1801 }
1802 return false;
1803}
1804
1805bool RenderBox::computeBackgroundIsKnownToBeObscured(const LayoutPoint& paintOffset)
1806{
1807 // Test to see if the children trivially obscure the background.
1808 // FIXME: This test can be much more comprehensive.
1809 if (!hasBackground())
1810 return false;
1811 // Table and root background painting is special.
1812 if (isTable() || isDocumentElementRenderer())
1813 return false;
1814
1815 LayoutRect backgroundRect;
1816 if (!getBackgroundPaintedExtent(paintOffset, backgroundRect))
1817 return false;
1818
1819 if (auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr) {
1820 if (scrollableArea->scrollingMayRevealBackground())
1821 return false;
1822 }
1823 return foregroundIsKnownToBeOpaqueInRect(backgroundRect, backgroundObscurationTestMaxDepth);
1824}
1825
1826bool RenderBox::backgroundHasOpaqueTopLayer() const
1827{
1828 auto& fillLayer = style().backgroundLayers();
1829 if (fillLayer.clip() != FillBox::Border)
1830 return false;
1831
1832 // Clipped with local scrolling
1833 if (hasNonVisibleOverflow() && fillLayer.attachment() == FillAttachment::LocalBackground)
1834 return false;
1835
1836 if (fillLayer.hasOpaqueImage(*this) && fillLayer.hasRepeatXY() && fillLayer.image()->canRender(this, style().effectiveZoom()))
1837 return true;
1838
1839 // If there is only one layer and no image, check whether the background color is opaque.
1840 if (!fillLayer.next() && !fillLayer.hasImage()) {
1841 Color bgColor = style().visitedDependentColorWithColorFilter(CSSPropertyBackgroundColor);
1842 if (bgColor.isOpaque())
1843 return true;
1844 }
1845
1846 return false;
1847}
1848
1849void RenderBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1850{
1851 if (!paintInfo.shouldPaintWithinRoot(*this) || style().visibility() != Visibility::Visible || paintInfo.phase != PaintPhase::Mask || paintInfo.context().paintingDisabled())
1852 return;
1853
1854 LayoutRect paintRect = LayoutRect(paintOffset, size());
1855 adjustBorderBoxRectForPainting(paintRect);
1856 paintMaskImages(paintInfo, paintRect);
1857}
1858
1859void RenderBox::paintClippingMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1860{
1861 if (!paintInfo.shouldPaintWithinRoot(*this) || style().visibility() != Visibility::Visible || paintInfo.phase != PaintPhase::ClippingMask || paintInfo.context().paintingDisabled())
1862 return;
1863
1864 LayoutRect paintRect = LayoutRect(paintOffset, size());
1865 paintInfo.context().fillRect(snappedIntRect(paintRect), Color::black);
1866}
1867
1868void RenderBox::paintMaskImages(const PaintInfo& paintInfo, const LayoutRect& paintRect)
1869{
1870 // Figure out if we need to push a transparency layer to render our mask.
1871 bool pushTransparencyLayer = false;
1872 bool compositedMask = hasLayer() && layer()->hasCompositedMask();
1873 bool flattenCompositingLayers = paintInfo.paintBehavior.contains(PaintBehavior::FlattenCompositingLayers);
1874 CompositeOperator compositeOp = CompositeOperator::SourceOver;
1875
1876 bool allMaskImagesLoaded = true;
1877
1878 if (!compositedMask || flattenCompositingLayers) {
1879 pushTransparencyLayer = true;
1880
1881 // Don't render a masked element until all the mask images have loaded, to prevent a flash of unmasked content.
1882 if (auto* maskBoxImage = style().maskBoxImage().image())
1883 allMaskImagesLoaded &= maskBoxImage->isLoaded();
1884
1885 allMaskImagesLoaded &= style().maskLayers().imagesAreLoaded();
1886
1887 paintInfo.context().setCompositeOperation(CompositeOperator::DestinationIn);
1888 paintInfo.context().beginTransparencyLayer(1);
1889 compositeOp = CompositeOperator::SourceOver;
1890 }
1891
1892 if (allMaskImagesLoaded) {
1893 paintFillLayers(paintInfo, Color(), style().maskLayers(), paintRect, BackgroundBleedNone, compositeOp);
1894 paintNinePieceImage(paintInfo.context(), paintRect, style(), style().maskBoxImage(), compositeOp);
1895 }
1896
1897 if (pushTransparencyLayer)
1898 paintInfo.context().endTransparencyLayer();
1899}
1900
1901LayoutRect RenderBox::maskClipRect(const LayoutPoint& paintOffset)
1902{
1903 const NinePieceImage& maskBoxImage = style().maskBoxImage();
1904 if (maskBoxImage.image()) {
1905 LayoutRect borderImageRect = borderBoxRect();
1906
1907 // Apply outsets to the border box.
1908 borderImageRect.expand(style().maskBoxImageOutsets());
1909 return borderImageRect;
1910 }
1911
1912 LayoutRect result;
1913 LayoutRect borderBox = borderBoxRect();
1914 for (auto* maskLayer = &style().maskLayers(); maskLayer; maskLayer = maskLayer->next()) {
1915 if (maskLayer->image()) {
1916 // Masks should never have fixed attachment, so it's OK for paintContainer to be null.
1917 result.unite(calculateBackgroundImageGeometry(nullptr, *maskLayer, paintOffset, borderBox).destRect());
1918 }
1919 }
1920 return result;
1921}
1922
1923void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& color, const FillLayer& fillLayer, const LayoutRect& rect,
1924 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderElement* backgroundObject)
1925{
1926 Vector<const FillLayer*, 8> layers;
1927 bool shouldDrawBackgroundInSeparateBuffer = false;
1928
1929 for (auto* layer = &fillLayer; layer; layer = layer->next()) {
1930 layers.append(layer);
1931
1932 if (layer->blendMode() != BlendMode::Normal)
1933 shouldDrawBackgroundInSeparateBuffer = true;
1934
1935 // Stop traversal when an opaque layer is encountered.
1936 // FIXME: It would be possible for the following occlusion culling test to be more aggressive
1937 // on layers with no repeat by testing whether the image covers the layout rect.
1938 // Testing that here would imply duplicating a lot of calculations that are currently done in
1939 // RenderBoxModelObject::paintFillLayerExtended. A more efficient solution might be to move
1940 // the layer recursion into paintFillLayerExtended, or to compute the layer geometry here
1941 // and pass it down.
1942
1943 // The clipOccludesNextLayers condition must be evaluated first to avoid short-circuiting.
1944 if (layer->clipOccludesNextLayers(layer == &fillLayer) && layer->hasOpaqueImage(*this) && layer->image()->canRender(this, style().effectiveZoom()) && layer->hasRepeatXY() && layer->blendMode() == BlendMode::Normal)
1945 break;
1946 }
1947
1948 auto& context = paintInfo.context();
1949 auto baseBgColorUsage = BaseBackgroundColorUse;
1950
1951 if (shouldDrawBackgroundInSeparateBuffer) {
1952 paintFillLayer(paintInfo, color, *layers.last(), rect, bleedAvoidance, op, backgroundObject, BaseBackgroundColorOnly);
1953 baseBgColorUsage = BaseBackgroundColorSkip;
1954 context.beginTransparencyLayer(1);
1955 }
1956
1957 auto topLayer = layers.rend();
1958 for (auto it = layers.rbegin(); it != topLayer; ++it)
1959 paintFillLayer(paintInfo, color, **it, rect, bleedAvoidance, op, backgroundObject, baseBgColorUsage);
1960
1961 if (shouldDrawBackgroundInSeparateBuffer)
1962 context.endTransparencyLayer();
1963}
1964
1965void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer& fillLayer, const LayoutRect& rect,
1966 BackgroundBleedAvoidance bleedAvoidance, CompositeOperator op, RenderElement* backgroundObject, BaseBackgroundColorUsage baseBgColorUsage)
1967{
1968 paintFillLayerExtended(paintInfo, c, fillLayer, rect, bleedAvoidance, { }, { }, op, backgroundObject, baseBgColorUsage);
1969}
1970
1971static StyleImage* findLayerUsedImage(WrappedImagePtr image, const FillLayer& layers)
1972{
1973 for (auto* layer = &layers; layer; layer = layer->next()) {
1974 if (layer->image() && image == layer->image()->data())
1975 return layer->image();
1976 }
1977 return nullptr;
1978}
1979
1980void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*)
1981{
1982 if (!parent())
1983 return;
1984
1985 if ((style().borderImage().image() && style().borderImage().image()->data() == image) ||
1986 (style().maskBoxImage().image() && style().maskBoxImage().image()->data() == image)) {
1987 repaint();
1988 return;
1989 }
1990
1991 ShapeValue* shapeOutsideValue = style().shapeOutside();
1992 if (!view().frameView().layoutContext().isInRenderTreeLayout() && isFloating() && shapeOutsideValue && shapeOutsideValue->image() && shapeOutsideValue->image()->data() == image) {
1993 ShapeOutsideInfo::ensureInfo(*this).markShapeAsDirty();
1994 markShapeOutsideDependentsForLayout();
1995 }
1996
1997 bool didFullRepaint = repaintLayerRectsForImage(image, style().backgroundLayers(), true);
1998 if (!didFullRepaint)
1999 repaintLayerRectsForImage(image, style().maskLayers(), false);
2000
2001 if (!isComposited())
2002 return;
2003
2004 if (layer()->hasCompositedMask() && findLayerUsedImage(image, style().maskLayers()))
2005 layer()->contentChanged(MaskImageChanged);
2006
2007 if (auto* styleImage = findLayerUsedImage(image, style().backgroundLayers())) {
2008 layer()->contentChanged(BackgroundImageChanged);
2009 incrementVisuallyNonEmptyPixelCountIfNeeded(flooredIntSize(styleImage->imageSize(this, style().effectiveZoom())));
2010 }
2011}
2012
2013void RenderBox::incrementVisuallyNonEmptyPixelCountIfNeeded(const IntSize& size)
2014{
2015 if (didContibuteToVisuallyNonEmptyPixelCount())
2016 return;
2017
2018 view().frameView().incrementVisuallyNonEmptyPixelCount(size);
2019 setDidContibuteToVisuallyNonEmptyPixelCount();
2020}
2021
2022bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer& layers, bool drawingBackground)
2023{
2024 LayoutRect rendererRect;
2025 RenderBox* layerRenderer = nullptr;
2026
2027 for (auto* layer = &layers; layer; layer = layer->next()) {
2028 if (layer->image() && image == layer->image()->data() && (layer->image()->isLoaded() || layer->image()->canRender(this, style().effectiveZoom()))) {
2029 // Now that we know this image is being used, compute the renderer and the rect if we haven't already.
2030 bool drawingRootBackground = drawingBackground && (isDocumentElementRenderer() || (isBody() && !document().documentElement()->renderer()->hasBackground()));
2031 if (!layerRenderer) {
2032 if (drawingRootBackground) {
2033 layerRenderer = &view();
2034
2035 LayoutUnit rw = downcast<RenderView>(*layerRenderer).frameView().contentsWidth();
2036 LayoutUnit rh = downcast<RenderView>(*layerRenderer).frameView().contentsHeight();
2037
2038 rendererRect = LayoutRect(-layerRenderer->marginLeft(),
2039 -layerRenderer->marginTop(),
2040 std::max(layerRenderer->width() + layerRenderer->horizontalMarginExtent() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw),
2041 std::max(layerRenderer->height() + layerRenderer->verticalMarginExtent() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh));
2042 } else {
2043 layerRenderer = this;
2044 rendererRect = borderBoxRect();
2045 }
2046 }
2047 // FIXME: Figure out how to pass absolute position to calculateBackgroundImageGeometry (for pixel snapping)
2048 BackgroundImageGeometry geometry = layerRenderer->calculateBackgroundImageGeometry(nullptr, *layer, LayoutPoint(), rendererRect);
2049 if (geometry.hasNonLocalGeometry()) {
2050 // Rather than incur the costs of computing the paintContainer for renderers with fixed backgrounds
2051 // in order to get the right destRect, just repaint the entire renderer.
2052 layerRenderer->repaint();
2053 return true;
2054 }
2055
2056 LayoutRect rectToRepaint = geometry.destRect();
2057 bool shouldClipToLayer = true;
2058
2059 // If this is the root background layer, we may need to extend the repaintRect if the FrameView has an
2060 // extendedBackground. We should only extend the rect if it is already extending the full width or height
2061 // of the rendererRect.
2062 if (drawingRootBackground && view().frameView().hasExtendedBackgroundRectForPainting()) {
2063 shouldClipToLayer = false;
2064 IntRect extendedBackgroundRect = view().frameView().extendedBackgroundRectForPainting();
2065 if (rectToRepaint.width() == rendererRect.width()) {
2066 rectToRepaint.move(extendedBackgroundRect.x(), 0);
2067 rectToRepaint.setWidth(extendedBackgroundRect.width());
2068 }
2069 if (rectToRepaint.height() == rendererRect.height()) {
2070 rectToRepaint.move(0, extendedBackgroundRect.y());
2071 rectToRepaint.setHeight(extendedBackgroundRect.height());
2072 }
2073 }
2074
2075 layerRenderer->repaintRectangle(rectToRepaint, shouldClipToLayer);
2076 if (geometry.destRect() == rendererRect)
2077 return true;
2078 }
2079 }
2080 return false;
2081}
2082
2083bool RenderBox::pushContentsClip(PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset)
2084{
2085 if (paintInfo.phase == PaintPhase::BlockBackground || paintInfo.phase == PaintPhase::SelfOutline || paintInfo.phase == PaintPhase::Mask)
2086 return false;
2087
2088 bool isControlClip = hasControlClip();
2089 bool isOverflowClip = hasNonVisibleOverflow() && !layer()->isSelfPaintingLayer();
2090
2091 if (!isControlClip && !isOverflowClip)
2092 return false;
2093
2094 if (paintInfo.phase == PaintPhase::Outline)
2095 paintInfo.phase = PaintPhase::ChildOutlines;
2096 else if (paintInfo.phase == PaintPhase::ChildBlockBackground) {
2097 paintInfo.phase = PaintPhase::BlockBackground;
2098 paintObject(paintInfo, accumulatedOffset);
2099 paintInfo.phase = PaintPhase::ChildBlockBackgrounds;
2100 }
2101 float deviceScaleFactor = document().deviceScaleFactor();
2102 FloatRect clipRect = snapRectToDevicePixels((isControlClip ? controlClipRect(accumulatedOffset) : overflowClipRect(accumulatedOffset, nullptr, IgnoreOverlayScrollbarSize, paintInfo.phase)), deviceScaleFactor);
2103 paintInfo.context().save();
2104 if (style().hasBorderRadius())
2105 paintInfo.context().clipRoundedRect(style().getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, size())).pixelSnappedRoundedRectForPainting(deviceScaleFactor));
2106 paintInfo.context().clip(clipRect);
2107
2108 if (paintInfo.phase == PaintPhase::EventRegion)
2109 paintInfo.eventRegionContext->pushClip(enclosingIntRect(clipRect));
2110
2111 return true;
2112}
2113
2114void RenderBox::popContentsClip(PaintInfo& paintInfo, PaintPhase originalPhase, const LayoutPoint& accumulatedOffset)
2115{
2116 ASSERT(hasControlClip() || (hasNonVisibleOverflow() && !layer()->isSelfPaintingLayer()));
2117
2118 if (paintInfo.phase == PaintPhase::EventRegion)
2119 paintInfo.eventRegionContext->popClip();
2120
2121 paintInfo.context().restore();
2122 if (originalPhase == PaintPhase::Outline) {
2123 paintInfo.phase = PaintPhase::SelfOutline;
2124 paintObject(paintInfo, accumulatedOffset);
2125 paintInfo.phase = originalPhase;
2126 } else if (originalPhase == PaintPhase::ChildBlockBackground)
2127 paintInfo.phase = originalPhase;
2128}
2129
2130LayoutRect RenderBox::overflowClipRect(const LayoutPoint& location, RenderFragmentContainer* fragment, OverlayScrollbarSizeRelevancy relevancy, PaintPhase) const
2131{
2132 LayoutRect clipRect = borderBoxRectInFragment(fragment);
2133 clipRect.setLocation(location + clipRect.location() + LayoutSize(borderLeft(), borderTop()));
2134 clipRect.setSize(clipRect.size() - LayoutSize(borderLeft() + borderRight(), borderTop() + borderBottom()));
2135 if (style().overflowX() == Overflow::Clip && style().overflowY() == Overflow::Visible) {
2136 LayoutRect infRect = LayoutRect::infiniteRect();
2137 clipRect.setY(infRect.y());
2138 clipRect.setHeight(infRect.height());
2139 } else if (style().overflowY() == Overflow::Clip && style().overflowX() == Overflow::Visible) {
2140 LayoutRect infRect = LayoutRect::infiniteRect();
2141 clipRect.setX(infRect.x());
2142 clipRect.setWidth(infRect.width());
2143 }
2144
2145 // Subtract out scrollbars if we have them.
2146 if (auto* scrollableArea = layer() ? layer()->scrollableArea() : nullptr) {
2147 if (shouldPlaceVerticalScrollbarOnLeft())
2148 clipRect.move(scrollableArea->verticalScrollbarWidth(relevancy), 0);
2149 clipRect.contract(scrollableArea->verticalScrollbarWidth(relevancy), scrollableArea->horizontalScrollbarHeight(relevancy));
2150 }
2151
2152 return clipRect;
2153}
2154
2155LayoutRect RenderBox::clipRect(const LayoutPoint& location, RenderFragmentContainer* fragment) const
2156{
2157 LayoutRect borderBoxRect = borderBoxRectInFragment(fragment);
2158 LayoutRect clipRect = LayoutRect(borderBoxRect.location() + location, borderBoxRect.size());
2159
2160 if (!style().clipLeft().isAuto()) {
2161 LayoutUnit c = valueForLength(style().clipLeft(), borderBoxRect.width());
2162 clipRect.move(c, 0_lu);
2163 clipRect.contract(c, 0_lu);
2164 }
2165
2166 // We don't use the fragment-specific border box's width and height since clip offsets are (stupidly) specified
2167 // from the left and top edges. Therefore it's better to avoid constraining to smaller widths and heights.
2168
2169 if (!style().clipRight().isAuto())
2170 clipRect.contract(width() - valueForLength(style().clipRight(), width()), 0_lu);
2171
2172 if (!style().clipTop().isAuto()) {
2173 LayoutUnit c = valueForLength(style().clipTop(), borderBoxRect.height());
2174 clipRect.move(0_lu, c);
2175 clipRect.contract(0_lu, c);
2176 }
2177
2178 if (!style().clipBottom().isAuto())
2179 clipRect.contract(0_lu, height() - valueForLength(style().clipBottom(), height()));
2180
2181 return clipRect;
2182}
2183
2184LayoutUnit RenderBox::shrinkLogicalWidthToAvoidFloats(LayoutUnit childMarginStart, LayoutUnit childMarginEnd, const RenderBlock& cb, RenderFragmentContainer* fragment) const
2185{
2186 RenderFragmentContainer* containingBlockFragment = nullptr;
2187 LayoutUnit logicalTopPosition = logicalTop();
2188 if (fragment) {
2189 LayoutUnit offsetFromLogicalTopOfFragment = fragment ? fragment->logicalTopForFragmentedFlowContent() - offsetFromLogicalTopOfFirstPage() : 0_lu;
2190 logicalTopPosition = std::max(logicalTopPosition, logicalTopPosition + offsetFromLogicalTopOfFragment);
2191 containingBlockFragment = cb.clampToStartAndEndFragments(fragment);
2192 }
2193
2194 LayoutUnit logicalHeight = cb.logicalHeightForChild(*this);
2195 LayoutUnit result = cb.availableLogicalWidthForLineInFragment(logicalTopPosition, DoNotIndentText, containingBlockFragment, logicalHeight) - childMarginStart - childMarginEnd;
2196
2197 // We need to see if margins on either the start side or the end side can contain the floats in question. If they can,
2198 // then just using the line width is inaccurate. In the case where a float completely fits, we don't need to use the line
2199 // offset at all, but can instead push all the way to the content edge of the containing block. In the case where the float
2200 // 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
2201 // "consumed" by the float. Negative margins aren't consumed by the float, and so we ignore them.
2202 if (childMarginStart > 0) {
2203 LayoutUnit startContentSide = cb.startOffsetForContent(containingBlockFragment);
2204 LayoutUnit startContentSideWithMargin = startContentSide + childMarginStart;
2205 LayoutUnit startOffset = cb.startOffsetForLineInFragment(logicalTopPosition, DoNotIndentText, containingBlockFragment, logicalHeight);
2206 if (startOffset > startContentSideWithMargin)
2207 result += childMarginStart;
2208 else
2209 result += startOffset - startContentSide;
2210 }
2211
2212 if (childMarginEnd > 0) {
2213 LayoutUnit endContentSide = cb.endOffsetForContent(containingBlockFragment);
2214 LayoutUnit endContentSideWithMargin = endContentSide + childMarginEnd;
2215 LayoutUnit endOffset = cb.endOffsetForLineInFragment(logicalTopPosition, DoNotIndentText, containingBlockFragment, logicalHeight);
2216 if (endOffset > endContentSideWithMargin)
2217 result += childMarginEnd;
2218 else
2219 result += endOffset - endContentSide;
2220 }
2221
2222 return result;
2223}
2224
2225LayoutUnit RenderBox::containingBlockLogicalWidthForContent() const
2226{
2227 if (hasOverridingContainingBlockContentLogicalWidth())
2228 return overridingContainingBlockContentLogicalWidth().value_or(0_lu);
2229
2230 if (RenderBlock* cb = containingBlock()) {
2231 if (isOutOfFlowPositioned())
2232 return cb->clientLogicalWidth();
2233 return cb->availableLogicalWidth();
2234 }
2235 return 0_lu;
2236}
2237
2238LayoutUnit RenderBox::containingBlockLogicalHeightForContent(AvailableLogicalHeightType heightType) const
2239{
2240 if (hasOverridingContainingBlockContentLogicalHeight()) {
2241 // FIXME: Containing block for a grid item is the grid area it's located in. We need to return whatever
2242 // height value we get from overridingContainingBlockContentLogicalHeight() here, including std::nullopt.
2243 if (auto height = overridingContainingBlockContentLogicalHeight())
2244 return height.value();
2245 }
2246
2247 if (RenderBlock* cb = containingBlock())
2248 return cb->availableLogicalHeight(heightType);
2249 return 0_lu;
2250}
2251
2252LayoutUnit RenderBox::containingBlockLogicalWidthForContentInFragment(RenderFragmentContainer* fragment) const
2253{
2254 if (!fragment)
2255 return containingBlockLogicalWidthForContent();
2256
2257 RenderBlock* cb = containingBlock();
2258 RenderFragmentContainer* containingBlockFragment = cb->clampToStartAndEndFragments(fragment);
2259 // FIXME: It's unclear if a fragment's content should use the containing block's override logical width.
2260 // If it should, the following line should call containingBlockLogicalWidthForContent.
2261 LayoutUnit result = cb->availableLogicalWidth();
2262 RenderBoxFragmentInfo* boxInfo = cb->renderBoxFragmentInfo(containingBlockFragment);
2263 if (!boxInfo)
2264 return result;
2265 return std::max<LayoutUnit>(0, result - (cb->logicalWidth() - boxInfo->logicalWidth()));
2266}
2267
2268LayoutUnit RenderBox::containingBlockAvailableLineWidthInFragment(RenderFragmentContainer* fragment) const
2269{
2270 RenderBlock* cb = containingBlock();
2271 RenderFragmentContainer* containingBlockFragment = nullptr;
2272 LayoutUnit logicalTopPosition = logicalTop();
2273 if (fragment) {
2274 LayoutUnit offsetFromLogicalTopOfFragment = fragment ? fragment->logicalTopForFragmentedFlowContent() - offsetFromLogicalTopOfFirstPage() : 0_lu;
2275 logicalTopPosition = std::max(logicalTopPosition, logicalTopPosition + offsetFromLogicalTopOfFragment);
2276 containingBlockFragment = cb->clampToStartAndEndFragments(fragment);
2277 }
2278 return cb->availableLogicalWidthForLineInFragment(logicalTopPosition, DoNotIndentText, containingBlockFragment, availableLogicalHeight(IncludeMarginBorderPadding));
2279}
2280
2281LayoutUnit RenderBox::perpendicularContainingBlockLogicalHeight() const
2282{
2283 if (hasOverridingContainingBlockContentLogicalHeight()) {
2284 if (auto height = overridingContainingBlockContentLogicalHeight())
2285 return height.value();
2286 }
2287
2288 RenderBlock* cb = containingBlock();
2289 if (cb->hasOverridingLogicalHeight())
2290 return cb->overridingContentLogicalHeight();
2291
2292 const RenderStyle& containingBlockStyle = cb->style();
2293 Length logicalHeightLength = containingBlockStyle.logicalHeight();
2294
2295 // FIXME: For now just support fixed heights. Eventually should support percentage heights as well.
2296 if (!logicalHeightLength.isFixed()) {
2297 LayoutUnit fillFallbackExtent = containingBlockStyle.isHorizontalWritingMode() ? view().frameView().layoutSize().height() : view().frameView().layoutSize().width();
2298 LayoutUnit fillAvailableExtent = containingBlock()->availableLogicalHeight(ExcludeMarginBorderPadding);
2299 view().addPercentHeightDescendant(const_cast<RenderBox&>(*this));
2300 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=158286 We also need to perform the same percentHeightDescendant treatment to the element which dictates the return value for containingBlock()->availableLogicalHeight() above.
2301 return std::min(fillAvailableExtent, fillFallbackExtent);
2302 }
2303
2304 // Use the content box logical height as specified by the style.
2305 return cb->adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit(logicalHeightLength.value()));
2306}
2307
2308void RenderBox::mapLocalToContainer(const RenderLayerModelObject* ancestorContainer, TransformState& transformState, OptionSet<MapCoordinatesMode> mode, bool* wasFixed) const
2309{
2310 if (ancestorContainer == this)
2311 return;
2312
2313 if (!ancestorContainer && view().frameView().layoutContext().isPaintOffsetCacheEnabled()) {
2314 auto* layoutState = view().frameView().layoutContext().layoutState();
2315 LayoutSize offset = layoutState->paintOffset() + locationOffset();
2316 if (style().hasInFlowPosition() && layer())
2317 offset += layer()->offsetForInFlowPosition();
2318 transformState.move(offset);
2319 return;
2320 }
2321
2322 bool containerSkipped;
2323 RenderElement* container = this->container(ancestorContainer, containerSkipped);
2324 if (!container)
2325 return;
2326
2327 bool isFixedPos = isFixedPositioned();
2328 // If this box has a transform, it acts as a fixed position container for fixed descendants,
2329 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
2330 if (isFixedPos)
2331 mode.add(IsFixed);
2332 else if (mode.contains(IsFixed) && canContainFixedPositionObjects())
2333 mode.remove(IsFixed);
2334
2335 if (wasFixed)
2336 *wasFixed = mode.contains(IsFixed);
2337
2338 LayoutSize containerOffset = offsetFromContainer(*container, LayoutPoint(transformState.mappedPoint()));
2339
2340 // Remove sticky positioning from the offset if it should be ignored. This is done here in
2341 // order to avoid piping this flag down the method chain.
2342 if (mode.contains(IgnoreStickyOffsets) && isStickilyPositioned())
2343 containerOffset -= stickyPositionOffset();
2344
2345 bool preserve3D = mode.contains(UseTransforms) && (container->style().preserves3D() || style().preserves3D());
2346 if (mode.contains(UseTransforms) && shouldUseTransformFromContainer(container)) {
2347 TransformationMatrix t;
2348 getTransformFromContainer(container, containerOffset, t);
2349 transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
2350 } else
2351 transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
2352
2353 if (containerSkipped) {
2354 // There can't be a transform between ancestorContainer and o, because transforms create containers, so it should be safe
2355 // to just subtract the delta between the ancestorContainer and o.
2356 LayoutSize containerOffset = ancestorContainer->offsetFromAncestorContainer(*container);
2357 transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
2358 return;
2359 }
2360
2361 mode.remove(ApplyContainerFlip);
2362
2363 container->mapLocalToContainer(ancestorContainer, transformState, mode, wasFixed);
2364}
2365
2366const RenderObject* RenderBox::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
2367{
2368 ASSERT(ancestorToStopAt != this);
2369
2370 bool ancestorSkipped;
2371 RenderElement* container = this->container(ancestorToStopAt, ancestorSkipped);
2372 if (!container)
2373 return nullptr;
2374
2375 bool isFixedPos = isFixedPositioned();
2376 LayoutSize adjustmentForSkippedAncestor;
2377 if (ancestorSkipped) {
2378 // There can't be a transform between repaintContainer and container, because transforms create containers, so it should be safe
2379 // to just subtract the delta between the ancestor and container.
2380 adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(*container);
2381 }
2382
2383 bool offsetDependsOnPoint = false;
2384 LayoutSize containerOffset = offsetFromContainer(*container, LayoutPoint(), &offsetDependsOnPoint);
2385
2386 bool preserve3D = container->style().preserves3D() || style().preserves3D();
2387 if (shouldUseTransformFromContainer(container) && (geometryMap.mapCoordinatesFlags() & UseTransforms)) {
2388 TransformationMatrix t;
2389 getTransformFromContainer(container, containerOffset, t);
2390 t.translateRight(adjustmentForSkippedAncestor.width(), adjustmentForSkippedAncestor.height());
2391
2392 geometryMap.push(this, t, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform());
2393 } else {
2394 containerOffset += adjustmentForSkippedAncestor;
2395 geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint, isFixedPos, hasTransform());
2396 }
2397
2398 return ancestorSkipped ? ancestorToStopAt : container;
2399}
2400
2401void RenderBox::mapAbsoluteToLocalPoint(OptionSet<MapCoordinatesMode> mode, TransformState& transformState) const
2402{
2403 bool isFixedPos = isFixedPositioned();
2404 if (isFixedPos)
2405 mode.add(IsFixed);
2406 else if (mode.contains(IsFixed) && canContainFixedPositionObjects()) {
2407 // If this box has a transform, it acts as a fixed position container for fixed descendants,
2408 // and may itself also be fixed position. So propagate 'fixed' up only if this box is fixed position.
2409 mode.remove(IsFixed);
2410 }
2411
2412 RenderBoxModelObject::mapAbsoluteToLocalPoint(mode, transformState);
2413}
2414
2415LayoutSize RenderBox::offsetFromContainer(RenderElement& container, const LayoutPoint&, bool* offsetDependsOnPoint) const
2416{
2417 // A fragment "has" boxes inside it without being their container.
2418 ASSERT(&container == this->container() || is<RenderFragmentContainer>(container));
2419
2420 LayoutSize offset;
2421 if (isInFlowPositioned())
2422 offset += offsetForInFlowPosition();
2423
2424 if (!isInline() || isReplacedOrInlineBlock())
2425 offset += topLeftLocationOffset();
2426
2427 if (is<RenderBox>(container))
2428 offset -= toLayoutSize(downcast<RenderBox>(container).scrollPosition());
2429
2430 if (isAbsolutelyPositioned() && container.isInFlowPositioned() && is<RenderInline>(container))
2431 offset += downcast<RenderInline>(container).offsetForInFlowPositionedInline(this);
2432
2433 if (offsetDependsOnPoint)
2434 *offsetDependsOnPoint |= is<RenderFragmentedFlow>(container);
2435
2436 return offset;
2437}
2438
2439std::unique_ptr<LegacyInlineElementBox> RenderBox::createInlineBox()
2440{
2441 return makeUnique<LegacyInlineElementBox>(*this);
2442}
2443
2444void RenderBox::dirtyLineBoxes(bool fullLayout)
2445{
2446 if (!m_inlineBoxWrapper)
2447 return;
2448
2449 if (fullLayout) {
2450 delete m_inlineBoxWrapper;
2451 m_inlineBoxWrapper = nullptr;
2452 } else
2453 m_inlineBoxWrapper->dirtyLineBoxes();
2454}
2455
2456void RenderBox::positionLineBox(LegacyInlineElementBox& box)
2457{
2458 if (isOutOfFlowPositioned()) {
2459 // Cache the x position only if we were an DisplayType::Inline type originally.
2460 bool wasInline = style().isOriginalDisplayInlineType();
2461 if (wasInline) {
2462 // The value is cached in the xPos of the box. We only need this value if
2463 // our object was inline originally, since otherwise it would have ended up underneath
2464 // the inlines.
2465 LegacyRootInlineBox& rootBox = box.root();
2466 rootBox.blockFlow().setStaticInlinePositionForChild(*this, rootBox.lineBoxTop(), LayoutUnit::fromFloatRound(box.logicalLeft()));
2467 if (style().hasStaticInlinePosition(box.isHorizontal()))
2468 setChildNeedsLayout(MarkOnlyThis); // Just mark the positioned object as needing layout, so it will update its position properly.
2469 } else {
2470 // Our object was a block originally, so we make our normal flow position be
2471 // just below the line box (as though all the inlines that came before us got
2472 // wrapped in an anonymous block, which is what would have happened had we been
2473 // in flow). This value was cached in the y() of the box.
2474 layer()->setStaticBlockPosition(LayoutUnit(box.logicalTop()));
2475 if (style().hasStaticBlockPosition(box.isHorizontal()))
2476 setChildNeedsLayout(MarkOnlyThis); // Just mark the positioned object as needing layout, so it will update its position properly.
2477 }
2478 return;
2479 }
2480
2481 if (isReplacedOrInlineBlock()) {
2482 setLocation(LayoutPoint(box.topLeft()));
2483 setInlineBoxWrapper(&box);
2484 }
2485}
2486
2487void RenderBox::deleteLineBoxWrapper()
2488{
2489 if (!m_inlineBoxWrapper)
2490 return;
2491
2492 if (!renderTreeBeingDestroyed())
2493 m_inlineBoxWrapper->removeFromParent();
2494 delete m_inlineBoxWrapper;
2495 m_inlineBoxWrapper = nullptr;
2496}
2497
2498LayoutRect RenderBox::clippedOverflowRect(const RenderLayerModelObject* repaintContainer, VisibleRectContext context) const
2499{
2500 if (isInsideEntirelyHiddenLayer())
2501 return { };
2502
2503 LayoutRect r = visualOverflowRect();
2504 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
2505 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
2506 r.move(view().frameView().layoutContext().layoutDelta());
2507 return computeRect(r, repaintContainer, context);
2508}
2509
2510LayoutRect RenderBox::computeVisibleRectUsingPaintOffset(const LayoutRect& rect) const
2511{
2512 LayoutRect adjustedRect = rect;
2513 auto* layoutState = view().frameView().layoutContext().layoutState();
2514
2515 if (layer() && layer()->transform())
2516 adjustedRect = LayoutRect(encloseRectToDevicePixels(layer()->transform()->mapRect(adjustedRect), document().deviceScaleFactor()));
2517
2518 // We can't trust the bits on RenderObject, because this might be called while re-resolving style.
2519 if (style().hasInFlowPosition() && layer())
2520 adjustedRect.move(layer()->offsetForInFlowPosition());
2521
2522 adjustedRect.moveBy(location());
2523 adjustedRect.move(layoutState->paintOffset());
2524 if (layoutState->isClipped())
2525 adjustedRect.intersect(layoutState->clipRect());
2526 return adjustedRect;
2527}
2528
2529std::optional<LayoutRect> RenderBox::computeVisibleRectInContainer(const LayoutRect& rect, const RenderLayerModelObject* container, VisibleRectContext context) const
2530{
2531 // The rect we compute at each step is shifted by our x/y offset in the parent container's coordinate space.
2532 // Only when we cross a writing mode boundary will we have to possibly flipForWritingMode (to convert into a more appropriate
2533 // offset corner for the enclosing container). This allows for a fully RL or BT document to repaint
2534 // properly even during layout, since the rect remains flipped all the way until the end.
2535 //
2536 // RenderView::computeVisibleRectInContainer then converts the rect to physical coordinates. We also convert to
2537 // physical when we hit a repaint container boundary. Therefore the final rect returned is always in the
2538 // physical coordinate space of the container.
2539 const RenderStyle& styleToUse = style();
2540 // Paint offset cache is only valid for root-relative, non-fixed position repainting
2541 if (view().frameView().layoutContext().isPaintOffsetCacheEnabled() && !container && styleToUse.position() != PositionType::Fixed && !context.options.contains(VisibleRectContextOption::UseEdgeInclusiveIntersection))
2542 return computeVisibleRectUsingPaintOffset(rect);
2543
2544 LayoutRect adjustedRect = rect;
2545 if (hasReflection())
2546 adjustedRect.unite(reflectedRect(adjustedRect));
2547
2548 if (container == this) {
2549 if (container->style().isFlippedBlocksWritingMode())
2550 flipForWritingMode(adjustedRect);
2551 if (context.descendantNeedsEnclosingIntRect)
2552 adjustedRect = enclosingIntRect(adjustedRect);
2553 return adjustedRect;
2554 }
2555
2556 bool containerIsSkipped;
2557 auto* localContainer = this->container(container, containerIsSkipped);
2558 if (!localContainer)
2559 return adjustedRect;
2560
2561 if (isWritingModeRoot()) {
2562 if (!isOutOfFlowPositioned() || !context.dirtyRectIsFlipped) {
2563 flipForWritingMode(adjustedRect);
2564 context.dirtyRectIsFlipped = true;
2565 }
2566 }
2567
2568 LayoutSize locationOffset = this->locationOffset();
2569 // FIXME: This is needed as long as RenderWidget snaps to integral size/position.
2570 if (isRenderReplaced() && isWidget()) {
2571 LayoutSize flooredLocationOffset = toIntSize(flooredIntPoint(locationOffset));
2572 adjustedRect.expand(locationOffset - flooredLocationOffset);
2573 locationOffset = flooredLocationOffset;
2574 context.descendantNeedsEnclosingIntRect = true;
2575 }
2576
2577 if (is<RenderMultiColumnFlow>(this)) {
2578 // We won't normally run this code. Only when the container is null (i.e., we're trying
2579 // to get the rect in view coordinates) will we come in here, since normally container
2580 // will be set and we'll stop at the flow thread. This case is mainly hit by the check for whether
2581 // or not images should animate.
2582 // FIXME: Just as with offsetFromContainer, we aren't really handling objects that span
2583 // multiple columns properly.
2584 LayoutPoint physicalPoint(flipForWritingMode(adjustedRect.location()));
2585 if (auto* fragment = downcast<RenderMultiColumnFlow>(*this).physicalTranslationFromFlowToFragment((physicalPoint))) {
2586 adjustedRect.setLocation(fragment->flipForWritingMode(physicalPoint));
2587 return fragment->computeVisibleRectInContainer(adjustedRect, container, context);
2588 }
2589 }
2590
2591 LayoutPoint topLeft = adjustedRect.location();
2592 topLeft.move(locationOffset);
2593
2594 // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box
2595 // in the parent's coordinate space that encloses us.
2596 auto position = styleToUse.position();
2597 if (hasLayer() && layer()->transform()) {
2598 context.hasPositionFixedDescendant = position == PositionType::Fixed;
2599 adjustedRect = LayoutRect(encloseRectToDevicePixels(layer()->transform()->mapRect(adjustedRect), document().deviceScaleFactor()));
2600 topLeft = adjustedRect.location();
2601 topLeft.move(locationOffset);
2602 } else if (position == PositionType::Fixed)
2603 context.hasPositionFixedDescendant = true;
2604
2605 if (position == PositionType::Absolute && localContainer->isInFlowPositioned() && is<RenderInline>(*localContainer))
2606 topLeft += downcast<RenderInline>(*localContainer).offsetForInFlowPositionedInline(this);
2607 else if (styleToUse.hasInFlowPosition() && layer()) {
2608 // Apply the relative position offset when invalidating a rectangle. The layer
2609 // is translated, but the render box isn't, so we need to do this to get the
2610 // right dirty rect. Since this is called from RenderObject::setStyle, the relative position
2611 // flag on the RenderObject has been cleared, so use the one on the style().
2612 topLeft += layer()->offsetForInFlowPosition();
2613 }
2614
2615 // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout,
2616 // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer.
2617 adjustedRect.setLocation(topLeft);
2618 if (localContainer->hasNonVisibleOverflow()) {
2619 RenderBox& containerBox = downcast<RenderBox>(*localContainer);
2620 bool isEmpty = !containerBox.applyCachedClipAndScrollPosition(adjustedRect, container, context);
2621 if (isEmpty) {
2622 if (context.options.contains(VisibleRectContextOption::UseEdgeInclusiveIntersection))
2623 return std::nullopt;
2624 return adjustedRect;
2625 }
2626 }
2627
2628 if (containerIsSkipped) {
2629 // If the container is below localContainer, then we need to map the rect into container's coordinates.
2630 LayoutSize containerOffset = container->offsetFromAncestorContainer(*localContainer);
2631 adjustedRect.move(-containerOffset);
2632 return adjustedRect;
2633 }
2634 return localContainer->computeVisibleRectInContainer(adjustedRect, container, context);
2635}
2636
2637void RenderBox::repaintDuringLayoutIfMoved(const LayoutRect& oldRect)
2638{
2639 if (oldRect.location() != m_frameRect.location()) {
2640 LayoutRect newRect = m_frameRect;
2641 // The child moved. Invalidate the object's old and new positions. We have to do this
2642 // since the object may not have gotten a layout.
2643 m_frameRect = oldRect;
2644 repaint();
2645 repaintOverhangingFloats(true);
2646 m_frameRect = newRect;
2647 repaint();
2648 repaintOverhangingFloats(true);
2649 }
2650}
2651
2652void RenderBox::repaintOverhangingFloats(bool)
2653{
2654}
2655
2656void RenderBox::updateLogicalWidth()
2657{
2658 LogicalExtentComputedValues computedValues;
2659 computeLogicalWidthInFragment(computedValues);
2660
2661 setLogicalWidth(computedValues.m_extent);
2662 setLogicalLeft(computedValues.m_position);
2663 setMarginStart(computedValues.m_margins.m_start);
2664 setMarginEnd(computedValues.m_margins.m_end);
2665}
2666
2667static LayoutUnit inlineSizeFromAspectRatio(LayoutUnit borderPaddingInlineSum, LayoutUnit borderPaddingBlockSum, double aspectRatio, BoxSizing boxSizing, LayoutUnit blockSize)
2668{
2669 if (boxSizing == BoxSizing::BorderBox)
2670 return LayoutUnit(blockSize * aspectRatio);
2671
2672 return LayoutUnit((blockSize - borderPaddingBlockSum) * aspectRatio) + borderPaddingInlineSum;
2673}
2674
2675void RenderBox::computeLogicalWidthInFragment(LogicalExtentComputedValues& computedValues, RenderFragmentContainer* fragment) const
2676{
2677 computedValues.m_extent = logicalWidth();
2678 computedValues.m_position = logicalLeft();
2679 computedValues.m_margins.m_start = marginStart();
2680 computedValues.m_margins.m_end = marginEnd();
2681
2682 if (isOutOfFlowPositioned()) {
2683 // FIXME: This calculation is not patched for block-flow yet.
2684 // https://bugs.webkit.org/show_bug.cgi?id=46500
2685 computePositionedLogicalWidth(computedValues, fragment);
2686 return;
2687 }
2688
2689 // If layout is limited to a subtree, the subtree root's logical width does not change.
2690 if (element() && !view().frameView().layoutContext().isLayoutPending() && view().frameView().layoutContext().subtreeLayoutRoot() == this)
2691 return;
2692
2693 // The parent box is flexing us, so it has increased or decreased our
2694 // width. Use the width from the style context.
2695 // FIXME: Account for block-flow in flexible boxes.
2696 // https://bugs.webkit.org/show_bug.cgi?id=46418
2697 if (hasOverridingLogicalWidth() && (isRubyRun() || (parent()->isFlexibleBoxIncludingDeprecated()))) {
2698 computedValues.m_extent = overridingLogicalWidth();
2699 return;
2700 }
2701
2702 // FIXME: Account for block-flow in flexible boxes.
2703 // https://bugs.webkit.org/show_bug.cgi?id=46418
2704 bool inVerticalBox = parent()->isDeprecatedFlexibleBox() && (parent()->style().boxOrient() == BoxOrient::Vertical);
2705 bool stretching = (parent()->style().boxAlign() == BoxAlignment::Stretch);
2706 // FIXME: Stretching is the only reason why we don't want the box to be treated as a replaced element, so we could perhaps
2707 // refactor all this logic, not only for flex and grid since alignment is intended to be applied to any block.
2708 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inVerticalBox || !stretching);
2709 treatAsReplaced = treatAsReplaced && (!isGridItem() || !hasStretchedLogicalWidth());
2710
2711 const RenderStyle& styleToUse = style();
2712 Length logicalWidthLength;
2713 if (hasOverridingLogicalWidthLength())
2714 logicalWidthLength = overridingLogicalWidthLength();
2715 else
2716 logicalWidthLength = treatAsReplaced ? Length(computeReplacedLogicalWidth(), LengthType::Fixed) : styleToUse.logicalWidth();
2717
2718 RenderBlock& cb = *containingBlock();
2719 LayoutUnit containerLogicalWidth = std::max<LayoutUnit>(0, containingBlockLogicalWidthForContentInFragment(fragment));
2720 bool hasPerpendicularContainingBlock = cb.isHorizontalWritingMode() != isHorizontalWritingMode();
2721
2722 if (isInline() && !isInlineBlockOrInlineTable()) {
2723 // just calculate margins
2724 computedValues.m_margins.m_start = minimumValueForLength(styleToUse.marginStart(), containerLogicalWidth);
2725 computedValues.m_margins.m_end = minimumValueForLength(styleToUse.marginEnd(), containerLogicalWidth);
2726 if (treatAsReplaced)
2727 computedValues.m_extent = std::max(LayoutUnit(floatValueForLength(logicalWidthLength, 0) + borderAndPaddingLogicalWidth()), minPreferredLogicalWidth());
2728 return;
2729 }
2730
2731 LayoutUnit containerWidthInInlineDirection = containerLogicalWidth;
2732 if (hasPerpendicularContainingBlock)
2733 containerWidthInInlineDirection = perpendicularContainingBlockLogicalHeight();
2734
2735 // Width calculations
2736 if (isGridItem() && hasOverridingLogicalWidth()) {
2737 computedValues.m_extent = overridingLogicalWidth();
2738 } else if (treatAsReplaced) {
2739 computedValues.m_extent = logicalWidthLength.value() + borderAndPaddingLogicalWidth();
2740 } else if (shouldComputeLogicalWidthFromAspectRatio() && style().logicalWidth().isAuto()) {
2741 computedValues.m_extent = computeLogicalWidthFromAspectRatio(fragment);
2742 } else {
2743 LayoutUnit preferredWidth = computeLogicalWidthInFragmentUsing(MainOrPreferredSize, hasOverridingLogicalWidthLength() ? logicalWidthLength : styleToUse.logicalWidth(), containerWidthInInlineDirection, cb, fragment);
2744 computedValues.m_extent = constrainLogicalWidthInFragmentByMinMax(preferredWidth, containerWidthInInlineDirection, cb, fragment);
2745 }
2746
2747 // Margin calculations.
2748 if (hasPerpendicularContainingBlock || isFloating() || isInline()) {
2749 computedValues.m_margins.m_start = minimumValueForLength(styleToUse.marginStart(), containerLogicalWidth);
2750 computedValues.m_margins.m_end = minimumValueForLength(styleToUse.marginEnd(), containerLogicalWidth);
2751 } else {
2752 LayoutUnit containerLogicalWidthForAutoMargins = containerLogicalWidth;
2753 if (avoidsFloats() && cb.containsFloats())
2754 containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInFragment(fragment);
2755 bool hasInvertedDirection = cb.style().isLeftToRightDirection() != style().isLeftToRightDirection();
2756 computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, computedValues.m_extent,
2757 hasInvertedDirection ? computedValues.m_margins.m_end : computedValues.m_margins.m_start,
2758 hasInvertedDirection ? computedValues.m_margins.m_start : computedValues.m_margins.m_end);
2759 }
2760
2761 if (!hasPerpendicularContainingBlock && containerLogicalWidth && containerLogicalWidth != (computedValues.m_extent + computedValues.m_margins.m_start + computedValues.m_margins.m_end)
2762 && !isFloating() && !isInline() && !cb.isFlexibleBoxIncludingDeprecated()
2763#if ENABLE(MATHML)
2764 // RenderMathMLBlocks take the size of their content so we must not adjust the margin to fill the container size.
2765 && !cb.isRenderMathMLBlock()
2766#endif
2767 && !cb.isRenderGrid()
2768 ) {
2769 LayoutUnit newMarginTotal = containerLogicalWidth - computedValues.m_extent;
2770 bool hasInvertedDirection = cb.style().isLeftToRightDirection() != style().isLeftToRightDirection();
2771 if (hasInvertedDirection)
2772 computedValues.m_margins.m_start = newMarginTotal - computedValues.m_margins.m_end;
2773 else
2774 computedValues.m_margins.m_end = newMarginTotal - computedValues.m_margins.m_start;
2775 }
2776}
2777
2778LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth) const
2779{
2780 LayoutUnit marginStart;
2781 LayoutUnit marginEnd;
2782 return fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd);
2783}
2784
2785LayoutUnit RenderBox::fillAvailableMeasure(LayoutUnit availableLogicalWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const
2786{
2787 bool isOrthogonalElement = isHorizontalWritingMode() != containingBlock()->isHorizontalWritingMode();
2788 LayoutUnit availableSizeForResolvingMargin = isOrthogonalElement ? containingBlockLogicalWidthForContent() : availableLogicalWidth;
2789 marginStart = minimumValueForLength(style().marginStart(), availableSizeForResolvingMargin);
2790 marginEnd = minimumValueForLength(style().marginEnd(), availableSizeForResolvingMargin);
2791 return availableLogicalWidth - marginStart - marginEnd;
2792}
2793
2794LayoutUnit RenderBox::computeIntrinsicLogicalWidthUsing(Length logicalWidthLength, LayoutUnit availableLogicalWidth, LayoutUnit borderAndPadding) const
2795{
2796 if (logicalWidthLength.isFillAvailable())
2797 return std::max(borderAndPadding, fillAvailableMeasure(availableLogicalWidth));
2798
2799 LayoutUnit minLogicalWidth;
2800 LayoutUnit maxLogicalWidth;
2801 if (!logicalWidthLength.isMinIntrinsic() && shouldComputeLogicalWidthFromAspectRatio()) {
2802 minLogicalWidth = maxLogicalWidth = computeLogicalWidthFromAspectRatioInternal() - borderAndPadding;
2803 if (firstChild()) {
2804 LayoutUnit minChildrenLogicalWidth;
2805 LayoutUnit maxChildrenLogicalWidth;
2806 computeIntrinsicKeywordLogicalWidths(minChildrenLogicalWidth, maxChildrenLogicalWidth);
2807 minLogicalWidth = std::max(minLogicalWidth, minChildrenLogicalWidth);
2808 maxLogicalWidth = std::max(maxLogicalWidth, maxChildrenLogicalWidth);
2809 }
2810 } else
2811 computeIntrinsicKeywordLogicalWidths(minLogicalWidth, maxLogicalWidth);
2812
2813 if (logicalWidthLength.isMinContent() || logicalWidthLength.isMinIntrinsic())
2814 return minLogicalWidth + borderAndPadding;
2815
2816 if (logicalWidthLength.isMaxContent())
2817 return maxLogicalWidth + borderAndPadding;
2818
2819 if (logicalWidthLength.isFitContent()) {
2820 minLogicalWidth += borderAndPadding;
2821 maxLogicalWidth += borderAndPadding;
2822 return std::max(minLogicalWidth, std::min(maxLogicalWidth, fillAvailableMeasure(availableLogicalWidth)));
2823 }
2824
2825 ASSERT_NOT_REACHED();
2826 return 0;
2827}
2828
2829LayoutUnit RenderBox::computeLogicalWidthInFragmentUsing(SizeType widthType, Length logicalWidth, LayoutUnit availableLogicalWidth,
2830 const RenderBlock& cb, RenderFragmentContainer* fragment) const
2831{
2832 ASSERT(widthType == MinSize || widthType == MainOrPreferredSize || !logicalWidth.isAuto());
2833 if (widthType == MinSize && logicalWidth.isAuto())
2834 return adjustBorderBoxLogicalWidthForBoxSizing(0, logicalWidth.type());
2835
2836 if (!logicalWidth.isIntrinsicOrAuto()) {
2837 // FIXME: If the containing block flow is perpendicular to our direction we need to use the available logical height instead.
2838 return adjustBorderBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, availableLogicalWidth), logicalWidth.type());
2839 }
2840
2841 if (logicalWidth.isIntrinsic() || logicalWidth.isMinIntrinsic())
2842 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth());
2843
2844 LayoutUnit marginStart;
2845 LayoutUnit marginEnd;
2846 LayoutUnit logicalWidthResult = fillAvailableMeasure(availableLogicalWidth, marginStart, marginEnd);
2847
2848 if (shrinkToAvoidFloats() && cb.containsFloats())
2849 logicalWidthResult = std::min(logicalWidthResult, shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, cb, fragment));
2850
2851 if (widthType == MainOrPreferredSize && sizesLogicalWidthToFitContent(widthType))
2852 return std::max(minPreferredLogicalWidth(), std::min(maxPreferredLogicalWidth(), logicalWidthResult));
2853 return logicalWidthResult;
2854}
2855
2856bool RenderBox::columnFlexItemHasStretchAlignment() const
2857{
2858 // auto margins mean we don't stretch. Note that this function will only be
2859 // used for widths, so we don't have to check marginBefore/marginAfter.
2860 const auto& parentStyle = parent()->style();
2861 ASSERT(parentStyle.isColumnFlexDirection());
2862 if (style().marginStart().isAuto() || style().marginEnd().isAuto())
2863 return false;
2864 return style().resolvedAlignSelf(&parentStyle, containingBlock()->selfAlignmentNormalBehavior()).position() == ItemPosition::Stretch;
2865}
2866
2867bool RenderBox::isStretchingColumnFlexItem() const
2868{
2869 if (parent()->isDeprecatedFlexibleBox() && parent()->style().boxOrient() == BoxOrient::Vertical && parent()->style().boxAlign() == BoxAlignment::Stretch)
2870 return true;
2871
2872 // We don't stretch multiline flexboxes because they need to apply line spacing (align-content) first.
2873 if (parent()->isFlexibleBox() && parent()->style().flexWrap() == FlexWrap::NoWrap && parent()->style().isColumnFlexDirection() && columnFlexItemHasStretchAlignment())
2874 return true;
2875 return false;
2876}
2877
2878// FIXME: Can/Should we move this inside specific layout classes (flex. grid)? Can we refactor columnFlexItemHasStretchAlignment logic?
2879bool RenderBox::hasStretchedLogicalHeight() const
2880{
2881 auto& style = this->style();
2882 if (!style.logicalHeight().isAuto() || style.marginBefore().isAuto() || style.marginAfter().isAuto())
2883 return false;
2884 RenderBlock* containingBlock = this->containingBlock();
2885 if (!containingBlock) {
2886 // We are evaluating align-self/justify-self, which default to 'normal' for the root element.
2887 // The 'normal' value behaves like 'start' except for Flexbox Items, which obviously should have a container.
2888 return false;
2889 }
2890 if (containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) {
2891 if (is<RenderGrid>(this) && downcast<RenderGrid>(this)->isSubgridInParentDirection(ForColumns))
2892 return true;
2893 return style.resolvedJustifySelf(&containingBlock->style(), containingBlock->selfAlignmentNormalBehavior(this)).position() == ItemPosition::Stretch;
2894 }
2895 if (is<RenderGrid>(this) && downcast<RenderGrid>(this)->isSubgridInParentDirection(ForRows))
2896 return true;
2897 return style.resolvedAlignSelf(&containingBlock->style(), containingBlock->selfAlignmentNormalBehavior(this)).position() == ItemPosition::Stretch;
2898}
2899
2900// FIXME: Can/Should we move this inside specific layout classes (flex. grid)? Can we refactor columnFlexItemHasStretchAlignment logic?
2901bool RenderBox::hasStretchedLogicalWidth(StretchingMode stretchingMode) const
2902{
2903 auto& style = this->style();
2904 if (!style.logicalWidth().isAuto() || style.marginStart().isAuto() || style.marginEnd().isAuto())
2905 return false;
2906 RenderBlock* containingBlock = this->containingBlock();
2907 if (!containingBlock) {
2908 // We are evaluating align-self/justify-self, which default to 'normal' for the root element.
2909 // The 'normal' value behaves like 'start' except for Flexbox Items, which obviously should have a container.
2910 return false;
2911 }
2912 auto normalItemPosition = stretchingMode == StretchingMode::Any ? containingBlock->selfAlignmentNormalBehavior(this) : ItemPosition::Normal;
2913 if (containingBlock->isHorizontalWritingMode() != isHorizontalWritingMode()) {
2914 if (is<RenderGrid>(this) && downcast<RenderGrid>(this)->isSubgridInParentDirection(ForRows))
2915 return true;
2916 return style.resolvedAlignSelf(&containingBlock->style(), normalItemPosition).position() == ItemPosition::Stretch;
2917 }
2918 if (is<RenderGrid>(this) && downcast<RenderGrid>(this)->isSubgridInParentDirection(ForColumns))
2919 return true;
2920 return style.resolvedJustifySelf(&containingBlock->style(), normalItemPosition).position() == ItemPosition::Stretch;
2921}
2922
2923bool RenderBox::sizesLogicalWidthToFitContent(SizeType widthType) const
2924{
2925 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
2926 // but they allow text to sit on the same line as the marquee.
2927 if (isFloating() || (isInlineBlockOrInlineTable() && !isHTMLMarquee()))
2928 return true;
2929
2930 if (isGridItem())
2931 return !hasStretchedLogicalWidth();
2932
2933 // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both
2934 // min-width and width. max-width is only clamped if it is also intrinsic.
2935 Length logicalWidth = (widthType == MaxSize) ? style().logicalMaxWidth() : style().logicalWidth();
2936 if (logicalWidth.type() == LengthType::Intrinsic)
2937 return true;
2938
2939 // Children of a horizontal marquee do not fill the container by default.
2940 // FIXME: Need to deal with MarqueeDirection::Auto value properly. It could be vertical.
2941 // FIXME: Think about block-flow here. Need to find out how marquee direction relates to
2942 // block-flow (as well as how marquee overflow should relate to block flow).
2943 // https://bugs.webkit.org/show_bug.cgi?id=46472
2944 if (parent()->isHTMLMarquee()) {
2945 MarqueeDirection dir = parent()->style().marqueeDirection();
2946 if (dir == MarqueeDirection::Auto || dir == MarqueeDirection::Forward || dir == MarqueeDirection::Backward || dir == MarqueeDirection::Left || dir == MarqueeDirection::Right)
2947 return true;
2948 }
2949
2950#if ENABLE(MATHML)
2951 // RenderMathMLBlocks take the size of their content, not of their container.
2952 if (parent()->isRenderMathMLBlock())
2953 return true;
2954#endif
2955
2956 // Flexible box items should shrink wrap, so we lay them out at their intrinsic widths.
2957 // In the case of columns that have a stretch alignment, we layout at the stretched size
2958 // to avoid an extra layout when applying alignment.
2959 if (parent()->isFlexibleBox()) {
2960 // For multiline columns, we need to apply align-content first, so we can't stretch now.
2961 if (!parent()->style().isColumnFlexDirection() || parent()->style().flexWrap() != FlexWrap::NoWrap)
2962 return true;
2963 if (!columnFlexItemHasStretchAlignment())
2964 return true;
2965 }
2966
2967 // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes
2968 // that don't stretch their kids lay out their children at their intrinsic widths.
2969 // FIXME: Think about block-flow here.
2970 // https://bugs.webkit.org/show_bug.cgi?id=46473
2971 if (parent()->isDeprecatedFlexibleBox() && (parent()->style().boxOrient() == BoxOrient::Horizontal || parent()->style().boxAlign() != BoxAlignment::Stretch))
2972 return true;
2973
2974 // Button, input, select, textarea, and legend treat width value of 'auto' as 'intrinsic' unless it's in a
2975 // stretching column flexbox.
2976 // FIXME: Think about block-flow here.
2977 // https://bugs.webkit.org/show_bug.cgi?id=46473
2978 if (logicalWidth.isAuto() && !isStretchingColumnFlexItem() && element() && (is<HTMLInputElement>(*element()) || is<HTMLSelectElement>(*element()) || is<HTMLButtonElement>(*element()) || is<HTMLTextAreaElement>(*element()) || is<HTMLLegendElement>(*element())))
2979 return true;
2980
2981 if (isHorizontalWritingMode() != containingBlock()->isHorizontalWritingMode())
2982 return true;
2983
2984 return false;
2985}
2986
2987void RenderBox::computeInlineDirectionMargins(const RenderBlock& containingBlock, LayoutUnit containerWidth, LayoutUnit childWidth, LayoutUnit& marginStart, LayoutUnit& marginEnd) const
2988{
2989
2990 const RenderStyle& containingBlockStyle = containingBlock.style();
2991 Length marginStartLength = style().marginStartUsing(&containingBlockStyle);
2992 Length marginEndLength = style().marginEndUsing(&containingBlockStyle);
2993
2994 if (isFloating() || isInline()) {
2995 // Inline blocks/tables and floats don't have their margins increased.
2996 marginStart = minimumValueForLength(marginStartLength, containerWidth);
2997 marginEnd = minimumValueForLength(marginEndLength, containerWidth);
2998 return;
2999 }
3000
3001 if (containingBlock.isFlexibleBox()) {
3002 // We need to let flexbox handle the margin adjustment - otherwise, flexbox
3003 // will think we're wider than we actually are and calculate line sizes
3004 // wrong. See also http://dev.w3.org/csswg/css-flexbox/#auto-margins
3005 if (marginStartLength.isAuto())
3006 marginStartLength = Length(0, LengthType::Fixed);
3007 if (marginEndLength.isAuto())
3008 marginEndLength = Length(0, LengthType::Fixed);
3009 }
3010
3011 // Case One: The object is being centered in the containing block's available logical width.
3012 if ((marginStartLength.isAuto() && marginEndLength.isAuto() && childWidth < containerWidth)
3013 || (!marginStartLength.isAuto() && !marginEndLength.isAuto() && containingBlock.style().textAlign() == TextAlignMode::WebKitCenter)) {
3014 // Other browsers center the margin box for align=center elements so we match them here.
3015 LayoutUnit marginStartWidth = minimumValueForLength(marginStartLength, containerWidth);
3016 LayoutUnit marginEndWidth = minimumValueForLength(marginEndLength, containerWidth);
3017 LayoutUnit centeredMarginBoxStart = std::max<LayoutUnit>(0, (containerWidth - childWidth - marginStartWidth - marginEndWidth) / 2);
3018 marginStart = centeredMarginBoxStart + marginStartWidth;
3019 marginEnd = containerWidth - childWidth - marginStart + marginEndWidth;
3020 return;
3021 }
3022
3023 // Case Two: The object is being pushed to the start of the containing block's available logical width.
3024 if (marginEndLength.isAuto() && childWidth < containerWidth) {
3025 marginStart = valueForLength(marginStartLength, containerWidth);
3026 marginEnd = containerWidth - childWidth - marginStart;
3027 return;
3028 }
3029
3030 // Case Three: The object is being pushed to the end of the containing block's available logical width.
3031 bool pushToEndFromTextAlign = !marginEndLength.isAuto() && ((!containingBlockStyle.isLeftToRightDirection() && containingBlockStyle.textAlign() == TextAlignMode::WebKitLeft)
3032 || (containingBlockStyle.isLeftToRightDirection() && containingBlockStyle.textAlign() == TextAlignMode::WebKitRight));
3033 if ((marginStartLength.isAuto() || pushToEndFromTextAlign) && childWidth < containerWidth) {
3034 marginEnd = valueForLength(marginEndLength, containerWidth);
3035 marginStart = containerWidth - childWidth - marginEnd;
3036 return;
3037 }
3038
3039 // Case Four: Either no auto margins, or our width is >= the container width (css2.1, 10.3.3). In that case
3040 // auto margins will just turn into 0.
3041 marginStart = minimumValueForLength(marginStartLength, containerWidth);
3042 marginEnd = minimumValueForLength(marginEndLength, containerWidth);
3043}
3044
3045RenderBoxFragmentInfo* RenderBox::renderBoxFragmentInfo(RenderFragmentContainer* fragment, RenderBoxFragmentInfoFlags cacheFlag) const
3046{
3047 // Make sure nobody is trying to call this with a null fragment.
3048 if (!fragment)
3049 return nullptr;
3050
3051 // If we have computed our width in this fragment already, it will be cached, and we can
3052 // just return it.
3053 RenderBoxFragmentInfo* boxInfo = fragment->renderBoxFragmentInfo(this);
3054 if (boxInfo && cacheFlag == CacheRenderBoxFragmentInfo)
3055 return boxInfo;
3056
3057 return nullptr;
3058}
3059
3060static bool shouldFlipBeforeAfterMargins(const RenderStyle& containingBlockStyle, const RenderStyle* childStyle)
3061{
3062 ASSERT(containingBlockStyle.isHorizontalWritingMode() != childStyle->isHorizontalWritingMode());
3063 WritingMode childWritingMode = childStyle->writingMode();
3064 bool shouldFlip = false;
3065 switch (containingBlockStyle.writingMode()) {
3066 case WritingMode::TopToBottom:
3067 shouldFlip = (childWritingMode == WritingMode::RightToLeft);
3068 break;
3069 case WritingMode::BottomToTop:
3070 shouldFlip = (childWritingMode == WritingMode::RightToLeft);
3071 break;
3072 case WritingMode::RightToLeft:
3073 shouldFlip = (childWritingMode == WritingMode::BottomToTop);
3074 break;
3075 case WritingMode::LeftToRight:
3076 shouldFlip = (childWritingMode == WritingMode::BottomToTop);
3077 break;
3078 }
3079
3080 if (!containingBlockStyle.isLeftToRightDirection())
3081 shouldFlip = !shouldFlip;
3082
3083 return shouldFlip;
3084}
3085
3086void RenderBox::cacheIntrinsicContentLogicalHeightForFlexItem(LayoutUnit height) const
3087{
3088 // FIXME: it should be enough with checking hasOverridingLogicalHeight() as this logic could be shared
3089 // by any layout system using overrides like grid or flex. However this causes a never ending sequence of calls
3090 // between layoutBlock() <-> relayoutToAvoidWidows().
3091 if (isFloatingOrOutOfFlowPositioned() || !parent() || !parent()->isFlexibleBox() || hasOverridingLogicalHeight() || shouldComputeLogicalHeightFromAspectRatio())
3092 return;
3093 downcast<RenderFlexibleBox>(parent())->setCachedChildIntrinsicContentLogicalHeight(*this, height);
3094}
3095
3096void RenderBox::updateLogicalHeight()
3097{
3098 if (shouldApplySizeContainment() && !isRenderGrid()) {
3099 // We need the exact width of border and padding here, yet we can't use borderAndPadding* interfaces.
3100 // Because these interfaces evetually call borderAfter/Before, and RenderBlock::borderBefore
3101 // adds extra border to fieldset by adding intrinsicBorderForFieldset which is not needed here.
3102 auto borderAndPadding = RenderBox::borderBefore() + RenderBox::paddingBefore() + RenderBox::borderAfter() + RenderBox::paddingAfter();
3103 setLogicalHeight(borderAndPadding + scrollbarLogicalHeight());
3104 }
3105
3106 cacheIntrinsicContentLogicalHeightForFlexItem(contentLogicalHeight());
3107 auto computedValues = computeLogicalHeight(logicalHeight(), logicalTop());
3108 setLogicalHeight(computedValues.m_extent);
3109 setLogicalTop(computedValues.m_position);
3110 setMarginBefore(computedValues.m_margins.m_before);
3111 setMarginAfter(computedValues.m_margins.m_after);
3112}
3113
3114RenderBox::LogicalExtentComputedValues RenderBox::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop) const
3115{
3116 LogicalExtentComputedValues computedValues;
3117 computedValues.m_extent = logicalHeight;
3118 computedValues.m_position = logicalTop;
3119
3120 // Cell height is managed by the table and inline non-replaced elements do not support a height property.
3121 if (isTableCell() || (isInline() && !isReplacedOrInlineBlock()))
3122 return computedValues;
3123
3124 Length h;
3125 if (isOutOfFlowPositioned())
3126 computePositionedLogicalHeight(computedValues);
3127 else {
3128 RenderBlock& cb = *containingBlock();
3129 bool hasPerpendicularContainingBlock = cb.isHorizontalWritingMode() != isHorizontalWritingMode();
3130
3131 if (!hasPerpendicularContainingBlock) {
3132 bool shouldFlipBeforeAfter = cb.style().writingMode() != style().writingMode();
3133 computeBlockDirectionMargins(cb,
3134 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before,
3135 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after);
3136 }
3137
3138 // For tables, calculate margins only.
3139 if (isTable()) {
3140 if (shouldComputeLogicalHeightFromAspectRatio())
3141 computedValues.m_extent = blockSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), logicalWidth());
3142 if (hasPerpendicularContainingBlock) {
3143 bool shouldFlipBeforeAfter = shouldFlipBeforeAfterMargins(cb.style(), &style());
3144 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), computedValues.m_extent,
3145 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before,
3146 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after);
3147 }
3148 return computedValues;
3149 }
3150
3151 // FIXME: Account for block-flow in flexible boxes.
3152 // https://bugs.webkit.org/show_bug.cgi?id=46418
3153 bool inHorizontalBox = parent()->isDeprecatedFlexibleBox() && parent()->style().boxOrient() == BoxOrient::Horizontal;
3154 bool stretching = parent()->style().boxAlign() == BoxAlignment::Stretch;
3155 bool treatAsReplaced = shouldComputeSizeAsReplaced() && (!inHorizontalBox || !stretching);
3156 bool checkMinMaxHeight = false;
3157 const auto& logicalHeightInUse = hasOverridingLogicalHeightLength() ? overridingLogicalHeightLength() : style().logicalHeight();
3158
3159 // The parent box is flexing us, so it has increased or decreased our height. We have to
3160 // grab our cached flexible height.
3161 // FIXME: Account for block-flow in flexible boxes.
3162 // https://bugs.webkit.org/show_bug.cgi?id=46418
3163 if (hasOverridingLogicalHeight() && (parent()->isFlexibleBoxIncludingDeprecated() || parent()->isRenderGrid())) {
3164 h = Length(overridingLogicalHeight(), LengthType::Fixed);
3165 } else if (treatAsReplaced)
3166 h = Length(computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight(), LengthType::Fixed);
3167 else {
3168 h = logicalHeightInUse;
3169 checkMinMaxHeight = true;
3170 }
3171
3172 // Block children of horizontal flexible boxes fill the height of the box.
3173 // FIXME: Account for block-flow in flexible boxes.
3174 // https://bugs.webkit.org/show_bug.cgi?id=46418
3175 if (h.isAuto() && is<RenderDeprecatedFlexibleBox>(*parent()) && parent()->style().boxOrient() == BoxOrient::Horizontal
3176 && downcast<RenderDeprecatedFlexibleBox>(*parent()).isStretchingChildren()) {
3177 h = Length(parentBox()->contentLogicalHeight() - marginBefore() - marginAfter(), LengthType::Fixed);
3178 checkMinMaxHeight = false;
3179 }
3180
3181 LayoutUnit heightResult;
3182 if (checkMinMaxHeight) {
3183 // Callers passing LayoutUnit::max() for logicalHeight means an indefinite height, so
3184 // translate this to a nullopt intrinsic height for further logical height computations.
3185 std::optional<LayoutUnit> intrinsicHeight;
3186 if (computedValues.m_extent != LayoutUnit::max())
3187 intrinsicHeight = computedValues.m_extent;
3188 if (shouldComputeLogicalHeightFromAspectRatio()) {
3189 if (intrinsicHeight && style().boxSizingForAspectRatio() == BoxSizing::ContentBox)
3190 *intrinsicHeight -= borderAndPaddingLogicalHeight();
3191 heightResult = blockSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), logicalWidth());
3192 } else {
3193 if (intrinsicHeight)
3194 *intrinsicHeight -= borderAndPaddingLogicalHeight();
3195 heightResult = computeLogicalHeightUsing(MainOrPreferredSize, logicalHeightInUse, intrinsicHeight).value_or(computedValues.m_extent);
3196 }
3197 heightResult = constrainLogicalHeightByMinMax(heightResult, intrinsicHeight);
3198 } else {
3199 ASSERT(h.isFixed());
3200 heightResult = h.value();
3201 }
3202
3203 computedValues.m_extent = heightResult;
3204
3205 if (hasPerpendicularContainingBlock) {
3206 bool shouldFlipBeforeAfter = shouldFlipBeforeAfterMargins(cb.style(), &style());
3207 computeInlineDirectionMargins(cb, containingBlockLogicalWidthForContent(), heightResult,
3208 shouldFlipBeforeAfter ? computedValues.m_margins.m_after : computedValues.m_margins.m_before,
3209 shouldFlipBeforeAfter ? computedValues.m_margins.m_before : computedValues.m_margins.m_after);
3210 }
3211 }
3212
3213 // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the
3214 // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height
3215 // is specified. When we're printing, we also need this quirk if the body or root has a percentage
3216 // height since we don't set a height in RenderView when we're printing. So without this quirk, the
3217 // height has nothing to be a percentage of, and it ends up being 0. That is bad.
3218 auto paginatedContentNeedsBaseHeight = [&] {
3219 if (!document().printing() || !h.isPercentOrCalculated() || isInline())
3220 return false;
3221 if (isDocumentElementRenderer())
3222 return true;
3223 auto* documentElementRenderer = document().documentElement()->renderer();
3224 return isBody() && parent() == documentElementRenderer && documentElementRenderer->style().logicalHeight().isPercentOrCalculated();
3225 };
3226 if (stretchesToViewport() || paginatedContentNeedsBaseHeight()) {
3227 LayoutUnit margins = collapsedMarginBefore() + collapsedMarginAfter();
3228 LayoutUnit visibleHeight = view().pageOrViewLogicalHeight();
3229 if (isDocumentElementRenderer())
3230 computedValues.m_extent = std::max(computedValues.m_extent, visibleHeight - margins);
3231 else {
3232 LayoutUnit marginsBordersPadding = margins + parentBox()->marginBefore() + parentBox()->marginAfter() + parentBox()->borderAndPaddingLogicalHeight();
3233 computedValues.m_extent = std::max(computedValues.m_extent, visibleHeight - marginsBordersPadding);
3234 }
3235 }
3236 return computedValues;
3237}
3238
3239LayoutUnit RenderBox::computeLogicalHeightWithoutLayout() const
3240{
3241 // FIXME:: We should probably return something other than just
3242 // border + padding, but for now we have no good way to do anything else
3243 // without layout, so we just use that.
3244 LogicalExtentComputedValues computedValues = computeLogicalHeight(borderAndPaddingLogicalHeight(), 0_lu);
3245 return computedValues.m_extent;
3246}
3247
3248std::optional<LayoutUnit> RenderBox::computeLogicalHeightUsing(SizeType heightType, const Length& height, std::optional<LayoutUnit> intrinsicContentHeight) const
3249{
3250 if (std::optional<LayoutUnit> logicalHeight = computeContentAndScrollbarLogicalHeightUsing(heightType, height, intrinsicContentHeight))
3251 return adjustBorderBoxLogicalHeightForBoxSizing(logicalHeight.value());
3252 return std::nullopt;
3253}
3254
3255std::optional<LayoutUnit> RenderBox::computeContentLogicalHeight(SizeType heightType, const Length& height, std::optional<LayoutUnit> intrinsicContentHeight) const
3256{
3257 if (std::optional<LayoutUnit> heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(heightType, height, intrinsicContentHeight))
3258 return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight());
3259 return std::nullopt;
3260}
3261
3262static inline bool isOrthogonal(const RenderBox& renderer, const RenderElement& ancestor)
3263{
3264 return renderer.isHorizontalWritingMode() != ancestor.isHorizontalWritingMode();
3265}
3266
3267std::optional<LayoutUnit> RenderBox::computeIntrinsicLogicalContentHeightUsing(Length logicalHeightLength, std::optional<LayoutUnit> intrinsicContentHeight, LayoutUnit borderAndPadding) const
3268{
3269 // FIXME: The CSS sizing spec is considering changing what min-content/max-content should resolve to.
3270 // If that happens, this code will have to change.
3271 if (logicalHeightLength.isMinContent() || logicalHeightLength.isMaxContent() || logicalHeightLength.isFitContent() || logicalHeightLength.isLegacyIntrinsic()) {
3272 if (intrinsicContentHeight)
3273 return adjustIntrinsicLogicalHeightForBoxSizing(intrinsicContentHeight.value());
3274 return { };
3275 }
3276 if (logicalHeightLength.isFillAvailable()) {
3277 auto* containingBlock = this->containingBlock();
3278
3279 auto canResolveAvailableSpace = [&] {
3280 // FIXME: We need to find a way to say: yes, the constraint value is set and we can resolve height against it.
3281 // Until then, this is mostly just guesswork.
3282 if (!containingBlock)
3283 return false;
3284 if (is<RenderView>(containingBlock))
3285 return true;
3286 auto containingBlockHasSpecifiedSpace = [&] {
3287 auto isOrthogonal = WebCore::isOrthogonal(*this, *containingBlock);
3288 auto& style = containingBlock->style();
3289 if ((!isOrthogonal && style.height().isSpecified()) || (isOrthogonal && style.width().isSpecified()))
3290 return true;
3291 if (containingBlock->isOutOfFlowPositioned()) {
3292 if ((!isOrthogonal && !style.top().isAuto() && !style.bottom().isAuto()) || (isOrthogonal && !style.left().isAuto() && !style.right().isAuto()))
3293 return true;
3294 }
3295 return false;
3296 };
3297 return containingBlockHasSpecifiedSpace() || containingBlock->hasOverridingLogicalHeight();
3298 };
3299 if (canResolveAvailableSpace())
3300 return containingBlock->availableLogicalHeight(ExcludeMarginBorderPadding) - borderAndPadding;
3301 return { };
3302 }
3303 ASSERT_NOT_REACHED();
3304 return 0_lu;
3305}
3306
3307std::optional<LayoutUnit> RenderBox::computeContentAndScrollbarLogicalHeightUsing(SizeType heightType, const Length& height, std::optional<LayoutUnit> intrinsicContentHeight) const
3308{
3309 if (height.isAuto()) {
3310 if (heightType != MinSize)
3311 return std::nullopt;
3312 if (intrinsicContentHeight && isFlexItem() && downcast<RenderFlexibleBox>(parent())->shouldApplyMinBlockSizeAutoForChild(*this))
3313 return adjustIntrinsicLogicalHeightForBoxSizing(intrinsicContentHeight.value());
3314 return std::optional<LayoutUnit>(0);
3315 }
3316 // FIXME: The CSS sizing spec is considering changing what min-content/max-content should resolve to.
3317 // If that happens, this code will have to change.
3318 if (height.isIntrinsic() || height.isLegacyIntrinsic())
3319 return computeIntrinsicLogicalContentHeightUsing(height, intrinsicContentHeight, borderAndPaddingLogicalHeight());
3320 if (height.isFixed())
3321 return LayoutUnit(height.value());
3322 if (height.isPercentOrCalculated())
3323 return computePercentageLogicalHeight(height);
3324 return std::nullopt;
3325}
3326
3327bool RenderBox::skipContainingBlockForPercentHeightCalculation(const RenderBox& containingBlock, bool isPerpendicularWritingMode) const
3328{
3329 // Flow threads for multicol or paged overflow should be skipped. They are invisible to the DOM,
3330 // and percent heights of children should be resolved against the multicol or paged container.
3331 if (containingBlock.isRenderFragmentedFlow() && !isPerpendicularWritingMode)
3332 return true;
3333
3334 // Render view is not considered auto height.
3335 if (is<RenderView>(containingBlock))
3336 return false;
3337
3338 // If the writing mode of the containing block is orthogonal to ours, it means
3339 // that we shouldn't skip anything, since we're going to resolve the
3340 // percentage height against a containing block *width*.
3341 if (isPerpendicularWritingMode)
3342 return false;
3343
3344 // Anonymous blocks should not impede percentage resolution on a child.
3345 // Examples of such anonymous blocks are blocks wrapped around inlines that
3346 // have block siblings (from the CSS spec) and multicol flow threads (an
3347 // implementation detail). Another implementation detail, ruby runs, create
3348 // anonymous inline-blocks, so skip those too. All other types of anonymous
3349 // objects, such as table-cells and flexboxes, will be treated as if they were
3350 // non-anonymous.
3351 if (containingBlock.isAnonymous())
3352 return containingBlock.style().display() == DisplayType::Block || containingBlock.style().display() == DisplayType::InlineBlock;
3353
3354 // For quirks mode, we skip most auto-height containing blocks when computing
3355 // percentages.
3356 return document().inQuirksMode() && !containingBlock.isTableCell() && !containingBlock.isOutOfFlowPositioned() && !containingBlock.isRenderGrid() && !containingBlock.isFlexibleBoxIncludingDeprecated() && containingBlock.style().logicalHeight().isAuto();
3357}
3358
3359bool RenderBox::shouldTreatChildAsReplacedInTableCells() const
3360{
3361 if (isReplacedOrInlineBlock())
3362 return true;
3363 return element() && (element()->isFormControlElement() || is<HTMLImageElement>(element()));
3364}
3365
3366static bool tableCellShouldHaveZeroInitialSize(const RenderBlock& block, const RenderBox& child, bool scrollsOverflowY)
3367{
3368 // Normally we would let the cell size intrinsically, but scrolling overflow has to be
3369 // treated differently, since WinIE lets scrolled overflow fragments shrink as needed.
3370 // While we can't get all cases right, we can at least detect when the cell has a specified
3371 // height or when the table has a specified height. In these cases we want to initially have
3372 // no size and allow the flexing of the table or the cell to its specified height to cause us
3373 // to grow to fill the space. This could end up being wrong in some cases, but it is
3374 // preferable to the alternative (sizing intrinsically and making the row end up too big).
3375 const RenderTableCell& cell = downcast<RenderTableCell>(block);
3376 return scrollsOverflowY && !child.shouldTreatChildAsReplacedInTableCells() && (!cell.style().logicalHeight().isAuto() || !cell.table()->style().logicalHeight().isAuto());
3377}
3378
3379std::optional<LayoutUnit> RenderBox::computePercentageLogicalHeight(const Length& height, UpdatePercentageHeightDescendants updateDescendants) const
3380{
3381 std::optional<LayoutUnit> availableHeight;
3382
3383 bool skippedAutoHeightContainingBlock = false;
3384 RenderBlock* cb = containingBlock();
3385 const RenderBox* containingBlockChild = this;
3386 LayoutUnit rootMarginBorderPaddingHeight;
3387 bool isHorizontal = isHorizontalWritingMode();
3388 while (cb && !is<RenderView>(*cb) && skipContainingBlockForPercentHeightCalculation(*cb, isHorizontal != cb->isHorizontalWritingMode())) {
3389 if (cb->isBody() || cb->isDocumentElementRenderer())
3390 rootMarginBorderPaddingHeight += cb->marginBefore() + cb->marginAfter() + cb->borderAndPaddingLogicalHeight();
3391 skippedAutoHeightContainingBlock = true;
3392 containingBlockChild = cb;
3393 cb = cb->containingBlock();
3394 }
3395 if (updateDescendants == UpdatePercentageHeightDescendants::Yes)
3396 cb->addPercentHeightDescendant(const_cast<RenderBox&>(*this));
3397
3398 bool isOrthogonal = isHorizontal != cb->isHorizontalWritingMode();
3399 if (hasOverridingContainingBlockContentLogicalWidth() && isOrthogonal)
3400 availableHeight = overridingContainingBlockContentLogicalWidth();
3401 else if (hasOverridingContainingBlockContentLogicalHeight() && !isOrthogonal)
3402 availableHeight = overridingContainingBlockContentLogicalHeight();
3403 else if (isOrthogonal)
3404 availableHeight = containingBlockChild->containingBlockLogicalWidthForContent();
3405 else if (is<RenderTableCell>(*cb)) {
3406 if (!skippedAutoHeightContainingBlock) {
3407 // Table cells violate what the CSS spec says to do with heights. Basically we
3408 // don't care if the cell specified a height or not. We just always make ourselves
3409 // be a percentage of the cell's current content height.
3410 if (!cb->hasOverridingLogicalHeight())
3411 return tableCellShouldHaveZeroInitialSize(*cb, *this, scrollsOverflowY()) ? std::optional<LayoutUnit>(0) : std::nullopt;
3412
3413 availableHeight = cb->overridingLogicalHeight() - cb->computedCSSPaddingBefore() - cb->computedCSSPaddingAfter() - cb->borderBefore() - cb->borderAfter();
3414 }
3415 } else
3416 availableHeight = cb->availableLogicalHeightForPercentageComputation();
3417
3418 if (!availableHeight)
3419 return availableHeight;
3420
3421 LayoutUnit result = valueForLength(height, availableHeight.value() - rootMarginBorderPaddingHeight + (isTable() && isOutOfFlowPositioned() ? cb->paddingBefore() + cb->paddingAfter() : 0_lu));
3422
3423 // |overridingLogicalHeight| is the maximum height made available by the
3424 // cell to its percent height children when we decide they can determine the
3425 // height of the cell. If the percent height child is box-sizing:content-box
3426 // then we must subtract the border and padding from the cell's
3427 // |availableHeight| (given by |overridingLogicalHeight|) to arrive
3428 // at the child's computed height.
3429 bool subtractBorderAndPadding = isTable() || (is<RenderTableCell>(*cb) && !skippedAutoHeightContainingBlock && cb->hasOverridingLogicalHeight() && style().boxSizing() == BoxSizing::ContentBox);
3430 if (subtractBorderAndPadding) {
3431 result -= borderAndPaddingLogicalHeight();
3432 return std::max(0_lu, result);
3433 }
3434 return result;
3435}
3436
3437LayoutUnit RenderBox::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
3438{
3439 return computeReplacedLogicalWidthRespectingMinMaxWidth(computeReplacedLogicalWidthUsing(MainOrPreferredSize, style().logicalWidth()), shouldComputePreferred);
3440}
3441
3442LayoutUnit RenderBox::computeReplacedLogicalWidthRespectingMinMaxWidth(LayoutUnit logicalWidth, ShouldComputePreferred shouldComputePreferred) const
3443{
3444 auto& logicalMinWidth = style().logicalMinWidth();
3445 auto& logicalMaxWidth = style().logicalMaxWidth();
3446 bool useLogicalWidthForMinWidth = (shouldComputePreferred == ComputePreferred && logicalMinWidth.isPercentOrCalculated()) || logicalMinWidth.isUndefined();
3447 bool useLogicalWidthForMaxWidth = (shouldComputePreferred == ComputePreferred && logicalMaxWidth.isPercentOrCalculated()) || logicalMaxWidth.isUndefined();
3448 auto minLogicalWidth = useLogicalWidthForMinWidth ? logicalWidth : computeReplacedLogicalWidthUsing(MinSize, logicalMinWidth);
3449 auto maxLogicalWidth = useLogicalWidthForMaxWidth ? logicalWidth : computeReplacedLogicalWidthUsing(MaxSize, logicalMaxWidth);
3450 return std::max(minLogicalWidth, std::min(logicalWidth, maxLogicalWidth));
3451}
3452
3453LayoutUnit RenderBox::computeReplacedLogicalWidthUsing(SizeType widthType, Length logicalWidth) const
3454{
3455 ASSERT(widthType == MinSize || widthType == MainOrPreferredSize || !logicalWidth.isAuto());
3456 if (widthType == MinSize && logicalWidth.isAuto())
3457 return adjustContentBoxLogicalWidthForBoxSizing(0, logicalWidth.type());
3458
3459 switch (logicalWidth.type()) {
3460 case LengthType::Fixed:
3461 return adjustContentBoxLogicalWidthForBoxSizing(logicalWidth);
3462 case LengthType::MinContent:
3463 case LengthType::MaxContent: {
3464 // MinContent/MaxContent don't need the availableLogicalWidth argument.
3465 LayoutUnit availableLogicalWidth;
3466 return computeIntrinsicLogicalWidthUsing(logicalWidth, availableLogicalWidth, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
3467 }
3468 case LengthType::FitContent:
3469 case LengthType::FillAvailable:
3470 case LengthType::Percent:
3471 case LengthType::Calculated: {
3472 LayoutUnit containerWidth;
3473 if (isOutOfFlowPositioned())
3474 containerWidth = containingBlockLogicalWidthForPositioned(downcast<RenderBoxModelObject>(*container()));
3475 else if (isHorizontalWritingMode() == containingBlock()->isHorizontalWritingMode())
3476 containerWidth = containingBlockLogicalWidthForContent();
3477 else
3478 containerWidth = perpendicularContainingBlockLogicalHeight();
3479 Length containerLogicalWidth = containingBlock()->style().logicalWidth();
3480 // FIXME: Handle cases when containing block width is calculated or viewport percent.
3481 // https://bugs.webkit.org/show_bug.cgi?id=91071
3482 if (logicalWidth.isIntrinsic())
3483 return computeIntrinsicLogicalWidthUsing(logicalWidth, containerWidth, borderAndPaddingLogicalWidth()) - borderAndPaddingLogicalWidth();
3484 if (containerWidth > 0 || (!containerWidth && (containerLogicalWidth.isFixed() || containerLogicalWidth.isPercentOrCalculated())))
3485 return adjustContentBoxLogicalWidthForBoxSizing(minimumValueForLength(logicalWidth, containerWidth), logicalWidth.type());
3486 return 0_lu;
3487 }
3488 case LengthType::Intrinsic:
3489 case LengthType::MinIntrinsic:
3490 case LengthType::Auto:
3491 case LengthType::Content:
3492 case LengthType::Relative:
3493 case LengthType::Undefined:
3494 return intrinsicLogicalWidth();
3495 }
3496
3497 ASSERT_NOT_REACHED();
3498 return 0;
3499}
3500
3501LayoutUnit RenderBox::computeReplacedLogicalHeight(std::optional<LayoutUnit>) const
3502{
3503 return computeReplacedLogicalHeightRespectingMinMaxHeight(computeReplacedLogicalHeightUsing(MainOrPreferredSize, style().logicalHeight()));
3504}
3505
3506static bool allowMinMaxPercentagesInAutoHeightBlocksQuirk()
3507{
3508#if PLATFORM(COCOA)
3509 return CocoaApplication::isIBooks();
3510#else
3511 return false;
3512#endif
3513}
3514
3515void RenderBox::computePreferredLogicalWidths()
3516{
3517 ASSERT(preferredLogicalWidthsDirty());
3518
3519 computePreferredLogicalWidths(style().logicalMinWidth(), style().logicalMaxWidth(), borderAndPaddingLogicalWidth());
3520 setPreferredLogicalWidthsDirty(false);
3521}
3522
3523void RenderBox::computePreferredLogicalWidths(const Length& minWidth, const Length& maxWidth, LayoutUnit borderAndPadding)
3524{
3525 if (shouldComputeLogicalHeightFromAspectRatio()) {
3526 auto [logicalMinWidth, logicalMaxWidth] = computeMinMaxLogicalWidthFromAspectRatio();
3527 m_minPreferredLogicalWidth = std::clamp(m_minPreferredLogicalWidth, logicalMinWidth, logicalMaxWidth);
3528 m_maxPreferredLogicalWidth = std::clamp(m_maxPreferredLogicalWidth, logicalMinWidth, logicalMaxWidth);
3529 }
3530
3531 if (maxWidth.isFixed()) {
3532 auto adjustContentBoxLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(maxWidth);
3533 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidth);
3534 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidth);
3535 }
3536
3537 if (minWidth.isFixed() && minWidth.value() > 0) {
3538 auto adjustContentBoxLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(minWidth);
3539 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidth);
3540 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidth);
3541 }
3542
3543 m_minPreferredLogicalWidth += borderAndPadding;
3544 m_maxPreferredLogicalWidth += borderAndPadding;
3545}
3546
3547bool RenderBox::replacedMinMaxLogicalHeightComputesAsNone(SizeType sizeType) const
3548{
3549 ASSERT(sizeType == MinSize || sizeType == MaxSize);
3550
3551 auto logicalHeight = sizeType == MinSize ? style().logicalMinHeight() : style().logicalMaxHeight();
3552 auto initialLogicalHeight = sizeType == MinSize ? RenderStyle::initialMinSize() : RenderStyle::initialMaxSize();
3553
3554 if (logicalHeight == initialLogicalHeight)
3555 return true;
3556
3557 if (logicalHeight.isPercentOrCalculated() && hasOverridingContainingBlockContentLogicalHeight())
3558 return overridingContainingBlockContentLogicalHeight() == std::nullopt;
3559
3560 // Make sure % min-height and % max-height resolve to none if the containing block has auto height.
3561 // Note that the "height" case for replaced elements was handled by hasReplacedLogicalHeight, which is why
3562 // min and max-height are the only ones handled here.
3563 // FIXME: For now we put in a quirk for iBooks until we can move them to viewport units.
3564 if (auto* cb = containingBlockForAutoHeightDetection(logicalHeight))
3565 return allowMinMaxPercentagesInAutoHeightBlocksQuirk() ? false : cb->hasAutoHeightOrContainingBlockWithAutoHeight();
3566
3567 return false;
3568}
3569
3570LayoutUnit RenderBox::computeReplacedLogicalHeightRespectingMinMaxHeight(LayoutUnit logicalHeight) const
3571{
3572 LayoutUnit minLogicalHeight;
3573 if (!replacedMinMaxLogicalHeightComputesAsNone(MinSize))
3574 minLogicalHeight = computeReplacedLogicalHeightUsing(MinSize, style().logicalMinHeight());
3575 LayoutUnit maxLogicalHeight = logicalHeight;
3576 if (!replacedMinMaxLogicalHeightComputesAsNone(MaxSize))
3577 maxLogicalHeight = computeReplacedLogicalHeightUsing(MaxSize, style().logicalMaxHeight());
3578 return std::max(minLogicalHeight, std::min(logicalHeight, maxLogicalHeight));
3579}
3580
3581LayoutUnit RenderBox::computeReplacedLogicalHeightUsing(SizeType heightType, Length logicalHeight) const
3582{
3583 ASSERT(heightType == MinSize || heightType == MainOrPreferredSize || !logicalHeight.isAuto());
3584 if (heightType == MinSize && logicalHeight.isAuto())
3585 return adjustContentBoxLogicalHeightForBoxSizing(std::optional<LayoutUnit>(0));
3586
3587 switch (logicalHeight.type()) {
3588 case LengthType::Fixed:
3589 return adjustContentBoxLogicalHeightForBoxSizing(LayoutUnit(logicalHeight.value()));
3590 case LengthType::Percent:
3591 case LengthType::Calculated: {
3592 auto* container = isOutOfFlowPositioned() ? this->container() : containingBlock();
3593 while (container && container->isAnonymous()) {
3594 // Stop at rendering context root.
3595 if (is<RenderView>(*container))
3596 break;
3597 container = container->containingBlock();
3598 }
3599 bool hasPerpendicularContainingBlock = container->isHorizontalWritingMode() != isHorizontalWritingMode();
3600 std::optional<LayoutUnit> stretchedHeight;
3601 if (is<RenderBlock>(container)) {
3602 auto* block = downcast<RenderBlock>(container);
3603 block->addPercentHeightDescendant(*const_cast<RenderBox*>(this));
3604 if (block->isFlexItem() && downcast<RenderFlexibleBox>(block->parent())->useChildOverridingLogicalHeightForPercentageResolution(*block))
3605 stretchedHeight = block->overridingContentLogicalHeight();
3606 else if (block->isGridItem() && block->hasOverridingLogicalHeight() && !hasPerpendicularContainingBlock)
3607 stretchedHeight = block->overridingContentLogicalHeight();
3608 }
3609
3610 // FIXME: This calculation is not patched for block-flow yet.
3611 // https://bugs.webkit.org/show_bug.cgi?id=46500
3612 if (container->isOutOfFlowPositioned()
3613 && container->style().height().isAuto()
3614 && !(container->style().top().isAuto() || container->style().bottom().isAuto())) {
3615 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(container->isRenderBlock());
3616 auto& block = downcast<RenderBlock>(*container);
3617 auto computedValues = block.computeLogicalHeight(block.logicalHeight(), 0);
3618 LayoutUnit newContentHeight = computedValues.m_extent - block.borderAndPaddingLogicalHeight() - block.scrollbarLogicalHeight();
3619 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, newContentHeight));
3620 }
3621
3622 LayoutUnit availableHeight;
3623 if (isOutOfFlowPositioned())
3624 availableHeight = containingBlockLogicalHeightForPositioned(downcast<RenderBoxModelObject>(*container));
3625 else if (stretchedHeight)
3626 availableHeight = stretchedHeight.value();
3627 else {
3628 availableHeight = hasPerpendicularContainingBlock ? containingBlockLogicalWidthForContent() : containingBlockLogicalHeightForContent(IncludeMarginBorderPadding);
3629 // It is necessary to use the border-box to match WinIE's broken
3630 // box model. This is essential for sizing inside
3631 // table cells using percentage heights.
3632 // FIXME: This needs to be made block-flow-aware. If the cell and image are perpendicular block-flows, this isn't right.
3633 // https://bugs.webkit.org/show_bug.cgi?id=46997
3634 while (container && !is<RenderView>(*container)
3635 && (container->style().logicalHeight().isAuto() || container->style().logicalHeight().isPercentOrCalculated())) {
3636 if (container->isTableCell()) {
3637 // Don't let table cells squeeze percent-height replaced elements
3638 // <http://bugs.webkit.org/show_bug.cgi?id=15359>
3639 availableHeight = std::max(availableHeight, intrinsicLogicalHeight());
3640 return valueForLength(logicalHeight, availableHeight - borderAndPaddingLogicalHeight());
3641 }
3642 downcast<RenderBlock>(*container).addPercentHeightDescendant(const_cast<RenderBox&>(*this));
3643 container = container->containingBlock();
3644 }
3645 }
3646 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeight, availableHeight));
3647 }
3648 case LengthType::MinContent:
3649 case LengthType::MaxContent:
3650 case LengthType::FitContent:
3651 case LengthType::FillAvailable:
3652 return adjustContentBoxLogicalHeightForBoxSizing(computeIntrinsicLogicalContentHeightUsing(logicalHeight, intrinsicLogicalHeight(), borderAndPaddingLogicalHeight()));
3653 default:
3654 return intrinsicLogicalHeight();
3655 }
3656}
3657
3658LayoutUnit RenderBox::availableLogicalHeight(AvailableLogicalHeightType heightType) const
3659{
3660 return constrainContentBoxLogicalHeightByMinMax(availableLogicalHeightUsing(style().logicalHeight(), heightType), std::nullopt);
3661}
3662
3663LayoutUnit RenderBox::availableLogicalHeightUsing(const Length& h, AvailableLogicalHeightType heightType) const
3664{
3665 // We need to stop here, since we don't want to increase the height of the table
3666 // artificially. We're going to rely on this cell getting expanded to some new
3667 // height, and then when we lay out again we'll use the calculation below.
3668 if (isTableCell() && (h.isAuto() || h.isPercentOrCalculated())) {
3669 if (hasOverridingLogicalHeight())
3670 return overridingLogicalHeight() - computedCSSPaddingBefore() - computedCSSPaddingAfter() - borderBefore() - borderAfter();
3671 return logicalHeight() - borderAndPaddingLogicalHeight();
3672 }
3673
3674 if (isFlexItem() && downcast<RenderFlexibleBox>(*parent()).useChildOverridingLogicalHeightForPercentageResolution(*this))
3675 return overridingContentLogicalHeight();
3676
3677 if (shouldComputeLogicalHeightFromAspectRatio())
3678 return blockSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), logicalWidth());
3679
3680 if (h.isPercentOrCalculated() && isOutOfFlowPositioned() && !isRenderFragmentedFlow()) {
3681 // FIXME: This is wrong if the containingBlock has a perpendicular writing mode.
3682 LayoutUnit availableHeight = containingBlockLogicalHeightForPositioned(*containingBlock());
3683 return adjustContentBoxLogicalHeightForBoxSizing(valueForLength(h, availableHeight));
3684 }
3685
3686 if (std::optional<LayoutUnit> heightIncludingScrollbar = computeContentAndScrollbarLogicalHeightUsing(MainOrPreferredSize, h, std::nullopt))
3687 return std::max<LayoutUnit>(0, adjustContentBoxLogicalHeightForBoxSizing(heightIncludingScrollbar) - scrollbarLogicalHeight());
3688
3689 // FIXME: Check logicalTop/logicalBottom here to correctly handle vertical writing-mode.
3690 // https://bugs.webkit.org/show_bug.cgi?id=46500
3691 if (is<RenderBlock>(*this) && isOutOfFlowPositioned() && style().height().isAuto() && !(style().top().isAuto() || style().bottom().isAuto())) {
3692 RenderBlock& block = const_cast<RenderBlock&>(downcast<RenderBlock>(*this));
3693 auto computedValues = block.computeLogicalHeight(block.logicalHeight(), 0);
3694 return computedValues.m_extent - block.borderAndPaddingLogicalHeight() - block.scrollbarLogicalHeight();
3695 }
3696
3697 LayoutUnit availableHeight = isOrthogonal(*this, *containingBlock()) ? containingBlockLogicalWidthForContent() : containingBlockLogicalHeightForContent(heightType);
3698 if (heightType == ExcludeMarginBorderPadding) {
3699 // FIXME: Margin collapsing hasn't happened yet, so this incorrectly removes collapsed margins.
3700 availableHeight -= marginBefore() + marginAfter() + borderAndPaddingLogicalHeight();
3701 }
3702 return availableHeight;
3703}
3704
3705void RenderBox::computeBlockDirectionMargins(const RenderBlock& containingBlock, LayoutUnit& marginBefore, LayoutUnit& marginAfter) const
3706{
3707 if (isTableCell()) {
3708 // FIXME: Not right if we allow cells to have different directionality than the table. If we do allow this, though,
3709 // we may just do it with an extra anonymous block inside the cell.
3710 marginBefore = 0;
3711 marginAfter = 0;
3712 return;
3713 }
3714
3715 // Margins are calculated with respect to the logical width of
3716 // the containing block (8.3)
3717 LayoutUnit cw = containingBlockLogicalWidthForContent();
3718 const RenderStyle& containingBlockStyle = containingBlock.style();
3719 marginBefore = minimumValueForLength(style().marginBeforeUsing(&containingBlockStyle), cw);
3720 marginAfter = minimumValueForLength(style().marginAfterUsing(&containingBlockStyle), cw);
3721}
3722
3723void RenderBox::computeAndSetBlockDirectionMargins(const RenderBlock& containingBlock)
3724{
3725 LayoutUnit marginBefore;
3726 LayoutUnit marginAfter;
3727 computeBlockDirectionMargins(containingBlock, marginBefore, marginAfter);
3728 containingBlock.setMarginBeforeForChild(*this, marginBefore);
3729 containingBlock.setMarginAfterForChild(*this, marginAfter);
3730}
3731
3732LayoutUnit RenderBox::containingBlockLogicalWidthForPositioned(const RenderBoxModelObject& containingBlock, RenderFragmentContainer* fragment, bool checkForPerpendicularWritingMode) const
3733{
3734 if (checkForPerpendicularWritingMode && containingBlock.isHorizontalWritingMode() != isHorizontalWritingMode())
3735 return containingBlockLogicalHeightForPositioned(containingBlock, false);
3736
3737 if (hasOverridingContainingBlockContentLogicalWidth()) {
3738 if (auto width = overridingContainingBlockContentLogicalWidth())
3739 return width.value();
3740 }
3741
3742 if (is<RenderBox>(containingBlock)) {
3743 bool isFixedPosition = isFixedPositioned();
3744
3745 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
3746 if (!fragmentedFlow) {
3747 if (isFixedPosition && is<RenderView>(containingBlock))
3748 return downcast<RenderView>(containingBlock).clientLogicalWidthForFixedPosition();
3749
3750 return downcast<RenderBox>(containingBlock).clientLogicalWidth();
3751 }
3752
3753 if (!is<RenderBlock>(containingBlock))
3754 return downcast<RenderBox>(containingBlock).clientLogicalWidth();
3755
3756 const RenderBlock& cb = downcast<RenderBlock>(containingBlock);
3757 RenderBoxFragmentInfo* boxInfo = nullptr;
3758 if (!fragment) {
3759 if (is<RenderFragmentedFlow>(containingBlock) && !checkForPerpendicularWritingMode)
3760 return downcast<RenderFragmentedFlow>(containingBlock).contentLogicalWidthOfFirstFragment();
3761 if (isWritingModeRoot()) {
3762 LayoutUnit cbPageOffset = cb.offsetFromLogicalTopOfFirstPage();
3763 RenderFragmentContainer* cbFragment = cb.fragmentAtBlockOffset(cbPageOffset);
3764 if (cbFragment)
3765 boxInfo = cb.renderBoxFragmentInfo(cbFragment);
3766 }
3767 } else if (fragmentedFlow->isHorizontalWritingMode() == containingBlock.isHorizontalWritingMode()) {
3768 RenderFragmentContainer* containingBlockFragment = cb.clampToStartAndEndFragments(fragment);
3769 boxInfo = cb.renderBoxFragmentInfo(containingBlockFragment);
3770 }
3771 return (boxInfo) ? std::max<LayoutUnit>(0, cb.clientLogicalWidth() - (cb.logicalWidth() - boxInfo->logicalWidth())) : cb.clientLogicalWidth();
3772 }
3773
3774 ASSERT(containingBlock.isInFlowPositioned());
3775
3776 const auto& flow = downcast<RenderInline>(containingBlock);
3777 LegacyInlineFlowBox* first = flow.firstLineBox();
3778 LegacyInlineFlowBox* last = flow.lastLineBox();
3779
3780 // If the containing block is empty, return a width of 0.
3781 if (!first || !last)
3782 return 0;
3783
3784 LayoutUnit fromLeft;
3785 LayoutUnit fromRight;
3786 if (containingBlock.style().isLeftToRightDirection()) {
3787 fromLeft = first->logicalLeft() + first->borderLogicalLeft();
3788 fromRight = last->logicalLeft() + last->logicalWidth() - last->borderLogicalRight();
3789 } else {
3790 fromRight = first->logicalLeft() + first->logicalWidth() - first->borderLogicalRight();
3791 fromLeft = last->logicalLeft() + last->borderLogicalLeft();
3792 }
3793
3794 return std::max<LayoutUnit>(0, fromRight - fromLeft);
3795}
3796
3797LayoutUnit RenderBox::containingBlockLogicalHeightForPositioned(const RenderBoxModelObject& containingBlock, bool checkForPerpendicularWritingMode) const
3798{
3799 if (checkForPerpendicularWritingMode && containingBlock.isHorizontalWritingMode() != isHorizontalWritingMode())
3800 return containingBlockLogicalWidthForPositioned(containingBlock, nullptr, false);
3801
3802 if (hasOverridingContainingBlockContentLogicalHeight()) {
3803 if (auto height = overridingContainingBlockContentLogicalHeight())
3804 return height.value();
3805 }
3806
3807 if (containingBlock.isBox()) {
3808 bool isFixedPosition = isFixedPositioned();
3809
3810 if (isFixedPosition && is<RenderView>(containingBlock))
3811 return downcast<RenderView>(containingBlock).clientLogicalHeightForFixedPosition();
3812
3813 const RenderBlock& cb = is<RenderBlock>(containingBlock) ? downcast<RenderBlock>(containingBlock) : *containingBlock.containingBlock();
3814 LayoutUnit result = cb.clientLogicalHeight();
3815 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
3816 if (fragmentedFlow && is<RenderFragmentedFlow>(containingBlock) && fragmentedFlow->isHorizontalWritingMode() == containingBlock.isHorizontalWritingMode())
3817 return downcast<RenderFragmentedFlow>(containingBlock).contentLogicalHeightOfFirstFragment();
3818 return result;
3819 }
3820
3821 ASSERT(containingBlock.isInFlowPositioned());
3822
3823 const auto& flow = downcast<RenderInline>(containingBlock);
3824 LegacyInlineFlowBox* first = flow.firstLineBox();
3825 LegacyInlineFlowBox* last = flow.lastLineBox();
3826
3827 // If the containing block is empty, return a height of 0.
3828 if (!first || !last)
3829 return 0;
3830
3831 LayoutUnit heightResult;
3832 LayoutRect boundingBox = flow.linesBoundingBox();
3833 if (containingBlock.isHorizontalWritingMode())
3834 heightResult = boundingBox.height();
3835 else
3836 heightResult = boundingBox.width();
3837 heightResult -= (containingBlock.borderBefore() + containingBlock.borderAfter());
3838 return heightResult;
3839}
3840
3841static void computeInlineStaticDistance(Length& logicalLeft, Length& logicalRight, const RenderBox* child, const RenderBoxModelObject& containerBlock, LayoutUnit containerLogicalWidth, RenderFragmentContainer* fragment)
3842{
3843 if (!logicalLeft.isAuto() || !logicalRight.isAuto())
3844 return;
3845
3846 auto* parent = child->parent();
3847 TextDirection parentDirection = parent->style().direction();
3848
3849 // This method is using enclosingBox() which is wrong for absolutely
3850 // positioned grid items, as they rely on the grid area. So for grid items if
3851 // both "left" and "right" properties are "auto", we can consider that one of
3852 // them (depending on the direction) is simply "0".
3853 if (parent->isRenderGrid() && parent == child->containingBlock()) {
3854 if (parentDirection == TextDirection::LTR)
3855 logicalLeft.setValue(LengthType::Fixed, 0);
3856 else
3857 logicalRight.setValue(LengthType::Fixed, 0);
3858 return;
3859 }
3860
3861 // For orthogonal flows we don't care whether the parent is LTR or RTL because it does not affect the position in our inline axis.
3862 if (parentDirection == TextDirection::LTR || isOrthogonal(*child, *parent)) {
3863 LayoutUnit staticPosition = isOrthogonal(*child, *parent) ? child->layer()->staticBlockPosition() - containerBlock.borderBefore() : child->layer()->staticInlinePosition() - containerBlock.borderLogicalLeft();
3864 for (auto* current = parent; current && current != &containerBlock; current = current->container()) {
3865 if (!is<RenderBox>(*current))
3866 continue;
3867 const auto& renderBox = downcast<RenderBox>(*current);
3868 staticPosition += isOrthogonal(*child, *parent) ? renderBox.logicalTop() : renderBox.logicalLeft();
3869 if (renderBox.isInFlowPositioned())
3870 staticPosition += renderBox.isHorizontalWritingMode() ? renderBox.offsetForInFlowPosition().width() : renderBox.offsetForInFlowPosition().height();
3871 if (fragment && is<RenderBlock>(*current)) {
3872 const RenderBlock& currentBlock = downcast<RenderBlock>(*current);
3873 fragment = currentBlock.clampToStartAndEndFragments(fragment);
3874 RenderBoxFragmentInfo* boxInfo = currentBlock.renderBoxFragmentInfo(fragment);
3875 if (boxInfo)
3876 staticPosition += boxInfo->logicalLeft();
3877 }
3878 }
3879 logicalLeft.setValue(LengthType::Fixed, staticPosition);
3880 } else {
3881 ASSERT(!isOrthogonal(*child, *parent));
3882 LayoutUnit staticPosition = child->layer()->staticInlinePosition() + containerLogicalWidth + containerBlock.borderLogicalLeft();
3883 auto& enclosingBox = parent->enclosingBox();
3884 if (&enclosingBox != &containerBlock && containerBlock.isDescendantOf(&enclosingBox)) {
3885 logicalRight.setValue(LengthType::Fixed, staticPosition);
3886 return;
3887 }
3888 staticPosition -= enclosingBox.logicalWidth();
3889 for (const RenderElement* current = &enclosingBox; current; current = current->container()) {
3890 if (!is<RenderBox>(*current))
3891 continue;
3892
3893 if (current != &containerBlock) {
3894 auto& renderBox = downcast<RenderBox>(*current);
3895 staticPosition -= renderBox.logicalLeft();
3896 if (renderBox.isInFlowPositioned())
3897 staticPosition -= renderBox.isHorizontalWritingMode() ? renderBox.offsetForInFlowPosition().width() : renderBox.offsetForInFlowPosition().height();
3898 }
3899 if (fragment && is<RenderBlock>(*current)) {
3900 auto& currentBlock = downcast<RenderBlock>(*current);
3901 fragment = currentBlock.clampToStartAndEndFragments(fragment);
3902 RenderBoxFragmentInfo* boxInfo = currentBlock.renderBoxFragmentInfo(fragment);
3903 if (boxInfo) {
3904 if (current != &containerBlock)
3905 staticPosition -= currentBlock.logicalWidth() - (boxInfo->logicalLeft() + boxInfo->logicalWidth());
3906 if (current == &enclosingBox)
3907 staticPosition += enclosingBox.logicalWidth() - boxInfo->logicalWidth();
3908 }
3909 }
3910 if (current == &containerBlock)
3911 break;
3912 }
3913 logicalRight.setValue(LengthType::Fixed, staticPosition);
3914 }
3915}
3916
3917void RenderBox::computePositionedLogicalWidth(LogicalExtentComputedValues& computedValues, RenderFragmentContainer* fragment) const
3918{
3919 if (isReplacedOrInlineBlock()) {
3920 // FIXME: Positioned replaced elements inside a flow thread are not working properly
3921 // with variable width fragments (see https://bugs.webkit.org/show_bug.cgi?id=69896 ).
3922 computePositionedLogicalWidthReplaced(computedValues);
3923 return;
3924 }
3925
3926 // QUESTIONS
3927 // FIXME 1: Should we still deal with these the cases of 'left' or 'right' having
3928 // the type 'static' in determining whether to calculate the static distance?
3929 // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1.
3930
3931 // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater
3932 // than or less than the computed width(). Be careful of box-sizing and
3933 // percentage issues.
3934
3935 // The following is based off of the W3C Working Draft from April 11, 2006 of
3936 // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements"
3937 // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width>
3938 // (block-style-comments in this function and in computePositionedLogicalWidthUsing()
3939 // correspond to text from the spec)
3940
3941
3942 // We don't use containingBlock(), since we may be positioned by an enclosing
3943 // relative positioned inline.
3944 const RenderBoxModelObject& containerBlock = downcast<RenderBoxModelObject>(*container());
3945
3946 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, fragment);
3947
3948 // Use the container block's direction except when calculating the static distance
3949 // This conforms with the reference results for abspos-replaced-width-margin-000.htm
3950 // of the CSS 2.1 test suite
3951 TextDirection containerDirection = containerBlock.style().direction();
3952
3953 bool isHorizontal = isHorizontalWritingMode();
3954 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalWidth();
3955 const Length marginLogicalLeft = isHorizontal ? style().marginLeft() : style().marginTop();
3956 const Length marginLogicalRight = isHorizontal ? style().marginRight() : style().marginBottom();
3957
3958 Length logicalLeftLength = style().logicalLeft();
3959 Length logicalRightLength = style().logicalRight();
3960
3961 /*---------------------------------------------------------------------------*\
3962 * For the purposes of this section and the next, the term "static position"
3963 * (of an element) refers, roughly, to the position an element would have had
3964 * in the normal flow. More precisely:
3965 *
3966 * * The static position for 'left' is the distance from the left edge of the
3967 * containing block to the left margin edge of a hypothetical box that would
3968 * have been the first box of the element if its 'position' property had
3969 * been 'static' and 'float' had been 'none'. The value is negative if the
3970 * hypothetical box is to the left of the containing block.
3971 * * The static position for 'right' is the distance from the right edge of the
3972 * containing block to the right margin edge of the same hypothetical box as
3973 * above. The value is positive if the hypothetical box is to the left of the
3974 * containing block's edge.
3975 *
3976 * But rather than actually calculating the dimensions of that hypothetical box,
3977 * user agents are free to make a guess at its probable position.
3978 *
3979 * For the purposes of calculating the static position, the containing block of
3980 * fixed positioned elements is the initial containing block instead of the
3981 * viewport, and all scrollable boxes should be assumed to be scrolled to their
3982 * origin.
3983 \*---------------------------------------------------------------------------*/
3984
3985 // see FIXME 1
3986 // Calculate the static distance if needed.
3987 computeInlineStaticDistance(logicalLeftLength, logicalRightLength, this, containerBlock, containerLogicalWidth, fragment);
3988
3989 // Calculate constraint equation values for 'width' case.
3990 computePositionedLogicalWidthUsing(MainOrPreferredSize, style().logicalWidth(), containerBlock, containerDirection,
3991 containerLogicalWidth, bordersPlusPadding,
3992 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
3993 computedValues);
3994
3995 LayoutUnit transferredMinSize = LayoutUnit::min();
3996 LayoutUnit transferredMaxSize = LayoutUnit::max();
3997 if (shouldComputeLogicalHeightFromAspectRatio())
3998 std::tie(transferredMinSize, transferredMaxSize) = computeMinMaxLogicalWidthFromAspectRatio();
3999
4000 LogicalExtentComputedValues maxValues;
4001 maxValues.m_extent = LayoutUnit::max();
4002 // Calculate constraint equation values for 'max-width' case.
4003 if (!style().logicalMaxWidth().isUndefined()) {
4004 computePositionedLogicalWidthUsing(MaxSize, style().logicalMaxWidth(), containerBlock, containerDirection,
4005 containerLogicalWidth, bordersPlusPadding,
4006 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
4007 maxValues);
4008 }
4009 if (transferredMaxSize < maxValues.m_extent) {
4010 computePositionedLogicalWidthUsing(MaxSize, Length(transferredMaxSize, LengthType::Fixed), containerBlock, containerDirection,
4011 containerLogicalWidth, bordersPlusPadding,
4012 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
4013 maxValues);
4014 }
4015 if (computedValues.m_extent > maxValues.m_extent) {
4016 computedValues.m_extent = maxValues.m_extent;
4017 computedValues.m_position = maxValues.m_position;
4018 computedValues.m_margins.m_start = maxValues.m_margins.m_start;
4019 computedValues.m_margins.m_end = maxValues.m_margins.m_end;
4020 }
4021
4022 LogicalExtentComputedValues minValues;
4023 minValues.m_extent = LayoutUnit::min();
4024 // Calculate constraint equation values for 'min-width' case.
4025 if (!style().logicalMinWidth().isZero() || style().logicalMinWidth().isIntrinsic()) {
4026 computePositionedLogicalWidthUsing(MinSize, style().logicalMinWidth(), containerBlock, containerDirection,
4027 containerLogicalWidth, bordersPlusPadding,
4028 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
4029 minValues);
4030 }
4031 if (transferredMinSize > minValues.m_extent) {
4032 computePositionedLogicalWidthUsing(MinSize, Length(transferredMinSize, LengthType::Fixed), containerBlock, containerDirection,
4033 containerLogicalWidth, bordersPlusPadding,
4034 logicalLeftLength, logicalRightLength, marginLogicalLeft, marginLogicalRight,
4035 minValues);
4036 }
4037 if (computedValues.m_extent < minValues.m_extent) {
4038 computedValues.m_extent = minValues.m_extent;
4039 computedValues.m_position = minValues.m_position;
4040 computedValues.m_margins.m_start = minValues.m_margins.m_start;
4041 computedValues.m_margins.m_end = minValues.m_margins.m_end;
4042 }
4043
4044 computedValues.m_extent += bordersPlusPadding;
4045 if (is<RenderBox>(containerBlock)) {
4046 auto& containingBox = downcast<RenderBox>(containerBlock);
4047 if (containingBox.shouldPlaceVerticalScrollbarOnLeft() && isHorizontalWritingMode())
4048 computedValues.m_position += containingBox.verticalScrollbarWidth();
4049 }
4050
4051 // Adjust logicalLeft if we need to for the flipped version of our writing mode in fragments.
4052 // FIXME: Add support for other types of objects as containerBlock, not only RenderBlock.
4053 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
4054 if (fragmentedFlow && !fragment && isWritingModeRoot() && isHorizontalWritingMode() == containerBlock.isHorizontalWritingMode() && is<RenderBlock>(containerBlock)) {
4055 ASSERT(containerBlock.canHaveBoxInfoInFragment());
4056 LayoutUnit logicalLeftPos = computedValues.m_position;
4057 const RenderBlock& renderBlock = downcast<RenderBlock>(containerBlock);
4058 LayoutUnit cbPageOffset = renderBlock.offsetFromLogicalTopOfFirstPage();
4059 RenderFragmentContainer* cbFragment = renderBlock.fragmentAtBlockOffset(cbPageOffset);
4060 if (cbFragment) {
4061 RenderBoxFragmentInfo* boxInfo = renderBlock.renderBoxFragmentInfo(cbFragment);
4062 if (boxInfo) {
4063 logicalLeftPos += boxInfo->logicalLeft();
4064 computedValues.m_position = logicalLeftPos;
4065 }
4066 }
4067 }
4068}
4069
4070static void computeLogicalLeftPositionedOffset(LayoutUnit& logicalLeftPos, const RenderBox* child, LayoutUnit logicalWidthValue, const RenderBoxModelObject& containerBlock, LayoutUnit containerLogicalWidth, bool logicalLeftIsAuto, bool logicalRightIsAuto)
4071{
4072 auto logicalLeftAndRightAreAuto = logicalLeftIsAuto && logicalRightIsAuto;
4073 bool isOverconstrained = !logicalLeftIsAuto && !logicalRightIsAuto && !child->style().logicalWidth().isAuto();
4074 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
4075 // along this axis, then we need to flip the coordinate. Auto positioned items do not need this correction as it was properly handled in
4076 // computeInlineStaticDistance().
4077 if (isOrthogonal(*child, containerBlock) && !logicalLeftAndRightAreAuto && !isOverconstrained && containerBlock.style().isFlippedBlocksWritingMode()) {
4078 logicalLeftPos = containerLogicalWidth - logicalWidthValue - logicalLeftPos;
4079 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock.borderRight() : containerBlock.borderBottom());
4080 } else
4081 logicalLeftPos += (child->isHorizontalWritingMode() ? containerBlock.borderLeft() : containerBlock.borderTop());
4082}
4083
4084void RenderBox::computePositionedLogicalWidthUsing(SizeType widthType, Length logicalWidth, const RenderBoxModelObject& containerBlock, TextDirection containerDirection,
4085 LayoutUnit containerLogicalWidth, LayoutUnit bordersPlusPadding,
4086 Length logicalLeft, Length logicalRight, Length marginLogicalLeft, Length marginLogicalRight,
4087 LogicalExtentComputedValues& computedValues) const
4088{
4089 ASSERT(widthType == MinSize || widthType == MainOrPreferredSize || !logicalWidth.isAuto());
4090 auto originalLogicalWidthType = logicalWidth.type();
4091 if (widthType == MinSize && logicalWidth.isAuto()) {
4092 if (shouldComputeLogicalWidthFromAspectRatio()) {
4093 LayoutUnit minLogicalWidth;
4094 LayoutUnit maxLogicalWidth;
4095 computeIntrinsicLogicalWidths(minLogicalWidth, maxLogicalWidth);
4096 logicalWidth = Length(minLogicalWidth, LengthType::Fixed);
4097 } else
4098 logicalWidth = Length(0, LengthType::Fixed);
4099 } else if (widthType == MainOrPreferredSize && logicalWidth.isAuto() && shouldComputeLogicalWidthFromAspectRatio())
4100 logicalWidth = Length(computeLogicalWidthFromAspectRatio(), LengthType::Fixed);
4101 else if (logicalWidth.isIntrinsic())
4102 logicalWidth = Length(computeIntrinsicLogicalWidthUsing(logicalWidth, containerLogicalWidth, bordersPlusPadding) - bordersPlusPadding, LengthType::Fixed);
4103
4104 // 'left' and 'right' cannot both be 'auto' because one would of been
4105 // converted to the static position already
4106 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
4107
4108 LayoutUnit logicalLeftValue;
4109
4110 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, nullptr, false);
4111
4112 bool logicalWidthIsAuto = logicalWidth.isIntrinsicOrAuto() && !shouldComputeLogicalWidthFromAspectRatio();
4113 bool logicalLeftIsAuto = logicalLeft.isAuto();
4114 bool logicalRightIsAuto = logicalRight.isAuto();
4115 LayoutUnit& marginLogicalLeftValue = style().isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end;
4116 LayoutUnit& marginLogicalRightValue = style().isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start;
4117
4118 if (!logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
4119 /*-----------------------------------------------------------------------*\
4120 * If none of the three is 'auto': If both 'margin-left' and 'margin-
4121 * right' are 'auto', solve the equation under the extra constraint that
4122 * the two margins get equal values, unless this would make them negative,
4123 * in which case when direction of the containing block is 'ltr' ('rtl'),
4124 * set 'margin-left' ('margin-right') to zero and solve for 'margin-right'
4125 * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto',
4126 * solve the equation for that value. If the values are over-constrained,
4127 * ignore the value for 'left' (in case the 'direction' property of the
4128 * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr')
4129 * and solve for that value.
4130 \*-----------------------------------------------------------------------*/
4131 // NOTE: It is not necessary to solve for 'right' in the over constrained
4132 // case because the value is not used for any further calculations.
4133
4134 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
4135 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth), originalLogicalWidthType);
4136
4137 const LayoutUnit availableSpace = containerLogicalWidth - (logicalLeftValue + computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth) + bordersPlusPadding);
4138
4139 // Margins are now the only unknown
4140 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
4141 // Both margins auto, solve for equality
4142 if (availableSpace >= 0) {
4143 marginLogicalLeftValue = availableSpace / 2; // split the difference
4144 marginLogicalRightValue = availableSpace - marginLogicalLeftValue; // account for odd valued differences
4145 } else {
4146 // Use the containing block's direction rather than the parent block's
4147 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
4148 if (containerDirection == TextDirection::LTR) {
4149 marginLogicalLeftValue = 0;
4150 marginLogicalRightValue = availableSpace; // will be negative
4151 } else {
4152 marginLogicalLeftValue = availableSpace; // will be negative
4153 marginLogicalRightValue = 0;
4154 }
4155 }
4156 } else if (marginLogicalLeft.isAuto()) {
4157 // Solve for left margin
4158 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
4159 marginLogicalLeftValue = availableSpace - marginLogicalRightValue;
4160 } else if (marginLogicalRight.isAuto()) {
4161 // Solve for right margin
4162 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
4163 marginLogicalRightValue = availableSpace - marginLogicalLeftValue;
4164 } else {
4165 // Over-constrained, solve for left if direction is RTL
4166 marginLogicalLeftValue = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
4167 marginLogicalRightValue = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
4168
4169 // Use the containing block's direction rather than the parent block's
4170 // per CSS 2.1 reference test abspos-non-replaced-width-margin-000.
4171 if (!isOrthogonal(*this, containerBlock) && containerDirection == TextDirection::RTL)
4172 logicalLeftValue = (availableSpace + logicalLeftValue) - marginLogicalLeftValue - marginLogicalRightValue;
4173 }
4174 } else {
4175 /*--------------------------------------------------------------------*\
4176 * Otherwise, set 'auto' values for 'margin-left' and 'margin-right'
4177 * to 0, and pick the one of the following six rules that applies.
4178 *
4179 * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the
4180 * width is shrink-to-fit. Then solve for 'left'
4181 *
4182 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
4183 * ------------------------------------------------------------------
4184 * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if
4185 * the 'direction' property of the containing block is 'ltr' set
4186 * 'left' to the static position, otherwise set 'right' to the
4187 * static position. Then solve for 'left' (if 'direction is 'rtl')
4188 * or 'right' (if 'direction' is 'ltr').
4189 * ------------------------------------------------------------------
4190 *
4191 * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the
4192 * width is shrink-to-fit . Then solve for 'right'
4193 * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve
4194 * for 'left'
4195 * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve
4196 * for 'width'
4197 * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve
4198 * for 'right'
4199 *
4200 * Calculation of the shrink-to-fit width is similar to calculating the
4201 * width of a table cell using the automatic table layout algorithm.
4202 * Roughly: calculate the preferred width by formatting the content
4203 * without breaking lines other than where explicit line breaks occur,
4204 * and also calculate the preferred minimum width, e.g., by trying all
4205 * possible line breaks. CSS 2.1 does not define the exact algorithm.
4206 * Thirdly, calculate the available width: this is found by solving
4207 * for 'width' after setting 'left' (in case 1) or 'right' (in case 3)
4208 * to 0.
4209 *
4210 * Then the shrink-to-fit width is:
4211 * min(max(preferred minimum width, available width), preferred width).
4212 \*--------------------------------------------------------------------*/
4213 // NOTE: For rules 3 and 6 it is not necessary to solve for 'right'
4214 // because the value is not used for any further calculations.
4215
4216 // Calculate margins, 'auto' margins are ignored.
4217 marginLogicalLeftValue = minimumValueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
4218 marginLogicalRightValue = minimumValueForLength(marginLogicalRight, containerRelativeLogicalWidth);
4219
4220 const LayoutUnit availableSpace = containerLogicalWidth - (marginLogicalLeftValue + marginLogicalRightValue + bordersPlusPadding);
4221
4222 // FIXME: Is there a faster way to find the correct case?
4223 // Use rule/case that applies.
4224 if (logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
4225 // RULE 1: (use shrink-to-fit for width, and solve of left)
4226 LayoutUnit logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
4227
4228 // FIXME: would it be better to have shrink-to-fit in one step?
4229 LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
4230 LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
4231 LayoutUnit availableWidth = availableSpace - logicalRightValue;
4232 computedValues.m_extent = std::min(std::max(preferredMinWidth, availableWidth), preferredWidth);
4233 logicalLeftValue = availableSpace - (computedValues.m_extent + logicalRightValue);
4234 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && logicalRightIsAuto) {
4235 // RULE 3: (use shrink-to-fit for width, and no need solve of right)
4236 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
4237
4238 // FIXME: would it be better to have shrink-to-fit in one step?
4239 LayoutUnit preferredWidth = maxPreferredLogicalWidth() - bordersPlusPadding;
4240 LayoutUnit preferredMinWidth = minPreferredLogicalWidth() - bordersPlusPadding;
4241 LayoutUnit availableWidth = availableSpace - logicalLeftValue;
4242 computedValues.m_extent = std::min(std::max(preferredMinWidth, availableWidth), preferredWidth);
4243 } else if (logicalLeftIsAuto && !logicalWidthIsAuto && !logicalRightIsAuto) {
4244 // RULE 4: (solve for left)
4245 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth), originalLogicalWidthType);
4246 logicalLeftValue = availableSpace - (computedValues.m_extent + valueForLength(logicalRight, containerLogicalWidth));
4247 } else if (!logicalLeftIsAuto && logicalWidthIsAuto && !logicalRightIsAuto) {
4248 // RULE 5: (solve for width)
4249 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
4250 computedValues.m_extent = availableSpace - (logicalLeftValue + valueForLength(logicalRight, containerLogicalWidth));
4251 } else if (!logicalLeftIsAuto && !logicalWidthIsAuto && logicalRightIsAuto) {
4252 // RULE 6: (no need solve for right)
4253 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
4254 computedValues.m_extent = adjustContentBoxLogicalWidthForBoxSizing(valueForLength(logicalWidth, containerLogicalWidth), originalLogicalWidthType);
4255 }
4256 }
4257
4258 // Use computed values to calculate the horizontal position.
4259
4260 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
4261 // positioned, inline because right now, it is using the logical left position
4262 // of the first line box when really it should use the last line box. When
4263 // this is fixed elsewhere, this block should be removed.
4264 if (is<RenderInline>(containerBlock) && !containerBlock.style().isLeftToRightDirection()) {
4265 const auto& flow = downcast<RenderInline>(containerBlock);
4266 LegacyInlineFlowBox* firstLine = flow.firstLineBox();
4267 LegacyInlineFlowBox* lastLine = flow.lastLineBox();
4268 if (firstLine && lastLine && firstLine != lastLine) {
4269 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft());
4270 return;
4271 }
4272 }
4273
4274 computedValues.m_position = logicalLeftValue + marginLogicalLeftValue;
4275 computeLogicalLeftPositionedOffset(computedValues.m_position, this, computedValues.m_extent + bordersPlusPadding, containerBlock, containerLogicalWidth, style().logicalLeft().isAuto(), style().logicalRight().isAuto());
4276}
4277
4278static void computeBlockStaticDistance(Length& logicalTop, Length& logicalBottom, const RenderBox* child, const RenderBoxModelObject& containerBlock)
4279{
4280 if (!logicalTop.isAuto() || !logicalBottom.isAuto())
4281 return;
4282
4283 auto* parent = child->parent();
4284
4285 // The static positions from the child's layer are relative to the container block's coordinate space (which is determined
4286 // by the writing mode and text direction), meaning that for orthogonal flows the logical top of the child (which depends on
4287 // the child's writing mode) is retrieved from the static inline position instead of the static block position.
4288 LayoutUnit staticLogicalTop = isOrthogonal(*child, *parent) ? child->layer()->staticInlinePosition() - containerBlock.borderLogicalLeft() : child->layer()->staticBlockPosition() - containerBlock.borderBefore();
4289 for (RenderElement* container = child->parent(); container && container != &containerBlock; container = container->container()) {
4290 if (!is<RenderBox>(*container))
4291 continue;
4292 const auto& renderBox = downcast<RenderBox>(*container);
4293 if (!is<RenderTableRow>(renderBox))
4294 staticLogicalTop += isOrthogonal(*child, *parent) ? renderBox.logicalLeft() : renderBox.logicalTop();
4295 if (renderBox.isInFlowPositioned())
4296 staticLogicalTop += renderBox.isHorizontalWritingMode() ? renderBox.offsetForInFlowPosition().height() : renderBox.offsetForInFlowPosition().width();
4297 }
4298
4299 // If the parent is RTL then we need to flip the coordinate by setting the logical bottom instead of the logical top. That only needs
4300 // to be done in case of orthogonal writing modes, for horizontal ones the text direction of the parent does not affect the block position.
4301 if (parent->style().direction() != TextDirection::LTR && isOrthogonal(*child, *parent))
4302 logicalBottom.setValue(LengthType::Fixed, staticLogicalTop);
4303 else
4304 logicalTop.setValue(LengthType::Fixed, staticLogicalTop);
4305}
4306
4307void RenderBox::computePositionedLogicalHeight(LogicalExtentComputedValues& computedValues) const
4308{
4309 if (isReplacedOrInlineBlock()) {
4310 computePositionedLogicalHeightReplaced(computedValues);
4311 return;
4312 }
4313
4314 // The following is based off of the W3C Working Draft from April 11, 2006 of
4315 // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements"
4316 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height>
4317 // (block-style-comments in this function and in computePositionedLogicalHeightUsing()
4318 // correspond to text from the spec)
4319
4320
4321 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
4322 const RenderBoxModelObject& containerBlock = downcast<RenderBoxModelObject>(*container());
4323
4324 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
4325
4326 const RenderStyle& styleToUse = style();
4327 const LayoutUnit bordersPlusPadding = borderAndPaddingLogicalHeight();
4328 const Length marginBefore = styleToUse.marginBefore();
4329 const Length marginAfter = styleToUse.marginAfter();
4330 Length logicalTopLength = styleToUse.logicalTop();
4331 Length logicalBottomLength = styleToUse.logicalBottom();
4332
4333 /*---------------------------------------------------------------------------*\
4334 * For the purposes of this section and the next, the term "static position"
4335 * (of an element) refers, roughly, to the position an element would have had
4336 * in the normal flow. More precisely, the static position for 'top' is the
4337 * distance from the top edge of the containing block to the top margin edge
4338 * of a hypothetical box that would have been the first box of the element if
4339 * its 'position' property had been 'static' and 'float' had been 'none'. The
4340 * value is negative if the hypothetical box is above the containing block.
4341 *
4342 * But rather than actually calculating the dimensions of that hypothetical
4343 * box, user agents are free to make a guess at its probable position.
4344 *
4345 * For the purposes of calculating the static position, the containing block
4346 * of fixed positioned elements is the initial containing block instead of
4347 * the viewport.
4348 \*---------------------------------------------------------------------------*/
4349
4350 // see FIXME 1
4351 // Calculate the static distance if needed.
4352 computeBlockStaticDistance(logicalTopLength, logicalBottomLength, this, containerBlock);
4353
4354 // Calculate constraint equation values for 'height' case.
4355 LayoutUnit logicalHeight = computedValues.m_extent;
4356 computePositionedLogicalHeightUsing(MainOrPreferredSize, styleToUse.logicalHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
4357 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
4358 computedValues);
4359
4360 // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults).
4361 // see FIXME 2
4362
4363 // Calculate constraint equation values for 'max-height' case.
4364 if (!styleToUse.logicalMaxHeight().isUndefined()) {
4365 LogicalExtentComputedValues maxValues;
4366
4367 computePositionedLogicalHeightUsing(MaxSize, styleToUse.logicalMaxHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
4368 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
4369 maxValues);
4370
4371 if (computedValues.m_extent > maxValues.m_extent) {
4372 computedValues.m_extent = maxValues.m_extent;
4373 computedValues.m_position = maxValues.m_position;
4374 computedValues.m_margins.m_before = maxValues.m_margins.m_before;
4375 computedValues.m_margins.m_after = maxValues.m_margins.m_after;
4376 }
4377 }
4378
4379 // Calculate constraint equation values for 'min-height' case.
4380 Length logicalMinHeight = styleToUse.logicalMinHeight();
4381 if (logicalMinHeight.isAuto() || !logicalMinHeight.isZero() || logicalMinHeight.isIntrinsic()) {
4382 LogicalExtentComputedValues minValues;
4383
4384 computePositionedLogicalHeightUsing(MinSize, styleToUse.logicalMinHeight(), containerBlock, containerLogicalHeight, bordersPlusPadding, logicalHeight,
4385 logicalTopLength, logicalBottomLength, marginBefore, marginAfter,
4386 minValues);
4387
4388 if (computedValues.m_extent < minValues.m_extent) {
4389 computedValues.m_extent = minValues.m_extent;
4390 computedValues.m_position = minValues.m_position;
4391 computedValues.m_margins.m_before = minValues.m_margins.m_before;
4392 computedValues.m_margins.m_after = minValues.m_margins.m_after;
4393 }
4394 }
4395
4396 // Set final height value.
4397 computedValues.m_extent += bordersPlusPadding;
4398
4399 // Adjust logicalTop if we need to for perpendicular writing modes in fragments.
4400 // FIXME: Add support for other types of objects as containerBlock, not only RenderBlock.
4401 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
4402 if (fragmentedFlow && isHorizontalWritingMode() != containerBlock.isHorizontalWritingMode() && is<RenderBlock>(containerBlock)) {
4403 ASSERT(containerBlock.canHaveBoxInfoInFragment());
4404 LayoutUnit logicalTopPos = computedValues.m_position;
4405 const RenderBlock& renderBox = downcast<RenderBlock>(containerBlock);
4406 LayoutUnit cbPageOffset = renderBox.offsetFromLogicalTopOfFirstPage() - logicalLeft();
4407 RenderFragmentContainer* cbFragment = renderBox.fragmentAtBlockOffset(cbPageOffset);
4408 if (cbFragment) {
4409 RenderBoxFragmentInfo* boxInfo = renderBox.renderBoxFragmentInfo(cbFragment);
4410 if (boxInfo) {
4411 logicalTopPos += boxInfo->logicalLeft();
4412 computedValues.m_position = logicalTopPos;
4413 }
4414 }
4415 }
4416}
4417
4418// The |containerLogicalHeightForPositioned| is already aware of orthogonal flows.
4419// The logicalTop concept is confusing here. It's the logical top from the child's POV. This means that is the physical
4420// y if the child is vertical or the physical x if the child is horizontal.
4421static void computeLogicalTopPositionedOffset(LayoutUnit& logicalTopPos, const RenderBox* child, LayoutUnit logicalHeightValue, const RenderBoxModelObject& containerBlock, LayoutUnit containerLogicalHeightForPositioned)
4422{
4423 auto logicalTopAndBottomAreAuto = child->style().logicalTop().isAuto() && child->style().logicalBottom().isAuto();
4424 auto haveOrthogonalWritingModes = isOrthogonal(*child, containerBlock);
4425 auto haveFlippedBlockAxis = child->style().isFlippedBlocksWritingMode() != containerBlock.style().isFlippedBlocksWritingMode();
4426
4427 // Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space. If the containing block is flipped
4428 // 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.
4429 if ((haveOrthogonalWritingModes && !logicalTopAndBottomAreAuto && child->style().isFlippedBlocksWritingMode())
4430 || (haveFlippedBlockAxis && !haveOrthogonalWritingModes))
4431 logicalTopPos = containerLogicalHeightForPositioned - logicalHeightValue - logicalTopPos;
4432
4433 // Our offset is from the logical bottom edge in a flipped environment, e.g., right for vertical-rl and bottom for horizontal-bt.
4434 if (containerBlock.style().isFlippedBlocksWritingMode() && !haveOrthogonalWritingModes) {
4435 if (child->isHorizontalWritingMode())
4436 logicalTopPos += containerBlock.borderBottom();
4437 else
4438 logicalTopPos += containerBlock.borderRight();
4439 } else {
4440 if (child->isHorizontalWritingMode())
4441 logicalTopPos += containerBlock.borderTop();
4442 else
4443 logicalTopPos += containerBlock.borderLeft();
4444 }
4445}
4446
4447void RenderBox::computePositionedLogicalHeightUsing(SizeType heightType, Length logicalHeightLength, const RenderBoxModelObject& containerBlock,
4448 LayoutUnit containerLogicalHeight, LayoutUnit bordersPlusPadding, LayoutUnit logicalHeight,
4449 Length logicalTop, Length logicalBottom, Length marginBefore, Length marginAfter,
4450 LogicalExtentComputedValues& computedValues) const
4451{
4452 ASSERT(heightType == MinSize || heightType == MainOrPreferredSize || !logicalHeightLength.isAuto());
4453 if (heightType == MinSize && logicalHeightLength.isAuto()) {
4454 if (shouldComputeLogicalHeightFromAspectRatio())
4455 logicalHeightLength = Length(logicalHeight, LengthType::Fixed);
4456 else
4457 logicalHeightLength = Length(0, LengthType::Fixed);
4458 }
4459
4460 // 'top' and 'bottom' cannot both be 'auto' because 'top would of been
4461 // converted to the static position in computePositionedLogicalHeight()
4462 ASSERT(!(logicalTop.isAuto() && logicalBottom.isAuto()));
4463
4464 LayoutUnit logicalHeightValue;
4465 LayoutUnit contentLogicalHeight = logicalHeight - bordersPlusPadding;
4466
4467 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, nullptr, false);
4468
4469 LayoutUnit logicalTopValue;
4470
4471 bool fromAspectRatio = heightType == MainOrPreferredSize && shouldComputeLogicalHeightFromAspectRatio();
4472 bool logicalHeightIsAuto = logicalHeightLength.isAuto() && !fromAspectRatio;
4473 bool logicalTopIsAuto = logicalTop.isAuto();
4474 bool logicalBottomIsAuto = logicalBottom.isAuto();
4475
4476 // Height is never unsolved for tables.
4477 LayoutUnit resolvedLogicalHeight;
4478 if (isTable()) {
4479 resolvedLogicalHeight = contentLogicalHeight;
4480 logicalHeightIsAuto = false;
4481 } else {
4482 if (logicalHeightLength.isIntrinsic())
4483 resolvedLogicalHeight = adjustContentBoxLogicalHeightForBoxSizing(computeIntrinsicLogicalContentHeightUsing(logicalHeightLength, contentLogicalHeight, bordersPlusPadding).value_or(0_lu));
4484 else if (fromAspectRatio) {
4485 resolvedLogicalHeight = blockSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), logicalWidth());
4486 resolvedLogicalHeight = std::max(LayoutUnit(), resolvedLogicalHeight - bordersPlusPadding);
4487 } else
4488 resolvedLogicalHeight = adjustContentBoxLogicalHeightForBoxSizing(valueForLength(logicalHeightLength, containerLogicalHeight));
4489 }
4490
4491 if (!logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
4492 /*-----------------------------------------------------------------------*\
4493 * If none of the three are 'auto': If both 'margin-top' and 'margin-
4494 * bottom' are 'auto', solve the equation under the extra constraint that
4495 * the two margins get equal values. If one of 'margin-top' or 'margin-
4496 * bottom' is 'auto', solve the equation for that value. If the values
4497 * are over-constrained, ignore the value for 'bottom' and solve for that
4498 * value.
4499 \*-----------------------------------------------------------------------*/
4500 // NOTE: It is not necessary to solve for 'bottom' in the over constrained
4501 // case because the value is not used for any further calculations.
4502
4503 logicalHeightValue = resolvedLogicalHeight;
4504 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
4505
4506 const LayoutUnit availableSpace = containerLogicalHeight - (logicalTopValue + logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight) + bordersPlusPadding);
4507
4508 // Margins are now the only unknown
4509 if (marginBefore.isAuto() && marginAfter.isAuto()) {
4510 // Both margins auto, solve for equality
4511 // NOTE: This may result in negative values.
4512 computedValues.m_margins.m_before = availableSpace / 2; // split the difference
4513 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before; // account for odd valued differences
4514 } else if (marginBefore.isAuto()) {
4515 // Solve for top margin
4516 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth);
4517 computedValues.m_margins.m_before = availableSpace - computedValues.m_margins.m_after;
4518 } else if (marginAfter.isAuto()) {
4519 // Solve for bottom margin
4520 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth);
4521 computedValues.m_margins.m_after = availableSpace - computedValues.m_margins.m_before;
4522 } else {
4523 // Over-constrained, (no need solve for bottom)
4524 computedValues.m_margins.m_before = valueForLength(marginBefore, containerRelativeLogicalWidth);
4525 computedValues.m_margins.m_after = valueForLength(marginAfter, containerRelativeLogicalWidth);
4526 }
4527 } else {
4528 /*--------------------------------------------------------------------*\
4529 * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom'
4530 * to 0, and pick the one of the following six rules that applies.
4531 *
4532 * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then
4533 * the height is based on the content, and solve for 'top'.
4534 *
4535 * OMIT RULE 2 AS IT SHOULD NEVER BE HIT
4536 * ------------------------------------------------------------------
4537 * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then
4538 * set 'top' to the static position, and solve for 'bottom'.
4539 * ------------------------------------------------------------------
4540 *
4541 * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then
4542 * the height is based on the content, and solve for 'bottom'.
4543 * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and
4544 * solve for 'top'.
4545 * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and
4546 * solve for 'height'.
4547 * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and
4548 * solve for 'bottom'.
4549 \*--------------------------------------------------------------------*/
4550 // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom'
4551 // because the value is not used for any further calculations.
4552
4553 // Calculate margins, 'auto' margins are ignored.
4554 computedValues.m_margins.m_before = minimumValueForLength(marginBefore, containerRelativeLogicalWidth);
4555 computedValues.m_margins.m_after = minimumValueForLength(marginAfter, containerRelativeLogicalWidth);
4556
4557 const LayoutUnit availableSpace = containerLogicalHeight - (computedValues.m_margins.m_before + computedValues.m_margins.m_after + bordersPlusPadding);
4558
4559 // Use rule/case that applies.
4560 if (logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
4561 // RULE 1: (height is content based, solve of top)
4562 logicalHeightValue = contentLogicalHeight;
4563 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight));
4564 } else if (!logicalTopIsAuto && logicalHeightIsAuto && logicalBottomIsAuto) {
4565 // RULE 3: (height is content based, no need solve of bottom)
4566 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
4567 logicalHeightValue = contentLogicalHeight;
4568 } else if (logicalTopIsAuto && !logicalHeightIsAuto && !logicalBottomIsAuto) {
4569 // RULE 4: (solve of top)
4570 logicalHeightValue = resolvedLogicalHeight;
4571 logicalTopValue = availableSpace - (logicalHeightValue + valueForLength(logicalBottom, containerLogicalHeight));
4572 } else if (!logicalTopIsAuto && logicalHeightIsAuto && !logicalBottomIsAuto) {
4573 // RULE 5: (solve of height)
4574 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
4575 logicalHeightValue = std::max<LayoutUnit>(0, availableSpace - (logicalTopValue + valueForLength(logicalBottom, containerLogicalHeight)));
4576 } else if (!logicalTopIsAuto && !logicalHeightIsAuto && logicalBottomIsAuto) {
4577 // RULE 6: (no need solve of bottom)
4578 logicalHeightValue = resolvedLogicalHeight;
4579 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
4580 }
4581 }
4582 computedValues.m_extent = logicalHeightValue;
4583
4584 // Use computed values to calculate the vertical position.
4585 computedValues.m_position = logicalTopValue + computedValues.m_margins.m_before;
4586 computeLogicalTopPositionedOffset(computedValues.m_position, this, logicalHeightValue + bordersPlusPadding, containerBlock, containerLogicalHeight);
4587}
4588
4589void RenderBox::computePositionedLogicalWidthReplaced(LogicalExtentComputedValues& computedValues) const
4590{
4591 // The following is based off of the W3C Working Draft from April 11, 2006 of
4592 // CSS 2.1: Section 10.3.8 "Absolutely positioned, replaced elements"
4593 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width>
4594 // (block-style-comments in this function correspond to text from the spec and
4595 // the numbers correspond to numbers in spec)
4596
4597 // We don't use containingBlock(), since we may be positioned by an enclosing
4598 // relative positioned inline.
4599 const RenderBoxModelObject& containerBlock = downcast<RenderBoxModelObject>(*container());
4600
4601 const LayoutUnit containerLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock);
4602 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, nullptr, false);
4603
4604 // To match WinIE, in quirks mode use the parent's 'direction' property
4605 // instead of the container block's.
4606 TextDirection containerDirection = containerBlock.style().direction();
4607
4608 // Variables to solve.
4609 bool isHorizontal = isHorizontalWritingMode();
4610 Length logicalLeft = style().logicalLeft();
4611 Length logicalRight = style().logicalRight();
4612 Length marginLogicalLeft = isHorizontal ? style().marginLeft() : style().marginTop();
4613 Length marginLogicalRight = isHorizontal ? style().marginRight() : style().marginBottom();
4614 LayoutUnit& marginLogicalLeftAlias = style().isLeftToRightDirection() ? computedValues.m_margins.m_start : computedValues.m_margins.m_end;
4615 LayoutUnit& marginLogicalRightAlias = style().isLeftToRightDirection() ? computedValues.m_margins.m_end : computedValues.m_margins.m_start;
4616
4617 /*-----------------------------------------------------------------------*\
4618 * 1. The used value of 'width' is determined as for inline replaced
4619 * elements.
4620 \*-----------------------------------------------------------------------*/
4621 // NOTE: This value of width is final in that the min/max width calculations
4622 // are dealt with in computeReplacedWidth(). This means that the steps to produce
4623 // correct max/min in the non-replaced version, are not necessary.
4624 computedValues.m_extent = computeReplacedLogicalWidth() + borderAndPaddingLogicalWidth();
4625
4626 const LayoutUnit availableSpace = containerLogicalWidth - computedValues.m_extent;
4627
4628 /*-----------------------------------------------------------------------*\
4629 * 2. If both 'left' and 'right' have the value 'auto', then if 'direction'
4630 * of the containing block is 'ltr', set 'left' to the static position;
4631 * else if 'direction' is 'rtl', set 'right' to the static position.
4632 \*-----------------------------------------------------------------------*/
4633 // see FIXME 1
4634 computeInlineStaticDistance(logicalLeft, logicalRight, this, containerBlock, containerLogicalWidth, nullptr); // FIXME: Pass the fragment.
4635
4636 /*-----------------------------------------------------------------------*\
4637 * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left'
4638 * or 'margin-right' with '0'.
4639 \*-----------------------------------------------------------------------*/
4640 if (logicalLeft.isAuto() || logicalRight.isAuto()) {
4641 if (marginLogicalLeft.isAuto())
4642 marginLogicalLeft.setValue(LengthType::Fixed, 0);
4643 if (marginLogicalRight.isAuto())
4644 marginLogicalRight.setValue(LengthType::Fixed, 0);
4645 }
4646
4647 /*-----------------------------------------------------------------------*\
4648 * 4. If at this point both 'margin-left' and 'margin-right' are still
4649 * 'auto', solve the equation under the extra constraint that the two
4650 * margins must get equal values, unless this would make them negative,
4651 * in which case when the direction of the containing block is 'ltr'
4652 * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for
4653 * 'margin-right' ('margin-left').
4654 \*-----------------------------------------------------------------------*/
4655 LayoutUnit logicalLeftValue;
4656 LayoutUnit logicalRightValue;
4657
4658 if (marginLogicalLeft.isAuto() && marginLogicalRight.isAuto()) {
4659 // 'left' and 'right' cannot be 'auto' due to step 3
4660 ASSERT(!(logicalLeft.isAuto() && logicalRight.isAuto()));
4661
4662 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
4663 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
4664
4665 LayoutUnit difference = availableSpace - (logicalLeftValue + logicalRightValue);
4666 if (difference > 0) {
4667 marginLogicalLeftAlias = difference / 2; // split the difference
4668 marginLogicalRightAlias = difference - marginLogicalLeftAlias; // account for odd valued differences
4669 } else {
4670 // Use the containing block's direction rather than the parent block's
4671 // per CSS 2.1 reference test abspos-replaced-width-margin-000.
4672 if (containerDirection == TextDirection::LTR) {
4673 marginLogicalLeftAlias = 0;
4674 marginLogicalRightAlias = difference; // will be negative
4675 } else {
4676 marginLogicalLeftAlias = difference; // will be negative
4677 marginLogicalRightAlias = 0;
4678 }
4679 }
4680
4681 /*-----------------------------------------------------------------------*\
4682 * 5. If at this point there is an 'auto' left, solve the equation for
4683 * that value.
4684 \*-----------------------------------------------------------------------*/
4685 } else if (logicalLeft.isAuto()) {
4686 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
4687 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
4688 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
4689
4690 // Solve for 'left'
4691 logicalLeftValue = availableSpace - (logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias);
4692 } else if (logicalRight.isAuto()) {
4693 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
4694 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
4695 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
4696
4697 // Solve for 'right'
4698 logicalRightValue = availableSpace - (logicalLeftValue + marginLogicalLeftAlias + marginLogicalRightAlias);
4699 } else if (marginLogicalLeft.isAuto()) {
4700 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
4701 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
4702 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
4703
4704 // Solve for 'margin-left'
4705 marginLogicalLeftAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalRightAlias);
4706 } else if (marginLogicalRight.isAuto()) {
4707 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
4708 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
4709 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
4710
4711 // Solve for 'margin-right'
4712 marginLogicalRightAlias = availableSpace - (logicalLeftValue + logicalRightValue + marginLogicalLeftAlias);
4713 } else {
4714 // Nothing is 'auto', just calculate the values.
4715 marginLogicalLeftAlias = valueForLength(marginLogicalLeft, containerRelativeLogicalWidth);
4716 marginLogicalRightAlias = valueForLength(marginLogicalRight, containerRelativeLogicalWidth);
4717 logicalRightValue = valueForLength(logicalRight, containerLogicalWidth);
4718 logicalLeftValue = valueForLength(logicalLeft, containerLogicalWidth);
4719 // If the containing block is right-to-left, then push the left position as far to the right as possible
4720 if (containerDirection == TextDirection::RTL) {
4721 int totalLogicalWidth = computedValues.m_extent + logicalLeftValue + logicalRightValue + marginLogicalLeftAlias + marginLogicalRightAlias;
4722 logicalLeftValue = containerLogicalWidth - (totalLogicalWidth - logicalLeftValue);
4723 }
4724 }
4725
4726 /*-----------------------------------------------------------------------*\
4727 * 6. If at this point the values are over-constrained, ignore the value
4728 * for either 'left' (in case the 'direction' property of the
4729 * containing block is 'rtl') or 'right' (in case 'direction' is
4730 * 'ltr') and solve for that value.
4731 \*-----------------------------------------------------------------------*/
4732 // NOTE: Constraints imposed by the width of the containing block and its content have already been accounted for above.
4733
4734 // FIXME: Deal with differing writing modes here. Our offset needs to be in the containing block's coordinate space, so that
4735 // can make the result here rather complicated to compute.
4736
4737 // Use computed values to calculate the horizontal position.
4738
4739 // FIXME: This hack is needed to calculate the logical left position for a 'rtl' relatively
4740 // positioned, inline containing block because right now, it is using the logical left position
4741 // of the first line box when really it should use the last line box. When
4742 // this is fixed elsewhere, this block should be removed.
4743 if (is<RenderInline>(containerBlock) && !containerBlock.style().isLeftToRightDirection()) {
4744 const auto& flow = downcast<RenderInline>(containerBlock);
4745 LegacyInlineFlowBox* firstLine = flow.firstLineBox();
4746 LegacyInlineFlowBox* lastLine = flow.lastLineBox();
4747 if (firstLine && lastLine && firstLine != lastLine) {
4748 computedValues.m_position = logicalLeftValue + marginLogicalLeftAlias + lastLine->borderLogicalLeft() + (lastLine->logicalLeft() - firstLine->logicalLeft());
4749 return;
4750 }
4751 }
4752
4753 LayoutUnit logicalLeftPos = logicalLeftValue + marginLogicalLeftAlias;
4754 // Border and padding have already been included in computedValues.m_extent.
4755 computeLogicalLeftPositionedOffset(logicalLeftPos, this, computedValues.m_extent, containerBlock, containerLogicalWidth, style().logicalLeft().isAuto(), style().logicalRight().isAuto());
4756 computedValues.m_position = logicalLeftPos;
4757}
4758
4759void RenderBox::computePositionedLogicalHeightReplaced(LogicalExtentComputedValues& computedValues) const
4760{
4761 // The following is based off of the W3C Working Draft from April 11, 2006 of
4762 // CSS 2.1: Section 10.6.5 "Absolutely positioned, replaced elements"
4763 // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height>
4764 // (block-style-comments in this function correspond to text from the spec and
4765 // the numbers correspond to numbers in spec)
4766
4767 // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline.
4768 const RenderBoxModelObject& containerBlock = downcast<RenderBoxModelObject>(*container());
4769
4770 const LayoutUnit containerLogicalHeight = containingBlockLogicalHeightForPositioned(containerBlock);
4771 const LayoutUnit containerRelativeLogicalWidth = containingBlockLogicalWidthForPositioned(containerBlock, nullptr, false);
4772
4773 // Variables to solve.
4774 Length marginBefore = style().marginBefore();
4775 Length marginAfter = style().marginAfter();
4776 LayoutUnit& marginBeforeAlias = computedValues.m_margins.m_before;
4777 LayoutUnit& marginAfterAlias = computedValues.m_margins.m_after;
4778
4779 Length logicalTop = style().logicalTop();
4780 Length logicalBottom = style().logicalBottom();
4781
4782 /*-----------------------------------------------------------------------*\
4783 * 1. The used value of 'height' is determined as for inline replaced
4784 * elements.
4785 \*-----------------------------------------------------------------------*/
4786 // NOTE: This value of height is final in that the min/max height calculations
4787 // are dealt with in computeReplacedHeight(). This means that the steps to produce
4788 // correct max/min in the non-replaced version, are not necessary.
4789 computedValues.m_extent = computeReplacedLogicalHeight() + borderAndPaddingLogicalHeight();
4790 const LayoutUnit availableSpace = containerLogicalHeight - computedValues.m_extent;
4791
4792 /*-----------------------------------------------------------------------*\
4793 * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top'
4794 * with the element's static position.
4795 \*-----------------------------------------------------------------------*/
4796 // see FIXME 1
4797 computeBlockStaticDistance(logicalTop, logicalBottom, this, containerBlock);
4798
4799 /*-----------------------------------------------------------------------*\
4800 * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or
4801 * 'margin-bottom' with '0'.
4802 \*-----------------------------------------------------------------------*/
4803 // FIXME: The spec. says that this step should only be taken when bottom is
4804 // auto, but if only top is auto, this makes step 4 impossible.
4805 if (logicalTop.isAuto() || logicalBottom.isAuto()) {
4806 if (marginBefore.isAuto())
4807 marginBefore.setValue(LengthType::Fixed, 0);
4808 if (marginAfter.isAuto())
4809 marginAfter.setValue(LengthType::Fixed, 0);
4810 }
4811
4812 /*-----------------------------------------------------------------------*\
4813 * 4. If at this point both 'margin-top' and 'margin-bottom' are still
4814 * 'auto', solve the equation under the extra constraint that the two
4815 * margins must get equal values.
4816 \*-----------------------------------------------------------------------*/
4817 LayoutUnit logicalTopValue;
4818 LayoutUnit logicalBottomValue;
4819
4820 if (marginBefore.isAuto() && marginAfter.isAuto()) {
4821 // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combined.
4822 ASSERT(!(logicalTop.isAuto() || logicalBottom.isAuto()));
4823
4824 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
4825 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
4826
4827 LayoutUnit difference = availableSpace - (logicalTopValue + logicalBottomValue);
4828 // NOTE: This may result in negative values.
4829 marginBeforeAlias = difference / 2; // split the difference
4830 marginAfterAlias = difference - marginBeforeAlias; // account for odd valued differences
4831
4832 /*-----------------------------------------------------------------------*\
4833 * 5. If at this point there is only one 'auto' left, solve the equation
4834 * for that value.
4835 \*-----------------------------------------------------------------------*/
4836 } else if (logicalTop.isAuto()) {
4837 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
4838 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
4839 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
4840
4841 // Solve for 'top'
4842 logicalTopValue = availableSpace - (logicalBottomValue + marginBeforeAlias + marginAfterAlias);
4843 } else if (logicalBottom.isAuto()) {
4844 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
4845 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
4846 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
4847
4848 // Solve for 'bottom'
4849 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
4850 // use the value.
4851 } else if (marginBefore.isAuto()) {
4852 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
4853 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
4854 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
4855
4856 // Solve for 'margin-top'
4857 marginBeforeAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginAfterAlias);
4858 } else if (marginAfter.isAuto()) {
4859 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
4860 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
4861 logicalBottomValue = valueForLength(logicalBottom, containerLogicalHeight);
4862
4863 // Solve for 'margin-bottom'
4864 marginAfterAlias = availableSpace - (logicalTopValue + logicalBottomValue + marginBeforeAlias);
4865 } else {
4866 // Nothing is 'auto', just calculate the values.
4867 marginBeforeAlias = valueForLength(marginBefore, containerRelativeLogicalWidth);
4868 marginAfterAlias = valueForLength(marginAfter, containerRelativeLogicalWidth);
4869 logicalTopValue = valueForLength(logicalTop, containerLogicalHeight);
4870 // NOTE: It is not necessary to solve for 'bottom' because we don't ever
4871 // use the value.
4872 }
4873
4874 /*-----------------------------------------------------------------------*\
4875 * 6. If at this point the values are over-constrained, ignore the value
4876 * for 'bottom' and solve for that value.
4877 \*-----------------------------------------------------------------------*/
4878 // NOTE: It is not necessary to do this step because we don't end up using
4879 // the value of 'bottom' regardless of whether the values are over-constrained
4880 // or not.
4881
4882 // Use computed values to calculate the vertical position.
4883 LayoutUnit logicalTopPos = logicalTopValue + marginBeforeAlias;
4884 // Border and padding have already been included in computedValues.m_extent.
4885 computeLogicalTopPositionedOffset(logicalTopPos, this, computedValues.m_extent, containerBlock, containerLogicalHeight);
4886 computedValues.m_position = logicalTopPos;
4887}
4888
4889VisiblePosition RenderBox::positionForPoint(const LayoutPoint& point, const RenderFragmentContainer* fragment)
4890{
4891 // no children...return this render object's element, if there is one, and offset 0
4892 if (!firstChild())
4893 return createVisiblePosition(nonPseudoElement() ? firstPositionInOrBeforeNode(nonPseudoElement()) : Position());
4894
4895 if (isTable() && nonPseudoElement()) {
4896 LayoutUnit right = contentWidth() + horizontalBorderAndPaddingExtent();
4897 LayoutUnit bottom = contentHeight() + verticalBorderAndPaddingExtent();
4898
4899 if (point.x() < 0 || point.x() > right || point.y() < 0 || point.y() > bottom) {
4900 if (point.x() <= right / 2)
4901 return createVisiblePosition(firstPositionInOrBeforeNode(nonPseudoElement()));
4902 return createVisiblePosition(lastPositionInOrAfterNode(nonPseudoElement()));
4903 }
4904 }
4905
4906 // Pass off to the closest child.
4907 LayoutUnit minDist = LayoutUnit::max();
4908 RenderBox* closestRenderer = nullptr;
4909 LayoutPoint adjustedPoint = point;
4910 if (isTableRow())
4911 adjustedPoint.moveBy(location());
4912
4913 for (auto& renderer : childrenOfType<RenderBox>(*this)) {
4914 if (is<RenderFragmentedFlow>(*this)) {
4915 ASSERT(fragment);
4916 if (!downcast<RenderFragmentedFlow>(*this).objectShouldFragmentInFlowFragment(&renderer, fragment))
4917 continue;
4918 }
4919
4920 if ((!renderer.firstChild() && !renderer.isInline() && !is<RenderBlockFlow>(renderer))
4921 || renderer.style().visibility() != Visibility::Visible)
4922 continue;
4923
4924 LayoutUnit top = renderer.borderTop() + renderer.paddingTop() + (is<RenderTableRow>(*this) ? 0_lu : renderer.y());
4925 LayoutUnit bottom = top + renderer.contentHeight();
4926 LayoutUnit left = renderer.borderLeft() + renderer.paddingLeft() + (is<RenderTableRow>(*this) ? 0_lu : renderer.x());
4927 LayoutUnit right = left + renderer.contentWidth();
4928
4929 if (point.x() <= right && point.x() >= left && point.y() <= top && point.y() >= bottom) {
4930 if (is<RenderTableRow>(renderer))
4931 return renderer.positionForPoint(point + adjustedPoint - renderer.locationOffset(), fragment);
4932 return renderer.positionForPoint(point - renderer.locationOffset(), fragment);
4933 }
4934
4935 // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces
4936 // and use a different compare depending on which piece (x, y) is in.
4937 LayoutPoint cmp;
4938 if (point.x() > right) {
4939 if (point.y() < top)
4940 cmp = LayoutPoint(right, top);
4941 else if (point.y() > bottom)
4942 cmp = LayoutPoint(right, bottom);
4943 else
4944 cmp = LayoutPoint(right, point.y());
4945 } else if (point.x() < left) {
4946 if (point.y() < top)
4947 cmp = LayoutPoint(left, top);
4948 else if (point.y() > bottom)
4949 cmp = LayoutPoint(left, bottom);
4950 else
4951 cmp = LayoutPoint(left, point.y());
4952 } else {
4953 if (point.y() < top)
4954 cmp = LayoutPoint(point.x(), top);
4955 else
4956 cmp = LayoutPoint(point.x(), bottom);
4957 }
4958
4959 LayoutSize difference = cmp - point;
4960
4961 LayoutUnit dist = difference.width() * difference.width() + difference.height() * difference.height();
4962 if (dist < minDist) {
4963 closestRenderer = &renderer;
4964 minDist = dist;
4965 }
4966 }
4967
4968 if (closestRenderer)
4969 return closestRenderer->positionForPoint(adjustedPoint - closestRenderer->locationOffset(), fragment);
4970
4971 return createVisiblePosition(firstPositionInOrBeforeNode(nonPseudoElement()));
4972}
4973
4974bool RenderBox::shrinkToAvoidFloats() const
4975{
4976 // Floating objects don't shrink. Objects that don't avoid floats don't shrink. Marquees don't shrink.
4977 if ((isInline() && !isHTMLMarquee()) || !avoidsFloats() || isFloating())
4978 return false;
4979
4980 // Only auto width objects can possibly shrink to avoid floats.
4981 return style().width().isAuto();
4982}
4983
4984bool RenderBox::establishesIndependentFormattingContext() const
4985{
4986 return isGridItem() || RenderElement::establishesIndependentFormattingContext();
4987}
4988
4989bool RenderBox::avoidsFloats() const
4990{
4991 return isReplacedOrInlineBlock() || isHR() || isLegend() || isFieldset() || createsNewFormattingContext();
4992}
4993
4994void RenderBox::addVisualEffectOverflow()
4995{
4996 bool hasBoxShadow = style().boxShadow();
4997 bool hasBorderImageOutsets = style().hasBorderImageOutsets();
4998 bool hasOutline = outlineStyleForRepaint().hasOutlineInVisualOverflow();
4999 if (!hasBoxShadow && !hasBorderImageOutsets && !hasOutline)
5000 return;
5001
5002 addVisualOverflow(applyVisualEffectOverflow(borderBoxRect()));
5003
5004 if (auto* fragmentedFlow = enclosingFragmentedFlow())
5005 fragmentedFlow->addFragmentsVisualEffectOverflow(this);
5006}
5007
5008LayoutRect RenderBox::applyVisualEffectOverflow(const LayoutRect& borderBox) const
5009{
5010 bool isFlipped = style().isFlippedBlocksWritingMode();
5011 bool isHorizontal = isHorizontalWritingMode();
5012
5013 LayoutUnit overflowMinX = borderBox.x();
5014 LayoutUnit overflowMaxX = borderBox.maxX();
5015 LayoutUnit overflowMinY = borderBox.y();
5016 LayoutUnit overflowMaxY = borderBox.maxY();
5017
5018 // Compute box-shadow overflow first.
5019 if (style().boxShadow()) {
5020 auto shadowExtent = style().boxShadowExtent();
5021
5022 // In flipped blocks writing modes such as vertical-rl, the physical right shadow value is actually at the lower x-coordinate.
5023 overflowMinX = borderBox.x() + ((!isFlipped || isHorizontal) ? shadowExtent.left() : -shadowExtent.right());
5024 overflowMaxX = borderBox.maxX() + ((!isFlipped || isHorizontal) ? shadowExtent.right() : -shadowExtent.left());
5025 overflowMinY = borderBox.y() + ((!isFlipped || !isHorizontal) ? shadowExtent.top() : -shadowExtent.bottom());
5026 overflowMaxY = borderBox.maxY() + ((!isFlipped || !isHorizontal) ? shadowExtent.bottom() : -shadowExtent.top());
5027 }
5028
5029 // Now compute border-image-outset overflow.
5030 if (style().hasBorderImageOutsets()) {
5031 auto borderOutsets = style().borderImageOutsets();
5032
5033 // In flipped blocks writing modes, the physical sides are inverted. For example in vertical-rl, the right
5034 // border is at the lower x coordinate value.
5035 overflowMinX = std::min(overflowMinX, borderBox.x() - ((!isFlipped || isHorizontal) ? borderOutsets.left() : borderOutsets.right()));
5036 overflowMaxX = std::max(overflowMaxX, borderBox.maxX() + ((!isFlipped || isHorizontal) ? borderOutsets.right() : borderOutsets.left()));
5037 overflowMinY = std::min(overflowMinY, borderBox.y() - ((!isFlipped || !isHorizontal) ? borderOutsets.top() : borderOutsets.bottom()));
5038 overflowMaxY = std::max(overflowMaxY, borderBox.maxY() + ((!isFlipped || !isHorizontal) ? borderOutsets.bottom() : borderOutsets.top()));
5039 }
5040
5041 if (outlineStyleForRepaint().hasOutlineInVisualOverflow()) {
5042 LayoutUnit outlineSize { outlineStyleForRepaint().outlineSize() };
5043 overflowMinX = std::min(overflowMinX, borderBox.x() - outlineSize);
5044 overflowMaxX = std::max(overflowMaxX, borderBox.maxX() + outlineSize);
5045 overflowMinY = std::min(overflowMinY, borderBox.y() - outlineSize);
5046 overflowMaxY = std::max(overflowMaxY, borderBox.maxY() + outlineSize);
5047 }
5048 // Add in the final overflow with shadows and outsets combined.
5049 return LayoutRect(overflowMinX, overflowMinY, overflowMaxX - overflowMinX, overflowMaxY - overflowMinY);
5050}
5051
5052void RenderBox::addOverflowFromChild(const RenderBox* child, const LayoutSize& delta)
5053{
5054 // Never allow flow threads to propagate overflow up to a parent.
5055 if (child->isRenderFragmentedFlow())
5056 return;
5057
5058 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
5059 if (fragmentedFlow)
5060 fragmentedFlow->addFragmentsOverflowFromChild(this, child, delta);
5061
5062 // Only propagate layout overflow from the child if the child isn't clipping its overflow. If it is, then
5063 // its overflow is internal to it, and we don't care about it. layoutOverflowRectForPropagation takes care of this
5064 // and just propagates the border box rect instead.
5065 LayoutRect childLayoutOverflowRect = child->layoutOverflowRectForPropagation(&style());
5066 childLayoutOverflowRect.move(delta);
5067 addLayoutOverflow(childLayoutOverflowRect);
5068
5069 if (paintContainmentApplies())
5070 return;
5071
5072 // Add in visual overflow from the child. Even if the child clips its overflow, it may still
5073 // have visual overflow of its own set from box shadows or reflections. It is unnecessary to propagate this
5074 // overflow if we are clipping our own overflow.
5075 if (child->hasSelfPaintingLayer() || hasPotentiallyScrollableOverflow())
5076 return;
5077 LayoutRect childVisualOverflowRect = child->visualOverflowRectForPropagation(&style());
5078 childVisualOverflowRect.move(delta);
5079 addVisualOverflow(childVisualOverflowRect);
5080}
5081
5082void RenderBox::addLayoutOverflow(const LayoutRect& rect)
5083{
5084 LayoutRect clientBox = flippedClientBoxRect();
5085 if (clientBox.contains(rect) || rect.isEmpty())
5086 return;
5087
5088 // For overflow clip objects, we don't want to propagate overflow into unreachable areas.
5089 LayoutRect overflowRect(rect);
5090 if (hasPotentiallyScrollableOverflow() || isRenderView()) {
5091 // Overflow is in the block's coordinate space and thus is flipped for horizontal-bt and vertical-rl
5092 // writing modes. At this stage that is actually a simplification, since we can treat horizontal-tb/bt as the same
5093 // and vertical-lr/rl as the same.
5094 bool hasTopOverflow = isTopLayoutOverflowAllowed();
5095 bool hasLeftOverflow = isLeftLayoutOverflowAllowed();
5096
5097 if (!hasTopOverflow)
5098 overflowRect.shiftYEdgeTo(std::max(overflowRect.y(), clientBox.y()));
5099 else
5100 overflowRect.shiftMaxYEdgeTo(std::min(overflowRect.maxY(), clientBox.maxY()));
5101 if (!hasLeftOverflow)
5102 overflowRect.shiftXEdgeTo(std::max(overflowRect.x(), clientBox.x()));
5103 else
5104 overflowRect.shiftMaxXEdgeTo(std::min(overflowRect.maxX(), clientBox.maxX()));
5105
5106 // Now re-test with the adjusted rectangle and see if it has become unreachable or fully
5107 // contained.
5108 if (clientBox.contains(overflowRect) || overflowRect.isEmpty())
5109 return;
5110 }
5111
5112 if (!m_overflow)
5113 m_overflow = adoptRef(new RenderOverflow(clientBox, borderBoxRect()));
5114
5115 m_overflow->addLayoutOverflow(overflowRect);
5116}
5117
5118void RenderBox::addVisualOverflow(const LayoutRect& rect)
5119{
5120 LayoutRect borderBox = borderBoxRect();
5121 if (borderBox.contains(rect) || rect.isEmpty())
5122 return;
5123
5124 if (!m_overflow)
5125 m_overflow = adoptRef(new RenderOverflow(flippedClientBoxRect(), borderBox));
5126
5127 m_overflow->addVisualOverflow(rect);
5128}
5129
5130void RenderBox::clearOverflow()
5131{
5132 m_overflow = nullptr;
5133 RenderFragmentedFlow* fragmentedFlow = enclosingFragmentedFlow();
5134 if (fragmentedFlow)
5135 fragmentedFlow->clearFragmentsOverflow(this);
5136}
5137
5138bool RenderBox::percentageLogicalHeightIsResolvable() const
5139{
5140 // Do this to avoid duplicating all the logic that already exists when computing
5141 // an actual percentage height.
5142 Length fakeLength(100, LengthType::Percent);
5143 return computePercentageLogicalHeight(fakeLength) != std::nullopt;
5144}
5145
5146bool RenderBox::hasUnsplittableScrollingOverflow() const
5147{
5148 // We will paginate as long as we don't scroll overflow in the pagination direction.
5149 bool isHorizontal = isHorizontalWritingMode();
5150 if ((isHorizontal && !scrollsOverflowY()) || (!isHorizontal && !scrollsOverflowX()))
5151 return false;
5152
5153 // We do have overflow. We'll still be willing to paginate as long as the block
5154 // has auto logical height, auto or undefined max-logical-height and a zero or auto min-logical-height.
5155 // Note this is just a heuristic, and it's still possible to have overflow under these
5156 // conditions, but it should work out to be good enough for common cases. Paginating overflow
5157 // with scrollbars present is not the end of the world and is what we used to do in the old model anyway.
5158 return !style().logicalHeight().isIntrinsicOrAuto()
5159 || (!style().logicalMaxHeight().isIntrinsicOrAuto() && !style().logicalMaxHeight().isUndefined() && (!style().logicalMaxHeight().isPercentOrCalculated() || percentageLogicalHeightIsResolvable()))
5160 || (!style().logicalMinHeight().isIntrinsicOrAuto() && style().logicalMinHeight().isPositive() && (!style().logicalMinHeight().isPercentOrCalculated() || percentageLogicalHeightIsResolvable()));
5161}
5162
5163bool RenderBox::isUnsplittableForPagination() const
5164{
5165 return isReplacedOrInlineBlock()
5166 || hasUnsplittableScrollingOverflow()
5167 || (parent() && isWritingModeRoot())
5168 || (isFloating() && style().styleType() == PseudoId::FirstLetter && style().initialLetterDrop() > 0)
5169 || shouldApplySizeContainment();
5170}
5171
5172LayoutUnit RenderBox::lineHeight(bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const
5173{
5174 if (isReplacedOrInlineBlock())
5175 return direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left();
5176 return 0;
5177}
5178
5179LayoutUnit RenderBox::baselinePosition(FontBaseline baselineType, bool /*firstLine*/, LineDirectionMode direction, LinePositionMode /*linePositionMode*/) const
5180{
5181 if (isReplacedOrInlineBlock()) {
5182 auto result = roundToInt(direction == HorizontalLine ? m_marginBox.top() + height() + m_marginBox.bottom() : m_marginBox.right() + width() + m_marginBox.left());
5183 if (baselineType == AlphabeticBaseline)
5184 return result;
5185 return result - result / 2;
5186 }
5187 return 0;
5188}
5189
5190RenderLayer* RenderBox::enclosingFloatPaintingLayer() const
5191{
5192 for (auto& box : lineageOfType<RenderBox>(*this)) {
5193 if (box.layer() && box.layer()->isSelfPaintingLayer())
5194 return box.layer();
5195 }
5196 return nullptr;
5197}
5198
5199LayoutRect RenderBox::logicalVisualOverflowRectForPropagation(const RenderStyle* parentStyle) const
5200{
5201 LayoutRect rect = visualOverflowRectForPropagation(parentStyle);
5202 if (!parentStyle->isHorizontalWritingMode())
5203 return rect.transposedRect();
5204 return rect;
5205}
5206
5207LayoutRect RenderBox::visualOverflowRectForPropagation(const RenderStyle* parentStyle) const
5208{
5209 // If the writing modes of the child and parent match, then we don't have to
5210 // do anything fancy. Just return the result.
5211 LayoutRect rect = visualOverflowRect();
5212 if (parentStyle->writingMode() == style().writingMode())
5213 return rect;
5214
5215 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
5216 // in a particular axis, then we have to flip the rect along that axis.
5217 if (style().writingMode() == WritingMode::RightToLeft || parentStyle->writingMode() == WritingMode::RightToLeft)
5218 rect.setX(width() - rect.maxX());
5219 else if (style().writingMode() == WritingMode::BottomToTop || parentStyle->writingMode() == WritingMode::BottomToTop)
5220 rect.setY(height() - rect.maxY());
5221
5222 return rect;
5223}
5224
5225LayoutRect RenderBox::logicalLayoutOverflowRectForPropagation(const RenderStyle* parentStyle) const
5226{
5227 LayoutRect rect = layoutOverflowRectForPropagation(parentStyle);
5228 if (!parentStyle->isHorizontalWritingMode())
5229 return rect.transposedRect();
5230 return rect;
5231}
5232
5233LayoutRect RenderBox::layoutOverflowRectForPropagation(const RenderStyle* parentStyle) const
5234{
5235 // Only propagate interior layout overflow if we don't completely clip it.
5236 auto rect = borderBoxRect();
5237 if (isGridItem()) {
5238 // As per https://github.com/w3c/csswg-drafts/issues/3653, child's margins should contribute to the scrollable overflow area.
5239 // FIXME: Expand it to non-grid cases when applicable.
5240 rect.setWidth(rect.width() + std::max(0_lu, marginEnd()));
5241 }
5242 if (!shouldApplyLayoutContainment()) {
5243 if (style().overflowX() == Overflow::Clip && style().overflowY() == Overflow::Visible) {
5244 LayoutRect clippedOverflowRect = layoutOverflowRect();
5245 clippedOverflowRect.setX(rect.x());
5246 clippedOverflowRect.setWidth(rect.width());
5247 rect.unite(clippedOverflowRect);
5248 } else if (style().overflowY() == Overflow::Clip && style().overflowX() == Overflow::Visible) {
5249 LayoutRect clippedOverflowRect = layoutOverflowRect();
5250 clippedOverflowRect.setY(rect.y());
5251 clippedOverflowRect.setHeight(rect.height());
5252 rect.unite(clippedOverflowRect);
5253 } else if (!hasNonVisibleOverflow())
5254 rect.unite(layoutOverflowRect());
5255 }
5256
5257 bool hasTransform = this->hasTransform();
5258 // While a stickily positioned renderer is also inflow positioned, they stretch the overflow rect with their inflow geometry
5259 // (as opposed to the paint geometry) because they are not stationary.
5260 bool paintGeometryAffectsLayoutOverflow = hasTransform || (isInFlowPositioned() && !isStickilyPositioned());
5261 if (paintGeometryAffectsLayoutOverflow) {
5262 // If we are relatively positioned or if we have a transform, then we have to convert
5263 // this rectangle into physical coordinates, apply relative positioning and transforms
5264 // to it, and then convert it back.
5265 // It ensures that the overflow rect tracks the paint geometry and not the inflow layout position.
5266 flipForWritingMode(rect);
5267
5268 if (hasTransform && hasLayer())
5269 rect = layer()->currentTransform().mapRect(rect);
5270
5271 if (isInFlowPositioned())
5272 rect.move(offsetForInFlowPosition());
5273
5274 // Now we need to flip back.
5275 flipForWritingMode(rect);
5276 }
5277
5278 // If the writing modes of the child and parent match, then we don't have to
5279 // do anything fancy. Just return the result.
5280 if (parentStyle->writingMode() == style().writingMode())
5281 return rect;
5282
5283 // We are putting ourselves into our parent's coordinate space. If there is a flipped block mismatch
5284 // in a particular axis, then we have to flip the rect along that axis.
5285 if (style().writingMode() == WritingMode::RightToLeft || parentStyle->writingMode() == WritingMode::RightToLeft)
5286 rect.setX(width() - rect.maxX());
5287 else if (style().writingMode() == WritingMode::BottomToTop || parentStyle->writingMode() == WritingMode::BottomToTop)
5288 rect.setY(height() - rect.maxY());
5289
5290 return rect;
5291}
5292
5293LayoutRect RenderBox::flippedClientBoxRect() const
5294{
5295 // Because of the special coordinate system used for overflow rectangles (not quite logical, not
5296 // quite physical), we need to flip the block progression coordinate in vertical-rl and
5297 // horizontal-bt writing modes. Apart from that, this method does the same as clientBoxRect().
5298
5299 LayoutUnit left = borderLeft();
5300 LayoutUnit top = borderTop();
5301 LayoutUnit right = borderRight();
5302 LayoutUnit bottom = borderBottom();
5303 // Calculate physical padding box.
5304 LayoutRect rect(left, top, width() - left - right, height() - top - bottom);
5305 // Flip block progression axis if writing mode is vertical-rl or horizontal-bt.
5306 flipForWritingMode(rect);
5307 // Subtract space occupied by scrollbars. They are at their physical edge in this coordinate
5308 // system, so order is important here: first flip, then subtract scrollbars.
5309 if (shouldPlaceVerticalScrollbarOnLeft() && isHorizontalWritingMode())
5310 rect.move(verticalScrollbarWidth(), 0);
5311 rect.contract(verticalScrollbarWidth(), horizontalScrollbarHeight());
5312 return rect;
5313}
5314
5315LayoutUnit RenderBox::offsetLeft() const
5316{
5317 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).x();
5318}
5319
5320LayoutUnit RenderBox::offsetTop() const
5321{
5322 return adjustedPositionRelativeToOffsetParent(topLeftLocation()).y();
5323}
5324
5325LayoutPoint RenderBox::flipForWritingModeForChild(const RenderBox& child, const LayoutPoint& point) const
5326{
5327 if (!style().isFlippedBlocksWritingMode())
5328 return point;
5329
5330 // The child is going to add in its x() and y(), so we have to make sure it ends up in
5331 // the right place.
5332 if (isHorizontalWritingMode())
5333 return LayoutPoint(point.x(), point.y() + height() - child.height() - (2 * child.y()));
5334 return LayoutPoint(point.x() + width() - child.width() - (2 * child.x()), point.y());
5335}
5336
5337void RenderBox::flipForWritingMode(LayoutRect& rect) const
5338{
5339 if (!style().isFlippedBlocksWritingMode())
5340 return;
5341
5342 if (isHorizontalWritingMode())
5343 rect.setY(height() - rect.maxY());
5344 else
5345 rect.setX(width() - rect.maxX());
5346}
5347
5348LayoutUnit RenderBox::flipForWritingMode(LayoutUnit position) const
5349{
5350 if (!style().isFlippedBlocksWritingMode())
5351 return position;
5352 return logicalHeight() - position;
5353}
5354
5355LayoutPoint RenderBox::flipForWritingMode(const LayoutPoint& position) const
5356{
5357 if (!style().isFlippedBlocksWritingMode())
5358 return position;
5359 return isHorizontalWritingMode() ? LayoutPoint(position.x(), height() - position.y()) : LayoutPoint(width() - position.x(), position.y());
5360}
5361
5362LayoutSize RenderBox::flipForWritingMode(const LayoutSize& offset) const
5363{
5364 if (!style().isFlippedBlocksWritingMode())
5365 return offset;
5366 return isHorizontalWritingMode() ? LayoutSize(offset.width(), height() - offset.height()) : LayoutSize(width() - offset.width(), offset.height());
5367}
5368
5369FloatPoint RenderBox::flipForWritingMode(const FloatPoint& position) const
5370{
5371 if (!style().isFlippedBlocksWritingMode())
5372 return position;
5373 return isHorizontalWritingMode() ? FloatPoint(position.x(), height() - position.y()) : FloatPoint(width() - position.x(), position.y());
5374}
5375
5376void RenderBox::flipForWritingMode(FloatRect& rect) const
5377{
5378 if (!style().isFlippedBlocksWritingMode())
5379 return;
5380
5381 if (isHorizontalWritingMode())
5382 rect.setY(height() - rect.maxY());
5383 else
5384 rect.setX(width() - rect.maxX());
5385}
5386
5387LayoutPoint RenderBox::topLeftLocation() const
5388{
5389 if (!view().frameView().hasFlippedBlockRenderers())
5390 return location();
5391
5392 RenderBlock* containerBlock = containingBlock();
5393 if (!containerBlock || containerBlock == this)
5394 return location();
5395 return containerBlock->flipForWritingModeForChild(*this, location());
5396}
5397
5398LayoutSize RenderBox::topLeftLocationOffset() const
5399{
5400 if (!view().frameView().hasFlippedBlockRenderers())
5401 return locationOffset();
5402
5403 RenderBlock* containerBlock = containingBlock();
5404 if (!containerBlock || containerBlock == this)
5405 return locationOffset();
5406
5407 LayoutRect rect(frameRect());
5408 containerBlock->flipForWritingMode(rect); // FIXME: This is wrong if we are an absolutely positioned object enclosed by a relative-positioned inline.
5409 return LayoutSize(rect.x(), rect.y());
5410}
5411
5412void RenderBox::applyTopLeftLocationOffsetWithFlipping(LayoutPoint& point) const
5413{
5414 RenderBlock* containerBlock = containingBlock();
5415 if (!containerBlock || containerBlock == this) {
5416 point.move(m_frameRect.x(), m_frameRect.y());
5417 return;
5418 }
5419
5420 LayoutRect rect(frameRect());
5421 containerBlock->flipForWritingMode(rect); // FIXME: This is wrong if we are an absolutely positioned object enclosed by a relative-positioned inline.
5422 point.move(rect.x(), rect.y());
5423}
5424
5425bool RenderBox::shouldIgnoreAspectRatio() const
5426{
5427 return !style().hasAspectRatio() || isTablePart();
5428}
5429
5430static inline bool shouldComputeLogicalWidthFromAspectRatioAndInsets(const RenderBox& renderer)
5431{
5432 if (!renderer.isOutOfFlowPositioned())
5433 return false;
5434
5435 auto& style = renderer.style();
5436 if (!style.logicalWidth().isAuto()) {
5437 // Not applicable for aspect ratio computation.
5438 return false;
5439 }
5440 // When both left and right are set, the out-of-flow positioned box is horizontally constrained and aspect ratio for the logical width is not applicable.
5441 auto hasConstrainedWidth = (!style.logicalLeft().isAuto() && !style.logicalRight().isAuto()) || renderer.intrinsicLogicalWidth();
5442 if (hasConstrainedWidth)
5443 return false;
5444
5445 // When both top and bottom are set, the out-of-flow positioned box is vertically constrained and it can be used as if it had a non-auto height value.
5446 auto hasConstrainedHeight = !style.logicalTop().isAuto() && !style.logicalBottom().isAuto();
5447 if (!hasConstrainedHeight)
5448 return false;
5449 // FIXME: This could probably be omitted and let the callers handle the height check (as they seem to be doing anyway).
5450 return style.logicalHeight().isAuto();
5451}
5452
5453bool RenderBox::shouldComputeLogicalHeightFromAspectRatio() const
5454{
5455 if (shouldIgnoreAspectRatio())
5456 return false;
5457
5458 if (shouldComputeLogicalWidthFromAspectRatioAndInsets(*this))
5459 return false;
5460
5461 auto h = style().logicalHeight();
5462 return h.isAuto() || h.isIntrinsic() || (!isOutOfFlowPositioned() && h.isPercentOrCalculated() && !percentageLogicalHeightIsResolvable());
5463}
5464
5465bool RenderBox::shouldComputeLogicalWidthFromAspectRatio() const
5466{
5467 if (shouldIgnoreAspectRatio())
5468 return false;
5469
5470 if (isGridItem()) {
5471 if (shouldComputeSizeAsReplaced()) {
5472 if (hasStretchedLogicalWidth() && hasStretchedLogicalHeight())
5473 return false;
5474 } else if (hasStretchedLogicalWidth(StretchingMode::Explicit))
5475 return false;
5476 }
5477
5478 auto isResolvablePercentageHeight = [&] {
5479 return style().logicalHeight().isPercentOrCalculated() && (isOutOfFlowPositioned() || percentageLogicalHeightIsResolvable());
5480 };
5481 return hasOverridingLogicalHeight() || shouldComputeLogicalWidthFromAspectRatioAndInsets(*this) || style().logicalHeight().isFixed() || isResolvablePercentageHeight();
5482}
5483
5484LayoutUnit RenderBox::computeLogicalWidthFromAspectRatioInternal() const
5485{
5486 ASSERT(shouldComputeLogicalWidthFromAspectRatio());
5487 auto computedValues = computeLogicalHeight(logicalHeight(), logicalTop());
5488 LayoutUnit logicalHeightforAspectRatio = computedValues.m_extent;
5489
5490 return inlineSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), logicalHeightforAspectRatio);
5491}
5492
5493LayoutUnit RenderBox::computeLogicalWidthFromAspectRatio(RenderFragmentContainer* fragment) const
5494{
5495 auto logicalWidth = computeLogicalWidthFromAspectRatioInternal();
5496 LayoutUnit containerWidthInInlineDirection = std::max<LayoutUnit>(0, containingBlockLogicalWidthForContentInFragment(fragment));
5497 return constrainLogicalWidthInFragmentByMinMax(logicalWidth, containerWidthInInlineDirection, *containingBlock(), fragment, AllowIntrinsic::No);
5498}
5499
5500std::pair<LayoutUnit, LayoutUnit> RenderBox::computeMinMaxLogicalWidthFromAspectRatio() const
5501{
5502 LayoutUnit blockMinSize = constrainLogicalHeightByMinMax(LayoutUnit(), std::nullopt);
5503 LayoutUnit blockMaxSize = constrainLogicalHeightByMinMax(LayoutUnit::max(), std::nullopt);
5504 LayoutUnit transferredMinSize = LayoutUnit();
5505 LayoutUnit transferredMaxSize = LayoutUnit::max();
5506 if (blockMinSize > LayoutUnit())
5507 transferredMinSize = inlineSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), blockMinSize);
5508 if (blockMaxSize != LayoutUnit::max())
5509 transferredMaxSize = inlineSizeFromAspectRatio(horizontalBorderAndPaddingExtent(), verticalBorderAndPaddingExtent(), style().logicalAspectRatio(), style().boxSizingForAspectRatio(), blockMaxSize);
5510 // Minimum size wins over maximum size.
5511 transferredMaxSize = std::max(transferredMaxSize, transferredMinSize);
5512 return { transferredMinSize, transferredMaxSize };
5513}
5514
5515bool RenderBox::hasRelativeDimensions() const
5516{
5517 return style().height().isPercentOrCalculated() || style().width().isPercentOrCalculated()
5518 || style().maxHeight().isPercentOrCalculated() || style().maxWidth().isPercentOrCalculated()
5519 || style().minHeight().isPercentOrCalculated() || style().minWidth().isPercentOrCalculated();
5520}
5521
5522bool RenderBox::hasRelativeLogicalHeight() const
5523{
5524 return style().logicalHeight().isPercentOrCalculated()
5525 || style().logicalMinHeight().isPercentOrCalculated()
5526 || style().logicalMaxHeight().isPercentOrCalculated();
5527}
5528
5529bool RenderBox::hasRelativeLogicalWidth() const
5530{
5531 return style().logicalWidth().isPercentOrCalculated()
5532 || style().logicalMinWidth().isPercentOrCalculated()
5533 || style().logicalMaxWidth().isPercentOrCalculated();
5534}
5535
5536LayoutUnit RenderBox::offsetFromLogicalTopOfFirstPage() const
5537{
5538 auto* layoutState = view().frameView().layoutContext().layoutState();
5539 if ((layoutState && !layoutState->isPaginated()) || (!layoutState && !enclosingFragmentedFlow()))
5540 return 0;
5541
5542 RenderBlock* containerBlock = containingBlock();
5543 return containerBlock->offsetFromLogicalTopOfFirstPage() + logicalTop();
5544}
5545
5546LayoutBoxExtent RenderBox::scrollPaddingForViewportRect(const LayoutRect& viewportRect)
5547{
5548 // We are using minimumValueForLength here, because scroll-padding values might be "auto". WebKit currently
5549 // interprets "auto" as 0. See: https://drafts.csswg.org/css-scroll-snap-1/#propdef-scroll-padding
5550 const auto& padding = style().scrollPadding();
5551 return LayoutBoxExtent(
5552 minimumValueForLength(padding.top(), viewportRect.height()), minimumValueForLength(padding.right(), viewportRect.width()),
5553 minimumValueForLength(padding.bottom(), viewportRect.height()), minimumValueForLength(padding.left(), viewportRect.width()));
5554}
5555
5556LayoutUnit synthesizedBaselineFromBorderBox(const RenderBox& box, LineDirectionMode direction)
5557{
5558 return direction == HorizontalLine ? box.height() : box.width();
5559}
5560
5561} // namespace WebCore
Note: See TracBrowser for help on using the repository browser.